Added THP's patches (5841) and Nelson's patch (#5782)
authorYves <ymarcoz@n900-sdk.(none)>
Sat, 29 May 2010 18:04:00 +0000 (11:04 -0700)
committerYves <ymarcoz@n900-sdk.(none)>
Sat, 29 May 2010 18:04:00 +0000 (11:04 -0700)
Makefile
debian/changelog
src/FeedingIt.py
src/aboutdialog.py [new file with mode: 0644]
src/config.py
src/rss.py
src/style.py [new file with mode: 0644]

index 0c4a421..cc3bc75 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,8 @@ install:
        install src/updatedbus.py ${DESTDIR}/opt/FeedingIt
        install src/BeautifulSoup.py ${DESTDIR}/opt/FeedingIt
        install src/feedingitdbus.py ${DESTDIR}/opt/FeedingIt
+       install src/aboutdialog.py ${DESTDIR}/opt/FeedingIt
+       install src/style.py ${DESTDIR}/opt/FeedingIt
        install -d ${DESTDIR}/usr/share/applications/hildon
        install src/FeedingIt.desktop ${DESTDIR}/usr/share/applications/hildon
        install -d ${DESTDIR}/usr/share/icons/hicolor/48x48/apps/
index d62be70..d66e2fb 100644 (file)
@@ -1,3 +1,23 @@
+feedingit (0.7.0-0) unstable; urgency=low
+
+  * Added THP's patches (TreeView for feed and article listing, About dialog)
+  * Added Nelson's patch (Hide read feeds and articles options)
+  * Fixed error message when viewing un-downloaded Archived Article
+
+ -- Yves <yves@marcoz.org>  Sat, 29 May 2010 10:53:19 -0800
+
+feedingit (0.6.2-2) unstable; urgency=low
+
+  * Fixed image caching in Archived Articles
+
+ -- Yves <yves@marcoz.org>  Mon, 24 May 2010 19:57:19 -0800
+
+feedingit (0.6.2-1) unstable; urgency=low
+
+  * Image cache expiry is time-based (removed imageHandler in memory object)
+
+ -- Yves <yves@marcoz.org>  Tue, 19 May 2010 22:02:19 -0800
+
 feedingit (0.6.2-0) unstable; urgency=low
 
   * Added icons to feeds
index c835909..e9cc1e0 100644 (file)
 #
 
 # ============================================================================
-# Name        : FeedingIt.py
-# Author      : Yves Marcoz
-# Version     : 0.6.0
-# Description : Simple RSS Reader
+__appname__ = 'FeedingIt'
+__author__  = 'Yves Marcoz'
+__version__ = '0.6.2'
+__description__ = 'A simple RSS Reader for Maemo 5'
 # ============================================================================
 
 import gtk
 from pango import FontDescription
+import pango
 import hildon
 #import gtkhtml2
 #try:
@@ -36,6 +37,7 @@ from webkit import WebView
 from os.path import isfile, isdir, exists
 from os import mkdir, remove, stat
 import gobject
+from aboutdialog import HeAboutDialog
 from portrait import FremantleRotation
 from threading import Thread, activeCount
 from feedingitdbus import ServerObject
@@ -53,6 +55,16 @@ timeout = 5
 setdefaulttimeout(timeout)
 del timeout
 
+LIST_ICON_SIZE = 32
+LIST_ICON_BORDER = 10
+
+USER_AGENT = 'Mozilla/5.0 (compatible; Maemo 5;) %s %s' % (__appname__, __version__)
+ABOUT_ICON = 'feedingit'
+ABOUT_COPYRIGHT = 'Copyright (c) 2010 %s' % __author__
+ABOUT_WEBSITE = 'http://feedingit.marcoz.org/'
+ABOUT_BUGTRACKER = 'https://garage.maemo.org/tracker/?group_id=1202'
+ABOUT_DONATE = None # TODO: Create a donation page + add its URL here
+
 color_style = gtk.rc_get_style_by_paths(gtk.settings_get_default() , 'GtkButton', 'osso-logical-colors', gtk.Button)
 unread_color = color_style.lookup_color('ActiveTextColor')
 read_color = color_style.lookup_color('DefaultTextColor')
@@ -64,6 +76,34 @@ LOCK = CONFIGDIR + "update.lock"
 from re import sub
 from htmlentitydefs import name2codepoint
 
+COLUMN_ICON, COLUMN_MARKUP, COLUMN_KEY = range(3)
+
+FEED_COLUMN_MARKUP, FEED_COLUMN_KEY = range(2)
+
+import style
+
+MARKUP_TEMPLATE = '<span font_desc="%s" foreground="%s">%%s</span>'
+
+# Build the markup template for the Maemo 5 text style
+head_font = style.get_font_desc('SystemFont')
+sub_font = style.get_font_desc('SmallSystemFont')
+
+head_color = style.get_color('ButtonTextColor')
+sub_color = style.get_color('DefaultTextColor')
+active_color = style.get_color('ActiveTextColor')
+
+head = MARKUP_TEMPLATE % (head_font.to_string(), head_color.to_string())
+normal_sub = MARKUP_TEMPLATE % (sub_font.to_string(), sub_color.to_string())
+
+active_head = MARKUP_TEMPLATE % (head_font.to_string(), active_color.to_string())
+active_sub = MARKUP_TEMPLATE % (sub_font.to_string(), active_color.to_string())
+
+FEED_TEMPLATE = '\n'.join((head, normal_sub))
+FEED_TEMPLATE_UNREAD = '\n'.join((head, active_sub))
+
+ENTRY_TEMPLATE = head
+ENTRY_TEMPLATE_UNREAD = active_head
+
 ##
 # Removes HTML or XML character references and entities from a text string.
 #
@@ -184,11 +224,11 @@ class DownloadBar(gtk.ProgressBar):
             (use_proxy, proxy) = self.config.getProxy()
             if use_proxy:
                 opener = build_opener(proxy)
-                opener.addheaders = [('User-agent', 'Mozilla/5.0 (compatible; Maemo 5;) FeedingIt 0.6.1')]
+                opener.addheaders = [('User-agent', USER_AGENT)]
                 install_opener(opener)
             else:
                 opener = build_opener()
-                opener.addheaders = [('User-agent', 'Mozilla/5.0 (compatible; Maemo 5;) FeedingIt 0.6.1')]
+                opener.addheaders = [('User-agent', USER_AGENT)]
                 install_opener(opener)
 
             if self.total>0:
@@ -403,7 +443,10 @@ class DisplayArticle(hildon.StackableWindow):
         contentLink = self.feed.getContentLink(self.id)
         self.feed.setEntryRead(self.id)
         #if key=="ArchivedArticles":
-        self.view.open("file://" + contentLink)
+        if contentLink.startswith("/home/user/"):
+            self.view.open("file://" + contentLink)
+        else:
+            self.view.load_html_string('This article has not been downloaded yet. Click <a href="%s">here</a> to view online.' % contentLink, "text/html", "utf-8", self.contentLink)
         self.view.connect("motion-notify-event", lambda w,ev: True)
         self.view.connect('load-started', self.load_started)
         self.view.connect('load-finished', self.load_finished)
@@ -575,8 +618,17 @@ class DisplayFeed(hildon.StackableWindow):
         
         self.displayFeed()
         
+        self.connect('configure-event', self.on_configure_event)
         self.connect("destroy", self.destroyWindow)
-        
+
+    def on_configure_event(self, window, event):
+        if getattr(self, 'markup_renderer', None) is None:
+            return
+
+        # Fix up the column width for wrapping the text when the window is
+        # resized (i.e. orientation changed)
+        self.markup_renderer.set_property('wrap-width', event.width-10)
+
     def destroyWindow(self, *args):
         #self.feed.saveUnread(CONFIGDIR)
         gobject.idle_add(self.feed.saveUnread, CONFIGDIR)
@@ -586,44 +638,54 @@ class DisplayFeed(hildon.StackableWindow):
         #gobject.idle_add(self.feed.saveFeed, CONFIGDIR)
         #self.listing.closeCurrentlyDisplayedFeed()
 
+    def fix_title(self, title):
+        return escape(unescape(title).replace("<em>","").replace("</em>","").replace("<nobr>","").replace("</nobr>","").replace("<wbr>",""))
+
     def displayFeed(self):
-        self.vboxFeed = gtk.VBox(False, 10)
         self.pannableFeed = hildon.PannableArea()
-        self.pannableFeed.add_with_viewport(self.vboxFeed)
-        self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
-        self.buttons = {}
+
+        self.pannableFeed.set_property('hscrollbar-policy', gtk.POLICY_NEVER)
+
+        self.feedItems = gtk.ListStore(str, str)
+        self.feedList = gtk.TreeView(self.feedItems)
+        self.feedList.connect('row-activated', self.on_feedList_row_activated)
+        self.pannableFeed.add(self.feedList)
+
+        self.markup_renderer = gtk.CellRendererText()
+        self.markup_renderer.set_property('wrap-mode', pango.WRAP_WORD_CHAR)
+        self.markup_renderer.set_property('wrap-width', 780)
+        markup_column = gtk.TreeViewColumn('', self.markup_renderer, \
+                markup=FEED_COLUMN_MARKUP)
+        self.feedList.append_column(markup_column)
+
+        #self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
+
         for id in self.feed.getIds():
-            title = self.feed.getTitle(id)
-            
-            esc_title = unescape(title).replace("<em>","").replace("</em>","").replace("<nobr>","").replace("</nobr>","").replace("<wbr>","")
-            #title.replace("<em>","").replace("</em>","").replace("&amp;","&").replace("&mdash;", "-").replace("&#8217;", "'")
-            button = gtk.Button(esc_title)
-            button.set_alignment(0,0)
-            label = button.child
-
-            if self.feed.isEntryRead(id):
-                #label.modify_font(FontDescription("sans 16"))
-                label.modify_font(FontDescription(self.config.getReadFont()))
-                label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
-            else:
-                #print self.listing.getFont() + " bold"
-                label.modify_font(FontDescription(self.config.getUnreadFont()))
-                label.modify_fg(gtk.STATE_NORMAL, unread_color)
-            label.set_line_wrap(True)
-            
-            label.set_size_request(self.get_size()[0]-50, -1)
-            button.connect("clicked", self.button_clicked, id)
-            self.buttons[id] = button
-            
-            self.vboxFeed.pack_start(button, expand=False)
+            if not ( self.feed.isEntryRead(id) and self.config.getHideReadArticles() ):
+                #title = self.feed.getTitle(id)
+                title = self.fix_title(self.feed.getTitle(id))
+    
+                if self.feed.isEntryRead(id):
+                    markup = ENTRY_TEMPLATE % title
+                else:
+                    markup = ENTRY_TEMPLATE_UNREAD % title
+    
+                self.feedItems.append((markup, id))
 
         self.add(self.pannableFeed)
         self.show_all()
-        
+
     def clear(self):
         self.pannableFeed.destroy()
         #self.remove(self.pannableFeed)
 
+    def on_feedList_row_activated(self, treeview, path, column):
+        model = treeview.get_model()
+        iter = model.get_iter(path)
+        key = model.get_value(iter, FEED_COLUMN_KEY)
+        # Emulate legacy "button_clicked" call via treeview
+        self.button_clicked(treeview, key)
+
     def button_clicked(self, button, index, previous=False, next=False):
         #newDisp = DisplayArticle(self.feedTitle, self.feed.getArticle(index), self.feed.getLink(index), index, self.key, self.listing, self.config)
         newDisp = DisplayArticle(self.feed, index, self.key, self.config, self.listing)
@@ -661,26 +723,30 @@ class DisplayFeed(hildon.StackableWindow):
     def destroyArticle(self, handle):
         handle.destroyWindow()
 
+    def mark_item_read(self, key):
+        it = self.feedItems.get_iter_first()
+        while it is not None:
+            k = self.feedItems.get_value(it, FEED_COLUMN_KEY)
+            if k == key:
+                title = self.fix_title(self.feed.getTitle(key))
+                markup = ENTRY_TEMPLATE % title
+                self.feedItems.set_value(it, FEED_COLUMN_MARKUP, markup)
+                break
+            it = self.feedItems.iter_next(it)
+
     def nextArticle(self, object, index):
-        label = self.buttons[index].child
-        label.modify_font(FontDescription(self.config.getReadFont()))
-        label.modify_fg(gtk.STATE_NORMAL, read_color) #  gtk.gdk.color_parse("white"))
+        self.mark_item_read(index)
         id = self.feed.getNextId(index)
         self.button_clicked(object, id, next=True)
 
     def previousArticle(self, object, index):
-        label = self.buttons[index].child
-        label.modify_font(FontDescription(self.config.getReadFont()))
-        label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
+        self.mark_item_read(index)
         id = self.feed.getPreviousId(index)
         self.button_clicked(object, id, previous=True)
 
     def onArticleClosed(self, object, index):
-        label = self.buttons[index].child
-        label.modify_font(FontDescription(self.config.getReadFont()))
-        label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
-        self.buttons[index].show()
-        
+        self.mark_item_read(index)
+
     def onArticleDeleted(self, object, index):
         self.clear()
         self.feed.removeArticle(index)
@@ -708,21 +774,36 @@ class DisplayFeed(hildon.StackableWindow):
     def buttonReadAllClicked(self, button):
         for index in self.feed.getIds():
             self.feed.setEntryRead(index)
-            label = self.buttons[index].child
-            label.modify_font(FontDescription(self.config.getReadFont()))
-            label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
-            self.buttons[index].show()
+            self.mark_item_read(index)
 
 
 class FeedingIt:
     def __init__(self):
         # Init the windows
         self.window = hildon.StackableWindow()
-        self.window.set_title("FeedingIt")
+        self.window.set_title(__appname__)
         hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
         self.mainVbox = gtk.VBox(False,10)
-        self.pannableListing = gtk.Label("Loading...")
+
+        self.pannableListing = hildon.PannableArea()
         self.mainVbox.pack_start(self.pannableListing)
+
+        self.feedItems = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
+        self.feedList = gtk.TreeView(self.feedItems)
+        self.feedList.connect('row-activated', self.on_feedList_row_activated)
+        self.pannableListing.add(self.feedList)
+
+        icon_renderer = gtk.CellRendererPixbuf()
+        icon_renderer.set_property('width', LIST_ICON_SIZE + 2*LIST_ICON_BORDER)
+        icon_column = gtk.TreeViewColumn('', icon_renderer, \
+                pixbuf=COLUMN_ICON)
+        self.feedList.append_column(icon_column)
+
+        markup_renderer = gtk.CellRendererText()
+        markup_column = gtk.TreeViewColumn('', markup_renderer, \
+                markup=COLUMN_MARKUP)
+        self.feedList.append_column(markup_column)
+
         self.window.add(self.mainVbox)
         self.window.show_all()
         self.config = Config(self.window, CONFIGDIR+"config.ini")
@@ -737,7 +818,7 @@ class FeedingIt:
         self.listing = Listing(CONFIGDIR)
         
         self.downloadDialog = False
-        self.orientation = FremantleRotation("FeedingIt", main_window=self.window, app=self)
+        self.orientation = FremantleRotation(__appname__, main_window=self.window, app=self)
         self.orientation.set_mode(self.config.getOrientation())
         
         menu = hildon.AppMenu()
@@ -771,6 +852,11 @@ class FeedingIt:
         button.set_label("Export Feeds")
         button.connect("clicked", self.button_export_clicked)
         menu.append(button)
+
+        button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+        button.set_label("About")
+        button.connect("clicked", self.button_about_clicked)
+        menu.append(button)
         
         self.window.set_app_menu(menu)
         menu.show_all()
@@ -795,7 +881,18 @@ class FeedingIt:
                 feed.setEntryRead(id)
             feed.saveUnread(CONFIGDIR)
             self.listing.updateUnread(key, feed.getNumberOfUnreadItems())
-        self.refreshList()
+        self.displayListing()
+
+    def button_about_clicked(self, button):
+        HeAboutDialog.present(self.window, \
+                __appname__, \
+                ABOUT_ICON, \
+                __version__, \
+                __description__, \
+                ABOUT_COPYRIGHT, \
+                ABOUT_WEBSITE, \
+                ABOUT_BUGTRACKER, \
+                ABOUT_DONATE)
 
     def button_export_clicked(self, button):
         opml = ExportOpmlData(self.window, self.listing)
@@ -836,8 +933,7 @@ class FeedingIt:
     def onDownloadsDone(self, *widget):
         self.downloadDialog.destroy()
         self.downloadDialog = False
-        #self.displayListing()
-        self.refreshList()
+        self.displayListing()
         self.updateDbusHandler.UpdateFinished()
         self.updateDbusHandler.ArticleCountUpdated()
 
@@ -857,74 +953,53 @@ class FeedingIt:
             return False
         
     def displayListing(self):
-        try:
-            self.mainVbox.remove(self.pannableListing)
-        except:
-            pass
-        self.vboxListing = gtk.VBox(False,10)
-        self.pannableListing = hildon.PannableArea()
-        self.pannableListing.add_with_viewport(self.vboxListing)
-
-        self.buttons = {}
-        list = self.listing.getListOfFeeds()[:]
-        #list.reverse()
         icon_theme = gtk.icon_theme_get_default()
-        for key in list:
-            #button = gtk.Button(item)
-            unreadItems = self.listing.getFeedNumberOfUnreadItems(key)
-            button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
-                              hildon.BUTTON_ARRANGEMENT_VERTICAL)
-            button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / " 
-                            + str(unreadItems) + " Unread Items")
-            icon = self.listing.getFavicon(key)
-            image = gtk.Image()
-            try:
-                pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon, 32, 32)
-            except:
-                pixbuf = icon_theme.load_icon("feedingit", 32, gtk.ICON_LOOKUP_USE_BUILTIN )
-
-            image.set_from_pixbuf(pixbuf)
-            button.set_image(image)
-            button.set_image_position(gtk.POS_LEFT)
-            button.set_alignment(0,0,1,1)
-            button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
-            self.vboxListing.pack_start(button, expand=False)
-            self.buttons[key] = button
-     
-        self.mainVbox.pack_start(self.pannableListing)
-        self.window.show_all()
-        gobject.idle_add(self.refreshList)
+        default_pixbuf = icon_theme.load_icon(ABOUT_ICON, LIST_ICON_SIZE, \
+                gtk.ICON_LOOKUP_USE_BUILTIN)
 
-    def refreshList(self):
+        self.feedItems.clear()
         for key in self.listing.getListOfFeeds():
-            if self.buttons.has_key(key):
-                button = self.buttons[key]
-                unreadItems = self.listing.getFeedNumberOfUnreadItems(key)
-                button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / " 
-                            + str(unreadItems) + " Unread Items")
-                label = button.child.child.get_children()[1].get_children()[1]
-                if unreadItems == 0:
-                    label.modify_fg(gtk.STATE_NORMAL, read_color)
+            unreadItems = self.listing.getFeedNumberOfUnreadItems(key)
+            if unreadItems > 0 or not self.config.getHideReadFeeds():
+                title = self.listing.getFeedTitle(key)
+                updateTime = self.listing.getFeedUpdateTime(key)
+                
+                subtitle = '%s / %d unread items' % (updateTime, unreadItems)
+    
+                if unreadItems:
+                    markup = FEED_TEMPLATE_UNREAD % (title, subtitle)
                 else:
-                    label.modify_fg(gtk.STATE_NORMAL, unread_color)
-            else:
-                self.displayListing()
-                break
+                    markup = FEED_TEMPLATE % (title, subtitle)
+    
+                try:
+                    icon_filename = self.listing.getFavicon(key)
+                    pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon_filename, \
+                            LIST_ICON_SIZE, LIST_ICON_SIZE)
+                except:
+                    pixbuf = default_pixbuf
+    
+                self.feedItems.append((pixbuf, markup, key))
+
+    def on_feedList_row_activated(self, treeview, path, column):
+        model = treeview.get_model()
+        iter = model.get_iter(path)
+        key = model.get_value(iter, COLUMN_KEY)
 
-    def buttonFeedClicked(widget, button, self, window, key):
         try:
             self.feed_lock
         except:
             # If feed_lock doesn't exist, we can open the feed, else we do nothing
             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 = DisplayFeed(self.listing, self.listing.getFeed(key), \
+                    self.listing.getFeedTitle(key), key, \
+                    self.config, self.updateDbusHandler)
             self.disp.connect("feed-closed", self.onFeedClosed)
 
     def onFeedClosed(self, object, key):
         #self.listing.saveConfig()
         #del self.feed_lock
         gobject.idle_add(self.onFeedClosedTimeout)
-        self.refreshList()
+        self.displayListing()
         #self.updateDbusHandler.ArticleCountUpdated()
         
     def onFeedClosedTimeout(self):
@@ -940,6 +1015,7 @@ class FeedingIt:
 
     def prefsClosed(self, *widget):
         self.orientation.set_mode(self.config.getOrientation())
+        self.displayListing()
         self.checkAutoUpdate()
 
     def checkAutoUpdate(self, *widget):
diff --git a/src/aboutdialog.py b/src/aboutdialog.py
new file mode 100644 (file)
index 0000000..c6d2ba1
--- /dev/null
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+#
+# gPodder - A media aggregator and podcast client
+# Copyright (c) 2005-2010 Thomas Perl and the gPodder Team
+#
+# gPodder is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# gPodder is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Python implementation of HeAboutDialog from hildon-extras
+# Copyright (c) 2010-04-11 Thomas Perl <thp@thpinfo.com>
+
+import hildon
+import gtk
+import dbus
+
+_ = lambda x: x
+
+class HeAboutDialog(gtk.Dialog):
+    RESPONSE_WEBSITE, \
+    RESPONSE_BUGTRACKER, \
+    RESPONSE_DONATE = range(3)
+
+    def __init__(self):
+        gtk.Dialog.__init__(self)
+
+        self.website_url = None
+        self.bugtracker_url = None
+        self.donate_url = None
+
+        self.set_title(_('About'))
+
+        self.image_icon = gtk.Image()
+        self.label_app_name = gtk.Label()
+        self.label_version = gtk.Label()
+        self.label_description = gtk.Label()
+        self.label_copyright = gtk.Label()
+        self.table_layout = gtk.Table(3, 3, False)
+
+        hildon.hildon_helper_set_logical_font(self.label_app_name, 'X-LargeSystemFont')
+        hildon.hildon_helper_set_logical_font(self.label_version, 'LargeSystemFont')
+        hildon.hildon_helper_set_logical_font(self.label_copyright, 'SmallSystemFont')
+        hildon.hildon_helper_set_logical_color(self.label_copyright, gtk.RC_FG, gtk.STATE_NORMAL, 'SecondaryTextColor')
+
+        self.label_app_name.set_alignment(0, 1)
+        self.label_version.set_alignment(0, 1)
+        self.label_description.set_alignment(0, 0)
+        self.label_copyright.set_alignment(0, 1)
+        self.label_version.set_padding(10, 0)
+        self.label_copyright.set_padding(0, 5)
+        self.image_icon.set_padding(5, 5)
+
+        #content_area = self.get_content_area() # Starting with PyGTK 2.14
+        content_area = self.vbox
+
+        self.table_layout.attach(self.image_icon, 0, 1, 0, 2, 0, gtk.EXPAND, 0, 0)
+        self.table_layout.attach(self.label_app_name, 1, 2, 0, 1, 0, gtk.EXPAND | gtk.FILL, 0, 0)
+        self.table_layout.attach(self.label_version, 2, 3, 0, 1, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0)
+        self.table_layout.attach(self.label_description, 1, 3, 1, 2, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0)
+        self.table_layout.attach(self.label_copyright, 0, 3, 2, 3, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0)
+        content_area.add(self.table_layout)
+        self.connect('response', self._on_response)
+        self.show_all()
+
+    def _on_response(self, dialog, response_id):
+        if response_id == HeAboutDialog.RESPONSE_WEBSITE:
+            self.open_webbrowser(self.website_url)
+        elif response_id == HeAboutDialog.RESPONSE_BUGTRACKER:
+            self.open_webbrowser(self.bugtracker_url)
+        elif response_id == HeAboutDialog.RESPONSE_DONATE:
+            self.open_webbrowser(self.donate_url)
+
+    def set_app_name(self, app_name):
+        self.label_app_name.set_text(app_name)
+        self.set_title(_('About %s') % app_name)
+
+    def set_icon_name(self, icon_name):
+        self.image_icon.set_from_icon_name(icon_name, gtk.ICON_SIZE_DIALOG)
+
+    def set_version(self, version):
+        self.label_version.set_text(version)
+
+    def set_description(self, description):
+        self.label_description.set_text(description)
+
+    def set_copyright(self, copyright):
+        self.label_copyright.set_text(copyright)
+
+    def set_website(self, url):
+        if self.website_url is None:
+            self.add_button(_('Visit website'), HeAboutDialog.RESPONSE_WEBSITE)
+        self.website_url = url
+
+    def set_bugtracker(self, url):
+        if self.bugtracker_url is None:
+            self.add_button(_('Report bug'), HeAboutDialog.RESPONSE_BUGTRACKER)
+        self.bugtracker_url = url
+
+    def set_donate_url(self, url):
+        if self.donate_url is None:
+            self.add_button(_('Donate'), HeAboutDialog.RESPONSE_DONATE)
+        self.donate_url = url
+
+    def open_webbrowser(self, url):
+        bus = dbus.SessionBus()
+        proxy = bus.get_object('com.nokia.osso_browser', '/com/nokia/osso_browser/request', 'com.nokia.osso_browser')
+        proxy.load_url(url, dbus_interface='com.nokia.osso_browser')
+
+    @classmethod
+    def present(cls, parent=None, app_name=None, icon_name=None, \
+            version=None, description=None, copyright=None, \
+            website_url=None, bugtracker_url=None, donate_url=None):
+        ad = cls()
+
+        if parent is not None:
+            ad.set_transient_for(parent)
+            ad.set_destroy_with_parent(True)
+
+        if app_name is not None:
+            ad.set_app_name(app_name)
+
+        ad.set_icon_name(icon_name)
+        ad.set_version(version)
+        ad.set_description(description)
+        ad.set_copyright(copyright)
+
+        if website_url is not None:
+            ad.set_website(website_url)
+
+        if bugtracker_url is not None:
+            ad.set_bugtracker(bugtracker_url)
+
+        if donate_url is not None:
+            ad.set_donate_url(donate_url)
+
+        ad.run()
+        ad.destroy()
+
index 409f93e..3234fbe 100644 (file)
@@ -52,6 +52,19 @@ class Config():
         
         vbox = gtk.VBox(False, 10)
         self.buttons = {}
+
+        button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+        button.set_label("View Known Issues and Tips")
+        button.connect("clicked", self.button_tips_clicked)
+        button.set_alignment(0,0,1,1)
+        vbox.pack_start(button, expand=False)  
+
+        button = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
+        button.set_label("Image Caching Enabled")
+        button.set_active(self.config["imageCache"])
+        button.connect("toggled", self.button_toggled, "imageCache")
+        vbox.pack_start(button, expand=False)      
+
         settings = ["fontSize", "artFontSize", "expiry", "orientation", "updateInterval",]
         for setting in settings:
             picker = hildon.PickerButton(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
@@ -65,15 +78,21 @@ class Config():
             vbox.pack_start(picker, expand=False)
         
         button = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        button.set_label("Auto-update Enabled.")
+        button.set_label("Auto-update Enabled")
         button.set_active(self.config["autoupdate"])
         button.connect("toggled", self.button_toggled, "autoupdate")
         vbox.pack_start(button, expand=False)
 
         button = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        button.set_label("Image Caching Enabled")
-        button.set_active(self.config["imageCache"])
-        button.connect("toggled", self.button_toggled, "imageCache")
+        button.set_label("Hide read feeds")
+        button.set_active(self.config["hidereadfeeds"])
+        button.connect("toggled", self.button_toggled, "hidereadfeeds")
+        vbox.pack_start(button, expand=False)
+
+        button = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
+        button.set_label("Hide read articles")
+        button.set_active(self.config["hidereadarticles"])
+        button.connect("toggled", self.button_toggled, "hidereadarticles")
         vbox.pack_start(button, expand=False)
         
         button = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
@@ -82,13 +101,6 @@ class Config():
         button.connect("toggled", self.button_toggled, "proxy")
         vbox.pack_start(button, expand=False)
         
-        button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
-        button.set_label("View Known Issues and Tips")
-        button.connect("clicked", self.button_tips_clicked)
-        button.set_alignment(0,0,1,1)
-        vbox.pack_start(button, expand=False)
-        
-        
         panArea.add_with_viewport(vbox)
         
         self.window.vbox.add(panArea)        
@@ -151,6 +163,12 @@ class Config():
             self.config["proxy"] = configParser.getboolean(section, "proxy")
         except:
             self.config["proxy"] = True
+        try:
+            self.config["hidereadfeeds"] = configParser.getboolean(section, "hidereadfeeds")
+            self.config["hidereadarticles"] = configParser.getboolean(section, "hidereadarticles")
+        except:
+            self.config["hidereadfeeds"] = False
+            self.config["hidereadarticles"] = False
         
     def saveConfig(self):
         configParser = RawConfigParser()
@@ -163,6 +181,8 @@ class Config():
         configParser.set(section, 'orientation', str(self.config["orientation"]))
         configParser.set(section, 'imageCache', str(self.config["imageCache"]))
         configParser.set(section, 'proxy', str(self.config["proxy"]))
+        configParser.set(section, 'hidereadfeeds', str(self.config["hidereadfeeds"]))
+        configParser.set(section, 'hidereadarticles', str(self.config["hidereadarticles"]))
 
         # Writing our configuration file
         file = open(self.configFilename, 'wb')
@@ -209,4 +229,8 @@ class Config():
             http = client_get_default().get_string('/system/http_proxy/host')
             proxy = ProxyHandler( {"http":"http://%s:%s/"% (http,port)} )
             return (True, proxy)
-        return (False, None)
\ No newline at end of file
+        return (False, None)
+    def getHideReadFeeds(self):
+        return self.config["hidereadfeeds"]
+    def getHideReadArticles(self):
+        return self.config["hidereadarticles"]
index 4d5d083..539b71f 100644 (file)
@@ -165,7 +165,7 @@ class Feed:
                except:
                    entry["link"] = ""
                tmpEntry = {"title":entry["title"], "content":self.extractContent(entry),
-                            "date":date, "dateTuple":dateTuple, "link":entry["link"] }
+                            "date":date, "dateTuple":dateTuple, "link":entry["link"], "images":[] }
                id = self.generateUniqueId(tmpEntry)
                
                #articleTime = time.mktime(self.entries[id]["dateTuple"])
@@ -178,6 +178,7 @@ class Feed:
                           try:
                             filename = self.addImage(configdir, self.uniqueId, baseurl, img['src'])
                             img['src']=filename
+                            tmpEntry["images"].append(filename)
                           except:
                               print "Error downloading image %s" % img
                    tmpEntry["contentLink"] = configdir+self.uniqueId+".d/"+id+".html"
@@ -194,6 +195,10 @@ class Feed:
                        file = open(filename,"a")
                        utime(filename, None)
                        file.close()
+                       for image in self.entries[id]["images"]:
+                            file = open(image,"a")
+                            utime(image, None)
+                            file.close()
                    except:
                        pass
                    tmpEntries[id] = self.entries[id]
@@ -438,7 +443,7 @@ class ArchivedArticles(Feed):
                     images = soup('img')
                     baseurl = entry["link"]
                     for img in images:
-                        filename = self.addImage(self.uniqueId, baseurl, img['src'])
+                        filename = self.addImage(configdir, self.uniqueId, baseurl, img['src'])
                         img['src']=filename
                     entry["contentLink"] = configdir+self.uniqueId+".d/"+id+".html"
                     file = open(entry["contentLink"], "w")
diff --git a/src/style.py b/src/style.py
new file mode 100644 (file)
index 0000000..25d1940
--- /dev/null
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+#
+# gPodder - A media aggregator and podcast client
+# Copyright (c) 2005-2010 Thomas Perl and the gPodder Team
+#
+# gPodder is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# gPodder is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import gtk
+
+# See the Fremantle Master Layout Guide for more information:
+# http://tinyurl.com/fremantle-master-layout-guide
+
+# For implementation details, consult hildon/hildon-helper.c
+# (the function is called hildon_change_style_recursive_from_list)
+
+logical_font_names = (
+        'SystemFont',
+        'EmpSystemFont',
+        'SmallSystemFont', # Used for secondary text in buttons/TreeViews
+        'EmpSmallSystemFont',
+        'LargeSystemFont', # Used for empty TreeView text
+        'X-LargeSystemFont',
+        'XX-LargeSystemFont',
+        'XXX-LargeSystemFont',
+        'HomeSystemFont',
+)
+
+logical_color_names = (
+        'ButtonTextColor',
+        'ButtonTextPressedColor',
+        'ButtonTextDisabledColor',
+        'ActiveTextColor', # Used for Button values, etc..
+        'SecondaryTextColor', # Used for additional/secondary information
+)
+
+def get_font_desc(logicalfontname):
+    settings = gtk.settings_get_default()
+    font_style = gtk.rc_get_style_by_paths(settings, logicalfontname, \
+            None, None)
+    font_desc = font_style.font_desc
+    return font_desc
+
+def get_color(logicalcolorname):
+    settings = gtk.settings_get_default()
+    color_style = gtk.rc_get_style_by_paths(settings, 'GtkButton', \
+            'osso-logical-colors', gtk.Button)
+    return color_style.lookup_color(logicalcolorname)
+
+
+# For debugging; usage: python -m gpodder.gtkui.frmntl.style
+if __name__ == '__main__':
+    for font_name in logical_font_names:
+        print font_name, '-> ', get_font_desc(font_name).to_string()
+    print '-----------'
+    for color_name in logical_color_names:
+        print color_name, '-> ', get_color(color_name).to_string()
+