#
# ============================================================================
-# Name : FeedingIt.py
-# Author : Yves Marcoz
-# Version : 0.6.0
-# Description : Simple RSS Reader
+__appname__ = 'FeedingIt'
+__author__ = 'Yves Marcoz'
+__version__ = '0.7.0'
+__description__ = 'A simple RSS Reader for Maemo 5'
# ============================================================================
import gtk
from pango import FontDescription
+import pango
import hildon
#import gtkhtml2
#try:
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
setdefaulttimeout(timeout)
del timeout
+import xml.sax
+
+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')
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>'
+MARKUP_TEMPLATE_ENTRY_UNREAD = '<span font_desc="%s %%s" foreground="%s">%%s</span>'
+MARKUP_TEMPLATE_ENTRY = '<span font_desc="%s italic %%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())
+
+entry_head = MARKUP_TEMPLATE_ENTRY % (head_font.get_family(), head_color.to_string())
+entry_normal_sub = MARKUP_TEMPLATE_ENTRY % (sub_font.get_family(), 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())
+
+entry_active_head = MARKUP_TEMPLATE_ENTRY_UNREAD % (head_font.get_family(), active_color.to_string())
+entry_active_sub = MARKUP_TEMPLATE_ENTRY_UNREAD % (sub_font.get_family(), active_color.to_string())
+
+FEED_TEMPLATE = '\n'.join((head, normal_sub))
+FEED_TEMPLATE_UNREAD = '\n'.join((head, active_sub))
+
+ENTRY_TEMPLATE = entry_head
+ENTRY_TEMPLATE_UNREAD = entry_active_head
+
##
# Removes HTML or XML character references and entities from a text string.
#
return sub("&#?\w+;", fixup, text)
-class AddWidgetWizard(hildon.WizardDialog):
-
- def __init__(self, parent, urlIn, titleIn=None):
- # Create a Notebook
- self.notebook = gtk.Notebook()
+class AddWidgetWizard(gtk.Dialog):
+ def __init__(self, parent, urlIn, titleIn=None, isEdit=False):
+ gtk.Dialog.__init__(self)
+ self.set_transient_for(parent)
+
+ if isEdit:
+ self.set_title('Edit RSS feed')
+ else:
+ self.set_title('Add new RSS feed')
+
+ if isEdit:
+ self.btn_add = self.add_button('Save', 2)
+ else:
+ self.btn_add = self.add_button('Add', 2)
+
+ self.set_default_response(2)
self.nameEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
- self.nameEntry.set_placeholder("Enter Feed Name")
- vbox = gtk.VBox(False,10)
- label = gtk.Label("Enter Feed Name:")
- vbox.pack_start(label)
- vbox.pack_start(self.nameEntry)
+ self.nameEntry.set_placeholder('Feed name')
if not titleIn == None:
self.nameEntry.set_text(titleIn)
- self.notebook.append_page(vbox, None)
-
+ self.nameEntry.select_region(-1, -1)
+
self.urlEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
- self.urlEntry.set_placeholder("Enter a URL")
+ self.urlEntry.set_placeholder('Feed URL')
self.urlEntry.set_text(urlIn)
- self.urlEntry.select_region(0,-1)
-
- vbox = gtk.VBox(False,10)
- label = gtk.Label("Enter Feed URL:")
- vbox.pack_start(label)
- vbox.pack_start(self.urlEntry)
- self.notebook.append_page(vbox, None)
-
- labelEnd = gtk.Label("Success")
-
- self.notebook.append_page(labelEnd, None)
-
- hildon.WizardDialog.__init__(self, parent, "Add Feed", self.notebook)
-
- # Set a handler for "switch-page" signal
- #self.notebook.connect("switch_page", self.on_page_switch, self)
-
- # Set a function to decide if user can go to next page
- self.set_forward_page_func(self.some_page_func)
-
+ self.urlEntry.select_region(-1, -1)
+ self.urlEntry.set_activates_default(True)
+
+ self.table = gtk.Table(2, 2, False)
+ self.table.set_col_spacings(5)
+ label = gtk.Label('Name:')
+ label.set_alignment(1., .5)
+ self.table.attach(label, 0, 1, 0, 1, gtk.FILL)
+ self.table.attach(self.nameEntry, 1, 2, 0, 1)
+ label = gtk.Label('URL:')
+ label.set_alignment(1., .5)
+ self.table.attach(label, 0, 1, 1, 2, gtk.FILL)
+ self.table.attach(self.urlEntry, 1, 2, 1, 2)
+ self.vbox.pack_start(self.table)
+
self.show_all()
-
+
def getData(self):
return (self.nameEntry.get_text(), self.urlEntry.get_text())
-
- def on_page_switch(self, notebook, page, num, dialog):
- return True
-
+
def some_page_func(self, nb, current, userdata):
# Validate data for 1st page
if current == 0:
(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')]
- install_opener(opener)
else:
opener = build_opener()
- opener.addheaders = [('User-agent', 'Mozilla/5.0 (compatible; Maemo 5;) FeedingIt 0.6.1')]
- install_opener(opener)
+
+ opener.addheaders = [('User-agent', USER_AGENT)]
+ install_opener(opener)
if self.total>0:
- self.set_text("Updating...")
+ # In preparation for i18n/l10n
+ def N_(a, b, n):
+ return (a if n == 1 else b)
+
+ self.set_text(N_('Updating %d feed', 'Updating %d feeds', self.total) % self.total)
+
self.fraction = 0
self.set_fraction(self.fraction)
self.show_all()
return True
-class SortList(gtk.Dialog):
- def __init__(self, parent, listing):
- gtk.Dialog.__init__(self, "Organizer", parent)
+class SortList(hildon.StackableWindow):
+ def __init__(self, parent, listing, feedingit, after_closing):
+ hildon.StackableWindow.__init__(self)
+ self.set_transient_for(parent)
+ self.set_title('Subscriptions')
self.listing = listing
-
- self.vbox2 = gtk.VBox(False, 10)
-
+ self.feedingit = feedingit
+ self.after_closing = after_closing
+ self.connect('destroy', lambda w: self.after_closing())
+ self.vbox2 = gtk.VBox(False, 2)
+
button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
- button.set_label("Move Up")
+ button.set_image(gtk.image_new_from_icon_name('keyboard_move_up', gtk.ICON_SIZE_BUTTON))
button.connect("clicked", self.buttonUp)
self.vbox2.pack_start(button, expand=False, fill=False)
-
+
button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
- button.set_label("Move Down")
+ button.set_image(gtk.image_new_from_icon_name('keyboard_move_down', gtk.ICON_SIZE_BUTTON))
button.connect("clicked", self.buttonDown)
self.vbox2.pack_start(button, expand=False, fill=False)
+ self.vbox2.pack_start(gtk.Label(), expand=True, fill=False)
+
button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
- button.set_label("Add Feed")
+ button.set_image(gtk.image_new_from_icon_name('general_add', gtk.ICON_SIZE_BUTTON))
button.connect("clicked", self.buttonAdd)
self.vbox2.pack_start(button, expand=False, fill=False)
button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
- button.set_label("Edit Feed")
+ button.set_image(gtk.image_new_from_icon_name('general_information', gtk.ICON_SIZE_BUTTON))
button.connect("clicked", self.buttonEdit)
self.vbox2.pack_start(button, expand=False, fill=False)
-
+
button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
- button.set_label("Delete")
+ button.set_image(gtk.image_new_from_icon_name('general_delete', gtk.ICON_SIZE_BUTTON))
button.connect("clicked", self.buttonDelete)
self.vbox2.pack_start(button, expand=False, fill=False)
-
+
#button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
#button.set_label("Done")
#button.connect("clicked", self.buttonDone)
self.displayFeeds()
self.hbox2.pack_end(self.vbox2, expand=False)
self.set_default_size(-1, 600)
- self.vbox.pack_start(self.hbox2)
+ self.add(self.hbox2)
+
+ menu = hildon.AppMenu()
+ button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ button.set_label("Import from OPML")
+ button.connect("clicked", self.feedingit.button_import_clicked)
+ menu.append(button)
+
+ button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ button.set_label("Export to OPML")
+ button.connect("clicked", self.feedingit.button_export_clicked)
+ menu.append(button)
+ self.set_app_menu(menu)
+ menu.show_all()
self.show_all()
#self.connect("destroy", self.buttonDone)
def buttonDelete(self, button):
key = self.getSelectedItem()
- if not key == None:
+
+ message = 'Really remove this feed and its entries?'
+ dlg = hildon.hildon_note_new_confirmation(self, message)
+ response = dlg.run()
+ dlg.destroy()
+ if response == gtk.RESPONSE_OK:
self.listing.removeFeed(key)
- self.refreshList()
+ self.refreshList()
def buttonEdit(self, button):
key = self.getSelectedItem()
- if not key == None:
- wizard = AddWidgetWizard(self, self.listing.getFeedUrl(key), self.listing.getFeedTitle(key))
+
+ if key == 'ArchivedArticles':
+ message = 'Cannot edit the archived articles feed.'
+ hildon.hildon_banner_show_information(self, '', message)
+ return
+
+ if key is not None:
+ wizard = AddWidgetWizard(self, self.listing.getFeedUrl(key), self.listing.getFeedTitle(key), True)
ret = wizard.run()
if ret == 2:
(title, url) = wizard.getData()
if (not title == '') and (not url == ''):
self.listing.editFeed(key, title, url)
+ self.refreshList()
wizard.destroy()
- self.refreshList()
def buttonDone(self, *args):
self.destroy()
contentLink = self.feed.getContentLink(self.id)
self.feed.setEntryRead(self.id)
#if key=="ArchivedArticles":
- self.view.open("file://" + contentLink)
+ self.loadedArticle = False
+ if contentLink.startswith("/home/user/"):
+ self.view.open("file://%s" % contentLink)
+ self.currentUrl = self.feed.getExternalLink(self.id)
+ else:
+ self.view.load_html_string('This article has not been downloaded yet. Click <a href="%s">here</a> to view online.' % contentLink, contentLink)
+ self.currentUrl = "%s" % 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)
- #else:
- #self.view.load_html_string(self.text, contentLink) # "text/html", "utf-8", self.link)
self.view.set_zoom_level(float(config.getArtFontSize())/10.)
- #else:
- # if not key == "ArchivedArticles":
- # Do not download images if the feed is "Archived Articles"
- # self.document.connect("request-url", self._signal_request_url)
-
- # self.document.clear()
- # self.document.open_stream("text/html")
- # self.document.write_stream(self.text)
- # self.document.close_stream()
menu = hildon.AppMenu()
# Create a button and add it to the menu
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Allow Horizontal Scrolling")
+ button.set_label("Allow horizontal scrolling")
button.connect("clicked", self.horiz_scrolling_button)
menu.append(button)
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Open in Browser")
- button.connect("clicked", self._signal_link_clicked, self.feed.getExternalLink(self.id))
+ button.set_label("Open in browser")
+ button.connect("clicked", self.open_in_browser)
menu.append(button)
if key == "ArchivedArticles":
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Remove from Archived Articles")
+ button.set_label("Remove from archived articles")
button.connect("clicked", self.remove_archive_button)
else:
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Add to Archived Articles")
+ button.set_label("Add to archived articles")
button.connect("clicked", self.archive_button)
menu.append(button)
self.set_app_menu(menu)
menu.show_all()
- #self.event_box = gtk.EventBox()
- #self.event_box.add(self.pannable_article)
self.add(self.pannable_article)
-
self.pannable_article.show_all()
self.destroyId = self.connect("destroy", self.destroyWindow)
+ #self.view.connect('navigation-policy-decision-requested', self.navigation_policy_decision)
+ ## Still using an old version of WebKit, so using navigation-requested signal
+ self.view.connect('navigation-requested', self.navigation_requested)
+
self.view.connect("button_press_event", self.button_pressed)
self.gestureId = self.view.connect("button_release_event", self.button_released)
- #self.timeout_handler_id = gobject.timeout_add(300, self.reloadArticle)
+
+ #def navigation_policy_decision(self, wv, fr, req, action, decision):
+ def navigation_requested(self, wv, fr, req):
+ if self.config.getOpenInExternalBrowser():
+ self.open_in_browser(None, req.get_uri())
+ return True
+ else:
+ return False
def load_started(self, *widget):
hildon.hildon_gtk_window_set_progress_indicator(self, 1)
def load_finished(self, *widget):
hildon.hildon_gtk_window_set_progress_indicator(self, 0)
+ frame = self.view.get_main_frame()
+ if self.loadedArticle:
+ self.currentUrl = frame.get_uri()
+ else:
+ self.loadedArticle = True
def button_pressed(self, window, event):
#print event.x, event.y
self.emit("article-previous", self.id)
elif (x<-15):
self.emit("article-next", self.id)
- #print x, y
- #print "Released"
-
- #def gesture(self, widget, direction, startx, starty):
- # if (direction == 3):
- # self.emit("article-next", self.index)
- # if (direction == 2):
- # self.emit("article-previous", self.index)
- #print startx, starty
- #self.timeout_handler_id = gobject.timeout_add(200, self.destroyWindow)
def destroyWindow(self, *args):
self.disconnect(self.destroyId)
def remove_archive_button(self, *widget):
self.set_for_removal = True
-
- #def reloadArticle(self, *widget):
- # if threading.activeCount() > 1:
- # Image thread are still running, come back in a bit
- # return True
- # else:
- # for (stream, imageThread) in self.images:
- # imageThread.join()
- # stream.write(imageThread.data)
- # stream.close()
- # return False
- # self.show_all()
-
- def _signal_link_clicked(self, object, link):
+
+ def open_in_browser(self, object, link=None):
import dbus
bus = dbus.SessionBus()
proxy = bus.get_object("com.nokia.osso_browser", "/com/nokia/osso_browser/request")
iface = dbus.Interface(proxy, 'com.nokia.osso_browser')
- iface.open_new_window(link)
-
- #def _signal_request_url(self, object, url, stream):
- #print url
- # self.imageDownloader.queueImage(url, stream)
- #imageThread = GetImage(url)
- #imageThread.start()
- #self.images.append((stream, imageThread))
-
+ if link == None:
+ iface.open_new_window(self.currentUrl)
+ else:
+ iface.open_new_window(link)
class DisplayFeed(hildon.StackableWindow):
def __init__(self, listing, feed, title, key, config, updateDbusHandler):
self.feedTitle = title
self.set_title(title)
self.key=key
+ self.current = list()
self.config = config
self.updateDbusHandler = updateDbusHandler
menu = hildon.AppMenu()
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Update Feed")
+ button.set_label("Update feed")
button.connect("clicked", self.button_update_clicked)
menu.append(button)
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Mark All As Read")
+ button.set_label("Mark all as read")
button.connect("clicked", self.buttonReadAllClicked)
menu.append(button)
if key=="ArchivedArticles":
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Purge Read Articles")
+ button.set_label("Delete read articles")
button.connect("clicked", self.buttonPurgeArticles)
menu.append(button)
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-20)
+ it = self.feedItems.get_iter_first()
+ while it is not None:
+ markup = self.feedItems.get_value(it, FEED_COLUMN_MARKUP)
+ self.feedItems.set_value(it, FEED_COLUMN_MARKUP, markup)
+ it = self.feedItems.iter_next(it)
+
def destroyWindow(self, *args):
#self.feed.saveUnread(CONFIGDIR)
gobject.idle_add(self.feed.saveUnread, CONFIGDIR)
#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 = hildon.GtkTreeView(gtk.HILDON_UI_MODE_NORMAL)
+ selection = self.feedList.get_selection()
+ selection.set_mode(gtk.SELECTION_NONE)
+ #selection.connect("changed", lambda w: True)
+
+ self.feedList.set_model(self.feedItems)
+ self.feedList.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL)
+
+
+ self.feedList.set_hover_selection(False)
+ #self.feedList.set_property('enable-grid-lines', True)
+ #self.feedList.set_property('hildon-mode', 1)
+ #self.pannableFeed.connect("motion-notify-event", lambda w,ev: True)
+
+ #self.feedList.connect('row-activated', self.on_feedList_row_activated)
+
+ vbox= gtk.VBox(False, 10)
+ vbox.pack_start(self.feedList)
+
+ self.pannableFeed.add_with_viewport(vbox)
+
+ self.markup_renderer = gtk.CellRendererText()
+ self.markup_renderer.set_property('wrap-mode', pango.WRAP_WORD_CHAR)
+ self.markup_renderer.set_property('background', "#333333")
+ (width, height) = self.get_size()
+ self.markup_renderer.set_property('wrap-width', width-20)
+ self.markup_renderer.set_property('ypad', 5)
+ self.markup_renderer.set_property('xpad', 5)
+ 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)
+ hideReadArticles = self.config.getHideReadArticles()
+ hasArticle = False
+ self.current = list()
for id in self.feed.getIds():
- title = self.feed.getTitle(id)
-
- esc_title = unescape(title).replace("<em>","").replace("</em>","")
- #title.replace("<em>","").replace("</em>","").replace("&","&").replace("—", "-").replace("’", "'")
- 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)
+ isRead = False
+ try:
+ isRead = self.feed.isEntryRead(id)
+ except:
+ pass
+ if not ( isRead and hideReadArticles ):
+ title = self.fix_title(self.feed.getTitle(id))
+ self.current.append(id)
+ if isRead:
+ markup = ENTRY_TEMPLATE % (self.config.getFontSize(), title)
+ else:
+ markup = ENTRY_TEMPLATE_UNREAD % (self.config.getFontSize(), title)
+
+ self.feedItems.append((markup, id))
+ hasArticle = True
+ if hasArticle:
+ self.feedList.connect('hildon-row-tapped', self.on_feedList_row_activated)
+ else:
+ markup = ENTRY_TEMPLATE % (self.config.getFontSize(), "No Articles To Display")
+ self.feedItems.append((markup, ""))
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):
+ selection = self.feedList.get_selection()
+ selection.set_mode(gtk.SELECTION_SINGLE)
+ self.feedList.get_selection().select_path(path)
+ model = treeview.get_model()
+ iter = model.get_iter(path)
+ key = model.get_value(iter, FEED_COLUMN_KEY)
+ # Emulate legacy "button_clicked" call via treeview
+ gobject.idle_add(self.button_clicked, treeview, key)
+ #return True
+
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)
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 % (self.config.getFontSize(), 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)
+ while id not in self.current and id != index:
+ id = self.feed.getNextId(id)
+ if id != 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)
+ while id not in self.current and id != index:
+ id = self.feed.getPreviousId(id)
+ if id != 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()
-
+ selection = self.feedList.get_selection()
+ selection.set_mode(gtk.SELECTION_NONE)
+ self.mark_item_read(index)
+
def onArticleDeleted(self, object, index):
self.clear()
self.feed.removeArticle(index)
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.mainVbox.pack_start(self.pannableListing)
+
+ self.introLabel = gtk.Label("Loading...")
+
+
+ self.mainVbox.pack_start(self.introLabel)
+
self.window.add(self.mainVbox)
self.window.show_all()
self.config = Config(self.window, CONFIGDIR+"config.ini")
def createWindow(self):
self.app_lock = get_lock("app_lock")
if self.app_lock == None:
- self.pannableListing.set_label("Update in progress, please wait.")
+ try:
+ self.stopButton.set_sensitive(True)
+ except:
+ self.stopButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ self.stopButton.set_text("Stop update","")
+ self.stopButton.connect("clicked", self.stop_running_update)
+ self.mainVbox.pack_end(self.stopButton, expand=False, fill=False)
+ self.window.show_all()
+ self.introLabel.set_label("Update in progress, please wait.")
gobject.timeout_add_seconds(3, self.createWindow)
return False
+ try:
+ self.stopButton.destroy()
+ except:
+ pass
self.listing = Listing(CONFIGDIR)
self.downloadDialog = False
- self.orientation = FremantleRotation("FeedingIt", main_window=self.window, app=self)
- self.orientation.set_mode(self.config.getOrientation())
+ try:
+ self.orientation = FremantleRotation(__appname__, main_window=self.window, app=self)
+ self.orientation.set_mode(self.config.getOrientation())
+ except:
+ print "Could not start rotation manager"
menu = hildon.AppMenu()
# Create a button and add it to the menu
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Update All Feeds")
+ button.set_label("Update feeds")
button.connect("clicked", self.button_update_clicked, "All")
menu.append(button)
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Mark All As Read")
+ button.set_label("Mark all as read")
button.connect("clicked", self.button_markAll)
menu.append(button)
-
+
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Organize Feeds")
+ button.set_label("Add new feed")
+ button.connect("clicked", lambda b: self.addFeed())
+ menu.append(button)
+
+ button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ button.set_label("Manage subscriptions")
button.connect("clicked", self.button_organize_clicked)
menu.append(button)
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Preferences")
+ button.set_label("Settings")
button.connect("clicked", self.button_preferences_clicked)
menu.append(button)
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Import Feeds")
- button.connect("clicked", self.button_import_clicked)
- menu.append(button)
-
- button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Export Feeds")
- button.connect("clicked", self.button_export_clicked)
+ button.set_label("About")
+ button.connect("clicked", self.button_about_clicked)
menu.append(button)
self.window.set_app_menu(menu)
menu.show_all()
- self.feedWindow = hildon.StackableWindow()
- self.articleWindow = hildon.StackableWindow()
+ #self.feedWindow = hildon.StackableWindow()
+ #self.articleWindow = hildon.StackableWindow()
+ self.introLabel.destroy()
+ self.pannableListing = hildon.PannableArea()
+ 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.mainVbox.pack_start(self.pannableListing)
+ self.mainVbox.show_all()
self.displayListing()
self.autoupdate = False
hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
gobject.idle_add(self.enableDbus)
+ def stop_running_update(self, button):
+ self.stopButton.set_sensitive(False)
+ import dbus
+ bus=dbus.SessionBus()
+ remote_object = bus.get_object("org.marcoz.feedingit", # Connection name
+ "/org/marcoz/feedingit/update" # Object's path
+ )
+ iface = dbus.Interface(remote_object, 'org.marcoz.feedingit')
+ iface.StopUpdate()
+
def enableDbus(self):
self.dbusHandler = ServerObject(self)
self.updateDbusHandler = UpdateServerObject(self)
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)
self.displayListing()
def button_organize_clicked(self, button):
- org = SortList(self.window, self.listing)
- org.run()
- org.destroy()
- self.listing.saveConfig()
- self.displayListing()
-
+ def after_closing():
+ self.listing.saveConfig()
+ self.displayListing()
+ SortList(self.window, self.listing, self, after_closing)
+
def button_update_clicked(self, button, key):
if not type(self.downloadDialog).__name__=="DownloadBar":
self.updateDbusHandler.UpdateStarted()
def onDownloadsDone(self, *widget):
self.downloadDialog.destroy()
self.downloadDialog = False
- #self.displayListing()
- self.refreshList()
+ self.displayListing()
self.updateDbusHandler.UpdateFinished()
self.updateDbusHandler.ArticleCountUpdated()
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)
+ icon_theme = gtk.icon_theme_get_default()
+ default_pixbuf = icon_theme.load_icon(ABOUT_ICON, LIST_ICON_SIZE, \
+ gtk.ICON_LOOKUP_USE_BUILTIN)
- self.buttons = {}
- list = self.listing.getListOfFeeds()[:]
- #list.reverse()
- 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")
- 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)
-
- def refreshList(self):
+ self.feedItems.clear()
+ feedInfo = {}
+ count = 0
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()[0].get_children()[1]
- if unreadItems == 0:
- label.modify_fg(gtk.STATE_NORMAL, read_color)
- else:
- label.modify_fg(gtk.STATE_NORMAL, unread_color)
+ unreadItems = self.listing.getFeedNumberOfUnreadItems(key)
+ if unreadItems > 0 or not self.config.getHideReadFeeds():
+ count=count+1
+ title = self.listing.getFeedTitle(key)
+ updateTime = self.listing.getFeedUpdateTime(key)
+ updateStamp = self.listing.getFeedUpdateStamp(key)
+ subtitle = '%s / %d unread items' % (updateTime, unreadItems)
+ feedInfo[key] = [count, unreadItems, updateStamp, title, subtitle, updateTime];
+
+ order = self.config.getFeedSortOrder();
+ if order == "Most unread":
+ keyorder = sorted(feedInfo, key = lambda k: feedInfo[k][1], reverse=True)
+ elif order == "Least unread":
+ keyorder = sorted(feedInfo, key = lambda k: feedInfo[k][1])
+ elif order == "Most recent":
+ keyorder = sorted(feedInfo, key = lambda k: feedInfo[k][2], reverse=True)
+ elif order == "Least recent":
+ keyorder = sorted(feedInfo, key = lambda k: feedInfo[k][2])
+ else: # order == "Manual" or invalid value...
+ keyorder = sorted(feedInfo, key = lambda k: feedInfo[k][0])
+
+ for key in keyorder:
+ unreadItems = feedInfo[key][1]
+ title = xml.sax.saxutils.escape(feedInfo[key][3])
+ subtitle = feedInfo[key][4]
+ updateTime = feedInfo[key][5]
+ if unreadItems:
+ markup = FEED_TEMPLATE_UNREAD % (title, subtitle)
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 buttonFeedClicked(widget, button, self, window, 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)
+ self.openFeed(key)
+
+ def openFeed(self, 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.connect("feed-closed", self.onFeedClosed)
+ 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.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):
del self.app_lock
def prefsClosed(self, *widget):
- self.orientation.set_mode(self.config.getOrientation())
+ try:
+ self.orientation.set_mode(self.config.getOrientation())
+ except:
+ pass
+ self.displayListing()
self.checkAutoUpdate()
def checkAutoUpdate(self, *widget):
# Need to check for internet connection
# If no internet connection, try again in 10 minutes:
# gobject.timeout_add(int(5*3600000), self.automaticUpdate)
- file = open("/home/user/.feedingit/feedingit_widget.log", "a")
- from time import localtime, strftime
- file.write("App: %s\n" % strftime("%a, %d %b %Y %H:%M:%S +0000", localtime()))
- file.close()
+ #file = open("/home/user/.feedingit/feedingit_widget.log", "a")
+ #from time import localtime, strftime
+ #file.write("App: %s\n" % strftime("%a, %d %b %Y %H:%M:%S +0000", localtime()))
+ #file.close()
self.button_update_clicked(None, None)
return True