2010-04-07 03:05:51 +00:00
#!/usr/bin/python
#
# Custom twitter client... mostly for learning Python
2010-04-17 02:44:22 +00:00
import sys , ConfigParser , os , re , optparse , shelve
2010-04-13 21:32:04 +00:00
import gtk , gtk . glade , gobject
2010-05-04 18:32:43 +00:00
from urllib2 import HTTPError , URLError
2010-04-14 15:03:20 +00:00
from twitterwidgets import TweetPane
2010-05-19 20:02:32 +00:00
from threading import enumerate
2010-04-22 18:57:27 +00:00
import apithreads
2010-04-07 03:05:51 +00:00
2010-05-20 19:33:05 +00:00
class Hrafn ( ) :
2010-04-07 03:05:51 +00:00
""" Display Tweets, post to twitter """
2010-05-18 03:58:45 +00:00
def __init__ ( self , config_file , resize ) :
2010-05-19 20:02:32 +00:00
global debug
2010-05-18 03:58:45 +00:00
self . resize = resize
2010-04-07 17:17:21 +00:00
config = ConfigParser . ConfigParser ( )
2010-04-14 21:58:52 +00:00
config . read ( os . path . expanduser ( config_file ) )
2010-04-20 18:04:13 +00:00
2010-04-21 18:30:36 +00:00
# Set config options to defaults, if they are not present
new_data = False
if not config . has_section ( ' global ' ) :
config . add_section ( ' global ' )
new_data = True
if not config . has_option ( ' global ' , ' entries ' ) :
config . set ( ' global ' , ' entries ' , ' 20 ' )
new_data = True
if not config . has_option ( ' global ' , ' refreshtime ' ) :
2010-05-20 03:02:25 +00:00
config . set ( ' global ' , ' refreshtime ' , ' 5 ' )
2010-04-21 18:30:36 +00:00
new_data = True
if not config . has_option ( ' global ' , ' dbfile ' ) :
2010-05-20 19:33:05 +00:00
config . set ( ' global ' , ' dbfile ' , ' ~/.hrafn.db ' )
2010-04-21 18:30:36 +00:00
new_data = True
# Write out new config data, if needed
if new_data :
config_filehandle = open ( os . path . expanduser ( config_file ) , ' wb ' )
config . write ( config_filehandle )
config_filehandle . close ( )
2010-05-19 17:58:11 +00:00
if config . has_option ( ' global ' , ' debug ' ) and config . get ( ' global ' , ' debug ' ) == ' 1 ' :
debug = True
2010-05-12 02:09:54 +00:00
# Init the glade stuff here, so we don't have a race condition with
# the lists-ready signal
self . init_user_interface ( ' ./default.glade ' )
2010-05-20 21:10:12 +00:00
self . first_account_item = None
2010-05-12 02:09:54 +00:00
2010-05-19 18:43:54 +00:00
# And init the DB stuff here
db_file = os . path . expanduser ( config . get ( ' global ' , ' dbfile ' ) )
self . db = shelve . open ( db_file )
2010-05-20 20:51:51 +00:00
if not self . db . has_key ( ' tokens ' ) :
2010-05-21 00:51:20 +00:00
self . db [ ' tokens ' ] = [ ]
2010-05-20 20:51:51 +00:00
2010-05-19 18:43:54 +00:00
if not self . db . has_key ( ' active_page ' ) :
self . db [ ' active_page ' ] = 0
self . num_entries = int ( config . get ( ' global ' , ' entries ' ) )
self . refresh_time = int ( config . get ( ' global ' , ' refreshtime ' ) )
2010-05-21 15:10:58 +00:00
if not self . db . has_key ( ' active_user ' ) :
self . db [ ' active_user ' ] = None
2010-05-19 18:43:54 +00:00
# Now set up the accounts and their corresponding APIs
2010-04-20 19:10:47 +00:00
self . accounts = { }
2010-05-20 20:51:51 +00:00
for token in self . db [ ' tokens ' ] :
2010-05-21 01:23:54 +00:00
api = apithreads . CustomApi ( token )
self . add_account ( api )
2010-05-20 20:51:51 +00:00
2010-05-19 18:43:54 +00:00
self . username = self . db [ ' active_user ' ]
2010-05-20 21:10:12 +00:00
try :
self . api = self . accounts [ self . username ]
except KeyError :
self . api = None
2010-04-19 14:53:28 +00:00
if not self . db . has_key ( ' open_tabs ' ) :
2010-05-20 21:10:12 +00:00
self . db [ ' open_tabs ' ] = [ ]
2010-04-19 14:53:28 +00:00
2010-05-20 03:02:25 +00:00
# refresh_time is in minutes... convert to seconds here
self . refresh_time * = 60
2010-04-07 03:05:51 +00:00
2010-04-09 04:56:51 +00:00
self . reply_id = None
2010-04-07 03:05:51 +00:00
2010-05-12 02:09:54 +00:00
# Load up all the programmatic GUI stuff
2010-04-08 20:39:56 +00:00
self . init_widgets ( )
2010-04-08 19:12:57 +00:00
2010-04-08 20:39:56 +00:00
def init_user_interface ( self , path_to_skin ) :
self . widget_tree = gtk . glade . XML ( path_to_skin , " window " )
self . widget_tree . signal_autoconnect ( self )
2010-04-08 19:12:57 +00:00
2010-04-12 18:11:27 +00:00
# Get widgets from glade
2010-05-18 03:58:45 +00:00
self . window = self . widget_tree . get_widget ( ' window ' )
2010-04-12 18:11:27 +00:00
self . tweet_notebook = self . widget_tree . get_widget ( ' tweet_notebook ' )
self . view_menu = self . widget_tree . get_widget ( ' view_menu ' )
2010-05-19 18:36:03 +00:00
self . accounts_menu = self . widget_tree . get_widget ( ' accounts_menu ' )
2010-04-08 20:39:56 +00:00
self . update_entry = self . widget_tree . get_widget ( ' update_entry ' )
self . update_count = self . widget_tree . get_widget ( ' update_count ' )
2010-04-11 05:37:09 +00:00
self . status_bar = self . widget_tree . get_widget ( ' status_bar ' )
2010-04-12 21:58:00 +00:00
self . search_entry = self . widget_tree . get_widget ( ' search_entry ' )
2010-04-14 18:24:40 +00:00
self . following_button = self . widget_tree . get_widget ( ' following_button ' )
2010-04-16 03:00:35 +00:00
self . at_button = self . widget_tree . get_widget ( ' at_button ' )
2010-04-16 21:32:17 +00:00
self . verified_label = self . widget_tree . get_widget ( ' verified_label ' )
2010-05-19 18:36:03 +00:00
self . account_label = self . widget_tree . get_widget ( ' account_label ' )
2010-05-19 20:02:32 +00:00
self . help_menu = self . widget_tree . get_widget ( ' help_menu ' )
2010-04-12 21:58:00 +00:00
2010-05-12 02:09:54 +00:00
def init_widgets ( self ) :
2010-05-18 03:58:45 +00:00
# Set the main window size
if self . resize and self . db . has_key ( ' width ' ) and self . db . has_key ( ' height ' ) :
self . window . resize ( self . db [ ' width ' ] , self . db [ ' height ' ] )
2010-04-11 05:44:44 +00:00
self . context_id = self . status_bar . get_context_id ( ' message ' )
2010-04-07 17:21:55 +00:00
2010-05-19 20:02:32 +00:00
# Add debug options to help menu
if debug :
menu_item = gtk . MenuItem ( ' debug: Show Threads ' )
menu_item . connect ( ' activate ' , self . debug_show_threads )
self . help_menu . append ( menu_item )
menu_item . show ( )
2010-05-19 18:36:03 +00:00
# Set the account label
self . update_account_label ( )
2010-04-21 14:01:59 +00:00
# Manual tweaks to the glade UI, to overcome its limitations
self . tweet_notebook . remove_page ( 0 )
2010-04-20 18:04:13 +00:00
2010-04-19 14:53:28 +00:00
# Add the tabs from last session to the notebook
2010-04-21 15:56:17 +00:00
page_num = self . db [ ' active_page ' ]
2010-05-18 18:23:17 +00:00
for tab , single_tweet , conversation in self . db [ ' open_tabs ' ] :
self . add_to_notebook ( tab , single_tweet , conversation )
2010-04-21 15:56:17 +00:00
self . tweet_notebook . set_current_page ( page_num )
2010-04-08 20:39:56 +00:00
# Timer to update periodically
2010-04-12 18:50:04 +00:00
gobject . timeout_add ( self . refresh_time * 1000 , self . update_windows )
2010-04-08 19:12:57 +00:00
2010-05-19 18:36:03 +00:00
def update_account_label ( self ) :
2010-05-21 15:10:58 +00:00
if self . username is not None :
2010-05-20 21:11:50 +00:00
self . account_label . set_text ( self . username + ' : ' )
2010-05-19 18:36:03 +00:00
2010-04-22 18:57:27 +00:00
# Spawns a thread for each pane, which updates that pane.
2010-04-12 18:50:04 +00:00
def update_windows ( self ) :
2010-04-12 19:19:09 +00:00
for i in range ( 0 , self . tweet_notebook . get_n_pages ( ) ) :
pane = self . tweet_notebook . get_nth_page ( i )
2010-05-06 17:18:02 +00:00
self . update_single_window ( pane )
2010-04-08 21:07:27 +00:00
2010-05-06 17:18:02 +00:00
# We have to return true, so the timeout_add event will keep happening
return True
def update_single_window ( self , pane ) :
2010-05-21 16:34:56 +00:00
print ' debug: updating ' + pane . get_list_name ( )
2010-05-06 17:18:02 +00:00
list_name = pane . get_list_name ( )
2010-04-20 19:10:47 +00:00
2010-05-06 17:18:02 +00:00
# Single tweets should never be updated here
if pane . get_single_tweet ( ) is not None :
return
2010-04-12 20:53:03 +00:00
2010-05-06 17:18:02 +00:00
# Determine username and appropriate account to use
2010-05-21 16:34:56 +00:00
account = None
2010-05-06 17:18:02 +00:00
username = re . sub ( ' /Home ' , ' ' , list_name )
if self . accounts . has_key ( username ) :
account = self . accounts [ username ]
2010-05-21 16:34:56 +00:00
if account is None :
2010-05-06 17:18:02 +00:00
username = re . sub ( ' @ ' , ' ' , list_name )
2010-04-22 18:57:27 +00:00
if self . accounts . has_key ( username ) :
account = self . accounts [ username ]
2010-05-21 16:34:56 +00:00
if account is None :
2010-05-06 17:18:02 +00:00
username = re . sub ( ' /Direct Messages ' , ' ' , list_name )
if self . accounts . has_key ( username ) :
account = self . accounts [ username ]
2010-04-07 18:17:57 +00:00
2010-05-21 16:34:56 +00:00
if account is None :
username = re . sub ( r ' list: (.*)/.* ' , r ' \ 1 ' , list_name )
if self . accounts . has_key ( username ) :
account = self . accounts [ username ]
if account is None :
2010-05-06 17:18:02 +00:00
account = self . api
username = self . username
apithreads . GetTweets ( api = account ,
list_name = list_name ,
pane = pane ,
num_entries = self . num_entries ,
username = username
) . start ( )
def update_window_callback ( self , widget ) :
2010-05-19 17:58:11 +00:00
pane = self . get_current_pane ( )
2010-05-06 17:18:02 +00:00
self . update_single_window ( pane )
2010-04-08 21:07:27 +00:00
2010-04-07 18:17:57 +00:00
def update_status ( self ) :
2010-05-11 22:15:18 +00:00
reply_id = self . reply_id
2010-04-08 20:39:56 +00:00
text = self . update_entry . get_text ( )
2010-05-11 22:15:18 +00:00
2010-05-18 21:41:32 +00:00
thread = apithreads . PostUpdate ( self . api , text , reply_id )
thread . sig_proxy . connect ( ' update-posted ' , self . on_update_posted )
self . update_entry . set_sensitive ( False )
self . update_status_bar ( ' Posting... ' )
thread . start ( )
2010-05-04 18:32:43 +00:00
2010-04-07 18:17:57 +00:00
2010-04-08 20:39:56 +00:00
def update_status_callback ( self , widget ) :
2010-04-07 18:17:57 +00:00
self . update_status ( )
2010-04-07 18:07:26 +00:00
2010-04-07 03:05:51 +00:00
2010-04-09 21:45:21 +00:00
def text_watcher ( self , widget ) :
''' Watch text entered on the update_entry, update things '''
text_len = self . update_entry . get_text_length ( )
new_count = str ( text_len ) + " /140 "
2010-04-08 20:39:56 +00:00
self . update_count . set_label ( new_count )
2010-04-09 21:45:21 +00:00
# If reply_id is set, unset it if we have removed the @ symbol
2010-05-11 22:15:18 +00:00
if self . reply_id and not re . match ( ' @ ' , self . update_entry . get_text ( ) ) :
2010-04-09 21:45:21 +00:00
self . reply_id = None
2010-04-08 20:39:56 +00:00
def gtk_main_quit ( self , widget ) :
2010-04-17 02:44:22 +00:00
self . db . close ( )
2010-04-08 20:39:56 +00:00
gtk . main_quit ( )
def on_about ( self , widget ) :
2010-04-09 21:45:21 +00:00
print " STUB: help->about not yet implemented "
2010-04-08 19:12:57 +00:00
2010-04-07 19:54:54 +00:00
2010-04-14 14:58:16 +00:00
def on_reply ( self , widget , data ) :
self . update_entry . set_text ( ' @ ' + data [ ' screen_name ' ] + ' ' )
self . reply_id = data [ ' id ' ]
2010-04-09 21:45:21 +00:00
self . update_entry . grab_focus ( )
2010-04-09 04:56:51 +00:00
2010-04-14 14:58:16 +00:00
def on_retweet ( self , widget , data ) :
2010-05-19 04:04:01 +00:00
thread = apithreads . PostRetweet ( self . api , data [ ' id ' ] )
thread . sig_proxy . connect ( ' retweet-posted ' , self . on_retweet_posted )
self . update_entry . set_sensitive ( False )
self . update_status_bar ( ' Posting retweet... ' )
thread . start ( )
2010-04-09 04:56:51 +00:00
2010-04-12 01:14:24 +00:00
2010-04-13 21:32:04 +00:00
def on_reply_to ( self , widget , data ) :
self . add_to_notebook ( data [ ' name ' ] , data [ ' id ' ] )
2010-05-18 16:26:31 +00:00
def on_conversation ( self , widget , data ) :
self . add_to_notebook ( data [ ' name ' ] , data [ ' id ' ] , True )
2010-04-20 19:10:47 +00:00
def on_view_selected ( self , event , username , name ) :
if name == ' Home ' or name == ' Direct Messages ' :
full_name = username + ' / ' + name
elif name == ' @ ' + username :
2010-04-16 02:43:48 +00:00
full_name = name
else :
2010-04-20 20:42:30 +00:00
full_name = ' list: ' + username + ' / ' + name
2010-04-16 02:37:13 +00:00
2010-04-12 19:19:09 +00:00
# Now, add a new tab with this list
2010-04-16 02:37:13 +00:00
self . add_to_notebook ( full_name )
2010-04-12 18:50:04 +00:00
2010-04-12 19:34:45 +00:00
# Remove one of the views from the tweet notebook.
# Called when the close button is clicked on one of the views
2010-04-19 14:53:28 +00:00
# or Ctrl + W is pressed while the view is active
2010-05-18 18:23:17 +00:00
def remove_view ( self , name , single_tweet , conversation ) :
2010-04-19 14:53:28 +00:00
ot = self . db [ ' open_tabs ' ]
2010-05-18 18:23:17 +00:00
ot . remove ( ( name , single_tweet , conversation ) )
2010-04-19 14:53:28 +00:00
self . db [ ' open_tabs ' ] = ot
2010-04-12 19:34:45 +00:00
for i in range ( self . tweet_notebook . get_n_pages ( ) ) :
pane = self . tweet_notebook . get_nth_page ( i )
if ( pane . get_list_name ( ) == name ) :
self . tweet_notebook . remove_page ( i )
return
2010-05-18 18:23:17 +00:00
def remove_view_callback ( self , event , name , single_tweet , conversation ) :
self . remove_view ( name , single_tweet , conversation )
2010-04-16 18:39:37 +00:00
2010-05-19 17:58:11 +00:00
def get_current_pane ( self ) :
return self . tweet_notebook . get_nth_page ( self . tweet_notebook . get_current_page ( ) )
2010-05-18 16:26:31 +00:00
def add_to_notebook ( self , name , single_tweet = None , conversation = False ) :
2010-04-16 03:02:14 +00:00
# If it already exists, don't add it, just switch to it
2010-04-16 02:43:48 +00:00
for i in range ( self . tweet_notebook . get_n_pages ( ) ) :
2010-04-16 03:02:14 +00:00
pane = self . tweet_notebook . get_nth_page ( i )
2010-04-16 15:29:56 +00:00
# Unless it is a single tweet... ignore those unless
# we are also a single tweet... then, special logic
if pane . get_single_tweet ( ) is not None :
if pane . get_single_tweet ( ) == single_tweet :
self . tweet_notebook . set_current_page ( i )
return
elif pane . get_list_name ( ) == name :
2010-04-16 03:02:14 +00:00
self . tweet_notebook . set_current_page ( i )
2010-04-16 02:43:48 +00:00
return
2010-04-26 05:00:12 +00:00
# Add the pane to the persistent database of open panes
2010-05-18 18:23:17 +00:00
if ( name , single_tweet , conversation ) not in self . db [ ' open_tabs ' ] :
2010-04-19 14:53:28 +00:00
ot = self . db [ ' open_tabs ' ]
2010-05-18 18:23:17 +00:00
ot . append ( ( name , single_tweet , conversation ) )
2010-04-19 14:53:28 +00:00
self . db [ ' open_tabs ' ] = ot
2010-04-14 16:54:03 +00:00
is_user = False
if re . match ( ' user: ' , name ) :
is_user = True
2010-05-18 18:23:17 +00:00
entries = self . num_entries
if single_tweet and not conversation :
entries = 1
new_pane = TweetPane ( name , num_entries = entries , single_tweet = single_tweet , is_user = is_user , conversation = conversation )
2010-04-27 20:21:21 +00:00
if is_user :
2010-05-20 04:04:44 +00:00
new_pane . connect ( ' at-clicked ' , self . on_at_button_clicked )
new_pane . connect ( ' follow-clicked ' , self . on_follow_button_clicked )
2010-04-27 20:21:21 +00:00
apithreads . GetFollowing ( api = self . api , pane = new_pane , user = name ) . start ( )
2010-05-20 04:04:44 +00:00
apithreads . GetUserInfo ( api = self . api , pane = new_pane , user = name ) . start ( )
2010-04-27 20:21:21 +00:00
2010-04-14 18:24:40 +00:00
self . tweet_notebook . append_page_menu ( new_pane , new_pane . get_tab_label ( ) , gtk . Label ( name ) )
2010-04-21 14:01:59 +00:00
self . tweet_notebook . set_tab_reorderable ( new_pane , True )
2010-05-18 18:23:17 +00:00
new_pane . get_tab_label ( ) . connect ( ' close-clicked ' , self . remove_view_callback , name , single_tweet , conversation )
2010-04-14 14:58:16 +00:00
new_pane . connect ( ' tweet-reply ' , self . on_reply )
new_pane . connect ( ' tweet-retweet ' , self . on_retweet )
new_pane . connect ( ' tweet-in-reply-to ' , self . on_reply_to )
2010-05-18 16:26:31 +00:00
new_pane . connect ( ' tweet-conversation ' , self . on_conversation )
2010-04-16 02:37:13 +00:00
new_pane . connect ( ' show-user ' , self . show_user_callback )
2010-04-14 14:58:16 +00:00
# Special logic for single tweet pane
if single_tweet is not None :
2010-05-18 16:26:31 +00:00
if conversation :
apithreads . GetConversation ( api = self . api ,
pane = new_pane ,
root_tweet_id = single_tweet ) . start ( )
else :
apithreads . GetSingleTweet ( api = self . api ,
pane = new_pane ,
single_tweet = single_tweet ) . start ( )
2010-05-18 02:47:57 +00:00
else :
self . update_single_window ( new_pane )
2010-04-12 19:34:45 +00:00
2010-05-18 18:23:17 +00:00
# Switch to the new pane
self . tweet_notebook . set_current_page ( - 1 )
2010-04-12 19:34:45 +00:00
2010-04-12 21:12:11 +00:00
def on_tab_change ( self , event , page , page_num ) :
2010-05-06 20:28:24 +00:00
last_page = self . db [ ' active_page ' ]
2010-04-21 15:16:41 +00:00
self . db [ ' active_page ' ] = page_num
2010-05-06 20:28:24 +00:00
# Now get the new page, and set everything up
2010-04-12 21:12:11 +00:00
pane = self . tweet_notebook . get_nth_page ( page_num )
pane . set_tweets_read ( )
2010-05-06 20:28:24 +00:00
2010-04-12 21:58:00 +00:00
2010-04-21 14:01:59 +00:00
def on_tabs_reordered ( self , widget , child , page_num ) :
2010-04-21 15:16:41 +00:00
self . db [ ' active_page ' ] = page_num
2010-04-21 14:01:59 +00:00
# Clear the persistent tabs list, and recreate it
# from scratch
tab_names = [ ]
for i in range ( self . tweet_notebook . get_n_pages ( ) ) :
2010-04-21 15:16:41 +00:00
pane = self . tweet_notebook . get_nth_page ( i )
2010-05-18 18:23:17 +00:00
tab_names . append ( ( pane . get_list_name ( ) , pane . get_single_tweet ( ) , pane . get_conversation ( ) ) )
2010-04-21 14:01:59 +00:00
self . db [ ' open_tabs ' ] = tab_names
2010-04-12 21:58:00 +00:00
def on_search ( self , event ) :
search_string = self . search_entry . get_text ( )
self . search_entry . set_text ( ' ' )
2010-04-16 02:37:13 +00:00
self . add_to_notebook ( search_string )
2010-04-12 21:58:00 +00:00
2010-04-14 14:58:16 +00:00
def update_status_bar ( self , text ) :
self . status_bar . pop ( self . context_id )
self . status_bar . push ( self . context_id , text )
2010-04-14 18:24:40 +00:00
2010-05-19 17:58:11 +00:00
def on_friendship_changed ( self , widget , data ) :
if data [ ' success ' ] :
if data [ ' follow ' ] :
self . update_status_bar ( ' Now following ' + data [ ' user_name ' ] )
else :
self . update_status_bar ( ' No longer following ' + data [ ' user_name ' ] )
else : # didn't succeed
if data [ ' follow ' ] :
self . update_status_bar ( ' Failed to follow ' + data [ ' user_name ' ] )
else :
self . update_status_bar ( ' Failed to unfollow ' + data [ ' user_name ' ] )
2010-05-04 18:32:43 +00:00
2010-04-16 02:37:13 +00:00
def show_user ( self , name ) :
self . add_to_notebook ( ' user: ' + name )
def show_user_callback ( self , widget , data ) :
self . show_user ( data )
2010-05-20 04:04:44 +00:00
def on_at_button_clicked ( self , widget , user_name ) :
2010-04-16 03:00:35 +00:00
self . add_to_notebook ( ' @ ' + user_name )
2010-05-20 04:16:10 +00:00
def on_follow_button_clicked ( self , widget , follow ) :
user_name = re . sub ( ' ^user: ' , ' ' , widget . get_list_name ( ) )
2010-05-20 04:21:21 +00:00
thread = apithreads . ChangeFriendship ( self . api , widget , user_name , follow )
2010-05-20 04:16:10 +00:00
thread . sig_proxy . connect ( ' friendship-changed ' , self . on_friendship_changed )
thread . start ( )
2010-04-16 18:39:37 +00:00
def global_key_press_handler ( self , widget , event ) :
keyname = gtk . gdk . keyval_name ( event . keyval )
if keyname == ' w ' and event . state & gtk . gdk . CONTROL_MASK :
self . close_current_tab ( )
2010-05-19 15:27:14 +00:00
# Ctrl + Shift + Tab or Ctrl + PgUp or Ctrl + Left should go to prev tab
elif event . state & gtk . gdk . CONTROL_MASK and ( ( keyname == ' Tab ' and event . state & gtk . gdk . SHIFT_MASK ) or keyname == ' ISO_Left_Tab ' or keyname == ' Page_Up ' or keyname == ' Left ' ) :
2010-04-21 17:53:23 +00:00
self . tweet_notebook . prev_page ( )
return True
2010-05-19 15:27:14 +00:00
# Ctrl + Tab or Ctrl + PgDown or Ctrl + Right should go to next tab
elif event . state & gtk . gdk . CONTROL_MASK and ( keyname == ' Tab ' or keyname == ' Page_Down ' or keyname == ' Right ' ) :
2010-04-21 17:53:23 +00:00
self . tweet_notebook . next_page ( )
return True
2010-04-16 18:39:37 +00:00
2010-05-19 15:27:14 +00:00
else :
scrolltype = None
if keyname == ' Page_Down ' :
scrolltype = gtk . SCROLL_PAGE_FORWARD
elif keyname == ' Page_Up ' :
scrolltype = gtk . SCROLL_PAGE_BACKWARD
elif keyname == ' Up ' :
scrolltype = gtk . SCROLL_STEP_BACKWARD
elif keyname == ' Down ' :
scrolltype = gtk . SCROLL_STEP_FORWARD
if scrolltype :
2010-05-19 17:58:11 +00:00
self . get_current_pane ( ) . emit ( ' scroll-child ' , scrolltype , False )
2010-05-19 15:27:14 +00:00
return True
2010-04-16 18:39:37 +00:00
def close_current_tab ( self ) :
2010-05-19 17:58:11 +00:00
current_pane = self . get_current_pane ( )
2010-05-18 18:23:17 +00:00
self . remove_view ( current_pane . get_list_name ( ) , current_pane . get_single_tweet ( ) , current_pane . get_conversation ( ) )
2010-04-16 18:39:37 +00:00
2010-05-19 18:36:03 +00:00
def on_account_changed ( self , widget , new_account ) :
2010-05-19 20:10:25 +00:00
if not ( widget . get_active ( ) and self . accounts . has_key ( new_account ) ) :
return
self . username = new_account
self . api = self . accounts [ self . username ]
self . db [ ' active_user ' ] = self . username
self . update_account_label ( )
for i in range ( 0 , self . tweet_notebook . get_n_pages ( ) ) :
pane = self . tweet_notebook . get_nth_page ( i )
if re . match ( r ' user: ' , pane . get_list_name ( ) ) :
user = re . sub ( r ' user: ' , r ' ' , pane . get_list_name ( ) )
apithreads . GetFollowing ( api = self . api , pane = pane , user = user ) . start ( )
2010-04-20 19:10:47 +00:00
2010-05-11 21:38:26 +00:00
def on_lists_ready ( self , widget , username , list_names ) :
# Setup the new sub-menu
2010-05-19 18:36:03 +00:00
outer_menu_item = gtk . MenuItem ( username , False )
2010-05-11 21:38:26 +00:00
self . view_menu . append ( outer_menu_item )
new_menu = gtk . Menu ( )
outer_menu_item . set_submenu ( new_menu )
# Insert the default list items
list_names . insert ( 0 , ' Home ' )
list_names . insert ( 1 , ' @ ' + username )
list_names . insert ( 2 , ' Direct Messages ' )
# Add the items to the submenu, connect handler
for l in list_names :
2010-05-19 18:36:03 +00:00
menu_item = gtk . MenuItem ( l , False )
2010-05-11 21:38:26 +00:00
new_menu . append ( menu_item )
menu_item . connect ( ' activate ' , self . on_view_selected , username , l )
menu_item . show ( )
outer_menu_item . show ( )
2010-05-18 03:58:45 +00:00
def on_resize ( self , widget , event ) :
self . db [ ' width ' ] = event . width
self . db [ ' height ' ] = event . height
2010-05-18 21:41:32 +00:00
def on_update_posted ( self , widget , success ) :
if success :
self . reply_id = None
self . update_entry . set_text ( " " )
self . update_status_bar ( ' Tweet Posted ' )
else :
self . update_status_bar ( ' Failed to post tweet ' )
self . update_entry . set_sensitive ( True )
2010-05-18 03:58:45 +00:00
2010-05-19 04:04:01 +00:00
def on_retweet_posted ( self , widget , success ) :
if success :
self . update_status_bar ( ' Retweet Posted ' )
else :
self . update_status_bar ( ' Failed to retweet ' )
self . update_entry . set_sensitive ( True )
2010-05-19 20:02:32 +00:00
def debug_show_threads ( self , widget ) :
print ' debug_show_threads() '
for thread in enumerate ( ) :
print ' debug: thread: ' + thread . name
2010-05-20 20:51:51 +00:00
def on_file_add_account ( self , widget ) :
2010-05-20 22:03:35 +00:00
token = apithreads . get_access_token ( self . window )
if token is None :
return
2010-05-21 01:23:54 +00:00
api = apithreads . CustomApi ( token )
if not self . accounts . has_key ( api . username ) :
2010-05-20 20:51:51 +00:00
tokens = self . db [ ' tokens ' ]
tokens . append ( token )
self . db [ ' tokens ' ] = tokens
2010-05-21 01:23:54 +00:00
self . add_account ( api )
2010-05-20 21:10:12 +00:00
2010-05-21 01:23:54 +00:00
def add_account ( self , api ) :
2010-05-20 21:10:12 +00:00
username = api . username
self . accounts [ username ] = api
self . accounts [ username ] . sig_proxy . connect ( ' lists-ready ' , self . on_lists_ready )
# Add account's menu item
menu_item = gtk . RadioMenuItem ( self . first_account_item , label = username , use_underline = False )
if not self . first_account_item :
self . first_account_item = menu_item
menu_item . set_draw_as_radio ( False )
2010-05-21 15:13:04 +00:00
if not self . db . has_key ( ' active_user ' ) :
self . db [ ' active_user ' ] = username
elif username == self . db [ ' active_user ' ] :
2010-05-20 21:10:12 +00:00
menu_item . set_active ( True )
2010-05-21 01:23:54 +00:00
menu_item . connect ( ' activate ' , self . on_account_changed , username )
self . accounts_menu . append ( menu_item )
menu_item . show ( )
2010-05-19 20:02:32 +00:00
2010-05-20 19:33:05 +00:00
### end class Hrafn
2010-04-09 04:56:51 +00:00
2010-04-07 03:05:51 +00:00
# main
2010-05-19 20:02:32 +00:00
debug = False
2010-05-19 17:58:11 +00:00
2010-04-15 04:05:49 +00:00
parser = optparse . OptionParser ( )
2010-05-20 19:33:05 +00:00
parser . add_option ( ' -c ' , ' --config ' , dest = " filename " , default = " ~/.hrafn.conf " , help = " read configuration from FILENAME instead of the default ~/.hrafn.conf " )
2010-05-18 03:58:45 +00:00
parser . add_option ( ' -n ' , ' --no-resize ' , dest = " resize " , action = ' store_false ' , default = True , help = " use the default window size instead of the size from the last session " )
2010-04-15 04:05:49 +00:00
( options , args ) = parser . parse_args ( )
2010-04-09 21:45:21 +00:00
2010-05-20 19:33:05 +00:00
my_twitter = Hrafn ( options . filename , options . resize )
2010-04-22 19:13:49 +00:00
2010-04-22 18:57:27 +00:00
gtk . gdk . threads_init ( )
2010-04-27 18:53:07 +00:00
gtk . gdk . threads_enter ( )
2010-04-08 20:39:56 +00:00
gtk . main ( )
2010-04-27 18:53:07 +00:00
gtk . gdk . threads_leave ( )