3 This is the main module that binds everything together.
7 __copyright__
= "Copyright (c) 2002-2005 Free Software Foundation, Inc."
8 __license__
= """ GNU General Public License
10 This program is free software; you can redistribute it and/or modify it under the
11 terms of the GNU General Public License as published by the Free Software
12 Foundation; either version 2 of the License, or (at your option) any later
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License along with
20 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21 Place - Suite 330, Boston, MA 02111-1307, USA. """
23 from Constants
import *
24 from FeedListView
import FeedsView
, FeedsPresenter
25 from ItemList
import ItemListPresenter
, ItemListView
26 from ItemView
import ItemView
27 from MainloopManager
import MainloopManager
28 from MessageManager
import post_status_message
, get_status_manager
, start_services
29 from OfflineToggle
import OfflineToggle
30 from subscribe
import subscribe_show
31 from xml
.sax
import saxutils
40 import gobject
, gtk
, gtk
.glade
42 import os
, gettext
, getopt
, sys
49 class StatusPresenter(MVP
.BasicPresenter
):
50 def _initialize(self
):
51 self
._mmgr
= get_status_manager()
52 self
._mmgr
.connect('changed', self
._display
)
54 def _display(self
, *args
):
55 cid
= self
._view
.get_context_id("straw_main")
57 self
._view
.push(cid
, self
._mmgr
.read_message())
61 def __init__(self
, widget
):
63 self
._tooltips
= gtk
.Tooltips()
65 #self._curr_feed = None
66 #self._curr_category = None
67 #fclist = FeedCategoryList.get_instance()
68 #fclist.connect('category-changed', self._category_changed)
70 def feedlist_selection_changed(self
, selection
, column
):
73 (model
, pathlist
) = selection
.get_selected_rows()
74 iters
= [model
.get_iter(path
) for path
in pathlist
]
77 nodes
= [model
.get_value(treeiter
, column
) for treeiter
in iters
]
79 errorfeeds
= [node
.feed
for node
in nodes
if node
.feed
and node
.feed
.error
]
80 errortexts
= [feed
.error
for feed
in errorfeeds
]
83 ### XXX display OPML Category error too
87 text
.append(_("Error:"))
92 self
._tooltips
.set_tip(self
._widget
,t
,t
)
93 self
._tooltips
.enable()
96 self
._tooltips
.disable()
103 class ToolbarView(MVP
.WidgetView
):
104 """ Widget: gtk.Toolbar"""
105 GCONF_DESKTOP_INTERFACE
= "/desktop/gnome/interface"
107 def _initialize(self
):
108 client
= gconf
.client_get_default()
109 if not client
.dir_exists(self
.GCONF_DESKTOP_INTERFACE
):
111 client
.add_dir(self
.GCONF_DESKTOP_INTERFACE
,
112 gconf
.CLIENT_PRELOAD_NONE
)
113 client
.notify_add(self
.GCONF_DESKTOP_INTERFACE
+"/toolbar_style",
114 self
._toolbar
_style
_changed
)
115 self
._widget
.set_tooltips(True)
117 def _toolbar_style_changed(self
, client
, notify_id
, entry
, *args
):
118 value
= entry
.value
.get_string()
119 self
._presenter
.style_changed(value
)
121 def set_style(self
, style
):
122 self
._widget
.set_style(style
)
125 client
= gconf
.client_get_default()
126 current_style
= client
.get_string(self
.GCONF_DESKTOP_INTERFACE
+"/toolbar_style")
129 class ToolbarPresenter(MVP
.BasicPresenter
):
130 STYLES
= {'both':gtk
.TOOLBAR_BOTH
,
131 'text':gtk
.TOOLBAR_TEXT
,
132 'icons':gtk
.TOOLBAR_ICONS
,
133 'both-horiz':gtk
.TOOLBAR_BOTH_HORIZ
}
135 def _initialize(self
):
136 style
= self
._view
.get_style()
138 if style
in self
.STYLES
.keys():
139 self
._view
.set_style(self
.STYLES
[style
])
141 widget_tree
= gtk
.glade
.get_widget_tree(self
._view
._widget
)
142 self
.change_read_state_button
= widget_tree
.get_widget("toolbar_change_read_state_button")
144 def style_changed(self
, value
):
145 if value
in self
.STYLES
.keys():
146 self
._view
.set_style(self
.STYLES
[value
])
148 def set_change_read_button_state(self
, new_state
):
149 self
.change_read_state_button
.set_active(new_state
)
151 class ApplicationPresenter(MVP
.BasicPresenter
):
152 def _initialize(self
):
153 FeedManager
._get
_instance
().connect("update-all-done", self
._on
_update
_all
_done
)
155 self
._current
_item
= None
158 self
._init
_presenters
()
161 def _init_widgets(self
):
162 widget_tree
= self
._view
.get_widget_tree()
164 self
._itemlist
_view
_notebook
= widget_tree
.get_widget('mode_view_notebook')
165 self
._feedlist
_view
_notebook
= widget_tree
.get_widget('left_mode_view_notebook')
167 item_view_container
= widget_tree
.get_widget('item_view_container')
168 item_list_container
= widget_tree
.get_widget('item_list_container')
170 self
.update_all_button
= widget_tree
.get_widget("toolbar_refresh_all_button")
172 parent_paned
= item_view_container
.get_parent()
173 parent_parent_widget
= parent_paned
.get_parent()
176 layout
= Config
.get(OPTION_PANE_LAYOUT
)
178 if layout
== 'vertical':
179 new_paned
= gtk
.VPaned()
180 child_list
.append(item_list_container
)
181 child_list
.append(item_view_container
)
182 elif layout
== 'horizontal':
183 new_paned
= gtk
.HPaned()
184 child_list
.append(item_view_container
)
185 child_list
.append(item_list_container
)
186 else: # Use vertical layout as default
187 new_paned
= gtk
.VPaned()
188 child_list
.append(item_list_container
)
189 child_list
.append(item_view_container
)
191 for child
in child_list
:
192 child
.reparent(new_paned
)
194 parent_parent_widget
.remove(parent_paned
)
195 parent_parent_widget
.add(new_paned
)
199 def _init_presenters(self
):
200 widget_tree
= self
._view
.get_widget_tree()
201 self
._toolbar
_presenter
= ToolbarPresenter(view
=ToolbarView(widget_tree
.get_widget('toolbar_default')))
202 self
._error
_presenter
= ErrorPresenter(widget_tree
.get_widget('statusbar_error_indicator'))
204 self
._item
_view
= ItemView(widget_tree
.get_widget('item_view_container'))
206 view
= ItemListView(widget_tree
.get_widget('item_selection_treeview'))
207 self
._itemlist
_presenter
= ItemListPresenter(view
=view
)
209 view
.add_selection_changed_listener(self
._item
_view
)
210 view
.add_selection_changed_listener(self
)
212 view
= FeedsView(widget_tree
.get_widget('feed_selection_treeview'))
213 self
._feed
_list
_presenter
= FeedsPresenter(view
=view
)
214 view
.add_selection_changed_listener(self
._itemlist
_presenter
)
215 view
.add_selection_changed_listener(self
._error
_presenter
)
217 self
._offline
_presenter
= OfflineToggle(widget_tree
.get_widget('offline_toggle'))
219 self
._status
_presenter
= StatusPresenter(view
= widget_tree
.get_widget("main_statusbar"))
220 # self._find_presenter = FindPresenter(view=FindView(widget_tree.get_widget("find_vbox")))
222 def set_current_item(self
, item
):
223 self
._current
_item
= item
224 self
._current
_item
_connect
_id
= item
.connect("is-read-changed", self
._on
_current
_item
_is
_read
_changed
)
226 def clear_current_item(self
):
227 if self
._current
_item
:
228 self
._current
_item
.disconnect(self
._current
_item
_connect
_id
)
229 self
._current
_item
= None
231 def set_current_item_is_read(self
, is_read
):
232 if self
._current
_item
:
233 self
._current
_item
.props
.is_read
= is_read
235 def _on_current_item_is_read_changed(self
, obj
, is_read
):
236 self
._toolbar
_presenter
.set_change_read_button_state(is_read
)
238 def itemlist_selection_changed(self
, selection
, column
):
239 self
.clear_current_item()
241 (model
, treeiter
) = selection
.get_selected()
242 if not treeiter
: return
243 item
= model
.get_value(treeiter
, column
)
244 self
._toolbar
_presenter
.set_change_read_button_state(item
.is_read
)
245 self
.set_current_item(item
)
247 def add_category(self
):
248 self
._feed
_list
_presenter
.add_category()
250 def copy_itemview_text_selection(self
):
251 helpers
.set_clipboard_text(self
._item
_view
.get_selected_text())
253 def check_allocation(self
, widget
, event
):
254 def check_size((width
, height
, widget
)):
255 if width
== widget
.allocation
.width
and height
== widget
.allocation
.height
:
256 Config
.set(OPTION_WINDOW_SIZE_W
, width
)
257 Config
.set(OPTION_WINDOW_SIZE_H
, height
)
259 if event
.width
!= widget
.allocation
.width
or event
.height
!= widget
.allocation
.height
:
260 gobject
.timeout_add(1000, check_size
, (
261 (event
.width
, event
.height
, widget
)))
263 def check_main_pane_position(self
, widget
):
265 """def check_position((position, widget)):
266 if position == widget.get_position():
267 Config.set(OPTION_PANE_LAYOUT, position)
269 pos = widget.get_position()
271 if pos != config.main_pane_position:
272 gobject.timeout_add(1000, check_position, (pos, widget))"""
274 def check_sub_pane_position(self
, widget
):
276 """config = Config.get_instance()
277 def check_position((position, widget)):
278 if position == widget.get_position():
279 config.sub_pane_position = position
280 pos = widget.get_position()
281 if pos != config.sub_pane_position:
282 gobject.timeout_add(1000, check_position, (pos, widget))"""
285 return helpers
.credits()
288 if not self
._warn
_if
_offline
():
291 if not FeedManager
.is_update_all_running():
292 FeedManager
.update_all_feeds({ "task-start": [ self
._on
_feed
_poll
_started
],
293 "task-done": [ self
._on
_feed
_poll
_done
] })
294 self
.update_all_button
.set_sensitive(True)
295 self
.update_all_button
.set_stock_id(gtk
.STOCK_STOP
)
297 self
.update_all_button
.set_sensitive(False)
298 FeedManager
.stop_update_all()
300 def _on_feed_poll_started(self
, handler
, feed
):
303 def _on_feed_poll_done(self
, handler
, data
):
306 def _on_update_all_done(self
, obj
):
307 self
.update_all_button
.set_sensitive(True)
308 self
.update_all_button
.set_stock_id(gtk
.STOCK_REFRESH
)
310 def poll_current_category(self
):
311 if self
._warn
_if
_offline
():
312 self
._poll
_categories
([self
._curr
_category
])
314 def poll_current_feed(self
):
315 if self
._warn
_if
_offline
():
316 print self
._curr
_feed
317 pm
= PollManager
.get_instance()
318 pm
.poll([self
._curr
_feed
])
320 def mark_feed_as_read(self
):
321 self
._curr
_feed
.mark_all_read()
323 def mark_all_as_read(self
):
324 print "TODO mark_all_as_read"
326 def remove_selected_feed(self
):
327 # self._feed_list_presenter.remove_selected_feed()
330 def show_search(self
):
331 # self._find_presenter.item_list.signal_connect(Event.ItemSelectionChangedSignal,
332 # self._item_view.item_selection_changed)
333 self
._item
_view
.display_empty_search()
334 self
._itemlist
_view
_notebook
.set_current_page(1)
335 self
._feedlist
_view
_notebook
.set_current_page(1)
336 self
._feedinfo
_presenter
.hide()
338 def hide_search(self
):
339 # self._find_presenter.clear()
340 self
._itemlist
_view
_notebook
.set_current_page(0)
341 self
._feedlist
_view
_notebook
.set_current_page(0)
342 self
._feedinfo
_presenter
.show()
343 # self._find_presenter.item_list.signal_disconnect(Event.ItemSelectionChangedSignal,
344 # self._item_view.item_selection_changed)
346 def _warn_if_offline(self
):
347 offline
= Config
.get(OPTION_OFFLINE
)
351 response
= self
._view
.show_offline_dialog()
353 if response
== gtk
.RESPONSE_OK
:
354 Config
.set(OPTION_OFFLINE
, not offline
)
361 def display_previous_feed(self
, item
= None):
363 Displays the feed before the current selected feed
365 self
._feed
_list
_presenter
.select_previous_feed()
367 def display_next_feed(self
, item
=None):
369 Displays the feed after the current selected feed
371 self
._feed
_list
_presenter
.select_next_feed()
374 def display_next_unread_feed(self
):
376 Displays the next feed with an unread item
378 self
._feed
_list
_presenter
.select_next_feed(with_unread
=True)
381 def display_previous_item(self
, item
=None):
383 Displays the item before the current selected item. If the item is the
384 first item, scrolls to the previous feed
386 is_prev
= self
._itemlist
_presenter
.select_previous_item()
388 # TODO HACK - implement select_previous_feed(select_last=True) ...
389 # ... to select previous feed's last item
390 self
._feed
_list
_presenter
.select_previous_feed()
391 self
._itemlist
_presenter
.select_last_item()
395 def display_next_item(self
, item
=None):
397 Displays the item after the current selected item. If the item is the
398 last item, selectes the next feed. If the current feed is the last
399 feed in the list, it goes back and selects the first feed
401 is_next
= self
._itemlist
_presenter
.select_next_item()
403 is_next_feed
= self
._feed
_list
_presenter
.select_next_feed()
405 self
._feed
_list
_presenter
.select_firsteed_feed()
408 def scroll_or_display_next_unread_item(self
, item
=None):
409 has_unread_item
= False
410 if not self
._item
_view
.scroll_down():
411 has_unread_item
= self
._itemlist
_presenter
.select_next_unread_item()
412 if not has_unread_item
:
413 self
._feed
_list
_presenter
.select_next_feed(with_unread
=True)
416 def show_preferences_dialog(self
, parent
):
419 def show_feed_properties(self
, parent
):
420 feedproperties
.show(self
._curr
_feed
)
425 def _setup_filechooser_dialog(self
, title
, action
, extra_widget_title
):
427 Setup the file chooser dialog. This includes an extra widget (a combobox)
428 to include the categories to import or export
430 dialog
= gtk
.FileChooserDialog(title
, action
=action
,
431 buttons
=(gtk
.STOCK_CANCEL
,
433 gtk
.STOCK_OK
, gtk
.RESPONSE_OK
))
436 for category
in FeedManager
.categories():
437 category_list
.append(category
)
439 model
= gtk
.ListStore(gobject
.TYPE_STRING
, gobject
.TYPE_PYOBJECT
)
440 combobox
= gtk
.ComboBox(model
)
441 celltitle
= gtk
.CellRendererText()
442 combobox
.pack_start(celltitle
,False)
443 combobox
.add_attribute(celltitle
, 'text', 0)
445 for category
in category_list
:
447 model
.set(it
, 0, category
.name
, 1, category
)
449 combobox
.set_active(0)
450 label
= gtk
.Label(extra_widget_title
)
451 label
.set_alignment(1.0,0.5)
452 hbox
= gtk
.HBox(spacing
=6)
453 hbox
.pack_start(label
,True,True,0)
454 hbox
.pack_end(combobox
,False,False,0)
457 dialog
.set_extra_widget(hbox
)
458 return (dialog
, combobox
)
460 def import_subscriptions(self
, parent
):
461 (dialog
,combobox
) = self
._setup
_filechooser
_dialog
(_("Import Subscriptions"),
462 gtk
.FILE_CHOOSER_ACTION_OPEN
,
463 _("Add new subscriptions in:"))
464 ffilter
= gtk
.FileFilter()
465 ffilter
.set_name(_("OPML Files Only"))
466 ffilter
.add_pattern("*.xml")
467 ffilter
.add_pattern("*.opml")
468 dialog
.add_filter(ffilter
)
469 dialog
.set_transient_for(parent
)
470 response
= dialog
.run()
471 if response
== gtk
.RESPONSE_OK
:
472 filename
= dialog
.get_filename()
473 model
= combobox
.get_model()
474 category
= model
[combobox
.get_active()][1]
476 FeedManager
.import_opml(filename
, category
)
479 def export_subscriptions(self
, parent
):
480 (dialog
,combobox
) = self
._setup
_filechooser
_dialog
(_("Export Subscriptions"),
481 gtk
.FILE_CHOOSER_ACTION_SAVE
,
482 _("Select category to export:"))
483 def selection_changed(widget
, dialog
):
484 model
= widget
.get_model()
485 category
= model
[widget
.get_active()][1]
486 dialog
.set_current_name("Straw-%s.xml" % category
.name
)
488 combobox
.connect('changed', selection_changed
, dialog
)
489 selection_changed(combobox
, dialog
)
490 dialog
.set_transient_for(parent
)
491 response
= dialog
.run()
493 if response
== gtk
.RESPONSE_OK
:
494 filename
= dialog
.get_filename()
495 model
= combobox
.get_model()
496 root_category
= model
[combobox
.get_active()][1]
497 FeedManager
.export_opml(root_category
.id, filename
)
502 subscribe_show(parent
=self
.view
._widget
)
504 class ApplicationView(MVP
.WidgetView
):
508 def _initialize(self
):
509 self
._initialize
_dnd
()
510 self
._initialize
_window
_updater
()
511 self
._create
_unmodified
_accelerator
_group
()
512 self
._attach
_unmodified
_accelerator
_group
()
513 self
._initialize
_window
()
514 self
._find
_toggled
= False
516 def _initialize_window(self
):
517 widget_tree
= gtk
.glade
.get_widget_tree(self
._widget
)
518 if Config
.get(OPTION_WINDOW_MAX
):
519 self
._widget
.maximize()
521 # we use resize here since configure-event seems to
522 # overwrite the default size if we use set_default_size.
523 self
._widget
.resize(Config
.get(OPTION_WINDOW_SIZE_W
), Config
.get(OPTION_WINDOW_SIZE_H
))
524 mmp
= widget_tree
.get_widget('main_main_pane')
525 msp
= widget_tree
.get_widget('main_sub_pane')
526 #mmp.set_position(Config.main_pane_position)
527 #msp.set_position(Config.sub_pane_position)
529 def _initialize_dnd(self
):
530 self
._widget
.drag_dest_set(
531 gtk
.DEST_DEFAULT_ALL
,
532 [('_NETSCAPE_URL', 0, 0), ('text/uri-list ', 0, 1),
533 ('x-url/http', 0, 2)],
534 gtk
.gdk
.ACTION_COPY | gtk
.gdk
.ACTION_MOVE
)
537 def _initialize_window_updater(self
):
538 #feedlist.signal_connect(Event.AllItemsReadSignal,
539 # lambda signal: self._update_title(feedlist))
540 #feedlist.signal_connect(Event.ItemReadSignal,
541 # lambda signal: self._update_title(feedlist))
542 #feedlist.signal_connect(Event.ItemsAddedSignal,
543 # lambda signal: self._update_title(feedlist))
544 #feedlist.signal_connect(Event.FeedsChangedSignal,
545 # lambda signal: self._update_title(feedlist))
548 def _update_title(self
, flist
):
549 uritems
= urfeeds
= 0
551 listfeeds
= flist
.flatten_list()
552 for ur
in [f
.number_of_unread
for f
in listfeeds
]:
557 urfeeds
= len(listfeeds
)
560 item_feed_map
= {'uritems': uritems
,
563 title
= _('%(uritems)d unread in %(urfeeds)d %(fstring)s') % item_feed_map
564 self
._widget
.set_title( title
+ " - %s" % straw
.defs
.PACKAGE
)
566 # We have a separate accelerator group for the unmodified and
567 # shifted accelerators, that is, stuff like space, N, P, etc. This
568 # is so that we can have the find pane work correctly
569 def _create_unmodified_accelerator_group(self
):
570 xml
= gtk
.glade
.get_widget_tree(self
._widget
)
571 agroup
= gtk
.AccelGroup()
572 accels
= (('feed_mark_as_read', 'R', gtk
.gdk
.SHIFT_MASK
),
573 ('feed_mark_all_as_read', 'A', gtk
.gdk
.SHIFT_MASK
),
574 ('next_item', 'N', gtk
.gdk
.SHIFT_MASK
),
575 ('next_unread_feed', ' ', 0),
576 ('previous_item', 'P', gtk
.gdk
.SHIFT_MASK
))
577 for widget_name
, key
, mask
in accels
:
578 widget
= xml
.get_widget(widget_name
)
579 widget
.add_accelerator("activate", agroup
, ord(key
), mask
,
581 self
._unmodified
_accelerator
_group
= agroup
583 def _on_category_add_activate(self
, *args
):
584 self
._presenter
.add_category()
586 def _on_toolbar_change_read_state_button_toggled(self
, button
):
587 self
._presenter
.set_current_item_is_read(button
.get_active())
589 def _attach_unmodified_accelerator_group(self
):
590 self
._widget
.add_accel_group(self
._unmodified
_accelerator
_group
)
592 def _detach_unmodified_accelerator_group(self
):
593 self
._widget
.remove_accel_group(self
._unmodified
_accelerator
_group
)
595 def _on_straw_main_destroy_event(self
, *args
):
596 return self
._presenter
.quit()
598 def _on_straw_main_delete_event(self
, *args
):
599 return self
._presenter
.quit()
601 def _on_straw_main_window_state_event(self
, widget
, event
):
602 is_maximized
= widget
.window
.get_state() == gtk
.gdk
.WINDOW_STATE_MAXIMIZED
603 Config
.set(OPTION_WINDOW_MAX
, is_maximized
)
605 def _on_straw_main_configure_event(self
, widget
, event
, *args
):
606 #if not (widget.window.get_state() & gtk.gdk.WINDOW_STATE_MAXIMIZED):
607 # self._presenter.check_allocation(widget, event)
610 def _on_main_main_pane_size_allocate(self
, widget
, *args
):
611 self
._presenter
.check_main_pane_position(widget
)
613 def _on_main_sub_pane_size_allocate(self
, widget
, *args
):
614 self
._presenter
.check_sub_pane_position(widget
)
617 def _on_feed_subscribe_activate(self
, *args
):
618 self
._presenter
.subscribe()
620 def _on_feed_unsubscribe_activate(self
, *args
):
621 self
._presenter
.remove_selected_feed()
623 def _on_subscription_import_activate(self
, *args
):
624 self
._presenter
.import_subscriptions(self
._widget
)
626 def _on_subscription_export_activate(self
, *args
):
627 self
._presenter
.export_subscriptions(self
._widget
)
629 def _on_feed_mark_as_read_activate(self
, *args
):
630 self
._presenter
.mark_feed_as_read()
632 def _on_feed_mark_all_as_read_activate(self
, *args
):
633 self
._presenter
.mark_all_as_read()
635 def _on_feed_refresh_selected_activate(self
, *args
):
636 self
._presenter
.poll_current_feed()
638 def _on_subscription_refresh_activate(self
, *args
):
639 #print self._widget.get_children()[0].set_stock_id(gtk.STOCK_STOP)
640 self
._presenter
.poll_all()
642 def _on_quit_activate(self
, *args
):
643 return self
._presenter
.quit()
646 def _on_copy_activate(self
, *args
):
647 self
._presenter
.copy_itemview_text_selection()
650 def _on_find_activate(self
, widget
, *args
):
651 xml
= gtk
.glade
.get_widget_tree(self
._widget
)
652 menu_find
= xml
.get_widget('menu_find')
653 accel_label
= menu_find
.get_child()
655 if not self
._find
_toggled
:
656 self
._presenter
.show_search()
657 self
._detach
_unmodified
_accelerator
_group
()
658 self
._find
_toggled
= True
660 # save the "Find..." stock text for later recovery
661 self
._old
_label
_text
= accel_label
.get_text()
662 accel_label
.set_text(_('Return to feed list...'))
665 self
._presenter
.hide_search()
666 self
._attach
_unmodified
_accelerator
_group
()
667 self
._find
_toggled
= False
668 accel_label
.set_text(self
._old
_label
_text
)
670 def _on_preferences_activate(self
, *args
):
671 self
._presenter
.show_preferences_dialog(self
._widget
)
673 def _on_feed_information_activate(self
, *args
):
674 self
._presenter
.show_feed_properties(self
._widget
)
677 def _on_previous_item_activate(self
, *args
):
678 self
._presenter
.display_previous_item()
680 def _on_next_item_activate(self
, *args
):
681 self
._presenter
.display_next_item()
683 def _on_next_feed_activate(self
, *args
):
684 self
._presenter
.display_next_feed()
686 def _on_previous_feed_activate(self
, *args
):
687 self
._presenter
.display_previous_feed()
689 def _on_next_unread_feed_activate(self
, *args
):
690 self
._presenter
.display_next_unread_feed()
692 def _on_scroll_unread_items_activate(self
, *args
):
693 self
._presenter
.scroll_or_display_next_unread_item()
697 def _on_report_problem_activate(self
, menuitem
, *args
):
698 helpers
.url_show("http://bugzilla.gnome.org/simple-bug-guide.cgi?product=straw")
700 def _on_about_activate(self
, menuitem
, *args
):
701 widget
= self
._presenter
.credits()
704 def _on_straw_main_drag_data_received(self
, w
, context
,
705 x
, y
, data
, info
, time
):
706 if data
and data
.format
== 8:
707 url
= data
.data
.split("\n")[0]
708 subscribe_show("%s" % url
, self
._widget
)
709 context
.finish(True, False, time
)
711 context
.finish(False, False, time
)
714 def show_offline_dialog(self
):
715 return helpers
.report_offline_status(self
._widget
)
717 def get_widget_tree(self
):
718 return gtk
.glade
.get_widget_tree(self
._widget
)
721 self
._widget
.present()
723 def should_present(self
):
724 if self
._widget
.window
.get_state() is not gtk
.gdk
.WINDOW_STATE_WITHDRAWN
:
727 self
._widget
.present()
732 gnome
.program_init(straw
.defs
.PACKAGE
, straw
.defs
.VERSION
)
735 # initialize threading and environment
736 gobject
.threads_init()
738 #Config.set(OPTION_OFFLINEnfig.reload_css = os.getenv('STRAW_RELOAD_CSS') is not None
740 # build tray status icon
742 iconfile
= os
.path
.normpath(straw
.defs
.STRAW_DATA_DIR
+ "/straw.png")
743 pixbuf
= gtk
.gdk
.pixbuf_new_from_file(iconfile
)
744 scaled_buf
= pixbuf
.scale_simple(16,16,gtk
.gdk
.INTERP_BILINEAR
)
745 image
.set_from_pixbuf(scaled_buf
)
747 self
.tray
= gtk
.status_icon_new_from_pixbuf(scaled_buf
)
748 self
.tray
.connect('activate', self
._tray
_clicked
)
749 except AttributeError:
752 self
.tray
= egg
.trayicon
.TrayIcon(straw
.defs
.PACKAGE
);
753 self
._eventbox
= gtk
.EventBox()
754 self
.tray
.add(self
._eventbox
)
755 self
._eventbox
.connect('button_press_event', self
._tray
_clicked
)
756 self
._eventbox
.connect("drag-data-received", self
._on
_drag
_data
_received
)
757 self
._eventbox
.drag_dest_set(
758 gtk
.DEST_DEFAULT_ALL
,
759 [('_NETSCAPE_URL', 0, 0),('text/uri-list ', 0, 1),('x-url/http', 0, 2)],
760 gtk
.gdk
.ACTION_COPY | gtk
.gdk
.ACTION_MOVE
)
761 self
._eventbox
.add(image
)
764 self
._tooltip
= gtk
.Tooltips()
765 self
.unread_count
= 0
766 # end build tray status icon
768 FeedManager
.setup(storage_path
= "test.db")
771 ItemManager
.setup(storage_path
= "test.db")
774 #FeedManager.import_opml(os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml"))
776 #if config.first_time:
777 # filepath = os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml")
778 # feeds.import_opml(filepath)
780 #ImageCache.initialize()
783 #print feedfinder.feeds("http://eclipse.org", True)
785 xml
= gtk
.glade
.XML(os
.path
.join(straw
.defs
.STRAW_DATA_DIR
,'straw.glade'), "straw_main", gettext
.textdomain())
786 window
= xml
.get_widget('straw_main')
787 self
._main
_presenter
= ApplicationPresenter(view
= ApplicationView(window
))
788 self
._main
_presenter
.view
.present()
790 #PollManager.get_instance().start_polling_loop()
791 # set the default icon for the windows
792 iconfile
= os
.path
.join(straw
.defs
.STRAW_DATA_DIR
,"straw.png")
793 gtk
.window_set_default_icon(gtk
.gdk
.pixbuf_new_from_file(iconfile
))
798 gtk
.gdk
.threads_init()
799 gtk
.gdk
.threads_enter()
801 gtk
.gdk
.threads_leave()
803 def _update(self
,flist
):
804 uritems
= urfeeds
= 0
805 for ur
in [f
.number_of_unread
for f
in flist
.flatten_list()]:
809 if uritems
== self
.unread_count
:
811 self
.unread_count
= uritems
813 self
._tooltip
.set_tip(self
.tray
, _("%d new items")%uritems
)
819 def _tray_clicked(self
, widget
, event
=None):
820 self
._main
_presenter
.view
.should_present()
821 if event
and not (event
.button
== 1):
823 self
._main
_presenter
.scroll_or_display_next_unread_item()
825 def _on_drag_data_received(self
, widget
, context
, x
, y
, data
, info
, timestamp
):
826 if data
and data
.format
== 8:
827 url
= data
.data
.split("\n")[0]
828 subscribe_show(url
="%s" % url
)
829 context
.finish(True, False, timestamp
)
831 context
.finish(False, False, timestamp
)