Implemented "mark all as read".
authorPaweł Paprota <ppawel@fastmail.fm>
Sat, 7 Jun 2008 17:26:12 +0000 (7 19:26 +0200)
committerPaweł Paprota <ppawel@fastmail.fm>
Sat, 7 Jun 2008 17:26:12 +0000 (7 19:26 +0200)
straw/Application.py
straw/FeedListView.py
straw/FeedManager.py
straw/ItemList.py
straw/model/__init__.py

index f58c8f4..8f4c056 100644 (file)
@@ -218,6 +218,8 @@ class ApplicationPresenter(MVP.BasicPresenter):
         view.add_selection_changed_listener(self._itemlist_presenter)
         view.add_selection_changed_listener(self._error_presenter)
 
+        view.add_mark_all_as_read_listener(self._itemlist_presenter)
+
         self._offline_presenter = OfflineToggle(widget_tree.get_widget('offline_toggle'))
 
         self._status_presenter = StatusPresenter(view = widget_tree.get_widget("main_statusbar"))
@@ -778,12 +780,12 @@ class Application:
         self.unread_count = 0
         # end build tray status icon
 
-        FeedManager.setup(storage_path = "test.db")
-        FeedManager.init()
-
         ItemManager.setup(storage_path = "test.db")
         ItemManager.init()
 
+        FeedManager.setup(storage_path = "test.db")
+        FeedManager.init()
+
         #FeedManager.import_opml(os.path.join(straw.STRAW_DATA_DIR, "default_subscriptions.opml"))
 
         #if config.first_time:
index dec017e..5d7d81e 100644 (file)
@@ -253,8 +253,8 @@ class FeedsView(MVP.WidgetView):
         action.connect('activate', self.on_menu_poll_selected_activate)
         action = uifactory.get_action('/feedlist_popup/add_child')
         action.connect('activate', self.on_menu_add_child_activate)
-        action = uifactory.get_action('/feedlist_popup/mark_as_read')
-        action.connect('activate', self.on_menu_mark_all_as_read_activate)
+        self.mark_all_as_read_action = uifactory.get_action('/feedlist_popup/mark_as_read')
+        self.mark_all_as_read_action.connect('activate', self.on_menu_mark_all_as_read_activate)
         action = uifactory.get_action('/feedlist_popup/stop_refresh')
         action.connect('activate', self.on_menu_stop_poll_selected_activate)
         action = uifactory.get_action('/feedlist_popup/remove')
@@ -396,6 +396,9 @@ class FeedsView(MVP.WidgetView):
         selection = self._widget.get_selection()
         selection.connect('changed', listener.feedlist_selection_changed, Column.object)
 
+    def add_mark_all_as_read_listener(self, listener):
+        self.mark_all_as_read_action.connect('activate', listener.on_mark_all_as_read)
+
     def _on_popup_menu(self, treeview, *args):
         self.popup.popup(None, None, None, 0, 0)
 
@@ -525,7 +528,7 @@ class FeedsView(MVP.WidgetView):
         self.foreach_selected(lambda o,*args: o.feed.router.stop_polling())
 
     def on_menu_mark_all_as_read_activate(self, *args):
-        self.foreach_selected(lambda o,*args: o.feed.mark_items_as_read())
+        self.foreach_selected(lambda o, *args: o.node.mark_items_as_read())
 
     def on_remove_selected_feed(self, *args):
         nodes = [tv_node for tv_node in self.selected()]
index fb585f8..3c9f287 100644 (file)
@@ -141,6 +141,7 @@ class FeedManager(GObject):
     def _setup_node_signals(self, node):
         node.connect("parent-changed", self.on_parent_changed)
         node.connect("norder-changed", self.on_norder_changed)
+        node.connect("mark-all-items-as-read", self.on_mark_all_items_as_read)
 
     def _set_feed_status(self, feed, status):
         if not hasattr(feed, "status"):
@@ -347,6 +348,13 @@ class FeedManager(GObject):
         #debug("norder changed, saving %d" % (obj.id))
         self.dao.save(obj)
 
+    def on_mark_all_items_as_read(self, node):
+        self.dao.tx_begin()
+        result = self.dao.query("UPDATE items SET is_read = 1 WHERE feed_id = %d" % node.id)
+        self.dao.tx_commit()
+
+        node.props.unread_count -= node.props.unread_count
+
 _instance = None
 
 def _get_instance():
index 04c7026..48e3896 100644 (file)
@@ -182,7 +182,10 @@ class ItemListView(MVP.WidgetView):
         model.set(treeiter, Column.FOREGROUND, colour,
                   Column.WEIGHT, weight,
                   Column.ITEM, item)
-        return
+
+    def mark_all_as_read(self):
+        for row in self._widget.get_model():
+            row[Column.WEIGHT] = pango.WEIGHT_NORMAL
 
     def _on_row_activated(self, treeview, path, column):
         ''' double-clicking an item opens that item in the web browser '''
@@ -199,6 +202,10 @@ class ItemListView(MVP.WidgetView):
     def select_first_item(self):
         selection = self._widget.get_selection()
         (model, treeiter) = selection.get_selected()
+
+        if not treeiter:
+            return False
+
         path = model.get_path(model.get_iter_first())
         selection.select_path(path)
         return True
@@ -260,7 +267,7 @@ class ItemListView(MVP.WidgetView):
         if not treeiter: return False
         nextiter = model.iter_next(treeiter)
         if not nextiter: return False # no more rows to iterate
-        treerow = model[nextiter]
+        treerow = model[treeiter]
         has_unread = False
         while(treerow):
             item = treerow[Column.ITEM]
@@ -277,6 +284,11 @@ class ItemListPresenter(MVP.BasicPresenter):
 
     def _initialize(self):
         self.model = ItemListModel()
+        self.presented_nodes = []
+
+    def populate(self, items):
+        self.model.populate(items)
+        self.view.set_model(self.model)
 
     def feedlist_selection_changed(self, feedlist_selection, column):
         (model, pathlist) = feedlist_selection.get_selected_rows()
@@ -284,33 +296,32 @@ class ItemListPresenter(MVP.BasicPresenter):
 
         if not iters:
             return
-        
+
         nodes = [model.get_value(treeiter, column) for treeiter in iters]
+
+        if nodes == self.presented_nodes:
+            # Selection hasn't actually changed so let's ignore the signal
+            return
+
+        self.presented_nodes = nodes
+
         items = []
         node = nodes[0]
 
-        def populate_items(items):
-            self.view._widget.set_model(None)
-            #import time
-            #s = time.time()
-            self.model.populate(items)
-            #print "populate took %s" % (time.time() - s)
-            #gobject.idle_add(self.model.populate, items)
-            self.view._widget.set_model(self._model.model)
-
         if node.node.type == "F":
             items = ItemManager.get_feed_items(node.node.id)
-            populate_items(items)
+            self.view.set_model(None)
+            self.model.populate(items)
+            self.view.set_model(self.model)
         elif node.node.type == "C":
-            items = ItemManager.get_category_items(node.node.id, populate_items)
+            self.view.set_model(None)
+            items = ItemManager.get_category_items(node.node.id, self.populate)
 
-        #self.view._widget.set_model(None)
-        #self.model.populate(items)
-        #self.view._widget.set_model(self._model.model)
+        if not self.view.select_next_unread_item():
+            self.view.select_first_item()
 
-        #if not len(self.model.model): return
-        #if not self.view.select_next_unread_item():
-        #    self.view.select_first_item()
+    def on_mark_all_as_read(self, action):
+        self.view.mark_all_as_read()
 
     def flatten(self, sequence):
         ''' python cookbook recipe 4.6 '''
index fc4cf6f..121c033 100644 (file)
@@ -31,7 +31,8 @@ class Node(gobject.GObject):
     __gsignals__ = {
         "parent-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
         "norder-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
-        "unread-count-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT))
+        "unread-count-changed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
+        "mark-all-items-as-read": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())
     }
 
     def __init__(self):
@@ -202,6 +203,10 @@ class Category(Node):
     def on_unread_count_changed(self, obj, old_value, delta):
         self.props.unread_count += delta
 
+    def mark_items_as_read(self):
+        for child in self.children:
+            child.mark_items_as_read()
+
 class Feed(Node):
     """
     Represents a feed - a node that can contain items. Feeds cannot have
@@ -269,6 +274,9 @@ class Feed(Node):
         else:
             self.props.unread_count += 1
 
+    def mark_items_as_read(self):
+        self.emit("mark-all-items-as-read")
+
 class Item(GObject):
     persistent_properties = [ "id", "title", "feed_id", "is_read", "link", "pub_date", "description" ]
     persistent_table = "items"