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 feeds
= feedlist_model
.get_feeds(pathlist
)
226 self
.display_feed_items(feeds
[0])
227 print "feed list selection chaanged in item list ", feeds
229 def select_previous_item(self
):
231 Selects the item before the current selection. If there
232 is no current selection, selects the last item. If there is no
233 previous row, returns False.
235 selection
= self
._view
.get_widget_selection()
236 model
, treeiter
= selection
.get_selected()
237 # select the first item if there is no selection
239 treeiter
= model
.get_iter_first()
240 path
= self
._model
.get_path(treeiter
)
241 # check if there's an item
244 prev_path
= path
[-1]-1
245 # we don't cycle through the items so return False if there are no
246 # more items to go to
249 selection
.select_path(path
[-1]-1,)
252 def select_next_item(self
):
254 Selects the item after the current selection. If there is no current
255 selection, selects the first item. If there is no next row, returns False.
257 selection
= self
._view
.get_widget_selection()
258 model
, treeiter
= selection
.get_selected()
260 treeiter
= model
.get_iter_first()
261 next_iter
= model
.iter_next(treeiter
)
262 if not next_iter
or not self
._model
.iter_is_valid(treeiter
):
264 next_path
= model
.get_path(next_iter
)
265 selection
.select_path(next_path
)
268 def select_last_item(self
):
270 Selects the last item in this list.
272 selection
= self
._view
.get_widget_selection()
273 selection
.select_path(len(self
._model
) - 1)
276 def select_next_unread_item(self
):
278 Selects the first unread item after the current selection. If there is
279 no current selection, or if there are no unread items, returns False..
281 By returning False, the caller can either go to the next feed, or go
282 to the next feed with an unread item.
285 selection
= self
._view
.get_widget_selection()
286 selected_row
= selection
.get_selected()
287 # check if we have a selection. if none,
288 # start searching from the first item
289 if not selected_row
[1]:
290 treerow
= self
._model
[0]
292 model
, treeiter
= selected_row
295 nextiter
= model
.iter_next(treeiter
)
296 if not nextiter
: # no more rows to iterate
298 treerow
= self
._model
[model
.get_path(nextiter
)]
300 item
= treerow
[Column
.ITEM
]
303 selection
.select_path(treerow
.path
)
305 treerow
= treerow
.next
309 def sticky_toggled(self
, cell
, path
, item_column
, stickyflag_column
):
310 treeiter
= self
._model
.get_iter((int(path
),))
311 item
= self
._model
.get_value(treeiter
, item_column
)
312 item
.sticky
= not item
.sticky
313 self
._model
.set(treeiter
, stickyflag_column
, item
.sticky
)
315 def display_feed_items(self
, feed
, select_first
=1):
316 redisplay
= self
._selected
_feed
is feed
317 if self
._selected
_feed
and not redisplay
:
318 self
._selected
_feed
.signal_disconnect(Event
.RefreshFeedDisplaySignal
,
319 self
._feed
_order
_changed
)
320 self
._selected
_feed
.signal_disconnect(Event
.AllItemsReadSignal
,
321 self
._all
_items
_read
)
322 self
._selected
_feed
.signal_disconnect(Event
.ItemReadSignal
,
324 self
._selected
_feed
= feed
326 self
._selected
_feed
.signal_connect(Event
.RefreshFeedDisplaySignal
,
327 self
._feed
_order
_changed
)
328 self
._selected
_feed
.signal_connect(Event
.AllItemsReadSignal
,
329 self
._all
_items
_read
)
330 self
._selected
_feed
.signal_connect(Event
.ItemReadSignal
,
332 count
= self
._render
_feed
(self
._selected
_feed
)
333 if not redisplay
and count
:
334 if not self
.select_next_unread_item():
335 selection
= self
._view
.get_widget_selection()
336 selection
.select_path((0,))
339 def _feed_order_changed(self
, event
):
340 if event
.sender
is self
._selected
_feed
:
342 selection
= self
._view
.get_widget_selection()
343 model
, treeiter
= selection
.get_selected()
344 if treeiter
is not None:
345 path
= model
.get_path(treeiter
)
346 item
= model
[path
[0]][Column
.ITEM
]
347 self
._render
_feed
(event
.sender
)
349 path
= (item
.feed
.get_item_index(item
),)
351 path
= (0,) # first item
352 selection
.select_path(path
)
354 def _all_items_read(self
, signal
):
355 for index
, item
in signal
.changed
:
356 self
._mark
_item
(item
, index
)
358 def _item_read(self
, signal
):
359 self
._mark
_item
(signal
.item
)
361 def _render_feed(self
, feed
):
364 for item
in feed
.items
:
365 weight
= (pango
.WEIGHT_BOLD
, pango
.WEIGHT_NORMAL
)[item
.seen
]
366 colour
= ("#0000FF", "#000000")[item
.seen
]
367 treeiter
= self
._model
.append()
368 self
._model
.set(treeiter
,
369 Column
.TITLE
, item
.title
,
371 Column
.WEIGHT
, weight
,
372 Column
.STICKY_FLAG
, item
.sticky
,
373 Column
.FOREGROUND
, colour
)
374 if item
is self
._selected
_item
:
378 path
= self
._model
.get_path(curr_iter
)
379 self
._view
.scroll_to_cell(path
)
380 return len(feed
.items
)
382 def _mark_item(self
, item
, path
=None):
384 path
= (item
.feed
.get_item_index(item
),)
385 self
._set
_column
_weight
(item
, path
)