diff --git a/TODO b/TODO index ce7ff0c..d37493a 100644 --- a/TODO +++ b/TODO @@ -2,13 +2,13 @@ This is a list of things that still need doing. It includes short-, medium-, an features: -* Make all API calls threaded * Make the buttons prettier * Add a graphical options menu * Come up with a better bloody name * Status bar icon * Support viewing a list of friends and unfollowing them * Support creating new lists and adding users to it (as well as removing users and deleting lists) +* Changing the active user needs to be more 'work'... for instance, user tabs need to be re-checked for followship... bugs: diff --git a/apithreads.py b/apithreads.py index 245dec3..5f4b19b 100644 --- a/apithreads.py +++ b/apithreads.py @@ -3,6 +3,7 @@ import re import gtk, gobject from threading import Thread,RLock +import twitter_pb2 from twitter import Api from urllib2 import HTTPError,URLError import avcache @@ -304,6 +305,46 @@ class PostRetweet(ApiThread): +class ChangeFriendship(ApiThread): + def __init__(self, api, pane, user_name, follow=True): + ApiThread.__init__(self, api) + self.sig_proxy = SigProxy() + self.user_name = user_name + self.follow = follow + self.pane = pane + + + def run(self): + try: + with self.api.lock: + if self.follow: + user = self.api.CreateFriendship(self.user_name) + else: + user = self.api.DestroyFriendship(self.user_name) + + if user.__class__ == twitter_pb2.User: + success = True + else: + success = False + + except (HTTPError, URLError): + success = False + + gtk.gdk.threads_enter() + try: + if self.follow: + self.pane.set_following(success) + else: + self.pane.set_following(not success) + finally: + gtk.gdk.threads_leave() + + self.sig_proxy.emit('friendship-changed', {'user_name': self.user_name, 'follow': self.follow, 'success': success}) + +### End class ChangeFriendship + + + class SigProxy(gtk.Alignment): """ This little class exists just so that we can have a gtk class in our @@ -328,6 +369,10 @@ gobject.signal_new("retweet-posted", SigProxy, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) +gobject.signal_new("friendship-changed", SigProxy, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) + # We use these classes to emulate a Status object when we need # one to be built out of something else. diff --git a/mytwitter.py b/mytwitter.py index 312bad1..0792ca6 100755 --- a/mytwitter.py +++ b/mytwitter.py @@ -40,6 +40,10 @@ class MyTwitter(): config.write(config_filehandle) config_filehandle.close() + if config.has_option('global', 'debug') and config.get('global', 'debug') == '1': + debug = True + + if len(config.sections()) < 2: print "Error: You must define at least one [account] section in " + config_file sys.exit(1) @@ -175,7 +179,7 @@ class MyTwitter(): def update_window_callback(self, widget): - pane = self.tweet_notebook.get_nth_page(self.tweet_notebook.get_current_page()) + pane = self.get_current_pane() self.update_single_window(pane) @@ -267,6 +271,10 @@ class MyTwitter(): self.remove_view(name, single_tweet, conversation) + def get_current_pane(self): + return self.tweet_notebook.get_nth_page(self.tweet_notebook.get_current_page()) + + def add_to_notebook(self, name, single_tweet=None, conversation=False): # If it already exists, don't add it, just switch to it for i in range(self.tweet_notebook.get_n_pages()): @@ -372,26 +380,29 @@ class MyTwitter(): def on_following_button_clicked(self, event): - current_pane = self.tweet_notebook.get_nth_page(self.tweet_notebook.get_current_page()) + current_pane = self.get_current_pane() user_name = re.sub('^user: ', '', current_pane.get_list_name()) if current_pane.get_following(): - with self.api.lock: - try: - self.api.DestroyFriendship(user_name) - except HTTPError,URLError: - self.update_status_bar('Failed to unfollow user.') - return - current_pane.set_following(False) + follow = False # destroy the friendship else: - with self.api.lock: - try: - self.api.CreateFriendship(user_name) - except HTTPError,URLError: - self.update_status_bar('Failed to follow user.') - return - current_pane.set_following(True) + follow = True - self.update_follow_button(current_pane) + thread = apithreads.ChangeFriendship(self.api, current_pane, user_name, follow) + thread.sig_proxy.connect('friendship-changed', self.on_friendship_changed) + thread.start() + + + 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']) # pane should be the currently active pane @@ -423,7 +434,7 @@ class MyTwitter(): def on_at_button_clicked(self, widget): - current_pane = self.tweet_notebook.get_nth_page(self.tweet_notebook.get_current_page()) + current_pane = self.get_current_pane() user_name = re.sub('^user: ', '', current_pane.get_list_name()) self.add_to_notebook('@' + user_name) @@ -459,13 +470,13 @@ class MyTwitter(): scrolltype = gtk.SCROLL_END if scrolltype: - self.tweet_notebook.get_nth_page(self.tweet_notebook.get_current_page()).emit('scroll-child', scrolltype, False) + self.get_current_pane().emit('scroll-child', scrolltype, False) return True def close_current_tab(self): - current_pane = self.tweet_notebook.get_nth_page(self.tweet_notebook.get_current_page()) + current_pane = self.get_current_pane() self.remove_view(current_pane.get_list_name(), current_pane.get_single_tweet(), current_pane.get_conversation()) @@ -527,6 +538,8 @@ class MyTwitter(): # main +debug = False # global debug variable + parser = optparse.OptionParser() parser.add_option('-c' ,'--config', dest="filename", default="~/.mytwitter.conf", help="read configuration from FILENAME instead of the default ~/.mytwitter.conf") 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")