diff --git a/mitterlib/ui/ui_pygtk.py b/mitterlib/ui/ui_pygtk.py
index a9e902f..469af0a 100644
--- a/mitterlib/ui/ui_pygtk.py
+++ b/mitterlib/ui/ui_pygtk.py
@@ -38,82 +38,14 @@ from mitterlib.ui.helpers import timesince
# Constants
-URL_RE = re.compile(
- r'((?:(?:https?|ftp)://|www[-\w]*\.)[^\s\n\r]+[-\w+&@#%=~])', re.I)
-
_log = logging.getLogger('ui.pygtk')
-class Columns:
- (PIC, NAME, MESSAGE, USERNAME, ID, DATETIME, ALL_DATA) = range(7)
-
class Interface(object):
"""Linux/GTK interface for Mitter."""
NAMESPACE = 'pygtk'
- def systray_cb(self, widget, user_param=None):
- if self.window.get_property('visible') and self.window.is_active():
- x, y = self.window.get_position()
- self.prefs['position_x'] = x
- self.prefs['position_y'] = y
- self.window.hide()
- else:
- self.window.move(
- self.prefs['position_x'],
- self.prefs['position_y'])
- self.window.deiconify()
- self.window.present()
-
- def create_settings_dialog(self):
- """Creates the settings dialog."""
-
- self.settings_window = gtk.Dialog(title="Settings",
- parent=self.window, flags=gtk.DIALOG_MODAL |
- gtk.DIALOG_DESTROY_WITH_PARENT,
- buttons=(gtk.STOCK_CANCEL, 0, gtk.STOCK_OK, 1))
- self.settings_box = gtk.Table(rows=4, columns=2, homogeneous=False)
-
- username_label = gtk.Label('Username:')
- password_label = gtk.Label('Password:')
- refresh_label = gtk.Label('Refresh interval (minutes):')
- https_label = gtk.Label('Use secure connections (HTTPS):')
-
- labels = [username_label, password_label, refresh_label, https_label]
- for label in labels:
- label.set_alignment(0, 0.5)
- label.set_padding(2, 0)
-
- self.username_field = gtk.Entry()
- self.password_field = gtk.Entry()
- self.password_field.set_visibility(False)
-
- self.refresh_interval_field = gtk.SpinButton()
- self.refresh_interval_field.set_range(1, 99)
- self.refresh_interval_field.set_numeric(True)
- self.refresh_interval_field.set_value(self.prefs['refresh_interval'])
- self.refresh_interval_field.set_increments(1, 5)
-
- self.https_field = gtk.CheckButton()
- self.https_field.set_active(self.https)
-
- self.settings_box.attach(username_label, 0, 1, 0, 1)
- self.settings_box.attach(self.username_field, 1, 2, 0, 1)
- self.settings_box.attach(password_label, 0, 1, 1, 2)
- self.settings_box.attach(self.password_field, 1, 2, 1, 2)
- self.settings_box.attach(refresh_label, 0, 1, 2, 3)
- self.settings_box.attach(self.refresh_interval_field, 1, 2, 2, 3)
- self.settings_box.attach(https_label, 0, 1, 3, 4)
- self.settings_box.attach(self.https_field, 1, 2, 3, 4)
-
- self.settings_box.show_all()
- self.settings_window.vbox.pack_start(self.settings_box, True,
- True, 0)
- self.settings_window.connect('close', self.close_dialog)
- self.settings_window.connect('response', self.update_preferences)
-
- return
-
def show_about(self, widget):
"""Show the about dialog."""
@@ -143,823 +75,6 @@ class Interface(object):
about_window.hide()
- # ------------------------------------------------------------
- # Widget creation functions
- # ------------------------------------------------------------
-
- # ------------------------------------------------------------
- # Grid cell content callback
- # ------------------------------------------------------------
-
- # Non-widget attached callbacks
- def set_auto_refresh(self):
- """Configure auto-refresh of tweets every `interval` minutes"""
-
- if self._refresh_id:
- gobject.source_remove(self._refresh_id)
-
- self._refresh_id = gobject.timeout_add(
- self.prefs['refresh_interval']*60*1000,
- self.refresh, None)
-
- return
-
- def update_friends_list(self):
- """Fetch the user's list of twitter friends and add it
- to the friends_store for @reply autocompletion"""
-
- _log.debug('Checking friends list...')
- friends = self.twitter.friends_list(self.post_update_friends_list)
- return
-
- def post_update_friends_list(self, friends, error):
- """Function called after we fetch the friends list."""
-
- _log.debug('Received the friends list')
-
- if error == 401: # TODO: Constants for this?
- # not authorized
- _log.error('User is not authorized yet')
- return
-
- if error in (500, 502, 503):
- _log.error('Twitter asked us to try getting friends list' \
- ' sometime later')
- gobject.timeout_add(5*60*1000, self.update_friends_list)
- return
-
- if error:
- # any error
- # well, we just don't add any friends, then.
- _log.error('Error getting friend list, leaving list empty')
- return
-
- # I'm not really sure if we need to set the thread locking here (as we
- # are just updating the store), but better safe than sorry!
-
- gtk.gdk.threads_enter()
- # Sometimes due to twitter API quirks and moon phases, the friends
- # list ends up getting populated more than once. So watch for
- # duplicates....
- known_friends = [row[0] for row in self.friends_store]
- _log.debug('known_friends: %s' % " ".join(known_friends))
- for friend in friends:
- try:
- screen_name = '@' + friend['screen_name'] + ': '
- if screen_name not in known_friends:
- _log.debug('Adding "%s" to the list' % (screen_name))
- self.friends_store.append([screen_name])
- except Exception, e:
- # No `error` does not always mean twitter sent us good data
- _log.error('Error processing friend list. %s' % str(e))
-
- gtk.gdk.threads_leave()
- _log.debug('friends list processing complete')
- return
-
- def prune_grid_store(self):
- """Prune the grid_store by removing the oldest rows."""
-
- if len(self.grid_store) <= MAX_STATUS_DISPLAY:
- return True # Required by gobject.idle_add() for this to be called
- # again
-
- _log.debug("prune_grid_store called")
-
- gtk.gdk.threads_enter()
-
- self.grid.freeze_child_notify()
- self.grid.set_model(None)
-
- # Since I don't know how to get the last row in grid_store,
- # I'll reverse the list and then pop out the first row instead.
-
- self.grid_store.set_sort_column_id(Columns.DATETIME,
- gtk.SORT_ASCENDING)
-
- iter = self.grid_store.get_iter_first()
-
- while (len(self.grid_store) > MAX_STATUS_DISPLAY) and iter:
- _log.debug("popping off tweet with id %s" %
- self.grid_store.get_value(iter, Columns.ID))
- self.grid_store.remove(iter) # iter is auto set to next row
-
-
- self.grid_store.set_sort_column_id(Columns.DATETIME,
- gtk.SORT_DESCENDING)
- self.grid.set_model(self.grid_store)
- self.grid.thaw_child_notify()
-
- gtk.gdk.threads_leave()
- return True
-
- # Main window callbacks
- def size_request(self, widget, requisition, data=None):
- """Callback when the window changes its sizes. We use it to set the
- proper word-wrapping for the message column."""
-
- self.prefs['width'], self.prefs['height'] = self.window.get_size()
-
- # this is based on a mail of Kristian Rietveld, on gtk maillist
-
- if not len(self.grid_store):
- # nothing to rearrange
- return
-
- column = self.message_column
- iter = self.grid_store.get_iter_first()
- path = self.grid_store.get_path(iter)
-
- column_rectangle = self.grid.get_cell_area(path, column)
-
- width = column_rectangle.width
- _log.debug('Width=%d' % (width))
-
- # there should be only
- renderers = column.get_cell_renderers()
- for render in renderers:
- _log.debug('Render update')
- render.set_property('wrap-width', width)
-
- while iter:
- path = self.grid_store.get_path(iter)
- self.grid_store.row_changed(path, iter)
- iter = self.grid_store.iter_next(iter)
-
- return
-
- def notify_reset(self, widget, event, user_data=None):
- if getattr(event, 'in_', False):
- self._main_window.set_urgency_hint(False)
- if self._systray:
- self._systray.set_tooltip('Mitter: Click to toggle ' \
- 'window visibility.')
- self._systray.set_from_file(self._app_icon)
- self.unread_tweets = 0
- return
-
- def notify(self, new_tweets=0):
- """Set the window hint as urgent, so Mitter window will flash,
- notifying the user about the new messages. Also send a notification
- message with one of the new tweets."""
- self.window.set_urgency_hint(True)
- if self._systray and self.unread_tweets > 0:
- self._systray.set_tooltip('Mitter: %s new' % self.unread_tweets)
- self._systray.set_from_file(self._app_icon_alert)
-
- if self.action_group.get_action('MuteNotify').get_active():
- _log.debug('notifications are currently muted')
- return
-
- if new_tweets and len(self.grid_store) > 0:
- iter = self.grid_store.get_iter_first()
- while iter:
- sender = self.grid_store.get_value(iter, Columns.USERNAME)
- if sender == self.username_field.get_text():
- iter = self.grid_store.iter_next(iter)
- continue
- else:
- tweet = self.grid_store.get_value(iter, Columns.MESSAGE)
- _log.debug('notify_broadcast with this tweet: %s' %
- tweet)
- break
-
- if new_tweets > 1:
- msg = '%d unread tweets including ' \
- 'this from %s:
%s' % (self.unread_tweets,
- sender, tweet)
- else:
- msg = 'One new tweet from %s:
%s' % (sender,
- tweet)
-
- if self.systray:
- gtk.gdk.threads_enter()
- screen, rect, orientation = self.systray.get_geometry()
- gtk.gdk.threads_leave()
- self.notify_broadcast(msg, rect.x, rect.y)
- return
-
- # settings callbacks
- def show_settings(self, widget, user_data=None):
- """Create and display the settings window."""
-
- self.settings_window.show()
- self.settings_window.run()
-
- return
-
- def close_dialog(self, user_data=None):
- """Hide the dialog window."""
-
- return True
-
- def update_preferences(self, widget, response_id=0, user_data=None):
- """
- Update the user preferences when the user press the "OK" button in the
- settings window."""
-
- if response_id == 1:
- self.statusbar.push(self.statusbar_context,
- 'Saving your profile...')
-
- self.save_interface_prefs()
-
- # update the (internal) twitter prefences too!
-
- self.twitter.username = self.username_field.get_text()
- self.twitter.password = self.password_field.get_text()
- self.twitter.https = self.https_field.get_active()
- refresh_interval = self.refresh_interval_field.get_value_as_int()
- self.prefs['refresh_interval'] = refresh_interval
-
- # update the list
-
- self.refresh(None)
- self.update_friends_list()
- self.statusbar.pop(self.statusbar_context)
-
- # update auto-refresh
-
- self.set_auto_refresh()
-
- self.settings_window.hide()
-
- return True
-
- # update status
- def update_status(self, user_data=None):
- """Update the user status on Twitter."""
-
- status = self.update_text.get_text()
- status = status.strip()
- if not str_len(status):
- return
-
- self.update_text.set_sensitive(False)
- self.statusbar.push(self.statusbar_context, 'Updating your status...')
-
- if str_len(status) > 140:
- error_message = 'Your message has more than 140 characters and' \
- ' Twitter may truncate it. It would still be visible ' \
- 'on the website. Do you still wish to go ahead?'
- if str_len(status) > 160:
- error_message = 'Your message has more than 160 characters ' \
- 'and it is very likely Twitter will refuse it. You ' \
- 'can try shortening your URLs before posting. Do ' \
- 'you still wish to go ahead?'
-
- error_dialog = gtk.MessageDialog(parent=self.window,
- type=gtk.MESSAGE_QUESTION,
- flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- message_format="Your status update message is too long.",
- buttons=gtk.BUTTONS_YES_NO)
- error_dialog.format_secondary_text(error_message)
-
- response = error_dialog.run()
- error_dialog.destroy()
- if response == gtk.RESPONSE_NO:
- self.statusbar.pop(self.statusbar_context)
- self.update_text.set_sensitive(True)
- self.window.set_focus(self.update_text)
- return
-
- data = self.twitter.update(status, self.post_update_status)
-
- def post_update_status(self, data, error):
- """Function called after we receive the answer from the update
- status."""
-
- if error:
- gtk.gdk.threads_enter()
- error_dialog = gtk.MessageDialog(parent=self.window,
- type=gtk.MESSAGE_ERROR,
- message_format='Error updating status. Please try again.',
- buttons=gtk.BUTTONS_OK)
- error_dialog.connect("response", lambda *a:
- error_dialog.destroy())
- error_dialog.run()
- gtk.gdk.threads_leave()
- else:
- if data:
- # i wonder if this will really work
- self.post_refresh([data], None, False)
- else:
- self.refresh(None, False)
-
- gtk.gdk.threads_enter()
- self.update_text.set_text("")
- gtk.gdk.threads_leave()
-
- gtk.gdk.threads_enter()
- self.statusbar.pop(self.statusbar_context)
- self.update_text.set_sensitive(True)
- self.window.set_focus(self.update_text)
- gtk.gdk.threads_leave()
-
- return True
-
- def shrink_url(self, widget, user_data=None):
- bounds = self.update_text.get_selection_bounds()
- if not bounds:
- return
- else:
- start, end = bounds
-
- longurl = self.update_text.get_chars(start, end).strip()
- if not longurl:
- return
-
- _log.debug('shrink url request for: %s' % longurl)
-
- self.update_text.set_sensitive(False)
- self.statusbar.push(self.statusbar_context, 'Shrinking URL...')
-
- self.twitter.download('http://is.gd/api.php?longurl=' + longurl,
- self.post_shrink_url, longurl=longurl,
- start=start, end=end)
-
- def post_shrink_url(self, url, error, longurl, start, end):
- if error:
- _log.error("Exception in shrinking url. ' \
- 'Error code: %s" % error)
- # error dialog
- gtk.gdk.threads_enter()
- error_dialog = gtk.MessageDialog(parent=self.window,
- type=gtk.MESSAGE_ERROR,
- message_format='Failed to shrink the URL %s' % longurl,
- buttons=gtk.BUTTONS_OK)
- error_dialog.connect("response", lambda *a:
- error_dialog.destroy())
- error_dialog.run()
- gtk.gdk.threads_leave()
- else:
- _log.debug('Got shrunk url: %s' % url)
- char = self.update_text.get_chars(start-1, start)
- if start and not char.isspace():
- url = ' '+url
- char = self.update_text.get_chars(end, end+1)
- if not char.isspace():
- url = url+' '
-
- gtk.gdk.threads_enter()
- self.update_text.delete_text(start, end)
- self.update_text.insert_text(url, start)
- self.update_text.set_position(start+len(url))
- gtk.gdk.threads_leave()
-
- gtk.gdk.threads_enter()
- self.statusbar.pop(self.statusbar_context)
- self.update_text.set_sensitive(True)
- self.update_text.grab_focus()
- gtk.gdk.threads_leave()
-
- # post related callbacks
- def reply_tweet(self, widget, user_data=None):
- """Reply by putting the username in your input"""
- cursor = self.grid.get_cursor()
- if not cursor:
- return
-
- path = cursor[0]
- iter = self.grid_store.get_iter(path)
- username = self.grid_store.get_value(iter, Columns.USERNAME)
- text_insert = '@%s: ' % (username)
-
- _log.debug('Inserting reply text: %s' % (text_insert))
-
- status = self.update_text.get_text()
- status = text_insert + status
- self.update_text.set_text(status)
- self.window.set_focus(self.update_text)
- self.update_text.set_position(len(status))
-
- def retweet(self, widget, user_data=None):
- """Retweet by putting the string rt and username in your input"""
-
- cursor = self.grid.get_cursor()
- if not cursor:
- return
-
- path = cursor[0]
- iter = self.grid_store.get_iter(path)
- username = self.grid_store.get_value(iter, Columns.USERNAME)
- msg = self.grid_store.get_value(iter, Columns.MESSAGE)
- text_insert = 'RT @%s: %s' % (username, msg)
-
- _log.debug('Inserting retweet text: %s' % (text_insert))
-
- status = text_insert + self.update_text.get_text()
- self.update_text.set_text(status)
- self.window.set_focus(self.update_text)
- self.update_text.set_position(str_len(status))
-
- def delete_tweet(self, widget, user_data=None):
- """Delete a twit."""
-
- cursor = self.grid.get_cursor()
- if not cursor:
- return
-
- path = cursor[0]
- iter = self.grid_store.get_iter(path)
- tweet_id = int(self.grid_store.get_value(iter, Columns.ID))
- _log.debug('Deleting tweet: %d' % (tweet_id))
-
- self.statusbar.push(self.statusbar_context, 'Deleting tweet...')
-
- self.twitter.tweet_destroy(tweet_id, self.post_delete_tweet,
- tweet=tweet_id)
-
- return
-
- def post_delete_tweet(self, data, error, tweet):
- """Function called after we delete a tweet on the server."""
-
- if error:
- gtk.gdk.threads_enter()
- error_dialog = gtk.MessageDialog(parent=self.window,
- type=gtk.MESSAGE_ERROR,
- message_format='Error deleting tweet. Please try again.',
- buttons=gtk.BUTTONS_OK)
- error_dialog.connect("response", lambda *a:
- error_dialog.destroy())
- error_dialog.run()
- gtk.gdk.threads_leave()
- else:
- # locate that tweet in the store and remove it.
- iter = self.grid_store.get_iter_first()
- tweet = int(tweet)
- while iter:
- id = self.grid_store.get_value(iter, Columns.ID)
- if int(id) == tweet:
- self.grid_store.remove(iter)
- break
- iter = self.grid_store.iter_next(iter)
-
- # update the interface
- gtk.gdk.threads_enter()
- self.statusbar.pop(self.statusbar_context)
- self.grid.queue_draw()
- gtk.gdk.threads_leave()
-
- return
-
- def check_post(self, treeview, user_data=None):
- """Callback when one of the rows is selected."""
- cursor = treeview.get_cursor()
- if not cursor:
- return
-
- path = cursor[0]
- iter = self.grid_store.get_iter(path)
- username = self.grid_store.get_value(iter, Columns.USERNAME)
-
- delete_action = self.action_group.get_action('Delete')
-
- if username == self.username_field.get_text():
- delete_action.set_property('sensitive', True)
- else:
- delete_action.set_property('sensitive', False)
-
- return
-
- def open_post(self, treeview, path, view_column, user_data=None):
- """Callback when one of the rows in activated."""
-
- iter = self.grid_store.get_iter(path)
- username = self.grid_store.get_value(iter, Columns.USERNAME)
- tweet_id = self.grid_store.get_value(iter, Columns.ID)
- message = self.grid_store.get_value(iter, Columns.MESSAGE)
- urls = url_re.search(message)
- if urls:
- # message contains a link; go to the link instead
- url = urls.groups()[0]
- else:
- url = 'http://twitter.com/%s/statuses/%s/' % (username, tweet_id)
-
- self.open_url(path, url)
-
- def click_post(self, treeview, event, user_data=None):
- """Callback when a mouse click event occurs on one of the rows."""
-
- if event.button != 3:
- # Only right clicks are processed
- return False
-
- x = int(event.x)
- y = int(event.y)
-
- pth = treeview.get_path_at_pos(x, y)
- if not pth:
- # The click wasn't on a row
- return False
-
- path, col, cell_x, cell_y = pth
- treeview.grab_focus()
- treeview.set_cursor(path, col, 0)
-
- self.show_post_popup(treeview, event)
- return True
-
- def show_post_popup(self, treeview, event, user_data=None):
- """Shows the popup context menu in the treeview"""
-
- cursor = treeview.get_cursor()
- if not cursor:
- return
-
- path = cursor[0]
- row_iter = self.grid_store.get_iter(path)
-
- popup_menu = gtk.Menu()
- popup_menu.set_screen(self.window.get_screen())
-
- # An open submenu with various choices underneath
- open_menu_items = []
-
- tweet = self.grid_store.get_value(row_iter, Columns.ALL_DATA)
-
- urls = url_re.findall(tweet['text'])
- for url in urls:
- if len(url) > 20:
- item_name = url[:20] + '...'
- else:
- item_name = url
- item = gtk.MenuItem(item_name)
- item.connect('activate', self.open_url, url)
- open_menu_items.append(item)
-
- if tweet['in_reply_to_status_id']:
- # I wish twitter made it easy to construct target url
- # without having to make another API call
- reply_to = re.search(r'@(?P\w+)', tweet['text'])
- if reply_to:
- url = 'http://twitter.com/%s/statuses/%s' % (
- reply_to.group('user'),
- tweet['in_reply_to_status_id'])
- item = gtk.MenuItem('In reply to')
- item.connect('activate', self.open_url, url)
- open_menu_items.append(item)
-
- item = gtk.MenuItem('This tweet')
- username = self.grid_store.get_value(row_iter, Columns.USERNAME)
- tweet_id = self.grid_store.get_value(row_iter, Columns.ID)
- url = 'http://twitter.com/%s/statuses/%s/' % (username, tweet_id)
- item.connect('activate', self.open_url, url)
- open_menu_items.append(item)
-
- open_menu = gtk.Menu()
- for item in open_menu_items:
- open_menu.append(item)
-
- open_item = gtk.MenuItem("Open")
- open_item.set_submenu(open_menu)
- popup_menu.append(open_item)
-
- # Reply, only if it's not yourself
- item = gtk.MenuItem("Reply")
- item.connect('activate', self.reply_tweet, "Reply")
- if username == self.username_field.get_text():
- item.set_property('sensitive', False)
- popup_menu.append(item)
-
- # Retweet, only if it's not yourself
- item = gtk.MenuItem("Retweet")
- item.connect('activate', self.retweet, "Retweet")
- if username == self.username_field.get_text():
- item.set_property('sensitive', False)
- popup_menu.append(item)
-
- item = gtk.MenuItem("Delete")
- item.connect('activate', self.delete_tweet, "Delete")
- if username != self.username_field.get_text():
- item.set_property('sensitive', False)
-
- popup_menu.append(item)
-
- popup_menu.show_all()
-
- if event:
- b = event.button
- t = event.time
- else:
- b = 1
- t = 0
-
- popup_menu.popup(None, None, None, b, t)
-
- return True
-
- # action callbacks
- # (yes, settings should be here, but there are more settings-related
- # callbacks, so let's keep them together somewhere else)
- def open_url(self, source, url):
- """Simply opens specified url in new browser tab. We need source
- parameter so that this function can be used as an event callback"""
-
- _log.debug('opening url: %s' % url)
- import webbrowser
- webbrowser.open_new_tab(url)
- self.window.set_focus(self.update_text)
-
- def refresh(self, widget, notify=True):
- """Update the list of twits."""
-
- if self.last_update:
- self.statusbar.pop(self.statusbar_context)
- self.last_update = datetime.datetime.now()
-
- _log.debug('Updating list of tweets...')
- self.statusbar.push(self.statusbar_context,
- 'Updating list of tweets...')
-
- self.twitter.friends_timeline(self.post_refresh, notify=notify)
-
- return True # required by gobject.timeout_add
-
- def post_refresh(self, data, error, notify):
- """Function called when the system retrieves the list of new
- tweets."""
-
- _log.debug('Data: %s' % (str(data)))
-
- if error == 401:
- # Not authorized, popup the settings window
- gtk.gdk.threads_enter()
- error_dialog = gtk.MessageDialog(parent=self.window,
- type=gtk.MESSAGE_ERROR,
- message_format='Autorization error, check your login ' \
- 'information in the prefrences',
- buttons=gtk.BUTTONS_OK)
- error_dialog.connect("response", lambda *a:
- error_dialog.destroy())
- error_dialog.run()
- gtk.gdk.threads_leave()
- return
-
- if not data:
- gtk.gdk.threads_enter()
- self.statusbar.pop(self.statusbar_context)
- self.show_last_update()
- _log.debug('No new data')
- gtk.gdk.threads_leave()
- return
-
- known_tweets = [row[Columns.ID] for row in self.grid_store]
- need_notify = False
- new_tweets = 0
- new_tweets_list = []
-
- for tweet in data:
- id = tweet['id']
- if str(id) in known_tweets:
- _log.debug('Tweet %s is already in the list' % (id))
- continue
-
- created_at = tweet['created_at']
- display_name = tweet['user']['name']
- username = tweet['user']['screen_name']
- user_pic = tweet['user']['profile_image_url']
- message = tweet['text']
-
- new_tweets_list.append((user_pic, display_name, message, username,
- id, created_at, tweet))
- self.queue_pic(user_pic)
-
- _log.debug('New tweet with id %s from %s' % (id, username))
- if not username == self.username_field.get_text():
- # we don't want to be notified about tweets from ourselves,
- # but from everyone else it is fine.
- new_tweets += 1
-
- # add the new tweets in the store
- gtk.gdk.threads_enter()
- for data in new_tweets_list:
- self.grid_store.append(data)
-
- self.statusbar.pop(self.statusbar_context)
-
- # there is new stuff, so we move to the top
-
- p = self.grid_store.get_path(self.grid_store.get_iter_first())
- self.grid.scroll_to_cell(p)
- self.show_last_update()
- _log.debug('Tweets updated')
- gtk.gdk.threads_leave()
-
- if new_tweets and notify:
- self.unread_tweets += new_tweets
- self.notify(new_tweets)
-
- self.refresh_rate_limit()
- self.prune_grid_store()
-
- # ------------------------------------------------------------
- # Helper functions
- # ------------------------------------------------------------
- def clear_list(self):
- """Clear the list, so we can add more items."""
-
- self.grid_store.clear()
-
- return
-
- def refresh_rate_limit(self):
- """Request the rate limit and check if we are doing okay."""
- self.twitter.rate_limit_status(self.post_refresh_rate_limit)
- return
-
- def post_refresh_rate_limit(self, data, error):
- """Callback for the refresh_rate_limit."""
- if error or not data:
- _log.error('Error fetching rate limit')
- return
-
- # Check if we are running low on our limit
- reset_time = datetime.datetime.fromtimestamp(
- int(data['reset_time_in_seconds']))
-
- if reset_time < datetime.datetime.now():
- # Clock differences can cause this
- return
-
- time_delta = reset_time - datetime.datetime.now()
- mins_till_reset = time_delta.seconds/60 # Good enough!
- needed_hits = mins_till_reset/self.prefs['refresh_interval']
- remaining_hits = int(data['remaining_hits'])
-
- _log.debug('remaining_hits: %s. reset in %s mins.'
- % (remaining_hits, mins_till_reset))
-
- if needed_hits > remaining_hits:
- gtk.gdk.threads_enter()
- error_dialog = gtk.MessageDialog(parent=self.window,
- type=gtk.MESSAGE_WARNING,
- flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- message_format='Refresh rate too high',
- buttons=gtk.BUTTONS_OK)
- error_dialog.format_secondary_text(
- "You have only %d twitter requests left until your " \
- "request count is reset in %d minutes. But at your " \
- "current refresh rate (every %d minutes), you will " \
- "exhaust your limit within %d minutes. You should " \
- "consider increasing the refresh interval in Mitter's " \
- "Settings dialog." % (remaining_hits, mins_till_reset,
- self.prefs['refresh_interval'],
- remaining_hits * self.prefs['refresh_interval']))
- error_dialog.connect("response", lambda *a:
- error_dialog.destroy())
- error_dialog.run()
- gtk.gdk.threads_leave()
-
- def show_last_update(self):
- """Add the last update time in the status bar."""
-
- last_update = self.last_update.strftime('%H:%M')
- next_update = (self.last_update +
- datetime.timedelta(minutes=self.prefs[
- 'refresh_interval'])).strftime('%H:%M')
-
- message = 'Last update %s, next update %s' % (last_update,
- next_update)
- self.statusbar.push(self.statusbar_context, message)
- return
-
- def queue_pic(self, pic):
- """Check if the pic is in the queue or already downloaded. If it is
- not in any of those, add it to the download queue."""
- if pic in self.user_pics:
- return
-
- if pic in self.pic_queue:
- return
-
- self.pic_queue.add(pic)
- self.twitter.download(pic, self.post_pic_download, id=pic)
- return
-
- def post_pic_download(self, data, error, id):
- """Function called once we downloaded the user pic."""
-
- _log.debug('Received pic %s' % (id))
-
- if error or not data:
- _log.debug('Error with the pic, not loading')
- return
-
- loader = gtk.gdk.PixbufLoader()
- loader.write(data)
- loader.close()
-
- self.user_pics[id] = loader.get_pixbuf()
- self.pic_queue.discard(id)
-
- # finally, request the grid to redraw itself
- gtk.gdk.threads_enter()
- self.grid.queue_draw()
- gtk.gdk.threads_leave()
-
- return
# ------------------------------------------------------------
# Helper functions
# ------------------------------------------------------------
@@ -1063,7 +178,7 @@ class Interface(object):
message_renderer.set_property('width', 10)
message_column = gtk.TreeViewColumn('Message',
- message_renderer, text=1)
+ message_renderer)
message_column.set_cell_data_func(message_renderer,
self._cell_renderer_message)
self.grid.append_column(message_column)
@@ -1088,7 +203,7 @@ class Interface(object):
refresh_action = gtk.Action('Refresh', '_Refresh',
'Update the listing', gtk.STOCK_REFRESH)
- refresh_action.connect('activate', self.refresh)
+ #refresh_action.connect('activate', self.refresh)
quit_action = gtk.Action('Quit', '_Quit',
'Exit Mitter', gtk.STOCK_QUIT)
@@ -1096,7 +211,7 @@ class Interface(object):
settings_action = gtk.Action('Settings', '_Settings',
'Settings', gtk.STOCK_PREFERENCES)
- settings_action.connect('activate', self.show_settings)
+ #settings_action.connect('activate', self.show_settings)
update_action = gtk.Action('Update', '_Update', 'Update your status',
gtk.STOCK_ADD)
@@ -1106,19 +221,19 @@ class Interface(object):
delete_action = gtk.Action('Delete', '_Delete', 'Delete a post',
gtk.STOCK_DELETE)
delete_action.set_property('sensitive', False)
- delete_action.connect('activate', self.delete_tweet)
+ #delete_action.connect('activate', self.delete_tweet)
about_action = gtk.Action('About', '_About', 'About Mitter',
gtk.STOCK_ABOUT)
about_action.connect('activate', self.show_about)
- shrink_url_action = gtk.Action('ShrinkURL', 'Shrink _URL',
- 'Shrink selected URL', gtk.STOCK_EXECUTE)
- shrink_url_action.connect('activate', self.shrink_url)
+ #shrink_url_action = gtk.Action('ShrinkURL', 'Shrink _URL',
+ # 'Shrink selected URL', gtk.STOCK_EXECUTE)
+ #shrink_url_action.connect('activate', self.shrink_url)
- mute_action = gtk.ToggleAction('MuteNotify', '_Mute Notifications',
- 'Mutes notifications on new tweets', gtk.STOCK_MEDIA_PAUSE)
- mute_action.set_active(False)
+ #mute_action = gtk.ToggleAction('MuteNotify', '_Mute Notifications',
+ # 'Mutes notifications on new tweets', gtk.STOCK_MEDIA_PAUSE)
+ #mute_action.set_active(False)
post_action = gtk.Action('Posts', '_Posts', 'Post management', None)
@@ -1140,8 +255,8 @@ class Interface(object):
self.action_group.add_action(edit_action)
self.action_group.add_action(help_action)
self.action_group.add_action(about_action)
- self.action_group.add_action_with_accel(shrink_url_action, 'u')
- self.action_group.add_action_with_accel(mute_action, 'm')
+ #self.action_group.add_action_with_accel(shrink_url_action, 'u')
+ #self.action_group.add_action_with_accel(mute_action, 'm')
self.action_group.add_action_with_accel(update_action,
'Return')