|
|
@ -230,13 +230,18 @@ class Interface(object): |
|
|
|
main_window.connect('delete-event', self._quit_app) |
|
|
|
main_window.connect('delete-event', self._quit_app) |
|
|
|
main_window.connect('size-request', self._grid_resize) |
|
|
|
main_window.connect('size-request', self._grid_resize) |
|
|
|
|
|
|
|
|
|
|
|
grid = self._create_grid() |
|
|
|
|
|
|
|
(menu, toolbar, accelerators) = self._create_menu_and_toolbar() |
|
|
|
(menu, toolbar, accelerators) = self._create_menu_and_toolbar() |
|
|
|
update_field = self._create_update_box() |
|
|
|
update_field = self._create_update_box() |
|
|
|
self._statusbar = self._create_statusbar() |
|
|
|
self._statusbar = self._create_statusbar() |
|
|
|
|
|
|
|
|
|
|
|
self._main_tabs = gtk.Notebook() |
|
|
|
self._main_tabs = gtk.Notebook() |
|
|
|
self._main_tabs.insert_page(grid, gtk.Label('Messages')) |
|
|
|
|
|
|
|
|
|
|
|
(grid_widget, self._grid) = self._create_grid('_new_message_count') |
|
|
|
|
|
|
|
self._main_tabs.insert_page(grid_widget, gtk.Label('Messages')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(replies_widget, self._replies) = self._create_grid( |
|
|
|
|
|
|
|
'_new_replies_count') |
|
|
|
|
|
|
|
self._main_tabs.insert_page(replies_widget, gtk.Label('Replies')) |
|
|
|
|
|
|
|
|
|
|
|
update_box = gtk.VPaned() |
|
|
|
update_box = gtk.VPaned() |
|
|
|
update_box.pack1(self._main_tabs, resize=True, shrink=False) |
|
|
|
update_box.pack1(self._main_tabs, resize=True, shrink=False) |
|
|
@ -255,16 +260,16 @@ class Interface(object): |
|
|
|
|
|
|
|
|
|
|
|
return main_window |
|
|
|
return main_window |
|
|
|
|
|
|
|
|
|
|
|
def _create_grid(self): |
|
|
|
def _create_grid(self, counter): |
|
|
|
"""Add the displaying grid.""" |
|
|
|
"""Add the displaying grid.""" |
|
|
|
# Store NetworkData objects only |
|
|
|
# Store NetworkData objects only |
|
|
|
grid_store = gtk.ListStore(object) |
|
|
|
grid_store = gtk.ListStore(object) |
|
|
|
grid_store.set_sort_column_id(0, gtk.SORT_ASCENDING) |
|
|
|
grid_store.set_sort_column_id(0, gtk.SORT_ASCENDING) |
|
|
|
grid_store.set_sort_func(0, self._order_datetime) |
|
|
|
grid_store.set_sort_func(0, self._order_datetime) |
|
|
|
|
|
|
|
|
|
|
|
self._grid = gtk.TreeView(grid_store) |
|
|
|
grid = gtk.TreeView(grid_store) |
|
|
|
self._grid.set_property('headers-visible', False) |
|
|
|
grid.set_property('headers-visible', False) |
|
|
|
self._grid.set_rules_hint(True) # change color for each row |
|
|
|
grid.set_rules_hint(True) # change color for each row |
|
|
|
|
|
|
|
|
|
|
|
user_renderer = gtk.CellRendererPixbuf() |
|
|
|
user_renderer = gtk.CellRendererPixbuf() |
|
|
|
user_column = gtk.TreeViewColumn('User', user_renderer) |
|
|
|
user_column = gtk.TreeViewColumn('User', user_renderer) |
|
|
@ -281,21 +286,20 @@ class Interface(object): |
|
|
|
message_column.set_cell_data_func(message_renderer, |
|
|
|
message_column.set_cell_data_func(message_renderer, |
|
|
|
self._cell_renderer_message) |
|
|
|
self._cell_renderer_message) |
|
|
|
|
|
|
|
|
|
|
|
self._grid.append_column(user_column) |
|
|
|
grid.append_column(user_column) |
|
|
|
self._grid.append_column(message_column) |
|
|
|
grid.append_column(message_column) |
|
|
|
|
|
|
|
|
|
|
|
self._grid.set_resize_mode(gtk.RESIZE_IMMEDIATE) |
|
|
|
grid.set_resize_mode(gtk.RESIZE_IMMEDIATE) |
|
|
|
self._grid.connect('cursor-changed', self._message_selected) |
|
|
|
grid.connect('cursor-changed', self._message_selected) |
|
|
|
self._grid.connect('button-press-event', self._click_message) |
|
|
|
grid.connect('button-press-event', self._click_message) |
|
|
|
self._grid.connect('popup-menu', self._message_popup) # Menu button |
|
|
|
grid.connect('popup-menu', self._message_popup) # Menu button |
|
|
|
self._grid.connect('cursor-changed', self._mark_message_read, |
|
|
|
grid.connect('cursor-changed', self._mark_message_read, counter) |
|
|
|
'_new_message_count') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scrolled_window = gtk.ScrolledWindow() |
|
|
|
scrolled_window = gtk.ScrolledWindow() |
|
|
|
scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) |
|
|
|
scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) |
|
|
|
scrolled_window.add(self._grid) |
|
|
|
scrolled_window.add(grid) |
|
|
|
|
|
|
|
|
|
|
|
return scrolled_window |
|
|
|
return (scrolled_window, grid) |
|
|
|
|
|
|
|
|
|
|
|
def _create_menu_and_toolbar(self): |
|
|
|
def _create_menu_and_toolbar(self): |
|
|
|
"""Create the main menu and the toolbar.""" |
|
|
|
"""Create the main menu and the toolbar.""" |
|
|
@ -692,6 +696,10 @@ class Interface(object): |
|
|
|
self._main_tabs.set_tab_label_text(child, 'Messages (%d)' % |
|
|
|
self._main_tabs.set_tab_label_text(child, 'Messages (%d)' % |
|
|
|
(self._new_message_count)) |
|
|
|
(self._new_message_count)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
child = self._main_tabs.get_nth_page(1) |
|
|
|
|
|
|
|
self._main_tabs.set_tab_label_text(child, 'Replies (%d)' % |
|
|
|
|
|
|
|
(self._new_replies_count)) |
|
|
|
|
|
|
|
|
|
|
|
if self._statusicon: |
|
|
|
if self._statusicon: |
|
|
|
if self._new_message_count == 0: |
|
|
|
if self._new_message_count == 0: |
|
|
|
self._statusicon.set_from_pixbuf(self._images['main']) |
|
|
|
self._statusicon.set_from_pixbuf(self._images['main']) |
|
|
@ -700,6 +708,53 @@ class Interface(object): |
|
|
|
|
|
|
|
|
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _fill_store(self, store, data): |
|
|
|
|
|
|
|
"""Using the results from the request, fill a gtk.Store.""" |
|
|
|
|
|
|
|
for message in data: |
|
|
|
|
|
|
|
_log.debug('Data: %s', str(message)) |
|
|
|
|
|
|
|
message.read = False |
|
|
|
|
|
|
|
pic = message.avatar |
|
|
|
|
|
|
|
if not pic in self._avatars: |
|
|
|
|
|
|
|
# set the user avatar to the default image, so it won't get |
|
|
|
|
|
|
|
# queued again. Once downloaded, the _post_download_pic will |
|
|
|
|
|
|
|
# update the image and force a redraw. |
|
|
|
|
|
|
|
self._avatars[pic] = self._images['avatar'] |
|
|
|
|
|
|
|
self._threads.add_work(self._post_download_pic, |
|
|
|
|
|
|
|
self._exception_download_pic, |
|
|
|
|
|
|
|
self._download_pic, |
|
|
|
|
|
|
|
pic) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
store.prepend([message]) |
|
|
|
|
|
|
|
store.sort_column_changed() |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _update_word_wrap(self, widget): |
|
|
|
|
|
|
|
"""Update the word wrap for the widget.""" |
|
|
|
|
|
|
|
model = widget.get_model() |
|
|
|
|
|
|
|
if len(model) == 0: |
|
|
|
|
|
|
|
# don't try to update if there are no data to be updated |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(win_width, win_height) = self._main_window.get_size() |
|
|
|
|
|
|
|
column = widget.get_column(1) |
|
|
|
|
|
|
|
iter = model.get_iter_first() |
|
|
|
|
|
|
|
path = model.get_path(iter) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cell_rectangle = widget.get_cell_area(path, column) |
|
|
|
|
|
|
|
width = win_width - 70 # 48 = icon size |
|
|
|
|
|
|
|
# TODO: Find out where those 12 pixels came from and/or if they |
|
|
|
|
|
|
|
# are platform specific. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for renderer in column.get_cell_renderers(): |
|
|
|
|
|
|
|
renderer.set_property('wrap-width', width) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while iter: |
|
|
|
|
|
|
|
path = model.get_path(iter) |
|
|
|
|
|
|
|
model.row_changed(path, iter) |
|
|
|
|
|
|
|
iter = model.iter_next(iter) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------ |
|
|
|
# ------------------------------------------------------------ |
|
|
|
# Widget callback functions |
|
|
|
# Widget callback functions |
|
|
|
# ------------------------------------------------------------ |
|
|
|
# ------------------------------------------------------------ |
|
|
@ -764,32 +819,8 @@ class Interface(object): |
|
|
|
"""Called when the window is resized. We use it to set the proper |
|
|
|
"""Called when the window is resized. We use it to set the proper |
|
|
|
word-wrapping in the message column.""" |
|
|
|
word-wrapping in the message column.""" |
|
|
|
|
|
|
|
|
|
|
|
model = self._grid.get_model() |
|
|
|
self._update_word_wrap(self._grid) |
|
|
|
if len(model) == 0: |
|
|
|
self._update_word_wrap(self._replies) |
|
|
|
# nothing in the list, so we don't have what to set proper word |
|
|
|
|
|
|
|
# wrapping |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(win_width, win_height) = self._main_window.get_size() |
|
|
|
|
|
|
|
#_log.debug('Widget size: %d', win_width) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
column = self._grid.get_column(1) |
|
|
|
|
|
|
|
iter = model.get_iter_first() |
|
|
|
|
|
|
|
path = model.get_path(iter) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cell_rectangle = self._grid.get_cell_area(path, column) |
|
|
|
|
|
|
|
width = win_width - 70 # 48 = icon size |
|
|
|
|
|
|
|
# TODO: Find out where those 12 pixels came from and/or if they |
|
|
|
|
|
|
|
# are platform specific. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for renderer in column.get_cell_renderers(): |
|
|
|
|
|
|
|
renderer.set_property('wrap-width', width) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while iter: |
|
|
|
|
|
|
|
path = model.get_path(iter) |
|
|
|
|
|
|
|
model.row_changed(path, iter) |
|
|
|
|
|
|
|
iter = model.iter_next(iter) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
def _order_datetime(self, model, iter1, iter2, user_data=None): |
|
|
|
def _order_datetime(self, model, iter1, iter2, user_data=None): |
|
|
@ -833,8 +864,14 @@ class Interface(object): |
|
|
|
|
|
|
|
|
|
|
|
def _clear_posts(self, widget, user_data=None): |
|
|
|
def _clear_posts(self, widget, user_data=None): |
|
|
|
"""Clear the list of posts from the grid.""" |
|
|
|
"""Clear the list of posts from the grid.""" |
|
|
|
|
|
|
|
page = self._main_tabs.get_current_page() |
|
|
|
|
|
|
|
if page == 0: |
|
|
|
|
|
|
|
# messages |
|
|
|
self._grid.get_model().clear() |
|
|
|
self._grid.get_model().clear() |
|
|
|
self._new_message_count = 0 |
|
|
|
self._new_message_count = 0 |
|
|
|
|
|
|
|
elif page == 1: |
|
|
|
|
|
|
|
self._replies.get_model().clear() |
|
|
|
|
|
|
|
self._new_replies_count = 0 |
|
|
|
self._message_count_updated() |
|
|
|
self._message_count_updated() |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
@ -1038,40 +1075,19 @@ class Interface(object): |
|
|
|
"""Function called after the data from the messages list is |
|
|
|
"""Function called after the data from the messages list is |
|
|
|
retrieved.""" |
|
|
|
retrieved.""" |
|
|
|
_log.debug('%d new tweets', len(results)) |
|
|
|
_log.debug('%d new tweets', len(results)) |
|
|
|
interval = self._options[self.NAMESPACE]['refresh_interval'] |
|
|
|
|
|
|
|
self._update_statusbar('%d new messages retrieved. Next update in ' \ |
|
|
|
|
|
|
|
'%d minutes.' % (len(results), interval)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
store = self._grid.get_model() |
|
|
|
store = self._grid.get_model() |
|
|
|
for message in results: |
|
|
|
self._fill_store(store, results) |
|
|
|
_log.debug('Data: %s', str(message)) |
|
|
|
|
|
|
|
message.read = False |
|
|
|
|
|
|
|
pic = message.avatar |
|
|
|
|
|
|
|
if not pic in self._avatars: |
|
|
|
|
|
|
|
# set the user avatar to the default image, so it won't get |
|
|
|
|
|
|
|
# queued again. Once downloaded, the _post_download_pic will |
|
|
|
|
|
|
|
# update the image and force a redraw. |
|
|
|
|
|
|
|
self._avatars[pic] = self._images['avatar'] |
|
|
|
|
|
|
|
self._threads.add_work(self._post_download_pic, |
|
|
|
|
|
|
|
self._exception_download_pic, |
|
|
|
|
|
|
|
self._download_pic, |
|
|
|
|
|
|
|
pic) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
store.prepend([message]) |
|
|
|
|
|
|
|
store.sort_column_changed() |
|
|
|
|
|
|
|
self._grid.queue_draw() |
|
|
|
self._grid.queue_draw() |
|
|
|
|
|
|
|
|
|
|
|
# once our update went fine, we can queue the next one. This avoids |
|
|
|
|
|
|
|
# any problems if case there is an exception. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
interval = self._options[self.NAMESPACE]['refresh_interval'] |
|
|
|
|
|
|
|
_log.debug('Queueing next refresh in %d minutes', interval) |
|
|
|
|
|
|
|
self._refresh_id = gobject.timeout_add( |
|
|
|
|
|
|
|
interval * 60 * 1000, |
|
|
|
|
|
|
|
self._refresh, None) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self._new_message_count += len(results) |
|
|
|
self._new_message_count += len(results) |
|
|
|
self._message_count_updated() |
|
|
|
self._message_count_updated() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# now get replies |
|
|
|
|
|
|
|
self._update_statusbar('Retrieving replies...') |
|
|
|
|
|
|
|
self._threads.add_work(self._post_get_replies, |
|
|
|
|
|
|
|
self._exception_get_messages, |
|
|
|
|
|
|
|
self._connection.replies) |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
def _exception_get_messages(self, widget, exception): |
|
|
|
def _exception_get_messages(self, widget, exception): |
|
|
@ -1091,6 +1107,31 @@ class Interface(object): |
|
|
|
self._statusicon.set_from_pixbuf(self._images['icon-error']) |
|
|
|
self._statusicon.set_from_pixbuf(self._images['icon-error']) |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### replies callback |
|
|
|
|
|
|
|
def _post_get_replies(self, widget, results): |
|
|
|
|
|
|
|
"""Called after we retrieve the replies.""" |
|
|
|
|
|
|
|
_log.debug("%d new replies", len(results)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
store = self._replies.get_model() |
|
|
|
|
|
|
|
self._fill_store(store, results) |
|
|
|
|
|
|
|
self._replies.queue_draw() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self._new_replies_count += len(results) |
|
|
|
|
|
|
|
self._message_count_updated() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# once our update went fine, we can queue the next one. This avoids |
|
|
|
|
|
|
|
# any problems if case there is an exception. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
interval = self._options[self.NAMESPACE]['refresh_interval'] |
|
|
|
|
|
|
|
_log.debug('Queueing next refresh in %d minutes', interval) |
|
|
|
|
|
|
|
self._update_statusbar('%d new messages retrieved. Next update in ' \ |
|
|
|
|
|
|
|
'%d minutes.' % (len(results), interval)) |
|
|
|
|
|
|
|
self._refresh_id = gobject.timeout_add( |
|
|
|
|
|
|
|
interval * 60 * 1000, |
|
|
|
|
|
|
|
self._refresh, None) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
### image download function |
|
|
|
### image download function |
|
|
|
def _download_pic(self, url): |
|
|
|
def _download_pic(self, url): |
|
|
|
"""Download a picture from the web. Can be used in a thread.""" |
|
|
|
"""Download a picture from the web. Can be used in a thread.""" |
|
|
@ -1250,6 +1291,7 @@ class Interface(object): |
|
|
|
self._reply_message_id = None |
|
|
|
self._reply_message_id = None |
|
|
|
self._favourite_iter = None |
|
|
|
self._favourite_iter = None |
|
|
|
self._new_message_count = 0 |
|
|
|
self._new_message_count = 0 |
|
|
|
|
|
|
|
self._new_replies_count = 0 |
|
|
|
|
|
|
|
|
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|