313c51c781e8f2bb58da5f1ce8a181db0a205bc2
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 log
= error
.get_logger()
51 class StatusPresenter(MVP
.BasicPresenter
):
52 def _initialize(self
):
53 self
._mmgr
= get_status_manager()
54 self
._mmgr
.connect('changed', self
._display
)
56 def _display(self
, *args
):
57 cid
= self
._view
.get_context_id("straw_main")
59 self
._view
.push(cid
, self
._mmgr
.read_message())
63 def __init__(self
, widget
):
65 self
._tooltips
= gtk
.Tooltips()
67 #self._curr_feed = None
68 #self._curr_category = None
69 #fclist = FeedCategoryList.get_instance()
70 #fclist.connect('category-changed', self._category_changed)
72 def feedlist_selection_changed(self
, selection
, column
):
75 (model
, pathlist
) = selection
.get_selected_rows()
76 iters
= [model
.get_iter(path
) for path
in pathlist
]
79 nodes
= [model
.get_value(treeiter
, column
) for treeiter
in iters
]
81 errorfeeds
= [node
.feed
for node
in nodes
if node
.feed
and node
.feed
.error
]
82 errortexts
= [feed
.error
for feed
in errorfeeds
]
85 ### XXX display OPML Category error too
89 text
.append(_("Error:"))
94 self
._tooltips
.set_tip(self
._widget
, t
, t
)
95 self
._tooltips
.enable()
98 self
._tooltips
.disable()
105 class ToolbarView(MVP
.WidgetView
):
106 """ Widget: gtk.Toolbar"""
107 GCONF_DESKTOP_INTERFACE
= "/desktop/gnome/interface"
109 def _initialize(self
):
110 client
= gconf
.client_get_default()
111 if not client
.dir_exists(self
.GCONF_DESKTOP_INTERFACE
):
113 client
.add_dir(self
.GCONF_DESKTOP_INTERFACE
,
114 gconf
.CLIENT_PRELOAD_NONE
)
115 client
.notify_add(self
.GCONF_DESKTOP_INTERFACE
+"/toolbar_style",
116 self
._toolbar
_style
_changed
)
117 self
._widget
.set_tooltips(True)
119 def _toolbar_style_changed(self
, client
, notify_id
, entry
, *args
):
120 value
= entry
.value
.get_string()
121 self
._presenter
.style_changed(value
)
123 def set_style(self
, style
):
124 self
._widget
.set_style(style
)
127 client
= gconf
.client_get_default()
128 current_style
= client
.get_string(self
.GCONF_DESKTOP_INTERFACE
+"/toolbar_style")
131 class ToolbarPresenter(MVP
.BasicPresenter
):
132 STYLES
= {'both':gtk
.TOOLBAR_BOTH
,
133 'text':gtk
.TOOLBAR_TEXT
,
134 'icons':gtk
.TOOLBAR_ICONS
,
135 'both-horiz':gtk
.TOOLBAR_BOTH_HORIZ
}
137 def _initialize(self
):
138 style
= self
._view
.get_style()
140 if style
in self
.STYLES
.keys():
141 self
._view
.set_style(self
.STYLES
[style
])
143 widget_tree
= gtk
.glade
.get_widget_tree(self
._view
._widget
)
144 self
.change_read_state_button
= widget_tree
.get_widget("toolbar_change_read_state_button")
146 def style_changed(self
, value
):
147 if value
in self
.STYLES
.keys():
148 self
._view
.set_style(self
.STYLES
[value
])
150 def set_change_read_button_state(self
, new_state
):
151 self
.change_read_state_button
.set_active(new_state
)
153 class ApplicationPresenter(MVP
.BasicPresenter
):
154 def _initialize(self
):
155 FeedManager
._get
_instance
().connect("update-all-done", self
._on
_update
_all
_done
)
157 self
._current
_item
= None
160 self
._init
_presenters
()
161 self
._restore
_state
()
164 def _init_widgets(self
):
165 widget_tree
= self
._view
.get_widget_tree()
167 self
._itemlist
_view
_notebook
= widget_tree
.get_widget('mode_view_notebook')
168 self
._feedlist
_view
_notebook
= widget_tree
.get_widget('left_mode_view_notebook')
170 item_view_container
= widget_tree
.get_widget('item_view_container')
171 item_list_container
= widget_tree
.get_widget('item_list_container')
173 self
.update_all_button
= widget_tree
.get_widget("toolbar_refresh_all_button")
175 parent_paned
= item_view_container
.get_parent()
176 parent_parent_widget
= parent_paned
.get_parent()
179 layout
= Config
.get(OPTION_PANE_LAYOUT
)
181 if layout
not in ("horizontal", "vertical"):
184 if layout
== "vertical":
185 new_paned
= gtk
.VPaned()
186 child_list
.append(item_list_container
)
187 child_list
.append(item_view_container
)
188 elif layout
== "horizontal":
189 new_paned
= gtk
.HPaned()
190 child_list
.append(item_view_container
)
191 child_list
.append(item_list_container
)
193 for child
in child_list
:
194 child
.reparent(new_paned
)
196 parent_parent_widget
.remove(parent_paned
)
197 parent_parent_widget
.add(new_paned
)
199 new_paned
.connect("size-allocate", self
._view
._on
_main
_sub
_pane
_size
_allocate
)
200 new_paned
.set_position(Config
.get(OPTION_SUB_PANE_POS
))
203 def _init_presenters(self
):
204 widget_tree
= self
._view
.get_widget_tree()
205 self
._toolbar
_presenter
= ToolbarPresenter(view
=ToolbarView(widget_tree
.get_widget('toolbar_default')))
206 self
._error
_presenter
= ErrorPresenter(widget_tree
.get_widget('statusbar_error_indicator'))
208 self
._item
_view
= ItemView(widget_tree
.get_widget('item_view_container'))
210 view
= ItemListView(widget_tree
.get_widget('item_selection_treeview'))
211 self
._itemlist
_presenter
= ItemListPresenter(view
=view
)
213 view
.add_selection_changed_listener(self
._item
_view
)
214 view
.add_selection_changed_listener(self
)
216 view
= FeedsView(widget_tree
.get_widget('feed_selection_treeview'))
217 self
._feed
_list
_presenter
= FeedsPresenter(view
=view
)
218 view
.add_selection_changed_listener(self
._itemlist
_presenter
)
219 view
.add_selection_changed_listener(self
._error
_presenter
)
221 view
.add_mark_all_as_read_listener(self
._itemlist
_presenter
)
223 self
._offline
_presenter
= OfflineToggle(widget_tree
.get_widget('offline_toggle'))
225 self
._status
_presenter
= StatusPresenter(view
= widget_tree
.get_widget("main_statusbar"))
226 #self._find_presenter = FindPresenter(view=FindView(widget_tree.get_widget("find_vbox")))
228 def set_current_item(self
, item
):
229 self
._current
_item
= item
230 self
._current
_item
_connect
_id
= item
.connect("is-read-changed", self
._on
_current
_item
_is
_read
_changed
)
232 def _restore_state(self
):
233 self
._feed
_list
_presenter
.restore_state()
235 def clear_current_item(self
):
236 if self
._current
_item
:
237 self
._current
_item
.disconnect(self
._current
_item
_connect
_id
)
238 self
._current
_item
= None
240 def set_current_item_is_read(self
, is_read
):
241 if self
._current
_item
:
242 self
._current
_item
.props
.is_read
= is_read
244 def _on_current_item_is_read_changed(self
, obj
, is_read
):
245 self
._toolbar
_presenter
.set_change_read_button_state(is_read
)
247 def itemlist_selection_changed(self
, selection
, column
):
248 self
.clear_current_item()
250 (model
, treeiter
) = selection
.get_selected()
251 if not treeiter
: return
252 item
= model
.get_value(treeiter
, column
)
253 self
._toolbar
_presenter
.set_change_read_button_state(item
.is_read
)
254 self
.set_current_item(item
)
256 def add_category(self
):
257 self
._feed
_list
_presenter
.add_category()
259 def copy_itemview_text_selection(self
):
260 helpers
.set_clipboard_text(self
._item
_view
.get_selected_text())
262 def check_allocation(self
, widget
, event
):
263 def check_size(widget
, width
, height
, x
, y
):
264 if width
== widget
.allocation
.width
and height
== widget
.allocation
.height
:
265 Config
.set(OPTION_WINDOW_SIZE_W
, width
)
266 Config
.set(OPTION_WINDOW_SIZE_H
, height
)
268 if widget
.window
and (x
, y
) == widget
.window
.get_position():
269 Config
.set(OPTION_WINDOW_LEFT
, x
)
270 Config
.set(OPTION_WINDOW_TOP
, y
)
272 config_position
= (Config
.get(OPTION_WINDOW_LEFT
), Config
.get(OPTION_WINDOW_TOP
))
274 if (event
.width
, event
.height
, event
.x
, event
.y
) != \
275 (widget
.allocation
.width
, widget
.allocation
.height
, config_position
[0], config_position
[1]):
276 gobject
.timeout_add(1000, check_size
, widget
, event
.width
, event
.height
, event
.x
, event
.y
)
278 def check_main_pane_position(self
, widget
):
279 def check_position((position
, widget
)):
280 if position
== widget
.get_position():
281 Config
.set(OPTION_MAIN_PANE_POS
, position
)
283 pos
= widget
.get_position()
285 if pos
!= Config
.get(OPTION_MAIN_PANE_POS
):
286 gobject
.timeout_add(1000, check_position
, (pos
, widget
))
288 def check_sub_pane_position(self
, widget
):
289 def check_position((position
, widget
)):
290 if position
== widget
.get_position():
291 Config
.set(OPTION_SUB_PANE_POS
, position
)
293 pos
= widget
.get_position()
295 if pos
!= Config
.get(OPTION_SUB_PANE_POS
):
296 gobject
.timeout_add(1000, check_position
, (pos
, widget
))
299 return helpers
.credits()
302 if not self
._warn
_if
_offline
():
305 if not FeedManager
.is_update_all_running():
306 FeedManager
.update_all_feeds({ "task-start": [ self
._on
_feed
_poll
_started
],
307 "task-done": [ self
._on
_feed
_poll
_done
] })
308 self
.update_all_button
.set_sensitive(True)
309 self
.update_all_button
.set_stock_id(gtk
.STOCK_STOP
)
311 self
.update_all_button
.set_sensitive(False)
312 FeedManager
.stop_update_all()
314 def _on_feed_poll_started(self
, handler
, feed
):
317 def _on_feed_poll_done(self
, handler
, data
):
320 def _on_update_all_done(self
, obj
):
321 self
.update_all_button
.set_sensitive(True)
322 self
.update_all_button
.set_stock_id(gtk
.STOCK_REFRESH
)
324 def poll_current_category(self
):
325 if self
._warn
_if
_offline
():
326 self
._poll
_categories
([self
._curr
_category
])
328 def poll_current_feed(self
):
329 if self
._warn
_if
_offline
():
330 pm
= PollManager
.get_instance()
331 pm
.poll([self
._curr
_feed
])
333 def mark_feed_as_read(self
):
334 self
._curr
_feed
.mark_all_read()
336 def mark_all_as_read(self
):
337 print "TODO mark_all_as_read"
339 def remove_selected_feed(self
):
340 # self._feed_list_presenter.remove_selected_feed()
343 def show_search(self
):
344 # self._find_presenter.item_list.signal_connect(Event.ItemSelectionChangedSignal,
345 # self._item_view.item_selection_changed)
346 self
._item
_view
.display_empty_search()
347 self
._itemlist
_view
_notebook
.set_current_page(1)
348 self
._feedlist
_view
_notebook
.set_current_page(1)
349 self
._feedinfo
_presenter
.hide()
351 def hide_search(self
):
352 # self._find_presenter.clear()
353 self
._itemlist
_view
_notebook
.set_current_page(0)
354 self
._feedlist
_view
_notebook
.set_current_page(0)
355 self
._feedinfo
_presenter
.show()
356 # self._find_presenter.item_list.signal_disconnect(Event.ItemSelectionChangedSignal,
357 # self._item_view.item_selection_changed)
359 def _warn_if_offline(self
):
360 offline
= Config
.get(OPTION_OFFLINE
)
364 response
= self
._view
.show_offline_dialog()
366 if response
== gtk
.RESPONSE_OK
:
367 Config
.set(OPTION_OFFLINE
, not offline
)
374 def display_previous_feed(self
, item
= None):
376 Displays the feed before the current selected feed
378 self
._feed
_list
_presenter
.select_previous_feed()
380 def display_next_feed(self
, item
=None):
382 Displays the feed after the current selected feed
384 self
._feed
_list
_presenter
.select_next_feed()
387 def display_next_unread_feed(self
):
389 Displays the next feed with an unread item
391 self
._feed
_list
_presenter
.select_next_feed(with_unread
=True)
394 def display_previous_item(self
, item
=None):
396 Displays the item before the current selected item. If the item is the
397 first item, scrolls to the previous feed
399 is_prev
= self
._itemlist
_presenter
.select_previous_item()
401 # TODO HACK - implement select_previous_feed(select_last=True) ...
402 # ... to select previous feed's last item
403 self
._feed
_list
_presenter
.select_previous_feed()
404 self
._itemlist
_presenter
.select_last_item()
408 def display_next_item(self
, item
=None):
410 Displays the item after the current selected item. If the item is the
411 last item, selectes the next feed. If the current feed is the last
412 feed in the list, it goes back and selects the first feed
414 is_next
= self
._itemlist
_presenter
.select_next_item()
416 is_next_feed
= self
._feed
_list
_presenter
.select_next_feed()
418 self
._feed
_list
_presenter
.select_firsteed_feed()
421 def scroll_or_display_next_unread_item(self
, item
=None):
422 has_unread_item
= False
423 if not self
._item
_view
.scroll_down():
424 has_unread_item
= self
._itemlist
_presenter
.select_next_unread_item()
425 if not has_unread_item
:
426 self
._feed
_list
_presenter
.select_next_feed(with_unread
=True)
429 def show_preferences_dialog(self
, parent
):
432 def show_feed_properties(self
, parent
):
433 feedproperties
.show(self
._curr
_feed
)
436 self
._feed
_list
_presenter
.store_state()
437 ItemManager
.shutdown()
440 def _setup_filechooser_dialog(self
, title
, action
, extra_widget_title
):
442 Setup the file chooser dialog. This includes an extra widget (a combobox)
443 to include the categories to import or export
445 dialog
= gtk
.FileChooserDialog(title
, action
=action
,
446 buttons
=(gtk
.STOCK_CANCEL
,
448 gtk
.STOCK_OK
, gtk
.RESPONSE_OK
))
451 for category
in FeedManager
.categories():
452 category_list
.append(category
)
454 model
= gtk
.ListStore(gobject
.TYPE_STRING
, gobject
.TYPE_PYOBJECT
)
455 combobox
= gtk
.ComboBox(model
)
456 celltitle
= gtk
.CellRendererText()
457 combobox
.pack_start(celltitle
, False)
458 combobox
.add_attribute(celltitle
, 'text', 0)
460 for category
in category_list
:
462 model
.set(it
, 0, category
.name
, 1, category
)
464 combobox
.set_active(0)
465 label
= gtk
.Label(extra_widget_title
)
466 label
.set_alignment(1.0, 0.5)
467 hbox
= gtk
.HBox(spacing
=6)
468 hbox
.pack_start(label
, True, True, 0)
469 hbox
.pack_end(combobox
, False, False, 0)
472 dialog
.set_extra_widget(hbox
)
473 return (dialog
, combobox
)
475 def import_subscriptions(self
, parent
):
476 (dialog
, combobox
) = self
._setup
_filechooser
_dialog
(_("Import Subscriptions"),
477 gtk
.FILE_CHOOSER_ACTION_OPEN
,
478 _("Add new subscriptions in:"))
479 ffilter
= gtk
.FileFilter()
480 ffilter
.set_name(_("OPML Files Only"))
481 ffilter
.add_pattern("*.xml")
482 ffilter
.add_pattern("*.opml")
483 dialog
.add_filter(ffilter
)
484 dialog
.set_transient_for(parent
)
485 response
= dialog
.run()
486 if response
== gtk
.RESPONSE_OK
:
487 filename
= dialog
.get_filename()
488 model
= combobox
.get_model()
489 category
= model
[combobox
.get_active()][1]
491 FeedManager
.import_opml(filename
, category
)
494 def export_subscriptions(self
, parent
):
495 (dialog
, combobox
) = self
._setup
_filechooser
_dialog
(_("Export Subscriptions"),
496 gtk
.FILE_CHOOSER_ACTION_SAVE
,
497 _("Select category to export:"))
498 def selection_changed(widget
, dialog
):
499 model
= widget
.get_model()
500 category
= model
[widget
.get_active()][1]
501 dialog
.set_current_name("Straw-%s.xml" % category
.name
)
503 combobox
.connect('changed', selection_changed
, dialog
)
504 selection_changed(combobox
, dialog
)
505 dialog
.set_transient_for(parent
)
506 response
= dialog
.run()
508 if response
== gtk
.RESPONSE_OK
:
509 filename
= dialog
.get_filename()
510 model
= combobox
.get_model()
511 root_category
= model
[combobox
.get_active()][1]
512 FeedManager
.export_opml(root_category
.id, filename
)
517 subscribe_show(parent
=self
.view
._widget
)
519 class ApplicationView(MVP
.WidgetView
):
523 def _initialize(self
):
524 self
._initialize
_dnd
()
525 self
._initialize
_window
_updater
()
526 self
._create
_unmodified
_accelerator
_group
()
527 self
._attach
_unmodified
_accelerator
_group
()
528 self
._initialize
_window
()
529 self
._find
_toggled
= False
531 def _initialize_window(self
):
532 widget_tree
= gtk
.glade
.get_widget_tree(self
._widget
)
533 if Config
.get(OPTION_WINDOW_MAX
):
534 self
._widget
.maximize()
536 # we use resize here since configure-event seems to
537 # overwrite the default size if we use set_default_size.
538 #self._widget.move(Config.get(OPTION_WINDOW_LEFT), Config.get(OPTION_WINDOW_TOP))
539 #self._widget.resize(Config.get(OPTION_WINDOW_SIZE_W), Config.get(OPTION_WINDOW_SIZE_H))
540 self
._widget
.window
.move_resize(Config
.get(OPTION_WINDOW_LEFT
), Config
.get(OPTION_WINDOW_TOP
),
541 Config
.get(OPTION_WINDOW_SIZE_W
), Config
.get(OPTION_WINDOW_SIZE_H
))
542 self
.mmp
= widget_tree
.get_widget('main_main_pane')
543 self
.msp
= widget_tree
.get_widget('main_sub_pane')
545 self
.mmp
.set_position(Config
.get(OPTION_MAIN_PANE_POS
))
546 self
.msp
.set_position(Config
.get(OPTION_SUB_PANE_POS
))
548 def _initialize_dnd(self
):
549 self
._widget
.drag_dest_set(
550 gtk
.DEST_DEFAULT_ALL
,
551 [('_NETSCAPE_URL', 0, 0), ('text/uri-list ', 0, 1),
552 ('x-url/http', 0, 2)],
553 gtk
.gdk
.ACTION_COPY | gtk
.gdk
.ACTION_MOVE
)
556 def _initialize_window_updater(self
):
557 #feedlist.signal_connect(Event.AllItemsReadSignal,
558 # lambda signal: self._update_title(feedlist))
559 #feedlist.signal_connect(Event.ItemReadSignal,
560 # lambda signal: self._update_title(feedlist))
561 #feedlist.signal_connect(Event.ItemsAddedSignal,
562 # lambda signal: self._update_title(feedlist))
563 #feedlist.signal_connect(Event.FeedsChangedSignal,
564 # lambda signal: self._update_title(feedlist))
567 def _update_title(self
, flist
):
568 uritems
= urfeeds
= 0
570 listfeeds
= flist
.flatten_list()
571 for ur
in [f
.number_of_unread
for f
in listfeeds
]:
576 urfeeds
= len(listfeeds
)
579 item_feed_map
= {'uritems': uritems
,
582 title
= _('%(uritems)d unread in %(urfeeds)d %(fstring)s') % item_feed_map
583 self
._widget
.set_title(title
+ " - %s" % straw
.defs
.PACKAGE
)
585 # We have a separate accelerator group for the unmodified and
586 # shifted accelerators, that is, stuff like space, N, P, etc. This
587 # is so that we can have the find pane work correctly
588 def _create_unmodified_accelerator_group(self
):
589 xml
= gtk
.glade
.get_widget_tree(self
._widget
)
590 agroup
= gtk
.AccelGroup()
591 accels
= (('feed_mark_as_read', 'R', gtk
.gdk
.SHIFT_MASK
),
592 ('feed_mark_all_as_read', 'A', gtk
.gdk
.SHIFT_MASK
),
593 ('next_item', 'N', gtk
.gdk
.SHIFT_MASK
),
594 ('scroll_unread_items', ' ', 0),
595 ('previous_item', 'P', gtk
.gdk
.SHIFT_MASK
))
596 for widget_name
, key
, mask
in accels
:
597 widget
= xml
.get_widget(widget_name
)
598 widget
.add_accelerator("activate", agroup
, ord(key
), mask
,
600 self
._unmodified
_accelerator
_group
= agroup
602 def _on_category_add_activate(self
, *args
):
603 self
._presenter
.add_category()
605 def _on_toolbar_change_read_state_button_toggled(self
, button
):
606 self
._presenter
.set_current_item_is_read(button
.get_active())
608 def _attach_unmodified_accelerator_group(self
):
609 self
._widget
.add_accel_group(self
._unmodified
_accelerator
_group
)
611 def _detach_unmodified_accelerator_group(self
):
612 self
._widget
.remove_accel_group(self
._unmodified
_accelerator
_group
)
614 def _on_straw_main_destroy_event(self
, *args
):
615 return self
._presenter
.quit()
617 def _on_straw_main_delete_event(self
, *args
):
618 return self
._presenter
.quit()
620 def _on_straw_main_window_state_event(self
, widget
, event
):
621 is_maximized
= widget
.window
.get_state() == gtk
.gdk
.WINDOW_STATE_MAXIMIZED
622 Config
.set(OPTION_WINDOW_MAX
, is_maximized
)
624 def _on_straw_main_configure_event(self
, widget
, event
, *args
):
625 if not (widget
.window
.get_state() & gtk
.gdk
.WINDOW_STATE_MAXIMIZED
):
626 self
._presenter
.check_allocation(widget
, event
)
628 def _on_main_main_pane_size_allocate(self
, widget
, *args
):
629 self
._presenter
.check_main_pane_position(widget
)
631 def _on_main_sub_pane_size_allocate(self
, widget
, *args
):
632 self
._presenter
.check_sub_pane_position(widget
)
635 def _on_feed_subscribe_activate(self
, *args
):
636 self
._presenter
.subscribe()
638 def _on_feed_unsubscribe_activate(self
, *args
):
639 self
._presenter
.remove_selected_feed()
641 def _on_subscription_import_activate(self
, *args
):
642 self
._presenter
.import_subscriptions(self
._widget
)
644 def _on_subscription_export_activate(self
, *args
):
645 self
._presenter
.export_subscriptions(self
._widget
)
647 def _on_feed_mark_as_read_activate(self
, *args
):
648 self
._presenter
.mark_feed_as_read()
650 def _on_feed_mark_all_as_read_activate(self
, *args
):
651 self
._presenter
.mark_all_as_read()
653 def _on_feed_refresh_selected_activate(self
, *args
):
654 self
._presenter
.poll_current_feed()
656 def _on_subscription_refresh_activate(self
, *args
):
657 self
._presenter
.poll_all()
659 def _on_quit_activate(self
, *args
):
660 return self
._presenter
.quit()
663 def _on_copy_activate(self
, *args
):
664 self
._presenter
.copy_itemview_text_selection()
666 def _on_find_activate(self
, widget
, *args
):
667 xml
= gtk
.glade
.get_widget_tree(self
._widget
)
668 menu_find
= xml
.get_widget('menu_find')
669 accel_label
= menu_find
.get_child()
671 if not self
._find
_toggled
:
672 self
._presenter
.show_search()
673 self
._detach
_unmodified
_accelerator
_group
()
674 self
._find
_toggled
= True
676 # save the "Find..." stock text for later recovery
677 self
._old
_label
_text
= accel_label
.get_text()
678 accel_label
.set_text(_('Return to feed list...'))
681 self
._presenter
.hide_search()
682 self
._attach
_unmodified
_accelerator
_group
()
683 self
._find
_toggled
= False
684 accel_label
.set_text(self
._old
_label
_text
)
686 def _on_preferences_activate(self
, *args
):
687 self
._presenter
.show_preferences_dialog(self
._widget
)
689 def _on_feed_information_activate(self
, *args
):
690 self
._presenter
.show_feed_properties(self
._widget
)
693 def _on_previous_item_activate(self
, *args
):
694 self
._presenter
.display_previous_item()
696 def _on_next_item_activate(self
, *args
):
697 self
._presenter
.display_next_item()
699 def _on_next_feed_activate(self
, *args
):
700 self
._presenter
.display_next_feed()
702 def _on_previous_feed_activate(self
, *args
):
703 self
._presenter
.display_previous_feed()
705 def _on_next_unread_feed_activate(self
, *args
):
706 self
._presenter
.display_next_unread_feed()
708 def _on_scroll_unread_items_activate(self
, *args
):
709 self
._presenter
.scroll_or_display_next_unread_item()
713 def _on_report_problem_activate(self
, menuitem
, *args
):
714 helpers
.url_show("http://bugzilla.gnome.org/simple-bug-guide.cgi?product=straw")
716 def _on_about_activate(self
, menuitem
, *args
):
717 widget
= self
._presenter
.credits()
720 def _on_straw_main_drag_data_received(self
, w
, context
,
721 x
, y
, data
, info
, time
):
722 if data
and data
.format
== 8:
723 url
= data
.data
.split("\n")[0]
724 subscribe_show("%s" % url
, self
._widget
)
725 context
.finish(True, False, time
)
727 context
.finish(False, False, time
)
730 def show_offline_dialog(self
):
731 return helpers
.report_offline_status(self
._widget
)
733 def get_widget_tree(self
):
734 return gtk
.glade
.get_widget_tree(self
._widget
)
737 self
._widget
.present()
739 def should_present(self
):
740 if self
._widget
.window
and self
._widget
.window
.get_state() is not gtk
.gdk
.WINDOW_STATE_WITHDRAWN
:
743 self
._widget
.present()
748 gnome
.program_init(straw
.defs
.PACKAGE
, straw
.defs
.VERSION
)
750 # initialize threading and environment
751 gobject
.threads_init()
753 #Config.set(OPTION_OFFLINEnfig.reload_css = os.getenv('STRAW_RELOAD_CSS') is not None
755 # build tray status icon
757 iconfile
= os
.path
.normpath(straw
.defs
.STRAW_DATA_DIR
+ "/straw.png")
758 pixbuf
= gtk
.gdk
.pixbuf_new_from_file(iconfile
)
759 scaled_buf
= pixbuf
.scale_simple(16, 16, gtk
.gdk
.INTERP_BILINEAR
)
760 image
.set_from_pixbuf(scaled_buf
)
762 self
.tray
= gtk
.status_icon_new_from_pixbuf(scaled_buf
)
763 self
.tray
.connect('activate', self
._tray
_clicked
)
764 except AttributeError:
767 self
.tray
= egg
.trayicon
.TrayIcon(straw
.defs
.PACKAGE
);
768 self
._eventbox
= gtk
.EventBox()
769 self
.tray
.add(self
._eventbox
)
770 self
._eventbox
.connect('button_press_event', self
._tray
_clicked
)
771 self
._eventbox
.connect("drag-data-received", self
._on
_drag
_data
_received
)
772 self
._eventbox
.drag_dest_set(
773 gtk
.DEST_DEFAULT_ALL
,
774 [('_NETSCAPE_URL', 0, 0), ('text/uri-list ', 0, 1), ('x-url/http', 0, 2)],
775 gtk
.gdk
.ACTION_COPY | gtk
.gdk
.ACTION_MOVE
)
776 self
._eventbox
.add(image
)
779 self
._tooltip
= gtk
.Tooltips()
780 self
.unread_count
= 0
781 # end build tray status icon
783 FeedManager
.setup(storage_path
= "test.db")
786 ItemManager
.setup(storage_path
= "test.db")
789 #FeedManager.import_opml(os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml"))
791 #if config.first_time:
792 # filepath = os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml")
793 # feeds.import_opml(filepath)
795 #ImageCache.initialize()
797 xml
= gtk
.glade
.XML(os
.path
.join(straw
.defs
.STRAW_DATA_DIR
, 'straw.glade'), "straw_main", gettext
.textdomain())
798 window
= xml
.get_widget('straw_main')
799 self
._main
_presenter
= ApplicationPresenter(view
= ApplicationView(window
))
800 self
._main
_presenter
.view
.present()
802 #PollManager.get_instance().start_polling_loop()
803 # set the default icon for the windows
804 iconfile
= os
.path
.join(straw
.defs
.STRAW_DATA_DIR
, "straw.png")
805 gtk
.window_set_default_icon(gtk
.gdk
.pixbuf_new_from_file(iconfile
))
810 gtk
.gdk
.threads_init()
811 gtk
.gdk
.threads_enter()
813 gtk
.gdk
.threads_leave()
815 def _update(self
, flist
):
816 uritems
= urfeeds
= 0
817 for ur
in [f
.number_of_unread
for f
in flist
.flatten_list()]:
821 if uritems
== self
.unread_count
:
823 self
.unread_count
= uritems
825 self
._tooltip
.set_tip(self
.tray
, _("%d new items")%uritems
)
831 def _tray_clicked(self
, widget
, event
=None):
832 self
._main
_presenter
.view
.should_present()
834 if event
and not (event
.button
== 1):
836 self
._main
_presenter
.scroll_or_display_next_unread_item()
838 def _on_drag_data_received(self
, widget
, context
, x
, y
, data
, info
, timestamp
):
839 if data
and data
.format
== 8:
840 url
= data
.data
.split("\n")[0]
841 subscribe_show(url
="%s" % url
)
842 context
.finish(True, False, timestamp
)
844 context
.finish(False, False, timestamp
)