Intermediate work commit.
[straw/fork.git] / straw / Application.py
blob3e895d5dd5a67387c582cf0fbbd4e9531a6541ce
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 FeedManager.update_all_feeds({})
256 def poll_current_category(self):
257 if self._warn_if_offline():
258 self._poll_categories([self._curr_category])
259 return
261 def poll_current_feed(self):
262 if self._warn_if_offline():
263 pm = PollManager.get_instance()
264 pm.poll([self._curr_feed])
265 return
267 def mark_feed_as_read(self):
268 self._curr_feed.mark_all_read()
270 def mark_all_as_read(self):
271 print "TODO mark_all_as_read"
273 def remove_selected_feed(self):
274 # self._feed_list_presenter.remove_selected_feed()
275 pass
277 def show_search(self):
278 # self._find_presenter.item_list.signal_connect(Event.ItemSelectionChangedSignal,
279 # self._item_view.item_selection_changed)
280 self._item_view.display_empty_search()
281 self._itemlist_view_notebook.set_current_page(1)
282 self._feedlist_view_notebook.set_current_page(1)
283 self._feedinfo_presenter.hide()
285 def hide_search(self):
286 # self._find_presenter.clear()
287 self._itemlist_view_notebook.set_current_page(0)
288 self._feedlist_view_notebook.set_current_page(0)
289 self._feedinfo_presenter.show()
290 # self._find_presenter.item_list.signal_disconnect(Event.ItemSelectionChangedSignal,
291 # self._item_view.item_selection_changed)
294 def _poll_categories(self, fclist):
295 pm = PollManager.get_instance()
296 pm.poll_categories(fclist)
297 return
299 def _warn_if_offline(self):
300 config = Config.get_instance()
301 will_poll = False
302 if config.offline:
303 response = self._view.show_offline_dialog()
304 if response == gtk.RESPONSE_OK:
305 config.offline = not config.offline
306 will_poll = True
307 else:
308 will_poll = True
309 return will_poll
311 def display_previous_feed(self, item = None):
313 Displays the feed before the current selected feed
315 self._feed_list_presenter.select_previous_feed()
317 def display_next_feed(self, item=None):
319 Displays the feed after the current selected feed
321 self._feed_list_presenter.select_next_feed()
322 return
324 def display_next_unread_feed(self):
326 Displays the next feed with an unread item
328 self._feed_list_presenter.select_next_feed(with_unread=True)
329 pass
331 def display_previous_item(self, item=None):
333 Displays the item before the current selected item. If the item is the
334 first item, scrolls to the previous feed
336 is_prev = self._itemlist_presenter.select_previous_item()
337 if not is_prev:
338 # TODO HACK - implement select_previous_feed(select_last=True) ...
339 # ... to select previous feed's last item
340 self._feed_list_presenter.select_previous_feed()
341 self._itemlist_presenter.select_last_item()
342 pass
343 return
345 def display_next_item(self, item=None):
347 Displays the item after the current selected item. If the item is the
348 last item, selectes the next feed. If the current feed is the last
349 feed in the list, it goes back and selects the first feed
351 is_next = self._itemlist_presenter.select_next_item()
352 if not is_next:
353 is_next_feed = self._feed_list_presenter.select_next_feed()
354 if not is_next_feed:
355 self._feed_list_presenter.select_firsteed_feed()
356 return
358 def scroll_or_display_next_unread_item(self, item=None):
359 has_unread_item = False
360 if not self._item_view.scroll_down():
361 has_unread_item = self._itemlist_presenter.select_next_unread_item()
362 if not has_unread_item:
363 self._feed_list_presenter.select_next_feed(with_unread=True)
364 return
366 def show_preferences_dialog(self, parent):
367 straw.preferences_show()
369 def show_feed_properties(self, parent):
370 straw.feed_properties_show(self._curr_feed)
372 def quit(self):
373 gtk.main_quit()
375 def _setup_filechooser_dialog(self, title, action, extra_widget_title):
377 Setup the file chooser dialog. This includes an extra widget (a combobox)
378 to include the categories to import or export
380 dialog = gtk.FileChooserDialog(title, action=action,
381 buttons=(gtk.STOCK_CANCEL,
382 gtk.RESPONSE_CANCEL,
383 gtk.STOCK_OK, gtk.RESPONSE_OK))
384 category_list = []
385 model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
386 combobox = gtk.ComboBox(model)
387 celltitle = gtk.CellRendererText()
388 combobox.pack_start(celltitle,False)
389 combobox.add_attribute(celltitle, 'text', 0)
391 # add 'All' Category
392 it = model.append()
393 #model.set(it, 0, category_list.all_category.title,
394 # 1, category_list.all_category)
396 # add user categories
397 #for category in category_list.user_categories:
398 # it = model.append()
399 # model.set(it, 0, category.title, 1, category)
401 # ..
402 combobox.set_active(0)
403 label = gtk.Label(extra_widget_title)
404 label.set_alignment(1.0,0.5)
405 hbox = gtk.HBox(spacing=6)
406 hbox.pack_start(label,True,True,0)
407 hbox.pack_end(combobox,False,False,0)
408 hbox.show_all()
410 dialog.set_extra_widget(hbox)
411 return (dialog, combobox)
414 def import_subscriptions(self, parent):
415 (dialog,combobox) = self._setup_filechooser_dialog(_("Import Subscriptions"),
416 gtk.FILE_CHOOSER_ACTION_OPEN,
417 _("Add new subscriptions in:"))
418 ffilter = gtk.FileFilter()
419 ffilter.set_name(_("OPML Files Only"))
420 ffilter.add_pattern("*.xml")
421 ffilter.add_pattern("*.opml")
422 dialog.add_filter(ffilter)
423 dialog.set_transient_for(parent)
424 response = dialog.run()
425 if response == gtk.RESPONSE_OK:
426 filename = dialog.get_filename()
427 model = combobox.get_model()
428 cat = model[combobox.get_active()][1]
429 dialog.hide()
430 FeedManager.import_opml(filename)
431 dialog.destroy()
433 def export_subscriptions(parent):
434 (dialog,combobox) = _setup_filechooser_dialog(_("Export Subscriptions"),
435 gtk.FILE_CHOOSER_ACTION_SAVE,
436 _("Select category to export:"))
437 def selection_changed(widget, dialog):
438 model = widget.get_model()
439 category = model[widget.get_active()][1]
440 dialog.set_current_name("Straw-%s.xml" % category.title)
442 combobox.connect('changed',
443 selection_changed,
444 dialog)
445 selection_changed(combobox, dialog)
446 dialog.set_transient_for(parent)
447 response = dialog.run()
448 if response == gtk.RESPONSE_OK:
449 filename = dialog.get_filename()
450 model = combobox.get_model()
451 cat = model[combobox.get_active()][1]
452 opml.export(cat.title, cat.feeds, filename)
453 dialog.destroy()
456 class ApplicationView(MVP.WidgetView):
458 Widget: straw_main
460 def _initialize(self):
461 self._config = Config.get_instance()
462 self._initialize_dnd()
463 self._initialize_window_updater()
464 self._create_unmodified_accelerator_group()
465 self._attach_unmodified_accelerator_group()
466 self._initialize_window()
467 self._find_toggled = False
469 def _initialize_window(self):
470 widget_tree = gtk.glade.get_widget_tree(self._widget)
471 if self._config.window_maximized:
472 self._widget.maximize()
473 else:
474 # we use resize here since configure-event seems to
475 # overwrite the default size if we use set_default_size.
476 self._widget.resize(*self._config.main_window_size)
477 mmp = widget_tree.get_widget('main_main_pane')
478 msp = widget_tree.get_widget('main_sub_pane')
479 mmp.set_position(self._config.main_pane_position)
480 msp.set_position(self._config.sub_pane_position)
482 def _initialize_dnd(self):
483 self._widget.drag_dest_set(
484 gtk.DEST_DEFAULT_ALL,
485 [('_NETSCAPE_URL', 0, 0), ('text/uri-list ', 0, 1),
486 ('x-url/http', 0, 2)],
487 gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
488 return
490 def _initialize_window_updater(self):
491 #feedlist.signal_connect(Event.AllItemsReadSignal,
492 # lambda signal: self._update_title(feedlist))
493 #feedlist.signal_connect(Event.ItemReadSignal,
494 # lambda signal: self._update_title(feedlist))
495 #feedlist.signal_connect(Event.ItemsAddedSignal,
496 # lambda signal: self._update_title(feedlist))
497 #feedlist.signal_connect(Event.FeedsChangedSignal,
498 # lambda signal: self._update_title(feedlist))
499 pass
501 def _update_title(self, flist):
502 uritems = urfeeds = 0
503 sfeeds = "feeds"
504 listfeeds = flist.flatten_list()
505 for ur in [f.number_of_unread for f in listfeeds]:
506 if ur:
507 uritems += ur
508 urfeeds += 1
509 else:
510 urfeeds = len(listfeeds)
511 if urfeeds < 2:
512 sfeeds = "feed"
513 item_feed_map = {'uritems': uritems,
514 'urfeeds': urfeeds,
515 'fstring' : sfeeds}
516 title = _('%(uritems)d unread in %(urfeeds)d %(fstring)s') % item_feed_map
517 self._widget.set_title( title + " - %s" % straw.PACKAGE)
518 return
520 # We have a separate accelerator group for the unmodified and
521 # shifted accelerators, that is, stuff like space, N, P, etc. This
522 # is so that we can have the find pane work correctly
523 def _create_unmodified_accelerator_group(self):
524 xml = gtk.glade.get_widget_tree(self._widget)
525 agroup = gtk.AccelGroup()
526 accels = (('feed_mark_as_read', 'R', gtk.gdk.SHIFT_MASK),
527 ('feed_mark_all_as_read', 'A', gtk.gdk.SHIFT_MASK),
528 ('next_item', 'N', gtk.gdk.SHIFT_MASK),
529 ('next_unread_feed', ' ', 0),
530 ('previous_item', 'P', gtk.gdk.SHIFT_MASK))
531 for widget_name, key, mask in accels:
532 widget = xml.get_widget(widget_name)
533 widget.add_accelerator("activate", agroup, ord(key), mask,
534 gtk.ACCEL_VISIBLE)
535 self._unmodified_accelerator_group = agroup
537 def _on_category_add_activate(self, *args):
538 print "add!"
540 def _attach_unmodified_accelerator_group(self):
541 self._widget.add_accel_group(self._unmodified_accelerator_group)
543 def _detach_unmodified_accelerator_group(self):
544 self._widget.remove_accel_group(self._unmodified_accelerator_group)
546 def _on_straw_main_destroy_event(self, *args):
547 return self._presenter.quit()
549 def _on_straw_main_delete_event(self, *args):
550 return self._presenter.quit()
552 def _on_straw_main_configure_event(self, widget, event, *args):
553 if widget.window.get_state() is not gtk.gdk.WINDOW_STATE_MAXIMIZED:
554 self._config.window_maximized = False
555 self._presenter.check_allocation(widget, event)
556 else:
557 self._config.window_maximized = True
558 return
560 def _on_main_main_pane_size_allocate(self, widget, *args):
561 self._presenter.check_main_pane_position(widget)
563 def _on_main_sub_pane_size_allocate(self, widget, *args):
564 self._presenter.check_sub_pane_position(widget)
566 # Actions menu
567 def _on_feed_subscribe_activate(self, *args):
568 straw.subscribe_show(parent=self._widget)
570 def _on_feed_unsubscribe_activate(self, *args):
571 self._presenter.remove_selected_feed()
573 def _on_subscription_import_activate(self, *args):
574 self._presenter.import_subscriptions(self._widget)
576 def _on_subscription_export_activate(self, *args):
577 self._presenter.export_subscriptions(self._widget)
579 def _on_feed_mark_as_read_activate(self, *args):
580 self._presenter.mark_feed_as_read()
582 def _on_feed_mark_all_as_read_activate(self, *args):
583 self._presenter.mark_all_as_read()
585 def _on_feed_refresh_selected_activate(self, *args):
586 self._presenter.poll_current_feed()
588 def _on_subscription_refresh_activate(self, *args):
589 #print self._widget.get_children()[0].set_stock_id(gtk.STOCK_STOP)
590 self._presenter.poll_all()
592 def _on_quit_activate(self, *args):
593 return self._presenter.quit()
595 # Edit menu
596 def _on_copy_activate(self, *args):
597 self._presenter.copy_itemview_text_selection()
600 def _on_find_activate(self, widget, *args):
601 xml = gtk.glade.get_widget_tree(self._widget)
602 menu_find = xml.get_widget('menu_find')
603 accel_label = menu_find.get_child()
605 if not self._find_toggled:
606 self._presenter.show_search()
607 self._detach_unmodified_accelerator_group()
608 self._find_toggled = True
610 # save the "Find..." stock text for later recovery
611 self._old_label_text = accel_label.get_text()
612 accel_label.set_text(_('Return to feed list...'))
614 else:
615 self._presenter.hide_search()
616 self._attach_unmodified_accelerator_group()
617 self._find_toggled = False
618 accel_label.set_text(self._old_label_text)
620 def _on_preferences_activate(self, *args):
621 self._presenter.show_preferences_dialog(self._widget)
623 def _on_feed_information_activate(self, *args):
624 self._presenter.show_feed_properties(self._widget)
626 # View menu
627 def _on_previous_item_activate(self, *args):
628 self._presenter.display_previous_item()
630 def _on_next_item_activate(self, *args):
631 self._presenter.display_next_item()
633 def _on_next_feed_activate(self, *args):
634 self._presenter.display_next_feed()
636 def _on_previous_feed_activate(self, *args):
637 self._presenter.display_previous_feed()
639 def _on_next_unread_feed_activate(self, *args):
640 self._presenter.display_next_unread_feed()
642 def _on_scroll_unread_items_activate(self, *args):
643 self._presenter.scroll_or_display_next_unread_item()
645 # Help menu
647 def _on_report_problem_activate(self, menuitem, *args):
648 helpers.url_show("http://bugzilla.gnome.org/simple-bug-guide.cgi?product=straw")
650 def _on_about_activate(self, menuitem, *args):
651 widget = self._presenter.credits()
652 widget.show()
654 def _on_straw_main_drag_data_received(self, w, context,
655 x, y, data, info, time):
656 if data and data.format == 8:
657 url = data.data.split("\n")[0]
658 straw.subscribe_show("%s" % url, self._widget)
659 context.finish(True, False, time)
660 else:
661 context.finish(False, False, time)
663 # FIXME
664 def show_offline_dialog(self):
665 return helpers.report_offline_status(self._widget)
667 def get_widget_tree(self):
668 return gtk.glade.get_widget_tree(self._widget)
670 def present(self):
671 self._widget.present()
673 def should_present(self):
674 if self._widget.window.get_state() is not gtk.gdk.WINDOW_STATE_WITHDRAWN:
675 self._widget.hide()
676 else:
677 self._widget.present()
680 class Application:
681 def __init__(self):
682 gnome.program_init(straw.PACKAGE, straw.VERSION)
684 error.setup_log()
685 # initialize threading and environment
686 gobject.threads_init()
687 config = Config.get_instance()
688 config.reload_css = os.getenv('STRAW_RELOAD_CSS') is not None
690 # build tray status icon
691 image = gtk.Image()
692 iconfile = os.path.normpath(straw.STRAW_DATA_DIR + "/straw.png")
693 pixbuf = gtk.gdk.pixbuf_new_from_file(iconfile)
694 scaled_buf = pixbuf.scale_simple(16,16,gtk.gdk.INTERP_BILINEAR)
695 image.set_from_pixbuf(scaled_buf)
696 try:
697 self.tray = gtk.status_icon_new_from_pixbuf(scaled_buf)
698 self.tray.connect('activate', self._tray_clicked)
699 except AttributeError:
700 import egg
701 import egg.trayicon
702 self.tray = egg.trayicon.TrayIcon(straw.PACKAGE);
703 self._eventbox = gtk.EventBox()
704 self.tray.add(self._eventbox)
705 self._eventbox.connect('button_press_event', self._tray_clicked)
706 self._eventbox.connect("drag-data-received", self._on_drag_data_received)
707 self._eventbox.drag_dest_set(
708 gtk.DEST_DEFAULT_ALL,
709 [('_NETSCAPE_URL', 0, 0),('text/uri-list ', 0, 1),('x-url/http', 0, 2)],
710 gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
711 self._eventbox.add(image)
712 self.tray.show_all()
714 self._tooltip = gtk.Tooltips()
715 self.unread_count = 0
716 # end build tray status icon
718 FeedManager.setup(storage_path = "test.db")
719 FeedManager.init()
721 TreeViewManager.setup(storage_path = "test.db")
722 TreeViewManager.init()
723 #FeedManager.import_opml(os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml"))
725 #if config.first_time:
726 # filepath = os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml")
727 # feeds.import_opml(filepath)
729 #ImageCache.initialize()
731 #import feedfinder
732 #print feedfinder.feeds("http://eclipse.org", True)
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()
752 def _update(self,flist):
753 uritems = urfeeds = 0
754 for ur in [f.number_of_unread for f in flist.flatten_list()]:
755 if ur:
756 uritems += ur
757 urfeeds += 1
758 if uritems == self.unread_count:
759 return
760 self.unread_count = uritems
761 if uritems:
762 self._tooltip.set_tip(self.tray, _("%d new items")%uritems)
763 self.tray.show_all()
764 else:
765 self.tray.hide()
766 return
768 def _tray_clicked(self, widget, event=None):
769 self._main_presenter.view.should_present()
770 if event and not (event.button == 1):
771 return
772 self._main_presenter.scroll_or_display_next_unread_item()
774 def _on_drag_data_received(self, widget, context, x, y, data, info, timestamp):
775 if data and data.format == 8:
776 url = data.data.split("\n")[0]
777 straw.subscribe_show(url="%s" % url)
778 context.finish(True, False, timestamp)
779 else:
780 context.finish(False, False, timestamp)
781 return