From f1c0b03db5b37a60dc9daca63bd9a8afc3dc50f2 Mon Sep 17 00:00:00 2001 From: Yves Marcoz Date: Fri, 16 Jul 2010 09:11:26 -0700 Subject: [PATCH] * Fixed some format upgrade issues * New widget with improved functionality --- src/FeedingIt.py | 38 ++++-- src/feedingit_widget.py | 297 +++++++++++++++++++++++++++++++++-------------- src/feedingitdbus.py | 6 + src/rss_sqlite.py | 53 ++++++--- src/update_feeds.py | 22 ++-- 5 files changed, 293 insertions(+), 123 deletions(-) diff --git a/src/FeedingIt.py b/src/FeedingIt.py index 9fde1f5..2e53a52 100644 --- a/src/FeedingIt.py +++ b/src/FeedingIt.py @@ -780,7 +780,7 @@ class DisplayFeed(hildon.StackableWindow): def buttonPurgeArticles(self, *widget): self.clear() self.feed.purgeReadArticles() - self.feed.saveFeed(CONFIGDIR) + #self.feed.saveFeed(CONFIGDIR) self.displayFeed() def destroyArticle(self, handle): @@ -821,7 +821,7 @@ class DisplayFeed(hildon.StackableWindow): def onArticleDeleted(self, object, index): self.clear() self.feed.removeArticle(index) - self.feed.saveFeed(CONFIGDIR) + #self.feed.saveFeed(CONFIGDIR) self.displayFeed() def button_update_clicked(self, button): @@ -842,9 +842,19 @@ class DisplayFeed(hildon.StackableWindow): self.updateDbusHandler.ArticleCountUpdated() def buttonReadAllClicked(self, button): - for index in self.feed.getIds(): - self.feed.setEntryRead(index) - self.mark_item_read(index) + #self.clear() + self.feed.markAllAsRead() + it = self.feedItems.get_iter_first() + while it is not None: + k = self.feedItems.get_value(it, FEED_COLUMN_KEY) + title = self.fix_title(self.feed.getTitle(k)) + markup = ENTRY_TEMPLATE % (self.config.getFontSize(), title) + self.feedItems.set_value(it, FEED_COLUMN_MARKUP, markup) + it = self.feedItems.iter_next(it) + #self.displayFeed() + #for index in self.feed.getIds(): + # self.feed.setEntryRead(index) + # self.mark_item_read(index) class FeedingIt: @@ -974,8 +984,9 @@ class FeedingIt: def button_markAll(self, button): for key in self.listing.getListOfFeeds(): feed = self.listing.getFeed(key) - for id in feed.getIds(): - feed.setEntryRead(id) + feed.markAllAsRead() + #for id in feed.getIds(): + # feed.setEntryRead(id) self.listing.updateUnread(key) self.displayListing() @@ -1093,6 +1104,19 @@ class FeedingIt: self.listing.getFeedTitle(key), key, \ self.config, self.updateDbusHandler) self.disp.connect("feed-closed", self.onFeedClosed) + + def openArticle(self, key, id): + try: + self.feed_lock + except: + # If feed_lock doesn't exist, we can open the feed, else we do nothing + if key != None: + self.feed_lock = get_lock(key) + self.disp = DisplayFeed(self.listing, self.listing.getFeed(key), \ + self.listing.getFeedTitle(key), key, \ + self.config, self.updateDbusHandler) + self.disp.button_clicked(None, id) + self.disp.connect("feed-closed", self.onFeedClosed) def onFeedClosed(self, object, key): diff --git a/src/feedingit_widget.py b/src/feedingit_widget.py index f6ca85c..73ed135 100644 --- a/src/feedingit_widget.py +++ b/src/feedingit_widget.py @@ -24,6 +24,10 @@ # ============================================================================ #import sys +import sqlite3 +from re import sub +from htmlentitydefs import name2codepoint + import gtk, pickle, gobject, dbus import hildondesktop, hildon #from rss import Listing @@ -37,6 +41,7 @@ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) from os import environ, remove bus = dbus.bus.BusConnection(environ["DBUS_SESSION_BUS_ADDRESS"]) from os.path import isfile +from cgi import escape settings = gtk.settings_get_default() color_style = gtk.rc_get_style_by_paths( settings, 'GtkButton', 'osso-logical-colors', gtk.Button) @@ -61,6 +66,31 @@ def get_font_desc(logicalfontname): font_desc = font_style.font_desc return font_desc +def unescape(text): + def fixup(m): + text = m.group(0) + if text[:2] == "&#": + # character reference + try: + if text[:3] == "&#x": + return unichr(int(text[3:-1], 16)) + else: + return unichr(int(text[2:-1])) + except ValueError: + pass + else: + # named entity + try: + text = unichr(name2codepoint[text[1:-1]]) + except KeyError: + pass + return text # leave as is + return sub("&#?\w+;", fixup, text) + +def fix_title(title): + return escape(unescape(title).replace("","").replace("","").replace("","").replace("","").replace("","")) + + class FeedingItHomePlugin(hildondesktop.HomePluginItem): def __init__(self): __gsignals__ = { @@ -73,6 +103,9 @@ class FeedingItHomePlugin(hildondesktop.HomePluginItem): self.connect("show-settings", self.show_settings) self.feed_list = {} self.total = 0 + self.status = 0 # 0=Showing feeds, 1=showing articles + self.updateStatus = 0 # 0=Not updating, 1=Update in progress + self.pageStatus = 0 if isfile(SOURCE): file = open(SOURCE) self.autoupdateId = int(file.read()) @@ -82,26 +115,47 @@ class FeedingItHomePlugin(hildondesktop.HomePluginItem): vbox = gtk.VBox(False, 0) + ## Prepare the main HBox + self.hbox1 = gtk.HBox(False, 0) #self.button = gtk.Button() - self.button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) - self.button.set_text("FeedingIt","") - self.button.set_sensitive(False) - self.label1 = self.button.child.child.get_children()[0].get_children()[0] - self.label2 = self.button.child.child.get_children()[0].get_children()[1] - self.label1.modify_fg(gtk.STATE_INSENSITIVE, default_color) - self.label1.modify_font(font_desc) - self.label2.modify_fg(gtk.STATE_INSENSITIVE, active_color) + self.buttonApp = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) + #self.buttonApp.set_text("FeedingIt","") + #self.button.set_sensitive(False) + #self.label1 = self.buttonApp.child.child.get_children()[0].get_children()[0] + #self.label2 = self.button.child.child.get_children()[0].get_children()[1] + #self.label1.modify_fg(gtk.STATE_INSENSITIVE, default_color) + #self.label1.modify_font(font_desc) + #self.label2.modify_fg(gtk.STATE_INSENSITIVE, active_color) icon_theme = gtk.icon_theme_get_default() - pixbuf = icon_theme.load_icon("feedingit", 32, gtk.ICON_LOOKUP_USE_BUILTIN ) + pixbuf = icon_theme.load_icon("feedingit", 20, gtk.ICON_LOOKUP_USE_BUILTIN ) image = gtk.Image() image.set_from_pixbuf(pixbuf) - self.button.set_image(image) - self.button.set_image_position(gtk.POS_LEFT) - + self.buttonApp.set_image(image) + self.buttonApp.set_image_position(gtk.POS_RIGHT) #button = gtk.Button("Update") - self.button.connect("clicked", self.button_clicked) + self.buttonApp.connect("clicked", self.button_clicked) + + self.buttonUpdate = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) + self.buttonUpdate.set_image(gtk.image_new_from_icon_name('general_refresh', gtk.ICON_SIZE_BUTTON)) + self.buttonUpdate.connect("clicked", self.buttonUpdate_clicked) + + self.buttonUp = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) + self.buttonUp.set_image(gtk.image_new_from_icon_name('keyboard_move_up', gtk.ICON_SIZE_BUTTON)) + self.buttonUp.set_sensitive(False) + self.buttonUp.connect("clicked", self.buttonUp_clicked) + + self.buttonDown = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) + self.buttonDown.set_image(gtk.image_new_from_icon_name('keyboard_move_down', gtk.ICON_SIZE_BUTTON)) + self.buttonDown.set_sensitive(False) + self.buttonDown.connect("clicked", self.buttonDown_clicked) + + self.hbox1.pack_start(self.buttonUpdate, expand=False) + self.hbox1.pack_start(self.buttonDown, expand=False) + self.hbox1.pack_start(self.buttonUp, expand=False) + self.hbox1.pack_start(self.buttonApp, expand=False) + #button.show_all() - vbox.pack_start(self.button, expand=False) + #for feed in ["Slashdot", "Engadget", "Cheez"]: # self.treestore.append([feed, "0"]) @@ -109,16 +163,18 @@ class FeedingItHomePlugin(hildondesktop.HomePluginItem): self.update_list() name_renderer = gtk.CellRendererText() name_renderer.set_property("font-desc", font_desc) - unread_renderer = gtk.CellRendererText() - unread_renderer.set_property("font-desc", font_desc) + self.unread_renderer = gtk.CellRendererText() + self.unread_renderer.set_property("font-desc", font_desc) + self.unread_renderer.set_property("xalign", 1.0) self.treeview.append_column(gtk.TreeViewColumn('Feed Name', name_renderer, text = 0)) - self.treeview.append_column(gtk.TreeViewColumn('Unread Items', unread_renderer, text = 1)) + self.treeview.append_column(gtk.TreeViewColumn('Unread Items', self.unread_renderer, text = 1)) #selection = self.treeview.get_selection() #selection.set_mode(gtk.SELECTION_NONE) #self.treeview.get_selection().set_mode(gtk.SELECTION_NONE) #hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview, gtk.HILDON_UI_MODE_NORMAL) vbox.pack_start(self.treeview) + vbox.pack_start(self.hbox1, expand=False) self.add(vbox) self.treeview.connect("hildon-row-tapped", self.row_activated) @@ -145,71 +201,134 @@ class FeedingItHomePlugin(hildondesktop.HomePluginItem): #file.close() def button_clicked(self, *widget): - self.button.set_sensitive(False) - self.label1.modify_fg(gtk.STATE_NORMAL, default_color) - self.label2.modify_fg(gtk.STATE_NORMAL, active_color) - self.update_label("Stopping") + #self.button.set_sensitive(False) + #self.label1.modify_fg(gtk.STATE_NORMAL, default_color) + #self.label2.modify_fg(gtk.STATE_NORMAL, active_color) + #self.update_label("Stopping") + if self.status == 0: + remote_object = bus.get_object("org.maemo.feedingit", # Connection name + "/org/maemo/feedingit" # Object's path + ) + iface = dbus.Interface(remote_object, 'org.maemo.feedingit') + else: + self.status = 0 + self.pageStatus = 0 + self.buttonUp.set_sensitive(False) + self.buttonDown.set_sensitive(False) + self.treeview.append_column(gtk.TreeViewColumn('Unread Items', self.unread_renderer, text = 1)) + self.update_list() + #iface.StopUpdate() + + def buttonUpdate_clicked(self, *widget): remote_object = bus.get_object("org.marcoz.feedingit", # Connection name - "/org/marcoz/feedingit/update" # Object's path - ) + "/org/marcoz/feedingit/update" # Object's path + ) iface = dbus.Interface(remote_object, 'org.marcoz.feedingit') - iface.StopUpdate() + if self.updateStatus == 0: + iface.UpdateAll() + else: + iface.StopUpdate() + + def buttonUp_clicked(self, *widget): + if self.pageStatus > 0: + self.pageStatus -= 1 + self.show_articles() + + def buttonDown_clicked(self, *widget): + self.pageStatus += 1 + self.show_articles() - def update_label(self, title, value=None): - self.button.set_title(title) + def update_label(self, value=None): if value != None: - self.button.set_value(value) + self.buttonApp.set_title(str(value)) else: - self.button.set_value("") + self.buttonApp.set_title("") - def row_activated(self, treeview, treepath): #, column): - (model, iter) = self.treeview.get_selection().get_selected() - key = model.get_value(iter, 2) + #def row_activated(self, treeview, treepath): #, column): + # (model, iter) = self.treeview.get_selection().get_selected() + # key = model.get_value(iter, 2) # Create an object that will proxy for a particular remote object. - remote_object = bus.get_object("org.maemo.feedingit", # Connection name - "/org/maemo/feedingit" # Object's path - ) - iface = dbus.Interface(remote_object, 'org.maemo.feedingit') - iface.OpenFeed(key) + # remote_object = bus.get_object("org.maemo.feedingit", # Connection name + # "/org/maemo/feedingit" # Object's path + # ) + # iface = dbus.Interface(remote_object, 'org.maemo.feedingit') + # iface.OpenFeed(key) + + def show_articles(self): + db = sqlite3.connect(CONFIGDIR+self.key+".d/"+self.key+".db") + count = db.execute("SELECT count(*) FROM feed WHERE read=0;").fetchone()[0] + if count>0: + maxPage = count/10 + if self.pageStatus > maxPage: + self.pageStatus = maxPage + rows = db.execute("SELECT id, title FROM feed WHERE read=0 ORDER BY date DESC LIMIT 10 OFFSET ?;", (self.pageStatus*10,) ) + treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) + for row in rows: + title = fix_title(row[1][0:32]) + id = row[0] + treestore.append((title, id)) + self.treeview.set_model(treestore) + + def row_activated(self, treeview, treepath): + if self.status == 0: + self.status = 1 + self.pageStatus = 0 + (model, iter) = self.treeview.get_selection().get_selected() + self.key = model.get_value(iter, 2) + treeviewcolumn = self.treeview.get_column(1) + self.treeview.remove_column(treeviewcolumn) + self.show_articles() + self.buttonApp.set_image(gtk.image_new_from_icon_name('general_back', gtk.ICON_SIZE_BUTTON)) + self.buttonUp.set_sensitive(True) + self.buttonDown.set_sensitive(True) + else: + (model, iter) = self.treeview.get_selection().get_selected() + id = model.get_value(iter, 1) + # Create an object that will proxy for a particular remote object. + remote_object = bus.get_object("org.maemo.feedingit", # Connection name + "/org/maemo/feedingit" # Object's path + ) + iface = dbus.Interface(remote_object, 'org.maemo.feedingit') + iface.OpenArticle(self.key, id) + def update_list(self, *widget): #listing = Listing(CONFIGDIR) - treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) - - if self.feed_list == {}: - self.load_config() - - if self.feed_list == None: - treestore.append(["Start Application", "", None]) - self.update_label("No feeds added yet") - self.treeview.set_model(treestore) + if self.status == 0: + treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) - else: - list = [] - oldtotal = self.total - self.total = 0 - #for key in listOfFeeds["feedingit-order"]: - for key in self.feed_list.keys(): - try: - file = open(CONFIGDIR+key+".d/unread", "r") - readItems = pickle.load( file ) - file.close() - countUnread = 0 - for id in readItems.keys(): - if readItems[id]==False: - countUnread = countUnread + 1 - list.append([self.feed_list[key][0:18], countUnread, key]) - self.total += countUnread - except: - pass - list = sorted(list, key=lambda item: item[1], reverse=True) - for item in list[0:8]: - treestore.append(item) - self.treeview.set_model(treestore) - if self.total > oldtotal: - self.update_label("%s Unread" %str(self.total), "%s more articles" %str(self.total-oldtotal)) + if self.feed_list == {}: + self.load_config() + + if self.feed_list == None: + treestore.append(["No feeds added yet", "", None]) + treestore.append(["Start Application", "", None]) + #self.update_label("No feeds added yet") + self.treeview.set_model(treestore) + else: - self.update_label("%s Unread" %str(self.total)) + list = [] + oldtotal = self.total + self.total = 0 + #for key in listOfFeeds["feedingit-order"]: + db = sqlite3.connect(CONFIGDIR+"feeds.db") + for key in self.feed_list.keys(): + try: + countUnread = db.execute("SELECT unread FROM feeds WHERE id=?;", (key,)).fetchone()[0] + list.append([self.feed_list[key][0:25], countUnread, key]) + self.total += countUnread + except: + pass + list = sorted(list, key=lambda item: item[1], reverse=True) + count = 0 + for item in list[0:10]: + count += 1 + treestore.append(item) + for i in range(count, 10): + treestore.append( ("", "", None) ) + self.treeview.set_model(treestore) + self.buttonApp.set_image(gtk.image_new_from_icon_name('feedingit', gtk.ICON_SIZE_BUTTON)) + #self.update_label(self.total) return True def create_selector(self, choices, setting): @@ -250,10 +369,9 @@ class FeedingItHomePlugin(hildondesktop.HomePluginItem): return picker def show_settings(self, widget): - if isfile(CONFIGDIR+"feeds.pickle"): - file = open(CONFIGDIR+"feeds.pickle") - listOfFeeds = pickle.load(file) - file.close() + if isfile(CONFIGDIR+"feeds.db"): + db = sqlite3.connect(CONFIGDIR+"feeds.db") + dialog = gtk.Dialog("Choose feeds to display", None, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) self.pannableArea = hildon.PannableArea() @@ -269,10 +387,12 @@ class FeedingItHomePlugin(hildondesktop.HomePluginItem): self.treestore_settings = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) self.treeview_settings.set_model(self.treestore_settings) - for key in listOfFeeds["feedingit-order"]: - title = listOfFeeds[key]["title"] - item = self.treestore_settings.append([title, key]) - if key in self.feed_list: + feeds = db.execute("SELECT title, id FROM feeds;") + + for feed in feeds: + # feed is (id, title) + item = self.treestore_settings.append(feed) + if feed[1] in self.feed_list: self.treeview_settings.get_selection().select_iter(item) self.pannableArea.add(self.treeview_settings) @@ -315,12 +435,12 @@ class FeedingItHomePlugin(hildondesktop.HomePluginItem): signal_name="UpdateFinished", path="/org/marcoz/feedingit/update") def update_started(self, *widget): - self.button.set_sensitive(True) - self.update_label("Updating...", "Click to stop update") + self.buttonUpdate.set_image(gtk.image_new_from_icon_name('general_stop', gtk.ICON_SIZE_BUTTON)) + self.updateStatus = 1 def update_finished(self, *widget): - self.button.set_sensitive(False) - self.update_label("Update done") + self.updateStatus = 0 + self.buttonUpdate.set_image(gtk.image_new_from_icon_name('general_refresh', gtk.ICON_SIZE_BUTTON)) def start_update(self): try: @@ -381,15 +501,12 @@ class FeedingItHomePlugin(hildondesktop.HomePluginItem): self.autoupdate = pickle.load( file ) file.close() self.setup_autoupdate() - elif isfile(CONFIGDIR+"feeds.pickle"): - file = open(CONFIGDIR+"feeds.pickle") - listOfFeeds = pickle.load(file) - file.close() + elif isfile(CONFIGDIR+"feeds.db"): + db = sqlite3.connect(CONFIGDIR+"feeds.db") + feeds = db.execute("SELECT id, title FROM feeds;") - #self.feed_list = listOfFeeds["feedingit-order"] - for key in listOfFeeds["feedingit-order"]: - self.feed_list[key] = listOfFeeds[key]["title"] - del listOfFeeds + for feed in feeds: + self.feed_list[feed[0]] = feed[1] self.autoupdate = 0 else: self.feed_list = None diff --git a/src/feedingitdbus.py b/src/feedingitdbus.py index 1974254..145f569 100644 --- a/src/feedingitdbus.py +++ b/src/feedingitdbus.py @@ -49,3 +49,9 @@ class ServerObject(dbus.service.Object): #self.app.buttonFeedClicked(None, self.app, None, key) self.app.openFeed(key) return "Done" + + @dbus.service.method('org.maemo.feedingit') + def OpenArticle(self, key, id): + #self.app.buttonFeedClicked(None, self.app, None, key) + self.app.openArticle(key, id) + return "Done" diff --git a/src/rss_sqlite.py b/src/rss_sqlite.py index 3e2bf51..3fd2c4d 100644 --- a/src/rss_sqlite.py +++ b/src/rss_sqlite.py @@ -135,7 +135,6 @@ class Feed: filename = self.addImage(configdir, self.key, baseurl, img['src']) img['src']=filename self.db.execute("INSERT INTO images (id, imagePath) VALUES (?, ?);", (id, filename) ) - self.db.commit() except: import traceback traceback.print_exc() @@ -146,7 +145,6 @@ class Feed: file.close() values = (id, tmpEntry["title"], tmpEntry["contentLink"], tmpEntry["date"], currentTime, tmpEntry["link"], 0) self.db.execute("INSERT INTO feed (id, title, contentLink, date, updated, link, read) VALUES (?, ?, ?, ?, ?, ?, ?);", values) - self.db.commit() else: try: filename = configdir+self.key+".d/"+id+".html" @@ -160,6 +158,7 @@ class Feed: file.close() except: pass + self.db.commit() rows = self.db.execute("SELECT id FROM feed WHERE (read=0 AND updated