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.
25 from xml
.sax
import saxutils
37 Constants for the item list treeview columns
39 title
, sticky
, item
, weight
, sticky_flag
, foreground
= range(6)
45 def __init__(self
, listener
):
46 self
.manager
= gtk
.UIManager()
48 ("mark_as_unread", gtk
.STOCK_CONVERT
, "Mark as _Unread", None,
49 "Mark this item as unread", listener
.on_menu_mark_as_unread_activate
)
51 actiongroup
= gtk
.ActionGroup("ItemListActions")
52 actiongroup
.add_actions(actions
)
53 self
.manager
.insert_action_group(actiongroup
, 1)
54 #popupui = os.path.join(utils.find_image_dir(), 'ui.xml')
55 #self.manager.add_ui_from_file(popupui)
59 return self
.manager
.get_widget('/itemlist_popup')
63 ''' A model of summary items for tree views '''
66 self
.store
= gtk
.ListStore(str, gobject
.TYPE_OBJECT
, gobject
.TYPE_PYOBJECT
, int, bool, str)
68 def populate(self
, summaryItems
):
70 for item
in summaryItems
:
71 weight
= (pango
.WEIGHT_BOLD
, pango
.WEIGHT_NORMAL
)[item
.seen
]
72 colour
= ("#0000FF", "#000000")[item
.seen
]
73 treeiter
= self
.store
.append()
74 self
.store
.set(treeiter
,
75 Column
.title
, item
.title
,
77 Column
.weight
, weight
,
78 Column
.sticky_flag
, item
.sticky
,
79 Column
.foreground
, colour
)
86 class ItemListView(MVP
.WidgetView
):
88 def _initialize(self
):
89 self
._widget
.set_rules_hint(False)
90 self
._widget
.connect("button_press_event",self
._on
_button
_press
_event
)
91 self
._widget
.connect("popup-menu", self
._on
_popup
_menu
)
92 self
._widget
.connect("row-activated", self
._on
_row
_activated
)
93 #self._popup = Popup(self)
95 renderer
= gtk
.CellRendererToggle()
96 column
= gtk
.TreeViewColumn(_('Keep'), renderer
,
97 active
=Column
.sticky_flag
)
98 column
.set_resizable(True)
99 column
.set_reorderable(True)
100 self
._widget
.append_column(column
)
101 renderer
.connect('toggled', self
._sticky
_toggled
)
103 renderer
= gtk
.CellRendererText()
104 column
= gtk
.TreeViewColumn(_('_Title'), renderer
,
106 foreground
=Column
.foreground
,
107 weight
=Column
.weight
)
108 column
.set_resizable(True)
109 column
.set_reorderable(True)
110 self
._widget
.append_column(column
)
112 selection
= self
._widget
.get_selection()
113 selection
.set_mode(gtk
.SELECTION_SINGLE
)
115 def add_selection_changed_listener(self
, listener
):
116 selection
= self
._widget
.get_selection()
117 selection
.connect('changed',
118 listener
.itemlist_selection_changed
,
121 def _model_set(self
):
122 self
._widget
.set_model(self
._model
.model
)
124 def _sticky_toggled(self
, cell
, path
):
125 print "STICKY TOGGLED ", cell
, path
126 #treeiter = self._model.get_iter((int(path),))
127 #item = self._model.get_value(treeiter, item_column)
128 #item.sticky = not item.sticky
129 #self._model.set(treeiter, stickyflag_column, item.sticky)
130 #self._presenter.sticky_toggled(cell, path,
131 # Column.item, Column.sticky_flag)
134 def _on_popup_menu(self
, treeview
, *args
):
135 self
._popup
.popup(None,None,None,0,0)
137 def _on_button_press_event(self
, treeview
, event
):
139 if event
.button
== 3:
142 time
= gtk
.get_current_event_time()
143 path
= treeview
.get_path_at_pos(x
, y
)
146 path
, col
, cellx
, celly
= path
147 treeview
.grab_focus()
148 treeview
.set_cursor( path
, col
, 0)
149 self
._popup
.popup(None, None, None, event
.button
, time
)
153 def on_menu_mark_as_unread_activate(self
, *args
):
154 selection
= self
._widget
.get_selection()
155 (model
, treeiter
) = selection
.get_selected()
156 if not treeiter
: return
157 item
= model
.get_value(treeiter
, Column
.item
)
159 weight
= (pango
.WEIGHT_BOLD
, pango
.WEIGHT_NORMAL
)[item
.seen
]
160 colour
= ("#0000FF", "#000000")[item
.seen
]
161 model
.set(treeiter
, Column
.foreground
, colour
,
162 Column
.weight
, weight
,
166 def _on_row_activated(self
, treeview
, path
, column
):
167 model
= self
._widget
.get_model()
169 treeiter
= model
.get_iter(path
)
172 link
= model
.get_value(treeiter
, Column
.ITEM
).link
177 def select_previous_item(self
):
179 Selects the item before the current selection. If there
180 is no current selection, selects the last item. If there is no
181 previous row, returns False.
183 selection
= self
._widget
.get_selection()
184 (model
, treeiter
) = selection
.get_selected()
185 # select the first item if there is no selection
187 treeiter
= model
.get_iter_first()
188 path
= model
.get_path(treeiter
)
191 prev_path
= path
[-1]-1
192 # we don't cycle through the items so return False if there are no
193 # more items to go to
196 selection
.select_path(path
[-1]-1,)
199 def select_next_item(self
):
201 Selects the item after the current selection. If there is no current
202 selection, selects the first item. If there is no next row, returns False.
204 selection
= self
._widget
.get_selection()
205 (model
, treeiter
) = selection
.get_selected()
207 treeiter
= model
.get_iter_first()
208 next_iter
= model
.iter_next(treeiter
)
209 if not next_iter
or not model
.iter_is_valid(treeiter
):
211 next_path
= model
.get_path(next_iter
)
212 selection
.select_path(next_path
)
215 def select_last_item(self
):
217 Selects the last item in this list.
219 selection
= self
._widget
.get_selection()
220 selection
.select_path(len(self
._model
.model
) - 1)
223 def select_next_unread_item(self
):
225 Selects the first unread item after the current selection. If there is
226 no current selection, or if there are no unread items, returns False..
228 By returning False, the caller can either go to the next feed, or go
229 to the next feed with an unread item.
232 selection
= self
._widget
.get_widget_selection()
233 (model
, treeiter
) = selection
.get_selected()
234 # check if we have a selection. if none,
235 # start searching from the first item
239 model
, treeiter
= selected_row
242 nextiter
= model
.iter_next(treeiter
)
243 if not nextiter
: # no more rows to iterate
245 treerow
= self
._model
[model
.get_path(nextiter
)]
247 item
= treerow
[Column
.item
]
250 selection
.select_path(treerow
.path
)
252 treerow
= treerow
.next
255 class ItemListPresenter(MVP
.BasicPresenter
):
257 ## XXX listen for RefreshFeedDisplaySignal, AllItemsReadSignal,
258 ## ItemReadSignal, FeedOrder changes,
260 def _initialize(self
):
261 self
.model
= ItemListModel()
263 def feedlist_selection_changed(self
, feedlist_selection
, column
):
264 (model
, pathlist
) = feedlist_selection
.get_selected_rows()
265 iters
= [model
.get_iter(path
) for path
in pathlist
]
266 ## XXX fix this, get and display the union of feeds
267 ## currently, it only displays the first feed at path
270 nodes
= [model
.get_value(treeiter
, column
) for treeiter
in iters
]
272 feeds
= [node
.feed
for node
in nodes
if node
.feed
]
273 map(self
.model
.populate
, [feed
.items
for feed
in feeds
])
275 ## XXX display child items of Category
278 def display_feed_items(self, feed, select_first=1):
279 redisplay = self._selected_feed is feed
280 #if self._selected_feed and not redisplay:
281 # self._selected_feed.signal_disconnect(Event.RefreshFeedDisplaySignal,
282 # self._feed_order_changed)
283 # self._selected_feed.signal_disconnect(Event.AllItemsReadSignal,
284 # self._all_items_read)
285 # self._selected_feed.signal_disconnect(Event.ItemReadSignal,
287 self._selected_feed = feed
289 #self._selected_feed.connect(Event.RefreshFeedDisplaySignal,
290 # self._feed_order_changed)
291 self._selected_feed.connect('items_read', self._all_items_read)
292 #self._selected_feed.signal_connect(Event.ItemReadSignal,
294 count = self._render_feed(self._selected_feed)
295 if not redisplay and count:
296 if not self.select_next_unread_item():
297 selection = self._view.get_widget_selection()
298 selection.select_path((0,))
301 def _feed_order_changed(self, event):
302 if event.sender is self._selected_feed:
304 selection = self._view.get_widget_selection()
305 model, treeiter = selection.get_selected()
306 if treeiter is not None:
307 path = model.get_path(treeiter)
308 item = model[path[0]][Column.ITEM]
309 self._render_feed(event.sender)
311 path = (item.feed.get_item_index(item),)
313 path = (0,) # first item
314 selection.select_path(path)
316 def _all_items_read(self, feed, unread_items):
318 for index, item in enumerate(unread_items):
319 self._mark_item(item, index)
321 def _item_read(self, signal):
322 self._mark_item(signal.item)
324 def _render_feed(self, feed):
327 for item in feed.items:
328 weight = (pango.WEIGHT_BOLD, pango.WEIGHT_NORMAL)[item.seen]
329 colour = ("#0000FF", "#000000")[item.seen]
330 treeiter = self._model.append()
331 self._model.set(treeiter,
332 Column.title, item.title,
334 Column.WEIGHT, weight,
335 Column.sticky_flag, item.sticky,
336 Column.FOREGROUND, colour)
337 if item is self._selected_item:
341 path = self._model.get_path(curr_iter)
342 self._view.scroll_to_cell(path)
343 return len(feed.items)
345 def _mark_item(self, item, path=None):
347 path = (item.feed.get_item_index(item),)
348 self._set_column_weight(item, path)