3 Handles listing of feed items in a view (i.e. GTK+ TreeView)
5 __copyright__
= "Copyright (c) 2002-2005 Free Software Foundation, Inc."
6 __license__
= """GNU General Public License
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public
19 License along with this program; if not, write to the
20 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.
24 from xml
.sax
import saxutils
37 Constants for the item list treeview columns
46 class ItemsView(MVP
.WidgetView
):
49 <popup name=\"itemlist_popup\">
50 <menuitem action=\"mark_as_unread\"/>
55 def _initialize(self
):
56 self
._widget
.connect("button_press_event",self
._on
_button
_press
_event
)
57 self
._widget
.connect("popup-menu", self
._on
_popup
_menu
)
58 self
._widget
.connect("row-activated", self
._on
_row
_activated
)
62 def scroll_to_cell(self
, path
):
63 selection
= self
._widget
.get_selection()
64 selection
.select_path(path
)
65 self
._widget
.scroll_to_cell(path
)
66 self
._widget
.set_cursor(path
)
69 def get_widget_selection(self
):
70 return self
._widget
.get_selection()
72 def _create_popup(self
):
74 ("mark_as_unread", gtk
.STOCK_CONVERT
, "Mark as _Unread", None,
75 "Mark this item as unread", self
._on
_menu
_mark
_as
_unread
_activate
)
77 self
._uimanager
= gtk
.UIManager()
78 actiongroup
= gtk
.ActionGroup("ItemListActions")
79 actiongroup
.add_actions(actions
)
80 self
._uimanager
.insert_action_group(actiongroup
, 0)
81 self
._uimanager
.add_ui_from_string(self
.ui
)
82 self
._popup
= self
._uimanager
.get_widget('/itemlist_popup')
84 def _on_popup_menu(self
, treeview
, *args
):
85 self
._popup
.popup(None,None,None,0,0)
87 def _on_button_press_event(self
, treeview
, event
):
92 time
= gtk
.get_current_event_time()
93 path
= treeview
.get_path_at_pos(x
, y
)
96 path
, col
, cellx
, celly
= path
98 treeview
.set_cursor( path
, col
, 0)
99 self
._popup
.popup(None, None, None, event
.button
, time
)
103 def _on_menu_mark_as_unread_activate(self
, *args
):
104 self
._presenter
.mark_selected_item_as_unread()
106 def _item_selection_changed(self
, selection
):
107 selected
= selection
.get_selected()
109 self
._presenter
.selection_changed(selected
)
112 def _on_row_activated(self
, treeview
, path
, column
):
113 model
= self
._widget
.get_model()
114 try: treeiter
= model
.get_iter(path
)
117 link
= model
.get_value(treeiter
, Column
.ITEM
).link
122 class ItemListView(ItemsView
):
126 Presenter: ItemListPresenter
128 def _initialize(self
):
129 ItemsView
._initialize
(self
)
130 self
._widget
.set_rules_hint(False)
132 selection
= self
._widget
.get_selection()
133 selection
.connect("changed", self
._item
_selection
_changed
)
135 def _model_set(self
):
136 self
._widget
.set_model(self
._model
)
137 self
._create
_columns
()
139 def _sticky_toggled(self
, cell
, path
):
140 self
._presenter
.sticky_toggled(cell
, path
,
141 Column
.ITEM
, Column
.STICKY_FLAG
)
144 def _create_columns(self
):
145 renderer
= gtk
.CellRendererToggle()
146 column
= gtk
.TreeViewColumn(_('Keep'), renderer
,
147 active
=Column
.STICKY_FLAG
)
148 column
.set_resizable(True)
149 column
.set_reorderable(True)
150 self
._widget
.append_column(column
)
151 renderer
.connect('toggled', self
._sticky
_toggled
)
153 renderer
= gtk
.CellRendererText()
154 column
= gtk
.TreeViewColumn(_('_Title'), renderer
,
156 foreground
=Column
.FOREGROUND
,
157 weight
=Column
.WEIGHT
)
158 column
.set_resizable(True)
159 column
.set_reorderable(True)
160 self
._widget
.append_column(column
)
163 class ItemsPresenter(MVP
.BasicPresenter
):
165 self
.initialize_slots(Event
.ItemSelectionChangedSignal
)
166 self
._selected
_item
= None
169 def selection_changed(self
, selected
):
170 model
, treeiter
= selected
171 if not treeiter
: return
172 assert model
is self
._model
173 path
= model
.get_path(treeiter
)
174 self
._item
_selected
(path
)
175 self
._view
.scroll_to_cell(path
)
177 def _item_selected(self
, path
):
178 item
= self
._model
[path
][Column
.ITEM
]
179 if self
._selected
_item
is not item
:
180 self
._selected
_item
= item
181 if not self
._selected
_item
.seen
:
182 self
._selected
_item
.seen
= 1
183 self
.emit_signal(Event
.ItemSelectionChangedSignal(self
,self
._selected
_item
))
186 def mark_selected_item_as_unread(self
):
187 self
._selected
_item
.seen
= 0
188 self
._mark
_item
(self
._selected
_item
)
190 def display_empty_feed(self
):
194 def _set_column_weight(self
, item
, path
):
195 tree_iter
= self
._model
.get_iter(path
)
196 weight
= (pango
.WEIGHT_BOLD
, pango
.WEIGHT_NORMAL
)[item
.seen
]
197 colour
= ("#0000FF", "#000000")[item
.seen
]
198 self
._model
.set_value(tree_iter
, Column
.FOREGROUND
, colour
)
199 self
._model
.set_value(tree_iter
, Column
.WEIGHT
, weight
)
202 def _mark_item(self
, item
, index
=None):
204 Marks an item as 'READ'
206 Subclass SHOULD implement this
210 class ItemListPresenter(ItemsPresenter
):
211 def _initialize(self
):
212 model
= gtk
.ListStore(gobject
.TYPE_STRING
, gobject
.TYPE_OBJECT
,
213 gobject
.TYPE_PYOBJECT
, gobject
.TYPE_INT
,
214 gobject
.TYPE_BOOLEAN
, gobject
.TYPE_STRING
)
217 self
._selected
_feed
= None
218 selection
= self
._view
.get_widget_selection()
219 selection
.set_mode(gtk
.SELECTION_SINGLE
)
221 def feedlist_selection_changed(self
, feedlist_selection
, feedlist_model
):
222 (model
, pathlist
) = feedlist_selection
.get_selected_rows()
223 ## XXX fix this, get and display the union of feeds
224 ## currently, it only displays the first feed at path
225 nodes
= feedlist_model
.get_selected_nodes(pathlist
)
229 print "displaying items of feed ", obj
.feed
230 self
.display_feed_items(obj
.feed
)
232 ## XXX display child items of Category
234 #print "feed list selection chaanged in item list ", nodes
236 def select_previous_item(self
):
238 Selects the item before the current selection. If there
239 is no current selection, selects the last item. If there is no
240 previous row, returns False.
242 selection
= self
._view
.get_widget_selection()
243 model
, treeiter
= selection
.get_selected()
244 # select the first item if there is no selection
246 treeiter
= model
.get_iter_first()
247 path
= self
._model
.get_path(treeiter
)
248 # check if there's an item
251 prev_path
= path
[-1]-1
252 # we don't cycle through the items so return False if there are no
253 # more items to go to
256 selection
.select_path(path
[-1]-1,)
259 def select_next_item(self
):
261 Selects the item after the current selection. If there is no current
262 selection, selects the first item. If there is no next row, returns False.
264 selection
= self
._view
.get_widget_selection()
265 model
, treeiter
= selection
.get_selected()
267 treeiter
= model
.get_iter_first()
268 next_iter
= model
.iter_next(treeiter
)
269 if not next_iter
or not self
._model
.iter_is_valid(treeiter
):
271 next_path
= model
.get_path(next_iter
)
272 selection
.select_path(next_path
)
275 def select_last_item(self
):
277 Selects the last item in this list.
279 selection
= self
._view
.get_widget_selection()
280 selection
.select_path(len(self
._model
) - 1)
283 def select_next_unread_item(self
):
285 Selects the first unread item after the current selection. If there is
286 no current selection, or if there are no unread items, returns False..
288 By returning False, the caller can either go to the next feed, or go
289 to the next feed with an unread item.
292 selection
= self
._view
.get_widget_selection()
293 selected_row
= selection
.get_selected()
294 # check if we have a selection. if none,
295 # start searching from the first item
296 if not selected_row
[1]:
297 treerow
= self
._model
[0]
299 model
, treeiter
= selected_row
302 nextiter
= model
.iter_next(treeiter
)
303 if not nextiter
: # no more rows to iterate
305 treerow
= self
._model
[model
.get_path(nextiter
)]
307 item
= treerow
[Column
.ITEM
]
310 selection
.select_path(treerow
.path
)
312 treerow
= treerow
.next
316 def sticky_toggled(self
, cell
, path
, item_column
, stickyflag_column
):
317 treeiter
= self
._model
.get_iter((int(path
),))
318 item
= self
._model
.get_value(treeiter
, item_column
)
319 item
.sticky
= not item
.sticky
320 self
._model
.set(treeiter
, stickyflag_column
, item
.sticky
)
322 def display_feed_items(self
, feed
, select_first
=1):
323 redisplay
= self
._selected
_feed
is feed
324 #if self._selected_feed and not redisplay:
325 # self._selected_feed.signal_disconnect(Event.RefreshFeedDisplaySignal,
326 # self._feed_order_changed)
327 # self._selected_feed.signal_disconnect(Event.AllItemsReadSignal,
328 # self._all_items_read)
329 # self._selected_feed.signal_disconnect(Event.ItemReadSignal,
331 self
._selected
_feed
= feed
333 #self._selected_feed.connect(Event.RefreshFeedDisplaySignal,
334 # self._feed_order_changed)
335 self
._selected
_feed
.connect('items_read', self
._all
_items
_read
)
336 #self._selected_feed.signal_connect(Event.ItemReadSignal,
338 count
= self
._render
_feed
(self
._selected
_feed
)
339 if not redisplay
and count
:
340 if not self
.select_next_unread_item():
341 selection
= self
._view
.get_widget_selection()
342 selection
.select_path((0,))
345 def _feed_order_changed(self
, event
):
346 if event
.sender
is self
._selected
_feed
:
348 selection
= self
._view
.get_widget_selection()
349 model
, treeiter
= selection
.get_selected()
350 if treeiter
is not None:
351 path
= model
.get_path(treeiter
)
352 item
= model
[path
[0]][Column
.ITEM
]
353 self
._render
_feed
(event
.sender
)
355 path
= (item
.feed
.get_item_index(item
),)
357 path
= (0,) # first item
358 selection
.select_path(path
)
360 def _all_items_read(self
, signal
):
361 for index
, item
in signal
.changed
:
362 self
._mark
_item
(item
, index
)
364 def _item_read(self
, signal
):
365 self
._mark
_item
(signal
.item
)
367 def _render_feed(self
, feed
):
370 for item
in feed
.items
:
371 weight
= (pango
.WEIGHT_BOLD
, pango
.WEIGHT_NORMAL
)[item
.seen
]
372 colour
= ("#0000FF", "#000000")[item
.seen
]
373 treeiter
= self
._model
.append()
374 self
._model
.set(treeiter
,
375 Column
.TITLE
, item
.title
,
377 Column
.WEIGHT
, weight
,
378 Column
.STICKY_FLAG
, item
.sticky
,
379 Column
.FOREGROUND
, colour
)
380 if item
is self
._selected
_item
:
384 path
= self
._model
.get_path(curr_iter
)
385 self
._view
.scroll_to_cell(path
)
386 return len(feed
.items
)
388 def _mark_item(self
, item
, path
=None):
390 path
= (item
.feed
.get_item_index(item
),)
391 self
._set
_column
_weight
(item
, path
)