install src/feedparser.py ${DESTDIR}/opt/FeedingIt
install src/portrait.py ${DESTDIR}/opt/FeedingIt
install src/rss.py ${DESTDIR}/opt/FeedingIt
+ install src/opml.py ${DESTDIR}/opt/FeedingIt
install src/feedingitdbus.py ${DESTDIR}/opt/FeedingIt
install -d ${DESTDIR}/usr/share/applications/hildon
install src/FeedingIt.desktop ${DESTDIR}/usr/share/applications/hildon
+feedingit (0.2.3-2) unstable; urgency=low
+
+ * Fixed missing python-osso dependency
+
+ -- Yves <yves@marcoz.org> Sat, 17 Jan 2010 13:31:19 -0800
+
+feedingit (0.2.3-1) unstable; urgency=low
+
+ * Added Font size option for feeds
+ * Added option to enable horizontal scrolling of articles (disabling gestures)
+
+ -- Yves <yves@marcoz.org> Sat, 17 Jan 2010 13:31:19 -0800
+
+feedingit (0.2.2-1) unstable; urgency=low
+
+ * Added support for Import/Export of OPML files
+
+ -- Yves <yves@marcoz.org> Sat, 16 Jan 2010 14:38:19 -0800
+
feedingit (0.2.1-2) unstable; urgency=low
* Fixed issue with feeds without dates (#5019)
Package: feedingit
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, python-gtkhtml2, python, python-hildon, libgtkhtml2-0, python-dbus
+Depends: ${shlibs:Depends}, ${misc:Depends}, python-gtkhtml2, python, python-hildon, libgtkhtml2-0, python-dbus, python-osso
Description: Simple RSS Reader
Simple RSS Reader, with portrait mode support, and swipe gestures in articles
XB-Maemo-Icon-26:
from feedingitdbus import ServerObject
from rss import *
+from opml import GetOpmlData, ExportOpmlData
class AddWidgetWizard(hildon.WizardDialog):
fraction = float(fin)/float(self.total) + float(x)/(self.total*2.)
#print x, k, fin, fraction
self.progress.set_fraction(fraction)
-
+
if len(self.listOfKeys)>0:
self.current = self.current+1
key = self.listOfKeys.pop()
#self.show_all()
def refreshList(self, selected=None, offset=0):
- #x = self.treeview.get_visible_rect().x
rect = self.treeview.get_visible_rect()
y = rect.y+rect.height
- #self.pannableArea.jump_to(-1, 0)
self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
for key in self.listing.getListOfFeeds():
item = self.treestore.append([self.listing.getFeedTitle(key), key])
if not selected == None:
self.treeview.get_selection().select_iter(selectedItem)
self.treeview.scroll_to_cell(self.treeview.get_model().get_path(selectedItem))
- #self.pannableArea.jump_to(-1, y+offset)
self.pannableArea.show_all()
def getSelectedItem(self):
if not key == None:
self.listing.moveUp(key)
self.refreshList(key, -10)
- #placement = self.pannableArea.get_placement()
- #placement = self.treeview.get_visible_rect().y
- #self.displayFeeds(key)
-
- #self.treeview.scroll_to_point(-1, y)
- #self.pannableArea.set_placement(placement)
def buttonDown(self, button):
key = self.getSelectedItem()
if not key == None:
self.listing.moveDown(key)
- #(before, after) = self.findIndex(key)
- #self.treestore.move_after(iter, after)
- #self.treeview.set_model(self.treestore)
- #self.treeview.show_all()
self.refreshList(key, 10)
-
- #placement = self.pannableArea.get_placement()
- #self.displayFeeds(key)
- #self.pannableArea.set_placement(placement)
def buttonDelete(self, button):
key = self.getSelectedItem()
self.pannable_article = hildon.PannableArea()
self.pannable_article.add(self.view)
#self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
- self.pannable_article.connect('horizontal-movement', self.gesture)
+ self.gestureId = self.pannable_article.connect('horizontal-movement', self.gesture)
self.document = gtkhtml2.Document()
self.view.set_document(self.document)
self.document.connect("link_clicked", self._signal_link_clicked)
menu = hildon.AppMenu()
# Create a button and add it to the menu
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Display Images")
- button.connect("clicked", self.reloadArticle)
+ button.set_label("Allow Horizontal Scrolling")
+ button.connect("clicked", self.horiz_scrolling_button)
menu.append(button)
self.set_app_menu(menu)
self.add(self.pannable_article)
self.show_all()
-
+
self.destroyId = self.connect("destroy", self.destroyWindow)
self.timeout_handler_id = gobject.timeout_add(300, self.reloadArticle)
-
+
def gesture(self, widget, direction, startx, starty):
if (direction == 3):
self.emit("article-next", self.index)
self.emit("article-closed", self.index)
self.destroy()
+ def horiz_scrolling_button(self, *widget):
+ self.pannable_article.disconnect(self.gestureId)
+ self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
+
def reloadArticle(self, *widget):
if threading.activeCount() > 1:
# Image thread are still running, come back in a bit
button.set_alignment(0,0)
label = button.child
if self.feed.isEntryRead(index):
- label.modify_font(pango.FontDescription("sans 16"))
+ #label.modify_font(pango.FontDescription("sans 16"))
+ label.modify_font(pango.FontDescription(self.listing.getReadFont()))
else:
- label.modify_font(pango.FontDescription("sans bold 16"))
+ #print self.listing.getFont() + " bold"
+ label.modify_font(pango.FontDescription(self.listing.getUnreadFont()))
+ #label.modify_font(pango.FontDescription("sans bold 23"))
+ #"sans bold 16"
label.set_line_wrap(True)
label.set_size_request(self.get_size()[0]-50, -1)
def nextArticle(self, object, index):
label = self.buttons[index].child
- label.modify_font(pango.FontDescription("sans 16"))
+ label.modify_font(pango.FontDescription(self.listing.getReadFont()))
index = (index+1) % self.feed.getNumberOfEntries()
self.button_clicked(object, index)
def previousArticle(self, object, index):
label = self.buttons[index].child
- label.modify_font(pango.FontDescription("sans 16"))
+ label.modify_font(pango.FontDescription(self.listing.getReadFont()))
index = (index-1) % self.feed.getNumberOfEntries()
self.button_clicked(object, index)
def onArticleClosed(self, object, index):
label = self.buttons[index].child
- label.modify_font(pango.FontDescription("sans 16"))
+ label.modify_font(pango.FontDescription(self.listing.getReadFont()))
self.buttons[index].show()
def button_update_clicked(self, button):
for index in range(self.feed.getNumberOfEntries()):
self.feed.setEntryRead(index)
label = self.buttons[index].child
- label.modify_font(pango.FontDescription("sans 16"))
+ label.modify_font(pango.FontDescription(self.listing.getReadFont()))
self.buttons[index].show()
menu.append(button)
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Delete Feed")
- button.connect("clicked", self.button_delete_clicked)
+ button.set_label("Organize Feeds")
+ button.connect("clicked", self.button_organize_clicked)
+ menu.append(button)
+
+ button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ button.set_label("Listing Font Size")
+ button.connect("clicked", self.button_font_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("Organize Feeds")
- button.connect("clicked", self.button_organize_clicked)
+ button.set_label("Export Feeds")
+ button.connect("clicked", self.button_export_clicked)
menu.append(button)
self.window.set_app_menu(menu)
self.articleWindow = hildon.StackableWindow()
self.displayListing()
+
+ def button_export_clicked(self, button):
+ opml = ExportOpmlData(self.window, self.listing)
+ def button_import_clicked(self, button):
+ opml = GetOpmlData(self.window)
+ feeds = opml.getData()
+ for (title, url) in feeds:
+ self.listing.addFeed(title, url)
+ self.displayListing()
+
def button_organize_clicked(self, button):
org = SortList(self.window, self.listing)
org.run()
disp = DownloadDialog(self.window, self.listing, self.listing.getListOfFeeds() )
self.displayListing()
- def button_delete_clicked(self, button):
+ def button_font_clicked(self, button):
self.pickerDialog = hildon.PickerDialog(self.window)
#HildonPickerDialog
- self.pickerDialog.set_selector(self.create_selector())
+ selector = self.create_selector()
+ self.pickerDialog.set_selector(selector)
self.pickerDialog.show_all()
def create_selector(self):
# Selection multiple
#selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
self.mapping = {}
- selector.connect("changed", self.selection_changed)
-
- for key in self.listing.getListOfFeeds():
- title=self.listing.getFeedTitle(key)
- selector.append_text(title)
- self.mapping[title]=key
+ current_size = self.listing.getFontSize()
+ index = 0
+ for size in range(12,24):
+ iter = selector.append_text(str(size))
+ if str(size) == current_size:
+ selector.set_active(0, index)
+ index += 1
+ selector.connect("changed", self.selection_changed)
return selector
def selection_changed(self, widget, data):
current_selection = widget.get_current_text()
- #print 'Current selection: %s' % current_selection
- #print "To Delete: %s" % self.mapping[current_selection]
+ if current_selection:
+ self.listing.setFont(current_selection)
self.pickerDialog.destroy()
- if self.show_confirmation_note(self.window, current_selection):
- self.listing.removeFeed(self.mapping[current_selection])
- self.listing.saveConfig()
- del self.mapping
- self.displayListing()
def show_confirmation_note(self, parent, title):
note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
# ============================================================================
from xml.dom.minidom import parse, parseString
+import urllib2
+import gtk
+import hildon
+import gobject
+import time
+from os.path import isfile, dirname
+import gobject
+
+class ExportOpmlData():
+ def __init__(self, parent, listing):
+ fs = hildon.FileSystemModel()
+ dialog = hildon.FileChooserDialog(parent, gtk.FILE_CHOOSER_ACTION_SAVE, fs)
+ #(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ #gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+ #)
+ #dialog = gobject.new(hildon.FileChooserDialog, \
+ # action=gtk.FILE_CHOOSER_ACTION_SAVE)
+ #dialog.set_default_response(gtk.RESPONSE_OK)
+ #dialog.set_property('autonaming',False)
+ #dialog.set_property('show-files',True)
+ dialog.set_current_folder('/home/user/MyDocs/')
+ dialog.set_current_name('feedingit-export')
+ dialog.set_extension('opml')
+ response = dialog.run()
+ dialog.hide()
+ if response == gtk.RESPONSE_OK:
+ filename = dialog.get_filename()
+ print filename
+ try:
+
+ cont = True
+ if isfile(filename):
+ note = "File already exists. Aborted"
+ confirm = hildon.Note ("confirmation", parent, "File already exists. Are you sure you want to overwrite it?", gtk.STOCK_DIALOG_WARNING )
+ confirm.set_button_texts ("Yes", "Cancel")
+ response = confirm.run()
+ confirm.destroy()
+ if response == gtk.RESPONSE_OK:
+ cont = True
+ else:
+ note = "Operation cancelled."
+ cont = False
+ if cont:
+ file = open(filename, "w")
+ file.write(self.getOpmlText(listing))
+ file.close()
+ note = "Feeds exported to %s" %filename
+ except:
+ note = "Failed to export feeds"
+
+ dialog.destroy()
+ dialog = hildon.Note ("information", parent, note , gtk.STOCK_DIALOG_INFO )
+ dialog.run()
+ dialog.destroy()
+ elif response == gtk.RESPONSE_CANCEL:
+ dialog.destroy()
+
+ def getOpmlText(self, listing):
+ time_now = time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime())
+ opml_text = """<?xml version="1.0" encoding="UTF-8"?>
+<opml version="1.0">
+<head>
+ <title>Feeding It Export</title>
+</head>
+<body>
+"""
+ for key in listing.getListOfFeeds():
+ title = listing.getFeedTitle(key)
+ url = listing.getFeedUrl(key)
+ opml_text += """\n\t\t<outline type="rss" text="%s" title="%s" xmlUrl="%s"/>""" % (title, title, url)
+ opml_text += """\n</body>\n</opml>\n"""
+ return opml_text
+
+
+class GetOpmlData():
+ def __init__(self, parent):
+ self.parent = parent
+ dialog = hildon.Note ("confirmation", parent, "What type of OPML?", gtk.STOCK_DIALOG_WARNING )
+ dialog.set_button_texts ("File", "URL")
+ response = dialog.run()
+ dialog.destroy()
+
+ if response == gtk.RESPONSE_OK:
+ # Choose a file
+ self.data = self.askForFile()
+ else:
+ # Download a URL
+ self.data = self.downloadFile()
+
+ def getData(self):
+ if not self.data == None:
+ dialog = OpmlDialog(self.parent, self.data)
+ response = dialog.run()
+ if response == gtk.RESPONSE_ACCEPT:
+ items = dialog.getItems()
+ else:
+ items = []
+ dialog.destroy()
+ return items
+ return []
+
+ def downloadFile(self):
+ dlg = gtk.Dialog("OPML Import", self.parent, gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_OK, gtk.RESPONSE_OK,
+ gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
+ lbl = gtk.Label("Enter the URL of the OPML file:")
+ lbl.show()
+ dlg.vbox.pack_start(lbl)
+ entry = gtk.Entry()
+ entry.set_text("http://")
+ entry.select_region(0,-1)
+ entry.show()
+ dlg.vbox.pack_start(entry, False)
+
+ resp = dlg.run()
+ url = entry.get_text()
+ dlg.destroy()
+ if resp == gtk.RESPONSE_CANCEL:
+ return None
+ try:
+ f = urllib2.urlopen(url)
+ data = f.read()
+ f.close()
+ except:
+ #Show error note
+ return None
+ return data
+
+ def askForFile(self):
+ #dialog = hildon.FileChooserDialog(self.parent,
+ # gtk.FILE_CHOOSER_ACTION_OPEN)
+ #dialog = gobject.new(hildon.FileChooserDialog, \
+ # action=gtk.FILE_CHOOSER_ACTION_OPEN)
+ #dialog.set_default_response(gtk.RESPONSE_OK)
+ fs = hildon.FileSystemModel()
+ dialog = hildon.FileChooserDialog(self.parent, gtk.FILE_CHOOSER_ACTION_OPEN, fs)
+
+ filter = gtk.FileFilter()
+ filter.set_name("All files")
+ filter.add_pattern("*")
+ dialog.add_filter(filter)
+
+ filter = gtk.FileFilter()
+ filter.set_name("OPML")
+ filter.add_pattern("*.xml")
+ filter.add_pattern("*.opml")
+ dialog.add_filter(filter)
+
+ response = dialog.run()
+ if response == gtk.RESPONSE_OK:
+ file = open(dialog.get_filename())
+ data = file.read()
+ file.close()
+ dialog.destroy()
+ return data
+ elif response == gtk.RESPONSE_CANCEL:
+ dialog.destroy()
+ return None
+
class OpmlDialog(gtk.Dialog):
def parse(self, opmlData):
- self.feeds = []
- dom1 = parseString(self.opmlData)
+ self.feeds = []
+ dom1 = parseString(opmlData)
- outlines = dom1.getElementsByTagName('outline')
- for outline in outlines:
- title = outline.getAttribute('text')
- url = outline.getAttribute('xmlUrl')
- if url == "":
- url = outline.getAttribute('htmlUrl')
- self.feeds.append(title, url)
+ outlines = dom1.getElementsByTagName('outline')
+ for outline in outlines:
+ title = outline.getAttribute('text')
+ url = outline.getAttribute('xmlUrl')
+ if url == "":
+ url = outline.getAttribute('htmlUrl')
+ if not url == "":
+ self.feeds.append( (title, url) )
def getFeedLinks(self):
- return self.feeds
+ return self.feeds
- def __init__(self, parent, opmlData)
- self.parse(opmlData)
-
- gtk.Dialog.__init__(self, "Import OPML Feeds", parent)
-
- self.vbox2 = gtk.VBox(False, 10)
+ def __init__(self, parent, opmlData):
+ self.parse(opmlData)
+ gtk.Dialog.__init__(self, "Select OPML Feeds", parent, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
- self.hbox2= gtk.HBox(False, 10)
self.pannableArea = hildon.PannableArea()
self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
self.treeview = gtk.TreeView(self.treestore)
- self.hbox2.pack_start(self.pannableArea, expand=True)
+
self.displayFeeds()
- self.hbox2.pack_end(self.vbox2, expand=False)
+
self.set_default_size(-1, 600)
- self.vbox.pack_start(self.hbox2)
+ self.vbox.pack_start(self.pannableArea)
+
+ button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ button.set_label("Select All")
+ button.connect("clicked", self.button_select_all_clicked)
+ self.action_area.pack_end(button)
+
+ button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ button.set_label("Unselect All")
+ button.connect("clicked", self.button_select_none_clicked)
+ self.action_area.pack_end(button)
self.show_all()
+ def button_select_all_clicked(self, button):
+ self.treeview.get_selection().select_all()
+
+ def button_select_none_clicked(self, button):
+ self.treeview.get_selection().unselect_all()
+
def displayFeeds(self):
self.treeview.destroy()
self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
self.treeview = gtk.TreeView()
- self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
+ self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview, gtk.HILDON_UI_MODE_EDIT)
self.refreshList()
self.treeview.append_column(gtk.TreeViewColumn('Feed Name', gtk.CellRendererText(), text = 0))
self.pannableArea.add(self.treeview)
-
+ self.pannableArea.show_all()
+ self.treeview.get_selection().select_all()
def refreshList(self, selected=None, offset=0):
- #x = self.treeview.get_visible_rect().x
rect = self.treeview.get_visible_rect()
y = rect.y+rect.height
- #self.pannableArea.jump_to(-1, 0)
self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
- for (title, url) in self.feeds():
- item = self.treestore.append([title, url])
- self.treeview.get_selection().select_iter(item)
self.treeview.set_model(self.treestore)
+ for (title, url) in self.feeds:
+ item = self.treestore.append([title, url])
+ self.treeview.get_selection().select_iter(item)
+ #self.treeview.get_selection().select_all()
self.pannableArea.show_all()
- def getSelectedItem(self):
- (model, iter) = self.treeview.get_selection().get_selected()
- if not iter:
- return None
- return model.get_value(iter, 1)
-
- def findIndex(self, key):
- after = None
- before = None
- found = False
- for row in self.treestore:
- if found:
- return (before, row.iter)
- if key == list(row)[0]:
- found = True
- else:
- before = row.iter
- return (before, None)
+ def getItems(self):
+ list = []
+ treeselection = self.treeview.get_selection()
+ (model, pathlist) = treeselection.get_selected_rows()
+ for path in pathlist:
+ list.append( (model.get_value(model.get_iter(path),0), model.get_value(model.get_iter(path),1)) )
+ return list
- def buttonDone(self, *args):
- self.destroy()
+def showOpmlData(widget, parent, button):
+ dialog = GetOpmlData(parent)
+ print dialog.getData()
+ #dialog.destroy()
+if __name__ == "__main__":
+ window = hildon.Window()
+ window.set_title("Test App")
+
+ button = gtk.Button("Click to confirm.")
+ window.add(button)
+ button.connect("clicked", showOpmlData, window, button)
+ window.connect("destroy", gtk.main_quit)
+ window.show_all()
+ gtk.main()
+ window.destroy()
# Contains all the info about a single feed (articles, ...), and expose the data
def __init__(self, name, url):
self.entries = []
+ self.fontSize = "+0"
self.readItems = {}
self.countUnread = 0
self.name = name
self.url = url
self.updateTime = "Never"
+ def getFontSize(self):
+ try:
+ return self.fontSize
+ except:
+ self.fontSize = "+0"
+ return self.fontSize
+
def saveFeed(self):
file = open(CONFIGDIR+getId(self.name), "w")
pickle.dump(self, file )
date = ""
#text = '''<div style="color: black; background-color: white;">'''
text = '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>'
- text = text + '<div><a href=\"' + link + '\">' + title + "</a>"
- text = text + "<BR /><small><i>Date: " + date + "</i></small></div>"
- text = text + "<BR /><BR />"
- text = text + content
+ text += '<div><a href=\"' + link + '\">' + title + "</a>"
+ text += "<BR /><small><i>Date: " + date + "</i></small></div>"
+ text += "<BR /><BR />"
+ text += content
return text
file.close()
else:
self.listOfFeeds = {getId("Slashdot"):{"title":"Slashdot", "url":"http://rss.slashdot.org/Slashdot/slashdot"}, }
+ if not self.listOfFeeds.has_key("font"):
+ self.setFont("16")
if self.listOfFeeds.has_key("feedingit-order"):
self.sortedKeys = self.listOfFeeds["feedingit-order"]
else:
self.sortedKeys = self.listOfFeeds.keys()
+ if "font" in self.sortedKeys:
+ self.sortedKeys.remove("font")
self.sortedKeys.sort(key=lambda obj: self.getFeedTitle(obj))
for key in self.sortedKeys:
- self.loadFeed(key)
+ try:
+ self.loadFeed(key)
+ except:
+ self.sortedKeys.remove(key)
#self.saveConfig()
+ def getFontSize(self):
+ return self.listOfFeeds["font"]
+
+ def getReadFont(self):
+ return "sans %s" % self.listOfFeeds["font"]
+
+ def getUnreadFont(self):
+ return "sans bold %s" % self.listOfFeeds["font"]
+
+ def setFont(self, fontname):
+ self.listOfFeeds["font"] = fontname
+
def loadFeed(self, key):
if isfile(CONFIGDIR+key):
file = open(CONFIGDIR+key)
self.feeds[key] = pickle.load(file)
file.close()
else:
- self.feeds[key] = Feed(self.listOfFeeds[key]["title"], self.listOfFeeds[key]["url"])
+ title = self.listOfFeeds[key]["title"]
+ url = self.listOfFeeds[key]["url"]
+ self.feeds[key] = Feed(title, url)
def updateFeeds(self):
for key in self.getListOfFeeds():
return self.sortedKeys
def addFeed(self, title, url):
- self.listOfFeeds[getId(title)] = {"title":title, "url":url}
- self.sortedKeys.append(getId(title))
- self.saveConfig()
- self.feeds[getId(title)] = Feed(title, url)
+ if not self.listOfFeeds.has_key(getId(title)):
+ self.listOfFeeds[getId(title)] = {"title":title, "url":url}
+ self.sortedKeys.append(getId(title))
+ self.saveConfig()
+ self.feeds[getId(title)] = Feed(title, url)
def removeFeed(self, key):
del self.listOfFeeds[key]