Make treeview refresh properly when adding nodes.
[straw/fork.git] / straw / Application.py
blob4ff07231c48f53b8002cf55e393390bb8ef42b78
1 """ Application.py
3 This is the main module that binds everything together.
5 """
6 __copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc."
7 __license__ = """ GNU General Public License
9 This program is free software; you can redistribute it and/or modify it under the
10 terms of the GNU General Public License as published by the Free Software
11 Foundation; either version 2 of the License, or (at your option) any later
12 version.
14 This program is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License along with
19 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20 Place - Suite 330, Boston, MA 02111-1307, USA. """
22 import os, gettext, getopt, sys
23 import time
24 from xml.sax import saxutils
25 import pygtk
26 pygtk.require('2.0')
27 import gobject, gtk, gtk.glade
28 import gnome, gconf
30 import Config
31 import error
32 from ItemView import ItemView
33 from ItemList import ItemListPresenter, ItemListView
34 from FeedListView import FeedsView, FeedsPresenter
35 from OfflineToggle import OfflineToggle
36 from MainloopManager import MainloopManager
37 #from Find import FindPresenter, FindView
38 import MVP
39 import ImageCache
40 import PollManager
41 import FeedManager
42 import TreeViewManager
44 import straw
45 from straw import helpers
47 class StatusPresenter(MVP.BasicPresenter):
48 def _initialize(self):
49 self._mmgr = straw.get_status_manager()
50 self._mmgr.connect('changed', self._display)
52 def _display(self, *args):
53 cid = self._view.get_context_id("straw_main")
54 self._view.pop(cid)
55 self._view.push(cid, self._mmgr.read_message())
56 return
58 class ErrorPresenter:
59 def __init__(self, widget):
60 self._widget = widget
61 self._tooltips = gtk.Tooltips()
62 self._text = ''
63 #self._curr_feed = None
64 #self._curr_category = None
65 #fclist = FeedCategoryList.get_instance()
66 #fclist.connect('category-changed', self._category_changed)
68 def feedlist_selection_changed(self, selection, column):
69 errortexts = None
70 return
71 (model, pathlist) = selection.get_selected_rows()
72 iters = [model.get_iter(path) for path in pathlist]
73 if not iters: return
74 errorfeeds = []
75 nodes = [model.get_value(treeiter, column) for treeiter in iters]
76 try:
77 errorfeeds = [node.feed for node in nodes if node.feed and node.feed.error]
78 errortexts = [feed.error for feed in errorfeeds]
79 except TypeError, te:
80 print te
81 ### XXX display OPML Category error too
83 text = list()
84 if errorfeeds:
85 text.append(_("Error:"))
86 text += errortexts
88 if text:
89 t = "\n".join(text)
90 self._tooltips.set_tip(self._widget,t,t)
91 self._tooltips.enable()
92 self._widget.show()
93 else:
94 self._tooltips.disable()
95 self._widget.hide()
96 return
98 def hide(self):
99 self._widget.hide()
101 class MenuFeedPropsPresenter(MVP.BasicPresenter):
102 # FIXME
103 def set_sensitive(self, s):
104 self._view.set_sensitive(s)
105 return
107 class ToolbarView(MVP.WidgetView):
108 """ Widget: gtk.Toolbar"""
109 GCONF_DESKTOP_INTERFACE = "/desktop/gnome/interface"
111 def _initialize(self):
112 client = gconf.client_get_default()
113 if not client.dir_exists(self.GCONF_DESKTOP_INTERFACE):
114 return
115 client.add_dir(self.GCONF_DESKTOP_INTERFACE,
116 gconf.CLIENT_PRELOAD_NONE)
117 client.notify_add(self.GCONF_DESKTOP_INTERFACE+"/toolbar_style",
118 self._toolbar_style_changed)
119 self._widget.set_tooltips(True)
121 def _toolbar_style_changed(self, client, notify_id, entry, *args):
122 value = entry.value.get_string()
123 self._presenter.style_changed(value)
125 def set_style(self, style):
126 self._widget.set_style(style)
128 def get_style(self):
129 client = gconf.client_get_default()
130 current_style = client.get_string(self.GCONF_DESKTOP_INTERFACE+"/toolbar_style")
131 return current_style
133 class ToolbarPresenter(MVP.BasicPresenter):
134 STYLES = {'both':gtk.TOOLBAR_BOTH,
135 'text':gtk.TOOLBAR_TEXT,
136 'icons':gtk.TOOLBAR_ICONS,
137 'both-horiz':gtk.TOOLBAR_BOTH_HORIZ}
139 def _initialize(self):
140 style = self._view.get_style()
141 if style in self.STYLES.keys():
142 self._view.set_style(self.STYLES[style])
143 return
145 def style_changed(self, value):
146 if value in self.STYLES.keys():
147 self._view.set_style(self.STYLES[value])
148 return
150 class ApplicationPresenter(MVP.BasicPresenter):
151 def _initialize(self):
152 self._curr_category = None
153 self._curr_feed = None
154 self._curr_item = None
155 self._init_widgets()
156 self._init_presenters()
157 self._view.present()
159 def _init_widgets(self):
160 widget_tree = self._view.get_widget_tree()
161 self._itemlist_view_notebook = widget_tree.get_widget('mode_view_notebook')
162 self._feedlist_view_notebook = widget_tree.get_widget('left_mode_view_notebook')
164 item_view_container = widget_tree.get_widget('item_view_container')
165 item_list_container = widget_tree.get_widget('item_list_container')
167 parent_paned = item_view_container.get_parent()
168 parent_parent_widget = parent_paned.get_parent()
170 config = Config.get_instance()
172 child_list = []
173 layout = config.pane_layout
174 if layout == 'vertical':
175 new_paned = gtk.VPaned()
176 child_list.append(item_list_container)
177 child_list.append(item_view_container)
178 elif layout == 'horizontal':
179 new_paned = gtk.HPaned()
180 child_list.append(item_view_container)
181 child_list.append(item_list_container)
182 else: # Use vertical layout as default
183 new_paned = gtk.VPaned()
184 child_list.append(item_list_container)
185 child_list.append(item_view_container)
187 for child in child_list:
188 child.reparent(new_paned)
190 parent_parent_widget.remove(parent_paned)
191 parent_parent_widget.add(new_paned)
193 new_paned.show()
195 def _init_presenters(self):
196 widget_tree = self._view.get_widget_tree()
197 toolbar_presenter = ToolbarPresenter(view=ToolbarView(widget_tree.get_widget('toolbar_default')))
198 self._error_presenter = ErrorPresenter(widget_tree.get_widget('statusbar_error_indicator'))
200 self._item_view = ItemView(widget_tree.get_widget('item_view_container'))
202 view = ItemListView(widget_tree.get_widget('item_selection_treeview'))
203 self._itemlist_presenter = ItemListPresenter(view=view)
204 view.add_selection_changed_listener(self._item_view)
206 view = FeedsView(widget_tree.get_widget('feed_selection_treeview'))
207 self._feed_list_presenter = FeedsPresenter(view=view)
208 view.add_selection_changed_listener(self._itemlist_presenter)
209 view.add_selection_changed_listener(self._error_presenter)
211 self._offline_presenter = OfflineToggle(widget_tree.get_widget('offline_toggle'))
213 self._status_presenter = StatusPresenter(view = widget_tree.get_widget("main_statusbar"))
214 self._menufp_presenter = MenuFeedPropsPresenter( view = widget_tree.get_widget('feed_information'))
215 self._menufp_presenter.set_sensitive(False)
216 # self._find_presenter = FindPresenter(view=FindView(widget_tree.get_widget("find_vbox")))
217 return
219 def copy_itemview_text_selection(self):
220 helpers.set_clipboard_text(self._item_view.get_selected_text())
222 def check_allocation(self, widget, event):
223 config = Config.get_instance()
224 def check_size((width, height, widget)):
225 if width == widget.allocation.width and height == widget.allocation.height:
226 config.main_window_size = (width, height)
227 if event.width != widget.allocation.width or event.height != widget.allocation.height:
228 gobject.timeout_add(1000, check_size, (
229 (event.width, event.height, widget)))
231 def check_main_pane_position(self, widget):
232 config = Config.get_instance()
233 def check_position((position, widget)):
234 if position == widget.get_position():
235 config.main_pane_position = position
236 pos = widget.get_position()
237 if pos != config.main_pane_position:
238 gobject.timeout_add(1000, check_position, (pos, widget))
240 def check_sub_pane_position(self, widget):
241 config = Config.get_instance()
242 def check_position((position, widget)):
243 if position == widget.get_position():
244 config.sub_pane_position = position
245 pos = widget.get_position()
246 if pos != config.sub_pane_position:
247 gobject.timeout_add(1000, check_position, (pos, widget))
249 def credits(self):
250 return helpers.credits()
252 def poll_all(self):
253 if self._warn_if_offline():
254 #fclist = feeds.category_list
255 #self._poll_categories(fclist.all_categories)
256 print "go!"
257 FeedManager.update_all_feeds({})
258 return
260 def poll_current_category(self):
261 if self._warn_if_offline():
262 self._poll_categories([self._curr_category])
263 return
265 def poll_current_feed(self):
266 if self._warn_if_offline():
267 pm = PollManager.get_instance()
268 pm.poll([self._curr_feed])
269 return
271 def mark_feed_as_read(self):
272 self._curr_feed.mark_all_read()
274 def mark_all_as_read(self):
275 print "TODO mark_all_as_read"
277 def remove_selected_feed(self):
278 # self._feed_list_presenter.remove_selected_feed()
279 pass
281 def show_search(self):
282 # self._find_presenter.item_list.signal_connect(Event.ItemSelectionChangedSignal,
283 # self._item_view.item_selection_changed)
284 self._item_view.display_empty_search()
285 self._itemlist_view_notebook.set_current_page(1)
286 self._feedlist_view_notebook.set_current_page(1)
287 self._feedinfo_presenter.hide()
289 def hide_search(self):
290 # self._find_presenter.clear()
291 self._itemlist_view_notebook.set_current_page(0)
292 self._feedlist_view_notebook.set_current_page(0)
293 self._feedinfo_presenter.show()
294 # self._find_presenter.item_list.signal_disconnect(Event.ItemSelectionChangedSignal,
295 # self._item_view.item_selection_changed)
298 def _poll_categories(self, fclist):
299 pm = PollManager.get_instance()
300 pm.poll_categories(fclist)
301 return
303 def _warn_if_offline(self):
304 config = Config.get_instance()
305 will_poll = False
306 if config.offline:
307 response = self._view.show_offline_dialog()
308 if response == gtk.RESPONSE_OK:
309 config.offline = not config.offline
310 will_poll = True
311 else:
312 will_poll = True
313 return will_poll
315 def display_previous_feed(self, item = None):
317 Displays the feed before the current selected feed
319 self._feed_list_presenter.select_previous_feed()
321 def display_next_feed(self, item=None):
323 Displays the feed after the current selected feed
325 self._feed_list_presenter.select_next_feed()
326 return
328 def display_next_unread_feed(self):
330 Displays the next feed with an unread item
332 self._feed_list_presenter.select_next_feed(with_unread=True)
333 pass
335 def display_previous_item(self, item=None):
337 Displays the item before the current selected item. If the item is the
338 first item, scrolls to the previous feed
340 is_prev = self._itemlist_presenter.select_previous_item()
341 if not is_prev:
342 # TODO HACK - implement select_previous_feed(select_last=True) ...
343 # ... to select previous feed's last item
344 self._feed_list_presenter.select_previous_feed()
345 self._itemlist_presenter.select_last_item()
346 pass
347 return
349 def display_next_item(self, item=None):
351 Displays the item after the current selected item. If the item is the
352 last item, selectes the next feed. If the current feed is the last
353 feed in the list, it goes back and selects the first feed
355 is_next = self._itemlist_presenter.select_next_item()
356 if not is_next:
357 is_next_feed = self._feed_list_presenter.select_next_feed()
358 if not is_next_feed:
359 self._feed_list_presenter.select_firsteed_feed()
360 return
362 def scroll_or_display_next_unread_item(self, item=None):
363 has_unread_item = False
364 if not self._item_view.scroll_down():
365 has_unread_item = self._itemlist_presenter.select_next_unread_item()
366 if not has_unread_item:
367 self._feed_list_presenter.select_next_feed(with_unread=True)
368 return
370 def show_preferences_dialog(self, parent):
371 straw.preferences_show()
373 def show_feed_properties(self, parent):
374 straw.feed_properties_show(self._curr_feed)
376 def quit(self):
377 gtk.main_quit()
378 ItemStore.get_instance().stop()
379 return
381 def _setup_filechooser_dialog(self, title, action, extra_widget_title):
383 Setup the file chooser dialog. This includes an extra widget (a combobox)
384 to include the categories to import or export
386 dialog = gtk.FileChooserDialog(title, action=action,
387 buttons=(gtk.STOCK_CANCEL,
388 gtk.RESPONSE_CANCEL,
389 gtk.STOCK_OK, gtk.RESPONSE_OK))
390 category_list = []
391 model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
392 combobox = gtk.ComboBox(model)
393 celltitle = gtk.CellRendererText()
394 combobox.pack_start(celltitle,False)
395 combobox.add_attribute(celltitle, 'text', 0)
397 # add 'All' Category
398 it = model.append()
399 #model.set(it, 0, category_list.all_category.title,
400 # 1, category_list.all_category)
402 # add user categories
403 #for category in category_list.user_categories:
404 # it = model.append()
405 # model.set(it, 0, category.title, 1, category)
407 # ..
408 combobox.set_active(0)
409 label = gtk.Label(extra_widget_title)
410 label.set_alignment(1.0,0.5)
411 hbox = gtk.HBox(spacing=6)
412 hbox.pack_start(label,True,True,0)
413 hbox.pack_end(combobox,False,False,0)
414 hbox.show_all()
416 dialog.set_extra_widget(hbox)
417 return (dialog, combobox)
420 def import_subscriptions(self, parent):
421 (dialog,combobox) = self._setup_filechooser_dialog(_("Import Subscriptions"),
422 gtk.FILE_CHOOSER_ACTION_OPEN,
423 _("Add new subscriptions in:"))
424 ffilter = gtk.FileFilter()
425 ffilter.set_name(_("OPML Files Only"))
426 ffilter.add_pattern("*.xml")
427 ffilter.add_pattern("*.opml")
428 dialog.add_filter(ffilter)
429 dialog.set_transient_for(parent)
430 response = dialog.run()
431 if response == gtk.RESPONSE_OK:
432 filename = dialog.get_filename()
433 model = combobox.get_model()
434 cat = model[combobox.get_active()][1]
435 dialog.hide()
436 FeedManager.import_opml(filename)
437 dialog.destroy()
439 def export_subscriptions(parent):
440 (dialog,combobox) = _setup_filechooser_dialog(_("Export Subscriptions"),
441 gtk.FILE_CHOOSER_ACTION_SAVE,
442 _("Select category to export:"))
443 def selection_changed(widget, dialog):
444 model = widget.get_model()
445 category = model[widget.get_active()][1]
446 dialog.set_current_name("Straw-%s.xml" % category.title)
448 combobox.connect('changed',
449 selection_changed,
450 dialog)
451 selection_changed(combobox, dialog)
452 dialog.set_transient_for(parent)
453 response = dialog.run()
454 if response == gtk.RESPONSE_OK:
455 filename = dialog.get_filename()
456 model = combobox.get_model()
457 cat = model[combobox.get_active()][1]
458 opml.export(cat.title, cat.feeds, filename)
459 dialog.destroy()
462 class ApplicationView(MVP.WidgetView):
464 Widget: straw_main
466 def _initialize(self):
467 self._config = Config.get_instance()
468 self._initialize_dnd()
469 self._initialize_window_updater()
470 self._create_unmodified_accelerator_group()
471 self._attach_unmodified_accelerator_group()
472 self._initialize_window()
473 self._find_toggled = False
475 def _initialize_window(self):
476 widget_tree = gtk.glade.get_widget_tree(self._widget)
477 if self._config.window_maximized:
478 self._widget.maximize()
479 else:
480 # we use resize here since configure-event seems to
481 # overwrite the default size if we use set_default_size.
482 self._widget.resize(*self._config.main_window_size)
483 mmp = widget_tree.get_widget('main_main_pane')
484 msp = widget_tree.get_widget('main_sub_pane')
485 mmp.set_position(self._config.main_pane_position)
486 msp.set_position(self._config.sub_pane_position)
488 def _initialize_dnd(self):
489 self._widget.drag_dest_set(
490 gtk.DEST_DEFAULT_ALL,
491 [('_NETSCAPE_URL', 0, 0), ('text/uri-list ', 0, 1),
492 ('x-url/http', 0, 2)],
493 gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
494 return
496 def _initialize_window_updater(self):
497 #feedlist.signal_connect(Event.AllItemsReadSignal,
498 # lambda signal: self._update_title(feedlist))
499 #feedlist.signal_connect(Event.ItemReadSignal,
500 # lambda signal: self._update_title(feedlist))
501 #feedlist.signal_connect(Event.ItemsAddedSignal,
502 # lambda signal: self._update_title(feedlist))
503 #feedlist.signal_connect(Event.FeedsChangedSignal,
504 # lambda signal: self._update_title(feedlist))
505 pass
507 def _update_title(self, flist):
508 uritems = urfeeds = 0
509 sfeeds = "feeds"
510 listfeeds = flist.flatten_list()
511 for ur in [f.number_of_unread for f in listfeeds]:
512 if ur:
513 uritems += ur
514 urfeeds += 1
515 else:
516 urfeeds = len(listfeeds)
517 if urfeeds < 2:
518 sfeeds = "feed"
519 item_feed_map = {'uritems': uritems,
520 'urfeeds': urfeeds,
521 'fstring' : sfeeds}
522 title = _('%(uritems)d unread in %(urfeeds)d %(fstring)s') % item_feed_map
523 self._widget.set_title( title + " - %s" % straw.PACKAGE)
524 return
526 # We have a separate accelerator group for the unmodified and
527 # shifted accelerators, that is, stuff like space, N, P, etc. This
528 # is so that we can have the find pane work correctly
529 def _create_unmodified_accelerator_group(self):
530 xml = gtk.glade.get_widget_tree(self._widget)
531 agroup = gtk.AccelGroup()
532 accels = (('feed_mark_as_read', 'R', gtk.gdk.SHIFT_MASK),
533 ('feed_mark_all_as_read', 'A', gtk.gdk.SHIFT_MASK),
534 ('next_item', 'N', gtk.gdk.SHIFT_MASK),
535 ('next_unread_feed', ' ', 0),
536 ('previous_item', 'P', gtk.gdk.SHIFT_MASK))
537 for widget_name, key, mask in accels:
538 widget = xml.get_widget(widget_name)
539 widget.add_accelerator("activate", agroup, ord(key), mask,
540 gtk.ACCEL_VISIBLE)
541 self._unmodified_accelerator_group = agroup
543 def _on_category_add_activate(self, *args):
544 print "add!"
546 def _attach_unmodified_accelerator_group(self):
547 self._widget.add_accel_group(self._unmodified_accelerator_group)
549 def _detach_unmodified_accelerator_group(self):
550 self._widget.remove_accel_group(self._unmodified_accelerator_group)
552 def _on_straw_main_destroy_event(self, *args):
553 return self._presenter.quit()
555 def _on_straw_main_delete_event(self, *args):
556 return self._presenter.quit()
558 def _on_straw_main_configure_event(self, widget, event, *args):
559 if widget.window.get_state() is not gtk.gdk.WINDOW_STATE_MAXIMIZED:
560 self._config.window_maximized = False
561 self._presenter.check_allocation(widget, event)
562 else:
563 self._config.window_maximized = True
564 return
566 def _on_main_main_pane_size_allocate(self, widget, *args):
567 self._presenter.check_main_pane_position(widget)
569 def _on_main_sub_pane_size_allocate(self, widget, *args):
570 self._presenter.check_sub_pane_position(widget)
572 # Actions menu
573 def _on_feed_subscribe_activate(self, *args):
574 straw.subscribe_show(parent=self._widget)
576 def _on_feed_unsubscribe_activate(self, *args):
577 self._presenter.remove_selected_feed()
579 def _on_subscription_import_activate(self, *args):
580 self._presenter.import_subscriptions(self._widget)
582 def _on_subscription_export_activate(self, *args):
583 self._presenter.export_subscriptions(self._widget)
585 def _on_feed_mark_as_read_activate(self, *args):
586 self._presenter.mark_feed_as_read()
588 def _on_feed_mark_all_as_read_activate(self, *args):
589 self._presenter.mark_all_as_read()
591 def _on_feed_refresh_selected_activate(self, *args):
592 self._presenter.poll_current_feed()
594 def _on_subscription_refresh_activate(self, *args):
595 self._presenter.poll_all()
597 def _on_quit_activate(self, *args):
598 return self._presenter.quit()
600 # Edit menu
601 def _on_copy_activate(self, *args):
602 self._presenter.copy_itemview_text_selection()
605 def _on_find_activate(self, widget, *args):
606 xml = gtk.glade.get_widget_tree(self._widget)
607 menu_find = xml.get_widget('menu_find')
608 accel_label = menu_find.get_child()
610 if not self._find_toggled:
611 self._presenter.show_search()
612 self._detach_unmodified_accelerator_group()
613 self._find_toggled = True
615 # save the "Find..." stock text for later recovery
616 self._old_label_text = accel_label.get_text()
617 accel_label.set_text(_('Return to feed list...'))
619 else:
620 self._presenter.hide_search()
621 self._attach_unmodified_accelerator_group()
622 self._find_toggled = False
623 accel_label.set_text(self._old_label_text)
625 def _on_preferences_activate(self, *args):
626 self._presenter.show_preferences_dialog(self._widget)
628 def _on_feed_information_activate(self, *args):
629 self._presenter.show_feed_properties(self._widget)
631 # View menu
632 def _on_previous_item_activate(self, *args):
633 self._presenter.display_previous_item()
635 def _on_next_item_activate(self, *args):
636 self._presenter.display_next_item()
638 def _on_next_feed_activate(self, *args):
639 self._presenter.display_next_feed()
641 def _on_previous_feed_activate(self, *args):
642 self._presenter.display_previous_feed()
644 def _on_next_unread_feed_activate(self, *args):
645 self._presenter.display_next_unread_feed()
647 def _on_scroll_unread_items_activate(self, *args):
648 self._presenter.scroll_or_display_next_unread_item()
650 # Help menu
652 def _on_report_problem_activate(self, menuitem, *args):
653 helpers.url_show("http://bugzilla.gnome.org/simple-bug-guide.cgi?product=straw")
655 def _on_about_activate(self, menuitem, *args):
656 widget = self._presenter.credits()
657 widget.show()
659 def _on_straw_main_drag_data_received(self, w, context,
660 x, y, data, info, time):
661 if data and data.format == 8:
662 url = data.data.split("\n")[0]
663 straw.subscribe_show("%s" % url, self._widget)
664 context.finish(True, False, time)
665 else:
666 context.finish(False, False, time)
668 # FIXME
669 def show_offline_dialog(self):
670 return helpers.report_offline_status(self._widget)
672 def get_widget_tree(self):
673 return gtk.glade.get_widget_tree(self._widget)
675 def present(self):
676 self._widget.present()
678 def should_present(self):
679 if self._widget.window.get_state() is not gtk.gdk.WINDOW_STATE_WITHDRAWN:
680 self._widget.hide()
681 else:
682 self._widget.present()
685 class Application:
686 def __init__(self):
687 gnome.program_init(straw.PACKAGE, straw.VERSION)
688 # initialize threading and environment
689 gobject.threads_init()
690 config = Config.get_instance()
691 config.reload_css = os.getenv('STRAW_RELOAD_CSS') is not None
693 # build tray status icon
694 image = gtk.Image()
695 iconfile = os.path.normpath(straw.STRAW_DATA_DIR + "/straw.png")
696 pixbuf = gtk.gdk.pixbuf_new_from_file(iconfile)
697 scaled_buf = pixbuf.scale_simple(16,16,gtk.gdk.INTERP_BILINEAR)
698 image.set_from_pixbuf(scaled_buf)
699 try:
700 self.tray = gtk.status_icon_new_from_pixbuf(scaled_buf)
701 self.tray.connect('activate', self._tray_clicked)
702 except AttributeError:
703 import egg
704 import egg.trayicon
705 self.tray = egg.trayicon.TrayIcon(straw.PACKAGE);
706 self._eventbox = gtk.EventBox()
707 self.tray.add(self._eventbox)
708 self._eventbox.connect('button_press_event', self._tray_clicked)
709 self._eventbox.connect("drag-data-received", self._on_drag_data_received)
710 self._eventbox.drag_dest_set(
711 gtk.DEST_DEFAULT_ALL,
712 [('_NETSCAPE_URL', 0, 0),('text/uri-list ', 0, 1),('x-url/http', 0, 2)],
713 gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
714 self._eventbox.add(image)
715 self.tray.show_all()
717 self._tooltip = gtk.Tooltips()
718 self.unread_count = 0
719 # end build tray status icon
721 FeedManager.setup(storage_path = "test.db")
722 FeedManager.init()
724 TreeViewManager.setup(storage_path = "test.db")
725 TreeViewManager.init()
726 #FeedManager.import_opml(os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml"))
728 #if config.first_time:
729 # filepath = os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml")
730 # feeds.import_opml(filepath)
732 #ImageCache.initialize()
734 xml = gtk.glade.XML(os.path.join(straw.STRAW_DATA_DIR,'straw.glade'), "straw_main", gettext.textdomain())
735 window = xml.get_widget('straw_main')
736 self._main_presenter = ApplicationPresenter(view = ApplicationView(window))
737 self._main_presenter.view.present()
739 #PollManager.get_instance().start_polling_loop()
740 # set the default icon for the windows
741 iconfile = os.path.join(straw.STRAW_DATA_DIR,"straw.png")
742 gtk.window_set_default_icon(gtk.gdk.pixbuf_new_from_file(iconfile))
744 straw.start_services()
746 def mainloop(self):
747 gtk.gdk.threads_init()
748 gtk.gdk.threads_enter()
749 gtk.main()
750 gtk.gdk.threads_leave()
751 return
753 def _update(self,flist):
754 uritems = urfeeds = 0
755 for ur in [f.number_of_unread for f in flist.flatten_list()]:
756 if ur:
757 uritems += ur
758 urfeeds += 1
759 if uritems == self.unread_count:
760 return
761 self.unread_count = uritems
762 if uritems:
763 self._tooltip.set_tip(self.tray, _("%d new items")%uritems)
764 self.tray.show_all()
765 else:
766 self.tray.hide()
767 return
770 def _tray_clicked(self, widget, event=None):
771 self._main_presenter.view.should_present()
772 if event and not (event.button == 1):
773 return
774 self._main_presenter.scroll_or_display_next_unread_item()
776 def _on_drag_data_received(self, widget, context, x, y, data, info, timestamp):
777 if data and data.format == 8:
778 url = data.data.split("\n")[0]
779 straw.subscribe_show(url="%s" % url)
780 context.finish(True, False, timestamp)
781 else:
782 context.finish(False, False, timestamp)
783 return