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 FeedListView
import FeedsView
, FeedsPresenter
24 from ItemList
import ItemListPresenter
, ItemListView
25 from ItemView
import ItemView
26 from MainloopManager
import MainloopManager
27 from OfflineToggle
import OfflineToggle
28 from xml
.sax
import saxutils
36 import gobject
, gtk
, gtk
.glade
38 import os
, gettext
, getopt
, sys
44 #from Find import FindPresenter, FindView
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")
55 self
._view
.push(cid
, self
._mmgr
.read_message())
59 def __init__(self
, widget
):
61 self
._tooltips
= gtk
.Tooltips()
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
):
71 (model
, pathlist
) = selection
.get_selected_rows()
72 iters
= [model
.get_iter(path
) for path
in pathlist
]
75 nodes
= [model
.get_value(treeiter
, column
) for treeiter
in iters
]
77 errorfeeds
= [node
.feed
for node
in nodes
if node
.feed
and node
.feed
.error
]
78 errortexts
= [feed
.error
for feed
in errorfeeds
]
81 ### XXX display OPML Category error too
85 text
.append(_("Error:"))
90 self
._tooltips
.set_tip(self
._widget
,t
,t
)
91 self
._tooltips
.enable()
94 self
._tooltips
.disable()
101 class MenuFeedPropsPresenter(MVP
.BasicPresenter
):
103 def set_sensitive(self
, s
):
104 self
._view
.set_sensitive(s
)
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
):
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
)
129 client
= gconf
.client_get_default()
130 current_style
= client
.get_string(self
.GCONF_DESKTOP_INTERFACE
+"/toolbar_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()
142 if style
in self
.STYLES
.keys():
143 self
._view
.set_style(self
.STYLES
[style
])
145 widget_tree
= gtk
.glade
.get_widget_tree(self
._view
._widget
)
146 self
.change_read_state_button
= widget_tree
.get_widget("toolbar_change_read_state_button")
148 def style_changed(self
, value
):
149 if value
in self
.STYLES
.keys():
150 self
._view
.set_style(self
.STYLES
[value
])
152 def set_change_read_button_state(self
, new_state
):
153 self
.change_read_state_button
.set_active(new_state
)
155 class ApplicationPresenter(MVP
.BasicPresenter
):
156 def _initialize(self
):
157 FeedManager
._get
_instance
().connect("update-all-done", self
._on
_update
_all
_done
)
159 self
._current
_item
= None
162 self
._init
_presenters
()
165 def _init_widgets(self
):
166 widget_tree
= self
._view
.get_widget_tree()
168 self
._itemlist
_view
_notebook
= widget_tree
.get_widget('mode_view_notebook')
169 self
._feedlist
_view
_notebook
= widget_tree
.get_widget('left_mode_view_notebook')
171 item_view_container
= widget_tree
.get_widget('item_view_container')
172 item_list_container
= widget_tree
.get_widget('item_list_container')
174 self
.update_all_button
= widget_tree
.get_widget("toolbar_refresh_all_button")
176 parent_paned
= item_view_container
.get_parent()
177 parent_parent_widget
= parent_paned
.get_parent()
179 config
= Config
.get_instance()
182 layout
= config
.pane_layout
183 if layout
== 'vertical':
184 new_paned
= gtk
.VPaned()
185 child_list
.append(item_list_container
)
186 child_list
.append(item_view_container
)
187 elif layout
== 'horizontal':
188 new_paned
= gtk
.HPaned()
189 child_list
.append(item_view_container
)
190 child_list
.append(item_list_container
)
191 else: # Use vertical layout as default
192 new_paned
= gtk
.VPaned()
193 child_list
.append(item_list_container
)
194 child_list
.append(item_view_container
)
196 for child
in child_list
:
197 child
.reparent(new_paned
)
199 parent_parent_widget
.remove(parent_paned
)
200 parent_parent_widget
.add(new_paned
)
204 def _init_presenters(self
):
205 widget_tree
= self
._view
.get_widget_tree()
206 self
._toolbar
_presenter
= ToolbarPresenter(view
=ToolbarView(widget_tree
.get_widget('toolbar_default')))
207 self
._error
_presenter
= ErrorPresenter(widget_tree
.get_widget('statusbar_error_indicator'))
209 self
._item
_view
= ItemView(widget_tree
.get_widget('item_view_container'))
211 view
= ItemListView(widget_tree
.get_widget('item_selection_treeview'))
212 self
._itemlist
_presenter
= ItemListPresenter(view
=view
)
214 view
.add_selection_changed_listener(self
._item
_view
)
215 view
.add_selection_changed_listener(self
)
217 view
= FeedsView(widget_tree
.get_widget('feed_selection_treeview'))
218 self
._feed
_list
_presenter
= FeedsPresenter(view
=view
)
219 view
.add_selection_changed_listener(self
._itemlist
_presenter
)
220 view
.add_selection_changed_listener(self
._error
_presenter
)
222 self
._offline
_presenter
= OfflineToggle(widget_tree
.get_widget('offline_toggle'))
224 self
._status
_presenter
= StatusPresenter(view
= widget_tree
.get_widget("main_statusbar"))
225 self
._menufp
_presenter
= MenuFeedPropsPresenter( view
= widget_tree
.get_widget('feed_information'))
226 self
._menufp
_presenter
.set_sensitive(False)
227 # self._find_presenter = FindPresenter(view=FindView(widget_tree.get_widget("find_vbox")))
229 def set_current_item(self
, item
):
230 self
._current
_item
= item
231 self
._current
_item
_connect
_id
= item
.connect("is-read-changed", self
._on
_current
_item
_is
_read
_changed
)
233 def clear_current_item(self
):
234 if self
._current
_item
:
235 self
._current
_item
.disconnect(self
._current
_item
_connect
_id
)
236 self
._current
_item
= None
238 def set_current_item_is_read(self
, is_read
):
239 if self
._current
_item
:
240 self
._current
_item
.props
.is_read
= is_read
242 def _on_current_item_is_read_changed(self
, obj
, is_read
):
243 self
._toolbar
_presenter
.set_change_read_button_state(is_read
)
245 def itemlist_selection_changed(self
, selection
, column
):
246 self
.clear_current_item()
248 (model
, treeiter
) = selection
.get_selected()
249 if not treeiter
: return
250 item
= model
.get_value(treeiter
, column
)
251 self
._toolbar
_presenter
.set_change_read_button_state(item
.is_read
)
252 self
.set_current_item(item
)
254 def add_category(self
):
255 self
._feed
_list
_presenter
.add_category()
257 def copy_itemview_text_selection(self
):
258 helpers
.set_clipboard_text(self
._item
_view
.get_selected_text())
260 def check_allocation(self
, widget
, event
):
261 config
= Config
.get_instance()
262 def check_size((width
, height
, widget
)):
263 if width
== widget
.allocation
.width
and height
== widget
.allocation
.height
:
264 config
.main_window_size
= (width
, height
)
265 if event
.width
!= widget
.allocation
.width
or event
.height
!= widget
.allocation
.height
:
266 gobject
.timeout_add(1000, check_size
, (
267 (event
.width
, event
.height
, widget
)))
269 def check_main_pane_position(self
, widget
):
270 config
= Config
.get_instance()
271 def check_position((position
, widget
)):
272 if position
== widget
.get_position():
273 config
.main_pane_position
= position
274 pos
= widget
.get_position()
275 if pos
!= config
.main_pane_position
:
276 gobject
.timeout_add(1000, check_position
, (pos
, widget
))
278 def check_sub_pane_position(self
, widget
):
279 config
= Config
.get_instance()
280 def check_position((position
, widget
)):
281 if position
== widget
.get_position():
282 config
.sub_pane_position
= position
283 pos
= widget
.get_position()
284 if pos
!= config
.sub_pane_position
:
285 gobject
.timeout_add(1000, check_position
, (pos
, widget
))
288 return helpers
.credits()
291 if not self
._warn
_if
_offline
():
294 if not FeedManager
.is_update_all_running():
295 FeedManager
.update_all_feeds({ "task-start": [ self
._on
_feed
_poll
_started
],
296 "task-done": [ self
._on
_feed
_poll
_done
] })
297 self
.update_all_button
.set_sensitive(True)
298 self
.update_all_button
.set_stock_id(gtk
.STOCK_STOP
)
300 self
.update_all_button
.set_sensitive(False)
301 JobManager
.stop_jobs()
303 def _on_feed_poll_started(self
, handler
, feed
):
306 def _on_feed_poll_done(self
, handler
, data
):
309 def _on_update_all_done(self
, obj
):
310 self
.update_all_button
.set_sensitive(True)
311 self
.update_all_button
.set_stock_id(gtk
.STOCK_REFRESH
)
313 def poll_current_category(self
):
314 if self
._warn
_if
_offline
():
315 self
._poll
_categories
([self
._curr
_category
])
317 def poll_current_feed(self
):
318 if self
._warn
_if
_offline
():
319 print self
._curr
_feed
320 pm
= PollManager
.get_instance()
321 pm
.poll([self
._curr
_feed
])
323 def mark_feed_as_read(self
):
324 self
._curr
_feed
.mark_all_read()
326 def mark_all_as_read(self
):
327 print "TODO mark_all_as_read"
329 def remove_selected_feed(self
):
330 # self._feed_list_presenter.remove_selected_feed()
333 def show_search(self
):
334 # self._find_presenter.item_list.signal_connect(Event.ItemSelectionChangedSignal,
335 # self._item_view.item_selection_changed)
336 self
._item
_view
.display_empty_search()
337 self
._itemlist
_view
_notebook
.set_current_page(1)
338 self
._feedlist
_view
_notebook
.set_current_page(1)
339 self
._feedinfo
_presenter
.hide()
341 def hide_search(self
):
342 # self._find_presenter.clear()
343 self
._itemlist
_view
_notebook
.set_current_page(0)
344 self
._feedlist
_view
_notebook
.set_current_page(0)
345 self
._feedinfo
_presenter
.show()
346 # self._find_presenter.item_list.signal_disconnect(Event.ItemSelectionChangedSignal,
347 # self._item_view.item_selection_changed)
349 def _poll_categories(self
, fclist
):
350 pm
= PollManager
.get_instance()
351 pm
.poll_categories(fclist
)
353 def _warn_if_offline(self
):
354 config
= Config
.get_instance()
357 response
= self
._view
.show_offline_dialog()
358 if response
== gtk
.RESPONSE_OK
:
359 config
.offline
= not config
.offline
365 def display_previous_feed(self
, item
= None):
367 Displays the feed before the current selected feed
369 self
._feed
_list
_presenter
.select_previous_feed()
371 def display_next_feed(self
, item
=None):
373 Displays the feed after the current selected feed
375 self
._feed
_list
_presenter
.select_next_feed()
378 def display_next_unread_feed(self
):
380 Displays the next feed with an unread item
382 self
._feed
_list
_presenter
.select_next_feed(with_unread
=True)
385 def display_previous_item(self
, item
=None):
387 Displays the item before the current selected item. If the item is the
388 first item, scrolls to the previous feed
390 is_prev
= self
._itemlist
_presenter
.select_previous_item()
392 # TODO HACK - implement select_previous_feed(select_last=True) ...
393 # ... to select previous feed's last item
394 self
._feed
_list
_presenter
.select_previous_feed()
395 self
._itemlist
_presenter
.select_last_item()
399 def display_next_item(self
, item
=None):
401 Displays the item after the current selected item. If the item is the
402 last item, selectes the next feed. If the current feed is the last
403 feed in the list, it goes back and selects the first feed
405 is_next
= self
._itemlist
_presenter
.select_next_item()
407 is_next_feed
= self
._feed
_list
_presenter
.select_next_feed()
409 self
._feed
_list
_presenter
.select_firsteed_feed()
412 def scroll_or_display_next_unread_item(self
, item
=None):
413 has_unread_item
= False
414 if not self
._item
_view
.scroll_down():
415 has_unread_item
= self
._itemlist
_presenter
.select_next_unread_item()
416 if not has_unread_item
:
417 self
._feed
_list
_presenter
.select_next_feed(with_unread
=True)
420 def show_preferences_dialog(self
, parent
):
421 straw
.preferences_show()
423 def show_feed_properties(self
, parent
):
424 straw
.feed_properties_show(self
._curr
_feed
)
429 def _setup_filechooser_dialog(self
, title
, action
, extra_widget_title
):
431 Setup the file chooser dialog. This includes an extra widget (a combobox)
432 to include the categories to import or export
434 dialog
= gtk
.FileChooserDialog(title
, action
=action
,
435 buttons
=(gtk
.STOCK_CANCEL
,
437 gtk
.STOCK_OK
, gtk
.RESPONSE_OK
))
440 for category
in FeedManager
.categories():
441 category_list
.append(category
)
443 model
= gtk
.ListStore(gobject
.TYPE_STRING
, gobject
.TYPE_PYOBJECT
)
444 combobox
= gtk
.ComboBox(model
)
445 celltitle
= gtk
.CellRendererText()
446 combobox
.pack_start(celltitle
,False)
447 combobox
.add_attribute(celltitle
, 'text', 0)
449 for category
in category_list
:
451 model
.set(it
, 0, category
.name
, 1, category
)
453 combobox
.set_active(0)
454 label
= gtk
.Label(extra_widget_title
)
455 label
.set_alignment(1.0,0.5)
456 hbox
= gtk
.HBox(spacing
=6)
457 hbox
.pack_start(label
,True,True,0)
458 hbox
.pack_end(combobox
,False,False,0)
461 dialog
.set_extra_widget(hbox
)
462 return (dialog
, combobox
)
464 def import_subscriptions(self
, parent
):
465 (dialog
,combobox
) = self
._setup
_filechooser
_dialog
(_("Import Subscriptions"),
466 gtk
.FILE_CHOOSER_ACTION_OPEN
,
467 _("Add new subscriptions in:"))
468 ffilter
= gtk
.FileFilter()
469 ffilter
.set_name(_("OPML Files Only"))
470 ffilter
.add_pattern("*.xml")
471 ffilter
.add_pattern("*.opml")
472 dialog
.add_filter(ffilter
)
473 dialog
.set_transient_for(parent
)
474 response
= dialog
.run()
475 if response
== gtk
.RESPONSE_OK
:
476 filename
= dialog
.get_filename()
477 model
= combobox
.get_model()
478 category
= model
[combobox
.get_active()][1]
480 FeedManager
.import_opml(filename
, category
)
483 def export_subscriptions(self
, parent
):
484 (dialog
,combobox
) = self
._setup
_filechooser
_dialog
(_("Export Subscriptions"),
485 gtk
.FILE_CHOOSER_ACTION_SAVE
,
486 _("Select category to export:"))
487 def selection_changed(widget
, dialog
):
488 model
= widget
.get_model()
489 category
= model
[widget
.get_active()][1]
490 dialog
.set_current_name("Straw-%s.xml" % category
.name
)
492 combobox
.connect('changed', selection_changed
, dialog
)
493 selection_changed(combobox
, dialog
)
494 dialog
.set_transient_for(parent
)
495 response
= dialog
.run()
497 if response
== gtk
.RESPONSE_OK
:
498 filename
= dialog
.get_filename()
499 model
= combobox
.get_model()
500 root_category
= model
[combobox
.get_active()][1]
501 FeedManager
.export_opml(root_category
.id, filename
)
506 straw
.subscribe_show(parent
=self
.view
._widget
)
508 class ApplicationView(MVP
.WidgetView
):
512 def _initialize(self
):
513 self
._config
= Config
.get_instance()
514 self
._initialize
_dnd
()
515 self
._initialize
_window
_updater
()
516 self
._create
_unmodified
_accelerator
_group
()
517 self
._attach
_unmodified
_accelerator
_group
()
518 self
._initialize
_window
()
519 self
._find
_toggled
= False
521 def _initialize_window(self
):
522 widget_tree
= gtk
.glade
.get_widget_tree(self
._widget
)
523 if self
._config
.window_maximized
:
524 self
._widget
.maximize()
526 # we use resize here since configure-event seems to
527 # overwrite the default size if we use set_default_size.
528 self
._widget
.resize(*self
._config
.main_window_size
)
529 mmp
= widget_tree
.get_widget('main_main_pane')
530 msp
= widget_tree
.get_widget('main_sub_pane')
531 mmp
.set_position(self
._config
.main_pane_position
)
532 msp
.set_position(self
._config
.sub_pane_position
)
534 def _initialize_dnd(self
):
535 self
._widget
.drag_dest_set(
536 gtk
.DEST_DEFAULT_ALL
,
537 [('_NETSCAPE_URL', 0, 0), ('text/uri-list ', 0, 1),
538 ('x-url/http', 0, 2)],
539 gtk
.gdk
.ACTION_COPY | gtk
.gdk
.ACTION_MOVE
)
542 def _initialize_window_updater(self
):
543 #feedlist.signal_connect(Event.AllItemsReadSignal,
544 # lambda signal: self._update_title(feedlist))
545 #feedlist.signal_connect(Event.ItemReadSignal,
546 # lambda signal: self._update_title(feedlist))
547 #feedlist.signal_connect(Event.ItemsAddedSignal,
548 # lambda signal: self._update_title(feedlist))
549 #feedlist.signal_connect(Event.FeedsChangedSignal,
550 # lambda signal: self._update_title(feedlist))
553 def _update_title(self
, flist
):
554 uritems
= urfeeds
= 0
556 listfeeds
= flist
.flatten_list()
557 for ur
in [f
.number_of_unread
for f
in listfeeds
]:
562 urfeeds
= len(listfeeds
)
565 item_feed_map
= {'uritems': uritems
,
568 title
= _('%(uritems)d unread in %(urfeeds)d %(fstring)s') % item_feed_map
569 self
._widget
.set_title( title
+ " - %s" % straw
.PACKAGE
)
571 # We have a separate accelerator group for the unmodified and
572 # shifted accelerators, that is, stuff like space, N, P, etc. This
573 # is so that we can have the find pane work correctly
574 def _create_unmodified_accelerator_group(self
):
575 xml
= gtk
.glade
.get_widget_tree(self
._widget
)
576 agroup
= gtk
.AccelGroup()
577 accels
= (('feed_mark_as_read', 'R', gtk
.gdk
.SHIFT_MASK
),
578 ('feed_mark_all_as_read', 'A', gtk
.gdk
.SHIFT_MASK
),
579 ('next_item', 'N', gtk
.gdk
.SHIFT_MASK
),
580 ('next_unread_feed', ' ', 0),
581 ('previous_item', 'P', gtk
.gdk
.SHIFT_MASK
))
582 for widget_name
, key
, mask
in accels
:
583 widget
= xml
.get_widget(widget_name
)
584 widget
.add_accelerator("activate", agroup
, ord(key
), mask
,
586 self
._unmodified
_accelerator
_group
= agroup
588 def _on_category_add_activate(self
, *args
):
589 self
._presenter
.add_category()
591 def _on_toolbar_change_read_state_button_toggled(self
, button
):
592 self
._presenter
.set_current_item_is_read(button
.get_active())
594 def _attach_unmodified_accelerator_group(self
):
595 self
._widget
.add_accel_group(self
._unmodified
_accelerator
_group
)
597 def _detach_unmodified_accelerator_group(self
):
598 self
._widget
.remove_accel_group(self
._unmodified
_accelerator
_group
)
600 def _on_straw_main_destroy_event(self
, *args
):
601 return self
._presenter
.quit()
603 def _on_straw_main_delete_event(self
, *args
):
604 return self
._presenter
.quit()
606 def _on_straw_main_configure_event(self
, widget
, event
, *args
):
607 if widget
.window
.get_state() is not gtk
.gdk
.WINDOW_STATE_MAXIMIZED
:
608 self
._config
.window_maximized
= False
609 self
._presenter
.check_allocation(widget
, event
)
611 self
._config
.window_maximized
= True
614 def _on_main_main_pane_size_allocate(self
, widget
, *args
):
615 self
._presenter
.check_main_pane_position(widget
)
617 def _on_main_sub_pane_size_allocate(self
, widget
, *args
):
618 self
._presenter
.check_sub_pane_position(widget
)
621 def _on_feed_subscribe_activate(self
, *args
):
622 self
._presenter
.subscribe()
624 def _on_feed_unsubscribe_activate(self
, *args
):
625 self
._presenter
.remove_selected_feed()
627 def _on_subscription_import_activate(self
, *args
):
628 self
._presenter
.import_subscriptions(self
._widget
)
630 def _on_subscription_export_activate(self
, *args
):
631 self
._presenter
.export_subscriptions(self
._widget
)
633 def _on_feed_mark_as_read_activate(self
, *args
):
634 self
._presenter
.mark_feed_as_read()
636 def _on_feed_mark_all_as_read_activate(self
, *args
):
637 self
._presenter
.mark_all_as_read()
639 def _on_feed_refresh_selected_activate(self
, *args
):
640 self
._presenter
.poll_current_feed()
642 def _on_subscription_refresh_activate(self
, *args
):
643 #print self._widget.get_children()[0].set_stock_id(gtk.STOCK_STOP)
644 self
._presenter
.poll_all()
646 def _on_quit_activate(self
, *args
):
647 return self
._presenter
.quit()
650 def _on_copy_activate(self
, *args
):
651 self
._presenter
.copy_itemview_text_selection()
654 def _on_find_activate(self
, widget
, *args
):
655 xml
= gtk
.glade
.get_widget_tree(self
._widget
)
656 menu_find
= xml
.get_widget('menu_find')
657 accel_label
= menu_find
.get_child()
659 if not self
._find
_toggled
:
660 self
._presenter
.show_search()
661 self
._detach
_unmodified
_accelerator
_group
()
662 self
._find
_toggled
= True
664 # save the "Find..." stock text for later recovery
665 self
._old
_label
_text
= accel_label
.get_text()
666 accel_label
.set_text(_('Return to feed list...'))
669 self
._presenter
.hide_search()
670 self
._attach
_unmodified
_accelerator
_group
()
671 self
._find
_toggled
= False
672 accel_label
.set_text(self
._old
_label
_text
)
674 def _on_preferences_activate(self
, *args
):
675 self
._presenter
.show_preferences_dialog(self
._widget
)
677 def _on_feed_information_activate(self
, *args
):
678 self
._presenter
.show_feed_properties(self
._widget
)
681 def _on_previous_item_activate(self
, *args
):
682 self
._presenter
.display_previous_item()
684 def _on_next_item_activate(self
, *args
):
685 self
._presenter
.display_next_item()
687 def _on_next_feed_activate(self
, *args
):
688 self
._presenter
.display_next_feed()
690 def _on_previous_feed_activate(self
, *args
):
691 self
._presenter
.display_previous_feed()
693 def _on_next_unread_feed_activate(self
, *args
):
694 self
._presenter
.display_next_unread_feed()
696 def _on_scroll_unread_items_activate(self
, *args
):
697 self
._presenter
.scroll_or_display_next_unread_item()
701 def _on_report_problem_activate(self
, menuitem
, *args
):
702 helpers
.url_show("http://bugzilla.gnome.org/simple-bug-guide.cgi?product=straw")
704 def _on_about_activate(self
, menuitem
, *args
):
705 widget
= self
._presenter
.credits()
708 def _on_straw_main_drag_data_received(self
, w
, context
,
709 x
, y
, data
, info
, time
):
710 if data
and data
.format
== 8:
711 url
= data
.data
.split("\n")[0]
712 straw
.subscribe_show("%s" % url
, self
._widget
)
713 context
.finish(True, False, time
)
715 context
.finish(False, False, time
)
718 def show_offline_dialog(self
):
719 return helpers
.report_offline_status(self
._widget
)
721 def get_widget_tree(self
):
722 return gtk
.glade
.get_widget_tree(self
._widget
)
725 self
._widget
.present()
727 def should_present(self
):
728 if self
._widget
.window
.get_state() is not gtk
.gdk
.WINDOW_STATE_WITHDRAWN
:
731 self
._widget
.present()
736 gnome
.program_init(straw
.PACKAGE
, straw
.VERSION
)
739 # initialize threading and environment
740 gobject
.threads_init()
741 config
= Config
.get_instance()
742 config
.reload_css
= os
.getenv('STRAW_RELOAD_CSS') is not None
744 # build tray status icon
746 iconfile
= os
.path
.normpath(straw
.STRAW_DATA_DIR
+ "/straw.png")
747 pixbuf
= gtk
.gdk
.pixbuf_new_from_file(iconfile
)
748 scaled_buf
= pixbuf
.scale_simple(16,16,gtk
.gdk
.INTERP_BILINEAR
)
749 image
.set_from_pixbuf(scaled_buf
)
751 self
.tray
= gtk
.status_icon_new_from_pixbuf(scaled_buf
)
752 self
.tray
.connect('activate', self
._tray
_clicked
)
753 except AttributeError:
756 self
.tray
= egg
.trayicon
.TrayIcon(straw
.PACKAGE
);
757 self
._eventbox
= gtk
.EventBox()
758 self
.tray
.add(self
._eventbox
)
759 self
._eventbox
.connect('button_press_event', self
._tray
_clicked
)
760 self
._eventbox
.connect("drag-data-received", self
._on
_drag
_data
_received
)
761 self
._eventbox
.drag_dest_set(
762 gtk
.DEST_DEFAULT_ALL
,
763 [('_NETSCAPE_URL', 0, 0),('text/uri-list ', 0, 1),('x-url/http', 0, 2)],
764 gtk
.gdk
.ACTION_COPY | gtk
.gdk
.ACTION_MOVE
)
765 self
._eventbox
.add(image
)
768 self
._tooltip
= gtk
.Tooltips()
769 self
.unread_count
= 0
770 # end build tray status icon
772 FeedManager
.setup(storage_path
= "test.db")
775 #FeedManager.import_opml(os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml"))
777 #if config.first_time:
778 # filepath = os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml")
779 # feeds.import_opml(filepath)
781 #ImageCache.initialize()
784 #print feedfinder.feeds("http://eclipse.org", True)
786 xml
= gtk
.glade
.XML(os
.path
.join(straw
.STRAW_DATA_DIR
,'straw.glade'), "straw_main", gettext
.textdomain())
787 window
= xml
.get_widget('straw_main')
788 self
._main
_presenter
= ApplicationPresenter(view
= ApplicationView(window
))
789 self
._main
_presenter
.view
.present()
791 #PollManager.get_instance().start_polling_loop()
792 # set the default icon for the windows
793 iconfile
= os
.path
.join(straw
.STRAW_DATA_DIR
,"straw.png")
794 gtk
.window_set_default_icon(gtk
.gdk
.pixbuf_new_from_file(iconfile
))
796 straw
.start_services()
799 gtk
.gdk
.threads_init()
800 gtk
.gdk
.threads_enter()
802 gtk
.gdk
.threads_leave()
804 def _update(self
,flist
):
805 uritems
= urfeeds
= 0
806 for ur
in [f
.number_of_unread
for f
in flist
.flatten_list()]:
810 if uritems
== self
.unread_count
:
812 self
.unread_count
= uritems
814 self
._tooltip
.set_tip(self
.tray
, _("%d new items")%uritems
)
820 def _tray_clicked(self
, widget
, event
=None):
821 self
._main
_presenter
.view
.should_present()
822 if event
and not (event
.button
== 1):
824 self
._main
_presenter
.scroll_or_display_next_unread_item()
826 def _on_drag_data_received(self
, widget
, context
, x
, y
, data
, info
, timestamp
):
827 if data
and data
.format
== 8:
828 url
= data
.data
.split("\n")[0]
829 straw
.subscribe_show(url
="%s" % url
)
830 context
.finish(True, False, timestamp
)
832 context
.finish(False, False, timestamp
)