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
37 import gobject
, gtk
, gtk
.glade
39 import os
, gettext
, getopt
, sys
42 from feedproperties
import feed_properties_show
43 from preferences
import preferences_show
44 from subscribe
import subscribe_show
45 from MessageManager
import post_status_message
, get_status_manager
, start_services
49 #from Find import FindPresenter, FindView
52 class StatusPresenter(MVP
.BasicPresenter
):
53 def _initialize(self
):
54 self
._mmgr
= get_status_manager()
55 self
._mmgr
.connect('changed', self
._display
)
57 def _display(self
, *args
):
58 cid
= self
._view
.get_context_id("straw_main")
60 self
._view
.push(cid
, self
._mmgr
.read_message())
64 def __init__(self
, widget
):
66 self
._tooltips
= gtk
.Tooltips()
68 #self._curr_feed = None
69 #self._curr_category = None
70 #fclist = FeedCategoryList.get_instance()
71 #fclist.connect('category-changed', self._category_changed)
73 def feedlist_selection_changed(self
, selection
, column
):
76 (model
, pathlist
) = selection
.get_selected_rows()
77 iters
= [model
.get_iter(path
) for path
in pathlist
]
80 nodes
= [model
.get_value(treeiter
, column
) for treeiter
in iters
]
82 errorfeeds
= [node
.feed
for node
in nodes
if node
.feed
and node
.feed
.error
]
83 errortexts
= [feed
.error
for feed
in errorfeeds
]
86 ### XXX display OPML Category error too
90 text
.append(_("Error:"))
95 self
._tooltips
.set_tip(self
._widget
,t
,t
)
96 self
._tooltips
.enable()
99 self
._tooltips
.disable()
106 class MenuFeedPropsPresenter(MVP
.BasicPresenter
):
108 def set_sensitive(self
, s
):
109 self
._view
.set_sensitive(s
)
112 class ToolbarView(MVP
.WidgetView
):
113 """ Widget: gtk.Toolbar"""
114 GCONF_DESKTOP_INTERFACE
= "/desktop/gnome/interface"
116 def _initialize(self
):
117 client
= gconf
.client_get_default()
118 if not client
.dir_exists(self
.GCONF_DESKTOP_INTERFACE
):
120 client
.add_dir(self
.GCONF_DESKTOP_INTERFACE
,
121 gconf
.CLIENT_PRELOAD_NONE
)
122 client
.notify_add(self
.GCONF_DESKTOP_INTERFACE
+"/toolbar_style",
123 self
._toolbar
_style
_changed
)
124 self
._widget
.set_tooltips(True)
126 def _toolbar_style_changed(self
, client
, notify_id
, entry
, *args
):
127 value
= entry
.value
.get_string()
128 self
._presenter
.style_changed(value
)
130 def set_style(self
, style
):
131 self
._widget
.set_style(style
)
134 client
= gconf
.client_get_default()
135 current_style
= client
.get_string(self
.GCONF_DESKTOP_INTERFACE
+"/toolbar_style")
138 class ToolbarPresenter(MVP
.BasicPresenter
):
139 STYLES
= {'both':gtk
.TOOLBAR_BOTH
,
140 'text':gtk
.TOOLBAR_TEXT
,
141 'icons':gtk
.TOOLBAR_ICONS
,
142 'both-horiz':gtk
.TOOLBAR_BOTH_HORIZ
}
144 def _initialize(self
):
145 style
= self
._view
.get_style()
147 if style
in self
.STYLES
.keys():
148 self
._view
.set_style(self
.STYLES
[style
])
150 widget_tree
= gtk
.glade
.get_widget_tree(self
._view
._widget
)
151 self
.change_read_state_button
= widget_tree
.get_widget("toolbar_change_read_state_button")
153 def style_changed(self
, value
):
154 if value
in self
.STYLES
.keys():
155 self
._view
.set_style(self
.STYLES
[value
])
157 def set_change_read_button_state(self
, new_state
):
158 self
.change_read_state_button
.set_active(new_state
)
160 class ApplicationPresenter(MVP
.BasicPresenter
):
161 def _initialize(self
):
162 FeedManager
._get
_instance
().connect("update-all-done", self
._on
_update
_all
_done
)
164 self
._current
_item
= None
167 self
._init
_presenters
()
170 def _init_widgets(self
):
171 widget_tree
= self
._view
.get_widget_tree()
173 self
._itemlist
_view
_notebook
= widget_tree
.get_widget('mode_view_notebook')
174 self
._feedlist
_view
_notebook
= widget_tree
.get_widget('left_mode_view_notebook')
176 item_view_container
= widget_tree
.get_widget('item_view_container')
177 item_list_container
= widget_tree
.get_widget('item_list_container')
179 self
.update_all_button
= widget_tree
.get_widget("toolbar_refresh_all_button")
181 parent_paned
= item_view_container
.get_parent()
182 parent_parent_widget
= parent_paned
.get_parent()
184 config
= Config
.get_instance()
187 layout
= config
.pane_layout
188 if layout
== 'vertical':
189 new_paned
= gtk
.VPaned()
190 child_list
.append(item_list_container
)
191 child_list
.append(item_view_container
)
192 elif layout
== 'horizontal':
193 new_paned
= gtk
.HPaned()
194 child_list
.append(item_view_container
)
195 child_list
.append(item_list_container
)
196 else: # Use vertical layout as default
197 new_paned
= gtk
.VPaned()
198 child_list
.append(item_list_container
)
199 child_list
.append(item_view_container
)
201 for child
in child_list
:
202 child
.reparent(new_paned
)
204 parent_parent_widget
.remove(parent_paned
)
205 parent_parent_widget
.add(new_paned
)
209 def _init_presenters(self
):
210 widget_tree
= self
._view
.get_widget_tree()
211 self
._toolbar
_presenter
= ToolbarPresenter(view
=ToolbarView(widget_tree
.get_widget('toolbar_default')))
212 self
._error
_presenter
= ErrorPresenter(widget_tree
.get_widget('statusbar_error_indicator'))
214 self
._item
_view
= ItemView(widget_tree
.get_widget('item_view_container'))
216 view
= ItemListView(widget_tree
.get_widget('item_selection_treeview'))
217 self
._itemlist
_presenter
= ItemListPresenter(view
=view
)
219 view
.add_selection_changed_listener(self
._item
_view
)
220 view
.add_selection_changed_listener(self
)
222 view
= FeedsView(widget_tree
.get_widget('feed_selection_treeview'))
223 self
._feed
_list
_presenter
= FeedsPresenter(view
=view
)
224 view
.add_selection_changed_listener(self
._itemlist
_presenter
)
225 view
.add_selection_changed_listener(self
._error
_presenter
)
227 self
._offline
_presenter
= OfflineToggle(widget_tree
.get_widget('offline_toggle'))
229 self
._status
_presenter
= StatusPresenter(view
= widget_tree
.get_widget("main_statusbar"))
230 self
._menufp
_presenter
= MenuFeedPropsPresenter( view
= widget_tree
.get_widget('feed_information'))
231 self
._menufp
_presenter
.set_sensitive(False)
232 # self._find_presenter = FindPresenter(view=FindView(widget_tree.get_widget("find_vbox")))
234 def set_current_item(self
, item
):
235 self
._current
_item
= item
236 self
._current
_item
_connect
_id
= item
.connect("is-read-changed", self
._on
_current
_item
_is
_read
_changed
)
238 def clear_current_item(self
):
239 if self
._current
_item
:
240 self
._current
_item
.disconnect(self
._current
_item
_connect
_id
)
241 self
._current
_item
= None
243 def set_current_item_is_read(self
, is_read
):
244 if self
._current
_item
:
245 self
._current
_item
.props
.is_read
= is_read
247 def _on_current_item_is_read_changed(self
, obj
, is_read
):
248 self
._toolbar
_presenter
.set_change_read_button_state(is_read
)
250 def itemlist_selection_changed(self
, selection
, column
):
251 self
.clear_current_item()
253 (model
, treeiter
) = selection
.get_selected()
254 if not treeiter
: return
255 item
= model
.get_value(treeiter
, column
)
256 self
._toolbar
_presenter
.set_change_read_button_state(item
.is_read
)
257 self
.set_current_item(item
)
259 def add_category(self
):
260 self
._feed
_list
_presenter
.add_category()
262 def copy_itemview_text_selection(self
):
263 helpers
.set_clipboard_text(self
._item
_view
.get_selected_text())
265 def check_allocation(self
, widget
, event
):
266 config
= Config
.get_instance()
267 def check_size((width
, height
, widget
)):
268 if width
== widget
.allocation
.width
and height
== widget
.allocation
.height
:
269 config
.main_window_size
= (width
, height
)
270 if event
.width
!= widget
.allocation
.width
or event
.height
!= widget
.allocation
.height
:
271 gobject
.timeout_add(1000, check_size
, (
272 (event
.width
, event
.height
, widget
)))
274 def check_main_pane_position(self
, widget
):
275 config
= Config
.get_instance()
276 def check_position((position
, widget
)):
277 if position
== widget
.get_position():
278 config
.main_pane_position
= position
279 pos
= widget
.get_position()
280 if pos
!= config
.main_pane_position
:
281 gobject
.timeout_add(1000, check_position
, (pos
, widget
))
283 def check_sub_pane_position(self
, widget
):
284 config
= Config
.get_instance()
285 def check_position((position
, widget
)):
286 if position
== widget
.get_position():
287 config
.sub_pane_position
= position
288 pos
= widget
.get_position()
289 if pos
!= config
.sub_pane_position
:
290 gobject
.timeout_add(1000, check_position
, (pos
, widget
))
293 return helpers
.credits()
296 if not self
._warn
_if
_offline
():
299 if not FeedManager
.is_update_all_running():
300 FeedManager
.update_all_feeds({ "task-start": [ self
._on
_feed
_poll
_started
],
301 "task-done": [ self
._on
_feed
_poll
_done
] })
302 self
.update_all_button
.set_sensitive(True)
303 self
.update_all_button
.set_stock_id(gtk
.STOCK_STOP
)
305 self
.update_all_button
.set_sensitive(False)
306 JobManager
.stop_jobs()
308 def _on_feed_poll_started(self
, handler
, feed
):
311 def _on_feed_poll_done(self
, handler
, data
):
314 def _on_update_all_done(self
, obj
):
315 self
.update_all_button
.set_sensitive(True)
316 self
.update_all_button
.set_stock_id(gtk
.STOCK_REFRESH
)
318 def poll_current_category(self
):
319 if self
._warn
_if
_offline
():
320 self
._poll
_categories
([self
._curr
_category
])
322 def poll_current_feed(self
):
323 if self
._warn
_if
_offline
():
324 print self
._curr
_feed
325 pm
= PollManager
.get_instance()
326 pm
.poll([self
._curr
_feed
])
328 def mark_feed_as_read(self
):
329 self
._curr
_feed
.mark_all_read()
331 def mark_all_as_read(self
):
332 print "TODO mark_all_as_read"
334 def remove_selected_feed(self
):
335 # self._feed_list_presenter.remove_selected_feed()
338 def show_search(self
):
339 # self._find_presenter.item_list.signal_connect(Event.ItemSelectionChangedSignal,
340 # self._item_view.item_selection_changed)
341 self
._item
_view
.display_empty_search()
342 self
._itemlist
_view
_notebook
.set_current_page(1)
343 self
._feedlist
_view
_notebook
.set_current_page(1)
344 self
._feedinfo
_presenter
.hide()
346 def hide_search(self
):
347 # self._find_presenter.clear()
348 self
._itemlist
_view
_notebook
.set_current_page(0)
349 self
._feedlist
_view
_notebook
.set_current_page(0)
350 self
._feedinfo
_presenter
.show()
351 # self._find_presenter.item_list.signal_disconnect(Event.ItemSelectionChangedSignal,
352 # self._item_view.item_selection_changed)
354 def _poll_categories(self
, fclist
):
355 pm
= PollManager
.get_instance()
356 pm
.poll_categories(fclist
)
358 def _warn_if_offline(self
):
359 config
= Config
.get_instance()
362 response
= self
._view
.show_offline_dialog()
363 if response
== gtk
.RESPONSE_OK
:
364 config
.offline
= not config
.offline
370 def display_previous_feed(self
, item
= None):
372 Displays the feed before the current selected feed
374 self
._feed
_list
_presenter
.select_previous_feed()
376 def display_next_feed(self
, item
=None):
378 Displays the feed after the current selected feed
380 self
._feed
_list
_presenter
.select_next_feed()
383 def display_next_unread_feed(self
):
385 Displays the next feed with an unread item
387 self
._feed
_list
_presenter
.select_next_feed(with_unread
=True)
390 def display_previous_item(self
, item
=None):
392 Displays the item before the current selected item. If the item is the
393 first item, scrolls to the previous feed
395 is_prev
= self
._itemlist
_presenter
.select_previous_item()
397 # TODO HACK - implement select_previous_feed(select_last=True) ...
398 # ... to select previous feed's last item
399 self
._feed
_list
_presenter
.select_previous_feed()
400 self
._itemlist
_presenter
.select_last_item()
404 def display_next_item(self
, item
=None):
406 Displays the item after the current selected item. If the item is the
407 last item, selectes the next feed. If the current feed is the last
408 feed in the list, it goes back and selects the first feed
410 is_next
= self
._itemlist
_presenter
.select_next_item()
412 is_next_feed
= self
._feed
_list
_presenter
.select_next_feed()
414 self
._feed
_list
_presenter
.select_firsteed_feed()
417 def scroll_or_display_next_unread_item(self
, item
=None):
418 has_unread_item
= False
419 if not self
._item
_view
.scroll_down():
420 has_unread_item
= self
._itemlist
_presenter
.select_next_unread_item()
421 if not has_unread_item
:
422 self
._feed
_list
_presenter
.select_next_feed(with_unread
=True)
425 def show_preferences_dialog(self
, parent
):
428 def show_feed_properties(self
, parent
):
429 feed_properties_show(self
._curr
_feed
)
434 def _setup_filechooser_dialog(self
, title
, action
, extra_widget_title
):
436 Setup the file chooser dialog. This includes an extra widget (a combobox)
437 to include the categories to import or export
439 dialog
= gtk
.FileChooserDialog(title
, action
=action
,
440 buttons
=(gtk
.STOCK_CANCEL
,
442 gtk
.STOCK_OK
, gtk
.RESPONSE_OK
))
445 for category
in FeedManager
.categories():
446 category_list
.append(category
)
448 model
= gtk
.ListStore(gobject
.TYPE_STRING
, gobject
.TYPE_PYOBJECT
)
449 combobox
= gtk
.ComboBox(model
)
450 celltitle
= gtk
.CellRendererText()
451 combobox
.pack_start(celltitle
,False)
452 combobox
.add_attribute(celltitle
, 'text', 0)
454 for category
in category_list
:
456 model
.set(it
, 0, category
.name
, 1, category
)
458 combobox
.set_active(0)
459 label
= gtk
.Label(extra_widget_title
)
460 label
.set_alignment(1.0,0.5)
461 hbox
= gtk
.HBox(spacing
=6)
462 hbox
.pack_start(label
,True,True,0)
463 hbox
.pack_end(combobox
,False,False,0)
466 dialog
.set_extra_widget(hbox
)
467 return (dialog
, combobox
)
469 def import_subscriptions(self
, parent
):
470 (dialog
,combobox
) = self
._setup
_filechooser
_dialog
(_("Import Subscriptions"),
471 gtk
.FILE_CHOOSER_ACTION_OPEN
,
472 _("Add new subscriptions in:"))
473 ffilter
= gtk
.FileFilter()
474 ffilter
.set_name(_("OPML Files Only"))
475 ffilter
.add_pattern("*.xml")
476 ffilter
.add_pattern("*.opml")
477 dialog
.add_filter(ffilter
)
478 dialog
.set_transient_for(parent
)
479 response
= dialog
.run()
480 if response
== gtk
.RESPONSE_OK
:
481 filename
= dialog
.get_filename()
482 model
= combobox
.get_model()
483 category
= model
[combobox
.get_active()][1]
485 FeedManager
.import_opml(filename
, category
)
488 def export_subscriptions(self
, parent
):
489 (dialog
,combobox
) = self
._setup
_filechooser
_dialog
(_("Export Subscriptions"),
490 gtk
.FILE_CHOOSER_ACTION_SAVE
,
491 _("Select category to export:"))
492 def selection_changed(widget
, dialog
):
493 model
= widget
.get_model()
494 category
= model
[widget
.get_active()][1]
495 dialog
.set_current_name("Straw-%s.xml" % category
.name
)
497 combobox
.connect('changed', selection_changed
, dialog
)
498 selection_changed(combobox
, dialog
)
499 dialog
.set_transient_for(parent
)
500 response
= dialog
.run()
502 if response
== gtk
.RESPONSE_OK
:
503 filename
= dialog
.get_filename()
504 model
= combobox
.get_model()
505 root_category
= model
[combobox
.get_active()][1]
506 FeedManager
.export_opml(root_category
.id, filename
)
511 subscribe_show(parent
=self
.view
._widget
)
513 class ApplicationView(MVP
.WidgetView
):
517 def _initialize(self
):
518 self
._config
= Config
.get_instance()
519 self
._initialize
_dnd
()
520 self
._initialize
_window
_updater
()
521 self
._create
_unmodified
_accelerator
_group
()
522 self
._attach
_unmodified
_accelerator
_group
()
523 self
._initialize
_window
()
524 self
._find
_toggled
= False
526 def _initialize_window(self
):
527 widget_tree
= gtk
.glade
.get_widget_tree(self
._widget
)
528 if self
._config
.window_maximized
:
529 self
._widget
.maximize()
531 # we use resize here since configure-event seems to
532 # overwrite the default size if we use set_default_size.
533 self
._widget
.resize(*self
._config
.main_window_size
)
534 mmp
= widget_tree
.get_widget('main_main_pane')
535 msp
= widget_tree
.get_widget('main_sub_pane')
536 mmp
.set_position(self
._config
.main_pane_position
)
537 msp
.set_position(self
._config
.sub_pane_position
)
539 def _initialize_dnd(self
):
540 self
._widget
.drag_dest_set(
541 gtk
.DEST_DEFAULT_ALL
,
542 [('_NETSCAPE_URL', 0, 0), ('text/uri-list ', 0, 1),
543 ('x-url/http', 0, 2)],
544 gtk
.gdk
.ACTION_COPY | gtk
.gdk
.ACTION_MOVE
)
547 def _initialize_window_updater(self
):
548 #feedlist.signal_connect(Event.AllItemsReadSignal,
549 # lambda signal: self._update_title(feedlist))
550 #feedlist.signal_connect(Event.ItemReadSignal,
551 # lambda signal: self._update_title(feedlist))
552 #feedlist.signal_connect(Event.ItemsAddedSignal,
553 # lambda signal: self._update_title(feedlist))
554 #feedlist.signal_connect(Event.FeedsChangedSignal,
555 # lambda signal: self._update_title(feedlist))
558 def _update_title(self
, flist
):
559 uritems
= urfeeds
= 0
561 listfeeds
= flist
.flatten_list()
562 for ur
in [f
.number_of_unread
for f
in listfeeds
]:
567 urfeeds
= len(listfeeds
)
570 item_feed_map
= {'uritems': uritems
,
573 title
= _('%(uritems)d unread in %(urfeeds)d %(fstring)s') % item_feed_map
574 self
._widget
.set_title( title
+ " - %s" % straw
.defs
.PACKAGE
)
576 # We have a separate accelerator group for the unmodified and
577 # shifted accelerators, that is, stuff like space, N, P, etc. This
578 # is so that we can have the find pane work correctly
579 def _create_unmodified_accelerator_group(self
):
580 xml
= gtk
.glade
.get_widget_tree(self
._widget
)
581 agroup
= gtk
.AccelGroup()
582 accels
= (('feed_mark_as_read', 'R', gtk
.gdk
.SHIFT_MASK
),
583 ('feed_mark_all_as_read', 'A', gtk
.gdk
.SHIFT_MASK
),
584 ('next_item', 'N', gtk
.gdk
.SHIFT_MASK
),
585 ('next_unread_feed', ' ', 0),
586 ('previous_item', 'P', gtk
.gdk
.SHIFT_MASK
))
587 for widget_name
, key
, mask
in accels
:
588 widget
= xml
.get_widget(widget_name
)
589 widget
.add_accelerator("activate", agroup
, ord(key
), mask
,
591 self
._unmodified
_accelerator
_group
= agroup
593 def _on_category_add_activate(self
, *args
):
594 self
._presenter
.add_category()
596 def _on_toolbar_change_read_state_button_toggled(self
, button
):
597 self
._presenter
.set_current_item_is_read(button
.get_active())
599 def _attach_unmodified_accelerator_group(self
):
600 self
._widget
.add_accel_group(self
._unmodified
_accelerator
_group
)
602 def _detach_unmodified_accelerator_group(self
):
603 self
._widget
.remove_accel_group(self
._unmodified
_accelerator
_group
)
605 def _on_straw_main_destroy_event(self
, *args
):
606 return self
._presenter
.quit()
608 def _on_straw_main_delete_event(self
, *args
):
609 return self
._presenter
.quit()
611 def _on_straw_main_configure_event(self
, widget
, event
, *args
):
612 if widget
.window
.get_state() is not gtk
.gdk
.WINDOW_STATE_MAXIMIZED
:
613 self
._config
.window_maximized
= False
614 self
._presenter
.check_allocation(widget
, event
)
616 self
._config
.window_maximized
= True
619 def _on_main_main_pane_size_allocate(self
, widget
, *args
):
620 self
._presenter
.check_main_pane_position(widget
)
622 def _on_main_sub_pane_size_allocate(self
, widget
, *args
):
623 self
._presenter
.check_sub_pane_position(widget
)
626 def _on_feed_subscribe_activate(self
, *args
):
627 self
._presenter
.subscribe()
629 def _on_feed_unsubscribe_activate(self
, *args
):
630 self
._presenter
.remove_selected_feed()
632 def _on_subscription_import_activate(self
, *args
):
633 self
._presenter
.import_subscriptions(self
._widget
)
635 def _on_subscription_export_activate(self
, *args
):
636 self
._presenter
.export_subscriptions(self
._widget
)
638 def _on_feed_mark_as_read_activate(self
, *args
):
639 self
._presenter
.mark_feed_as_read()
641 def _on_feed_mark_all_as_read_activate(self
, *args
):
642 self
._presenter
.mark_all_as_read()
644 def _on_feed_refresh_selected_activate(self
, *args
):
645 self
._presenter
.poll_current_feed()
647 def _on_subscription_refresh_activate(self
, *args
):
648 #print self._widget.get_children()[0].set_stock_id(gtk.STOCK_STOP)
649 self
._presenter
.poll_all()
651 def _on_quit_activate(self
, *args
):
652 return self
._presenter
.quit()
655 def _on_copy_activate(self
, *args
):
656 self
._presenter
.copy_itemview_text_selection()
659 def _on_find_activate(self
, widget
, *args
):
660 xml
= gtk
.glade
.get_widget_tree(self
._widget
)
661 menu_find
= xml
.get_widget('menu_find')
662 accel_label
= menu_find
.get_child()
664 if not self
._find
_toggled
:
665 self
._presenter
.show_search()
666 self
._detach
_unmodified
_accelerator
_group
()
667 self
._find
_toggled
= True
669 # save the "Find..." stock text for later recovery
670 self
._old
_label
_text
= accel_label
.get_text()
671 accel_label
.set_text(_('Return to feed list...'))
674 self
._presenter
.hide_search()
675 self
._attach
_unmodified
_accelerator
_group
()
676 self
._find
_toggled
= False
677 accel_label
.set_text(self
._old
_label
_text
)
679 def _on_preferences_activate(self
, *args
):
680 self
._presenter
.show_preferences_dialog(self
._widget
)
682 def _on_feed_information_activate(self
, *args
):
683 self
._presenter
.show_feed_properties(self
._widget
)
686 def _on_previous_item_activate(self
, *args
):
687 self
._presenter
.display_previous_item()
689 def _on_next_item_activate(self
, *args
):
690 self
._presenter
.display_next_item()
692 def _on_next_feed_activate(self
, *args
):
693 self
._presenter
.display_next_feed()
695 def _on_previous_feed_activate(self
, *args
):
696 self
._presenter
.display_previous_feed()
698 def _on_next_unread_feed_activate(self
, *args
):
699 self
._presenter
.display_next_unread_feed()
701 def _on_scroll_unread_items_activate(self
, *args
):
702 self
._presenter
.scroll_or_display_next_unread_item()
706 def _on_report_problem_activate(self
, menuitem
, *args
):
707 helpers
.url_show("http://bugzilla.gnome.org/simple-bug-guide.cgi?product=straw")
709 def _on_about_activate(self
, menuitem
, *args
):
710 widget
= self
._presenter
.credits()
713 def _on_straw_main_drag_data_received(self
, w
, context
,
714 x
, y
, data
, info
, time
):
715 if data
and data
.format
== 8:
716 url
= data
.data
.split("\n")[0]
717 subscribe_show("%s" % url
, self
._widget
)
718 context
.finish(True, False, time
)
720 context
.finish(False, False, time
)
723 def show_offline_dialog(self
):
724 return helpers
.report_offline_status(self
._widget
)
726 def get_widget_tree(self
):
727 return gtk
.glade
.get_widget_tree(self
._widget
)
730 self
._widget
.present()
732 def should_present(self
):
733 if self
._widget
.window
.get_state() is not gtk
.gdk
.WINDOW_STATE_WITHDRAWN
:
736 self
._widget
.present()
741 gnome
.program_init(straw
.defs
.PACKAGE
, straw
.defs
.VERSION
)
744 # initialize threading and environment
745 gobject
.threads_init()
746 config
= Config
.get_instance()
747 config
.reload_css
= os
.getenv('STRAW_RELOAD_CSS') is not None
749 # build tray status icon
751 iconfile
= os
.path
.normpath(straw
.defs
.STRAW_DATA_DIR
+ "/straw.png")
752 pixbuf
= gtk
.gdk
.pixbuf_new_from_file(iconfile
)
753 scaled_buf
= pixbuf
.scale_simple(16,16,gtk
.gdk
.INTERP_BILINEAR
)
754 image
.set_from_pixbuf(scaled_buf
)
756 self
.tray
= gtk
.status_icon_new_from_pixbuf(scaled_buf
)
757 self
.tray
.connect('activate', self
._tray
_clicked
)
758 except AttributeError:
761 self
.tray
= egg
.trayicon
.TrayIcon(straw
.defs
.PACKAGE
);
762 self
._eventbox
= gtk
.EventBox()
763 self
.tray
.add(self
._eventbox
)
764 self
._eventbox
.connect('button_press_event', self
._tray
_clicked
)
765 self
._eventbox
.connect("drag-data-received", self
._on
_drag
_data
_received
)
766 self
._eventbox
.drag_dest_set(
767 gtk
.DEST_DEFAULT_ALL
,
768 [('_NETSCAPE_URL', 0, 0),('text/uri-list ', 0, 1),('x-url/http', 0, 2)],
769 gtk
.gdk
.ACTION_COPY | gtk
.gdk
.ACTION_MOVE
)
770 self
._eventbox
.add(image
)
773 self
._tooltip
= gtk
.Tooltips()
774 self
.unread_count
= 0
775 # end build tray status icon
777 FeedManager
.setup(storage_path
= "test.db")
780 ItemManager
.setup(storage_path
= "test.db")
783 #FeedManager.import_opml(os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml"))
785 #if config.first_time:
786 # filepath = os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml")
787 # feeds.import_opml(filepath)
789 #ImageCache.initialize()
792 #print feedfinder.feeds("http://eclipse.org", True)
794 xml
= gtk
.glade
.XML(os
.path
.join(straw
.defs
.STRAW_DATA_DIR
,'straw.glade'), "straw_main", gettext
.textdomain())
795 window
= xml
.get_widget('straw_main')
796 self
._main
_presenter
= ApplicationPresenter(view
= ApplicationView(window
))
797 self
._main
_presenter
.view
.present()
799 #PollManager.get_instance().start_polling_loop()
800 # set the default icon for the windows
801 iconfile
= os
.path
.join(straw
.defs
.STRAW_DATA_DIR
,"straw.png")
802 gtk
.window_set_default_icon(gtk
.gdk
.pixbuf_new_from_file(iconfile
))
807 gtk
.gdk
.threads_init()
808 gtk
.gdk
.threads_enter()
810 gtk
.gdk
.threads_leave()
812 def _update(self
,flist
):
813 uritems
= urfeeds
= 0
814 for ur
in [f
.number_of_unread
for f
in flist
.flatten_list()]:
818 if uritems
== self
.unread_count
:
820 self
.unread_count
= uritems
822 self
._tooltip
.set_tip(self
.tray
, _("%d new items")%uritems
)
828 def _tray_clicked(self
, widget
, event
=None):
829 self
._main
_presenter
.view
.should_present()
830 if event
and not (event
.button
== 1):
832 self
._main
_presenter
.scroll_or_display_next_unread_item()
834 def _on_drag_data_received(self
, widget
, context
, x
, y
, data
, info
, timestamp
):
835 if data
and data
.format
== 8:
836 url
= data
.data
.split("\n")[0]
837 subscribe_show(url
="%s" % url
)
838 context
.finish(True, False, timestamp
)
840 context
.finish(False, False, timestamp
)