1 #!/usr/bin/env python2.5
4 # Copyright (c) 2007-2008 INdT.
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Lesser General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 # ============================================================================
21 # Author : Yves Marcoz
23 # Description : Simple RSS Reader
24 # ============================================================================
40 from os.path import isfile, isdir
45 from portrait import FremantleRotation
48 from feedingitdbus import ServerObject
49 from config import Config
52 from opml import GetOpmlData, ExportOpmlData
56 socket.setdefaulttimeout(timeout)
58 color_style = gtk.rc_get_style_by_paths(gtk.settings_get_default() , 'GtkButton', 'osso-logical-colors', gtk.Button)
59 unread_color = color_style.lookup_color('ActiveTextColor')
60 read_color = color_style.lookup_color('DefaultTextColor')
63 CONFIGDIR="/home/user/.feedingit/"
65 class AddWidgetWizard(hildon.WizardDialog):
67 def __init__(self, parent, urlIn, titleIn=None):
69 self.notebook = gtk.Notebook()
71 self.nameEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
72 self.nameEntry.set_placeholder("Enter Feed Name")
73 vbox = gtk.VBox(False,10)
74 label = gtk.Label("Enter Feed Name:")
75 vbox.pack_start(label)
76 vbox.pack_start(self.nameEntry)
77 if not titleIn == None:
78 self.nameEntry.set_text(titleIn)
79 self.notebook.append_page(vbox, None)
81 self.urlEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
82 self.urlEntry.set_placeholder("Enter a URL")
83 self.urlEntry.set_text(urlIn)
84 self.urlEntry.select_region(0,-1)
86 vbox = gtk.VBox(False,10)
87 label = gtk.Label("Enter Feed URL:")
88 vbox.pack_start(label)
89 vbox.pack_start(self.urlEntry)
90 self.notebook.append_page(vbox, None)
92 labelEnd = gtk.Label("Success")
94 self.notebook.append_page(labelEnd, None)
96 hildon.WizardDialog.__init__(self, parent, "Add Feed", self.notebook)
98 # Set a handler for "switch-page" signal
99 #self.notebook.connect("switch_page", self.on_page_switch, self)
101 # Set a function to decide if user can go to next page
102 self.set_forward_page_func(self.some_page_func)
107 return (self.nameEntry.get_text(), self.urlEntry.get_text())
109 def on_page_switch(self, notebook, page, num, dialog):
112 def some_page_func(self, nb, current, userdata):
113 # Validate data for 1st page
115 return len(self.nameEntry.get_text()) != 0
117 # Check the url is not null, and starts with http
118 return ( (len(self.urlEntry.get_text()) != 0) and (self.urlEntry.get_text().lower().startswith("http")) )
124 #class GetImage(threading.Thread):
125 # def __init__(self, url, stream):
126 # threading.Thread.__init__(self)
128 # self.stream = stream
131 # f = urllib2.urlopen(self.url)
134 # self.stream.write(data)
135 # self.stream.close()
137 #class ImageDownloader():
138 # def __init__(self):
140 # self.downloading = False
142 # def queueImage(self, url, stream):
143 # self.images.append((url, stream))
144 # if not self.downloading:
145 # self.downloading = True
146 # gobject.timeout_add(50, self.checkQueue)
148 # def checkQueue(self):
149 # for i in range(4-threading.activeCount()):
150 # if len(self.images) > 0:
151 # (url, stream) = self.images.pop()
152 # GetImage(url, stream).start()
153 # if len(self.images)>0:
154 # gobject.timeout_add(200, self.checkQueue)
156 # self.downloading=False
162 class Download(threading.Thread):
163 def __init__(self, listing, key, config):
164 threading.Thread.__init__(self)
165 self.listing = listing
170 (use_proxy, proxy) = self.config.getProxy()
172 self.listing.updateFeed(self.key, self.config.getExpiry(), proxy=proxy, imageCache=self.config.getImageCache() )
174 self.listing.updateFeed(self.key, self.config.getExpiry(), imageCache=self.config.getImageCache() )
177 class DownloadBar(gtk.ProgressBar):
178 def __init__(self, parent, listing, listOfKeys, config, single=False):
179 gtk.ProgressBar.__init__(self)
180 self.listOfKeys = listOfKeys[:]
181 self.listing = listing
182 self.total = len(self.listOfKeys)
188 #self.progress = gtk.ProgressBar()
189 #self.waitingWindow = hildon.Note("cancel", parent, "Downloading",
190 # progressbar=self.progress)
191 self.set_text("Updating...")
193 self.set_fraction(self.fraction)
196 self.timeout_handler_id = gobject.timeout_add(50, self.update_progress_bar)
197 #self.waitingWindow.show_all()
198 #response = self.waitingWindow.run()
199 #self.listOfKeys = []
200 #while threading.activeCount() > 1:
201 # Wait for current downloads to finish
203 #self.waitingWindow.destroy()
205 def update_progress_bar(self):
206 #self.progress_bar.pulse()
207 if threading.activeCount() < 4:
208 x = threading.activeCount() - 1
209 k = len(self.listOfKeys)
210 fin = self.total - k - x
211 fraction = float(fin)/float(self.total) + float(x)/(self.total*2.)
212 #print x, k, fin, fraction
213 self.set_fraction(fraction)
215 if len(self.listOfKeys)>0:
216 self.current = self.current+1
217 key = self.listOfKeys.pop()
218 if (not self.listing.getCurrentlyDisplayedFeed() == key) or (self.single == True):
219 # Check if the feed is being displayed
220 download = Download(self.listing, key, self.config)
223 elif threading.activeCount() > 1:
226 #self.waitingWindow.destroy()
228 self.emit("download-done", "success")
233 class SortList(gtk.Dialog):
234 def __init__(self, parent, listing):
235 gtk.Dialog.__init__(self, "Organizer", parent)
236 self.listing = listing
238 self.vbox2 = gtk.VBox(False, 10)
240 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
241 button.set_label("Move Up")
242 button.connect("clicked", self.buttonUp)
243 self.vbox2.pack_start(button, expand=False, fill=False)
245 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
246 button.set_label("Move Down")
247 button.connect("clicked", self.buttonDown)
248 self.vbox2.pack_start(button, expand=False, fill=False)
250 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
251 button.set_label("Add Feed")
252 button.connect("clicked", self.buttonAdd)
253 self.vbox2.pack_start(button, expand=False, fill=False)
255 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
256 button.set_label("Edit Feed")
257 button.connect("clicked", self.buttonEdit)
258 self.vbox2.pack_start(button, expand=False, fill=False)
260 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
261 button.set_label("Delete")
262 button.connect("clicked", self.buttonDelete)
263 self.vbox2.pack_start(button, expand=False, fill=False)
265 #button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
266 #button.set_label("Done")
267 #button.connect("clicked", self.buttonDone)
268 #self.vbox.pack_start(button)
269 self.hbox2= gtk.HBox(False, 10)
270 self.pannableArea = hildon.PannableArea()
271 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
272 self.treeview = gtk.TreeView(self.treestore)
273 self.hbox2.pack_start(self.pannableArea, expand=True)
275 self.hbox2.pack_end(self.vbox2, expand=False)
276 self.set_default_size(-1, 600)
277 self.vbox.pack_start(self.hbox2)
280 #self.connect("destroy", self.buttonDone)
282 def displayFeeds(self):
283 self.treeview.destroy()
284 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
285 self.treeview = gtk.TreeView()
287 self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
288 hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview, gtk.HILDON_UI_MODE_EDIT)
290 self.treeview.append_column(gtk.TreeViewColumn('Feed Name', gtk.CellRendererText(), text = 0))
292 self.pannableArea.add(self.treeview)
296 def refreshList(self, selected=None, offset=0):
297 rect = self.treeview.get_visible_rect()
298 y = rect.y+rect.height
299 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
300 for key in self.listing.getListOfFeeds():
301 item = self.treestore.append([self.listing.getFeedTitle(key), key])
304 self.treeview.set_model(self.treestore)
305 if not selected == None:
306 self.treeview.get_selection().select_iter(selectedItem)
307 self.treeview.scroll_to_cell(self.treeview.get_model().get_path(selectedItem))
308 self.pannableArea.show_all()
310 def getSelectedItem(self):
311 (model, iter) = self.treeview.get_selection().get_selected()
314 return model.get_value(iter, 1)
316 def findIndex(self, key):
320 for row in self.treestore:
322 return (before, row.iter)
323 if key == list(row)[0]:
327 return (before, None)
329 def buttonUp(self, button):
330 key = self.getSelectedItem()
332 self.listing.moveUp(key)
333 self.refreshList(key, -10)
335 def buttonDown(self, button):
336 key = self.getSelectedItem()
338 self.listing.moveDown(key)
339 self.refreshList(key, 10)
341 def buttonDelete(self, button):
342 key = self.getSelectedItem()
344 self.listing.removeFeed(key)
347 def buttonEdit(self, button):
348 key = self.getSelectedItem()
350 wizard = AddWidgetWizard(self, self.listing.getFeedUrl(key), self.listing.getFeedTitle(key))
353 (title, url) = wizard.getData()
354 if (not title == '') and (not url == ''):
355 self.listing.editFeed(key, title, url)
359 def buttonDone(self, *args):
362 def buttonAdd(self, button, urlIn="http://"):
363 wizard = AddWidgetWizard(self, urlIn)
366 (title, url) = wizard.getData()
367 if (not title == '') and (not url == ''):
368 self.listing.addFeed(title, url)
373 class DisplayArticle(hildon.StackableWindow):
374 def __init__(self, feed, id, key, config, listing):
375 hildon.StackableWindow.__init__(self)
376 #self.imageDownloader = ImageDownloader()
381 self.set_title(feed.getTitle(id))
384 # Init the article display
385 #if self.config.getWebkitSupport():
386 self.view = webkit.WebView()
387 #self.view.set_editable(False)
390 # self.view = gtkhtml2.View()
391 # self.document = gtkhtml2.Document()
392 # self.view.set_document(self.document)
393 # self.document.connect("link_clicked", self._signal_link_clicked)
394 self.pannable_article = hildon.PannableArea()
395 self.pannable_article.add(self.view)
396 #self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
397 #self.gestureId = self.pannable_article.connect('horizontal-movement', self.gesture)
399 #if self.config.getWebkitSupport():
400 contentLink = self.feed.getContentLink(self.id)
401 self.feed.setEntryRead(self.id)
402 #if key=="ArchivedArticles":
403 self.view.open("file://" + contentLink)
404 self.view.connect("motion-notify-event", lambda w,ev: True)
407 #self.view.load_html_string(self.text, contentLink) # "text/html", "utf-8", self.link)
408 self.view.set_zoom_level(float(config.getArtFontSize())/10.)
410 # if not key == "ArchivedArticles":
411 # Do not download images if the feed is "Archived Articles"
412 # self.document.connect("request-url", self._signal_request_url)
414 # self.document.clear()
415 # self.document.open_stream("text/html")
416 # self.document.write_stream(self.text)
417 # self.document.close_stream()
419 menu = hildon.AppMenu()
420 # Create a button and add it to the menu
421 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
422 button.set_label("Allow Horizontal Scrolling")
423 button.connect("clicked", self.horiz_scrolling_button)
426 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
427 button.set_label("Open in Browser")
428 button.connect("clicked", self._signal_link_clicked, self.feed.getExternalLink(self.id))
431 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
432 button.set_label("Add to Archived Articles")
433 button.connect("clicked", self.archive_button)
436 self.set_app_menu(menu)
439 #self.event_box = gtk.EventBox()
440 #self.event_box.add(self.pannable_article)
441 self.add(self.pannable_article)
444 self.pannable_article.show_all()
446 self.destroyId = self.connect("destroy", self.destroyWindow)
448 self.view.connect("button_press_event", self.button_pressed)
449 self.gestureId = self.view.connect("button_release_event", self.button_released)
450 #self.timeout_handler_id = gobject.timeout_add(300, self.reloadArticle)
452 def button_pressed(self, window, event):
453 #print event.x, event.y
454 self.coords = (event.x, event.y)
456 def button_released(self, window, event):
457 x = self.coords[0] - event.x
458 y = self.coords[1] - event.y
462 self.emit("article-previous", self.id)
464 self.emit("article-next", self.id)
468 #def gesture(self, widget, direction, startx, starty):
469 # if (direction == 3):
470 # self.emit("article-next", self.index)
471 # if (direction == 2):
472 # self.emit("article-previous", self.index)
473 #print startx, starty
474 #self.timeout_handler_id = gobject.timeout_add(200, self.destroyWindow)
476 def destroyWindow(self, *args):
477 self.disconnect(self.destroyId)
478 self.emit("article-closed", self.id)
479 #self.imageDownloader.stopAll()
482 def horiz_scrolling_button(self, *widget):
483 self.pannable_article.disconnect(self.gestureId)
484 self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
486 def archive_button(self, *widget):
487 # Call the listing.addArchivedArticle
488 self.listing.addArchivedArticle(self.key, self.id)
490 #def reloadArticle(self, *widget):
491 # if threading.activeCount() > 1:
492 # Image thread are still running, come back in a bit
495 # for (stream, imageThread) in self.images:
497 # stream.write(imageThread.data)
502 def _signal_link_clicked(self, object, link):
503 bus = dbus.SessionBus()
504 proxy = bus.get_object("com.nokia.osso_browser", "/com/nokia/osso_browser/request")
505 iface = dbus.Interface(proxy, 'com.nokia.osso_browser')
506 iface.open_new_window(link)
508 #def _signal_request_url(self, object, url, stream):
510 # self.imageDownloader.queueImage(url, stream)
511 #imageThread = GetImage(url)
513 #self.images.append((stream, imageThread))
516 class DisplayFeed(hildon.StackableWindow):
517 def __init__(self, listing, feed, title, key, config):
518 hildon.StackableWindow.__init__(self)
519 self.listing = listing
521 self.feedTitle = title
522 self.set_title(title)
526 self.downloadDialog = False
528 self.listing.setCurrentlyDisplayedFeed(self.key)
532 menu = hildon.AppMenu()
533 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
534 button.set_label("Update Feed")
535 button.connect("clicked", self.button_update_clicked)
538 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
539 button.set_label("Mark All As Read")
540 button.connect("clicked", self.buttonReadAllClicked)
542 self.set_app_menu(menu)
547 self.connect("destroy", self.destroyWindow)
549 def destroyWindow(self, *args):
550 self.feed.saveUnread(CONFIGDIR)
551 self.listing.updateUnread(self.key, self.feed.getNumberOfUnreadItems())
552 self.emit("feed-closed", self.key)
554 #gobject.idle_add(self.feed.saveFeed, CONFIGDIR)
555 self.listing.closeCurrentlyDisplayedFeed()
557 def displayFeed(self):
558 self.vboxFeed = gtk.VBox(False, 10)
559 self.pannableFeed = hildon.PannableArea()
560 self.pannableFeed.add_with_viewport(self.vboxFeed)
561 self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
563 for id in self.feed.getIds():
564 button = gtk.Button(self.feed.getTitle(id))
565 button.set_alignment(0,0)
567 if self.feed.isEntryRead(id):
568 #label.modify_font(pango.FontDescription("sans 16"))
569 label.modify_font(pango.FontDescription(self.config.getReadFont()))
570 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
572 #print self.listing.getFont() + " bold"
573 label.modify_font(pango.FontDescription(self.config.getUnreadFont()))
574 label.modify_fg(gtk.STATE_NORMAL, unread_color)
575 label.set_line_wrap(True)
577 label.set_size_request(self.get_size()[0]-50, -1)
578 button.connect("clicked", self.button_clicked, id)
579 self.buttons[id] = button
581 self.vboxFeed.pack_start(button, expand=False)
583 self.add(self.pannableFeed)
587 self.pannableFeed.destroy()
588 #self.remove(self.pannableFeed)
590 def button_clicked(self, button, index, previous=False, next=False):
591 #newDisp = DisplayArticle(self.feedTitle, self.feed.getArticle(index), self.feed.getLink(index), index, self.key, self.listing, self.config)
592 newDisp = DisplayArticle(self.feed, index, self.key, self.config, self.listing)
593 stack = hildon.WindowStack.get_default()
596 stack.pop_and_push(1, newDisp, tmp)
598 gobject.timeout_add(200, self.destroyArticle, tmp)
603 if type(self.disp).__name__ == "DisplayArticle":
604 gobject.timeout_add(200, self.destroyArticle, self.disp)
611 self.ids.append(self.disp.connect("article-closed", self.onArticleClosed))
612 self.ids.append(self.disp.connect("article-next", self.nextArticle))
613 self.ids.append(self.disp.connect("article-previous", self.previousArticle))
615 def destroyArticle(self, handle):
616 handle.destroyWindow()
618 def nextArticle(self, object, index):
619 label = self.buttons[index].child
620 label.modify_font(pango.FontDescription(self.config.getReadFont()))
621 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
622 id = self.feed.getNextId(index)
623 self.button_clicked(object, id, next=True)
625 def previousArticle(self, object, index):
626 label = self.buttons[index].child
627 label.modify_font(pango.FontDescription(self.config.getReadFont()))
628 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
629 id = self.feed.getPreviousId(index)
630 self.button_clicked(object, id, previous=True)
632 def onArticleClosed(self, object, index):
633 label = self.buttons[index].child
634 label.modify_font(pango.FontDescription(self.config.getReadFont()))
635 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
636 self.buttons[index].show()
638 def button_update_clicked(self, button):
639 #bar = DownloadBar(self, self.listing, [self.key,], self.config )
640 if not type(self.downloadDialog).__name__=="DownloadBar":
641 self.pannableFeed.destroy()
642 self.vbox = gtk.VBox(False, 10)
643 self.downloadDialog = DownloadBar(self.window, self.listing, [self.key,], self.config, single=True )
644 self.downloadDialog.connect("download-done", self.onDownloadsDone)
645 self.vbox.pack_start(self.downloadDialog, expand=False, fill=False)
649 def onDownloadsDone(self, *widget):
651 self.feed = self.listing.getFeed(self.key)
654 def buttonReadAllClicked(self, button):
655 for index in self.feed.getIds():
656 self.feed.setEntryRead(index)
657 label = self.buttons[index].child
658 label.modify_font(pango.FontDescription(self.config.getReadFont()))
659 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
660 self.buttons[index].show()
666 self.window = hildon.StackableWindow()
667 self.window.set_title("FeedingIt")
668 hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
669 self.mainVbox = gtk.VBox(False,10)
670 self.pannableListing = gtk.Label("Loading...")
671 self.mainVbox.pack_start(self.pannableListing)
672 self.window.add(self.mainVbox)
673 self.window.show_all()
674 self.config = Config(self.window, CONFIGDIR+"config.ini")
675 gobject.idle_add(self.createWindow)
677 def createWindow(self):
678 self.listing = Listing(CONFIGDIR)
680 self.downloadDialog = False
681 self.orientation = FremantleRotation("FeedingIt", main_window=self.window, app=self)
682 self.orientation.set_mode(self.config.getOrientation())
684 menu = hildon.AppMenu()
685 # Create a button and add it to the menu
686 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
687 button.set_label("Update All Feeds")
688 button.connect("clicked", self.button_update_clicked, "All")
691 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
692 button.set_label("Mark All As Read")
693 button.connect("clicked", self.button_markAll)
696 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
697 button.set_label("Organize Feeds")
698 button.connect("clicked", self.button_organize_clicked)
701 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
702 button.set_label("Preferences")
703 button.connect("clicked", self.button_preferences_clicked)
706 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
707 button.set_label("Import Feeds")
708 button.connect("clicked", self.button_import_clicked)
711 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
712 button.set_label("Export Feeds")
713 button.connect("clicked", self.button_export_clicked)
716 self.window.set_app_menu(menu)
719 self.feedWindow = hildon.StackableWindow()
720 self.articleWindow = hildon.StackableWindow()
722 self.displayListing()
723 self.autoupdate = False
724 self.checkAutoUpdate()
725 hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
726 gobject.idle_add(self.enableDbus)
728 def enableDbus(self):
729 dbusHandler = ServerObject(self)
731 def button_markAll(self, button):
732 for key in self.listing.getListOfFeeds():
733 feed = self.listing.getFeed(key)
734 for id in feed.getIds():
735 feed.setEntryRead(id)
736 feed.saveUnread(CONFIGDIR)
737 self.listing.updateUnread(key, feed.getNumberOfUnreadItems())
740 def button_export_clicked(self, button):
741 opml = ExportOpmlData(self.window, self.listing)
743 def button_import_clicked(self, button):
744 opml = GetOpmlData(self.window)
745 feeds = opml.getData()
746 for (title, url) in feeds:
747 self.listing.addFeed(title, url)
748 self.displayListing()
750 def addFeed(self, urlIn="http://"):
751 wizard = AddWidgetWizard(self.window, urlIn)
754 (title, url) = wizard.getData()
755 if (not title == '') and (not url == ''):
756 self.listing.addFeed(title, url)
758 self.displayListing()
760 def button_organize_clicked(self, button):
761 org = SortList(self.window, self.listing)
764 self.listing.saveConfig()
765 self.displayListing()
767 def button_update_clicked(self, button, key):
768 if not type(self.downloadDialog).__name__=="DownloadBar":
769 self.downloadDialog = DownloadBar(self.window, self.listing, self.listing.getListOfFeeds(), self.config )
770 self.downloadDialog.connect("download-done", self.onDownloadsDone)
771 self.mainVbox.pack_end(self.downloadDialog, expand=False, fill=False)
772 self.mainVbox.show_all()
773 #self.displayListing()
775 def onDownloadsDone(self, *widget):
776 self.downloadDialog.destroy()
777 self.downloadDialog = False
778 #self.displayListing()
781 def button_preferences_clicked(self, button):
782 dialog = self.config.createDialog()
783 dialog.connect("destroy", self.prefsClosed)
785 def show_confirmation_note(self, parent, title):
786 note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
788 retcode = gtk.Dialog.run(note)
791 if retcode == gtk.RESPONSE_OK:
796 def displayListing(self):
798 self.mainVbox.remove(self.pannableListing)
801 self.vboxListing = gtk.VBox(False,10)
802 self.pannableListing = hildon.PannableArea()
803 self.pannableListing.add_with_viewport(self.vboxListing)
806 list = self.listing.getListOfFeeds()[:]
809 #button = gtk.Button(item)
810 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
811 hildon.BUTTON_ARRANGEMENT_VERTICAL)
812 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
813 + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
814 button.set_alignment(0,0,1,1)
815 button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
816 self.vboxListing.pack_start(button, expand=False)
817 self.buttons[key] = button
819 self.mainVbox.pack_start(self.pannableListing)
820 self.window.show_all()
822 def refreshList(self):
823 for key in self.listing.getListOfFeeds():
824 if self.buttons.has_key(key):
825 button = self.buttons[key]
826 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
827 + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
829 self.displayListing()
832 def buttonFeedClicked(widget, button, self, window, key):
833 self.disp = DisplayFeed(self.listing, self.listing.getFeed(key), self.listing.getFeedTitle(key), key, self.config)
834 self.disp.connect("feed-closed", self.onFeedClosed)
836 def onFeedClosed(self, object, key):
837 self.listing.saveConfig()
841 self.window.connect("destroy", gtk.main_quit)
843 self.listing.saveConfig()
845 def prefsClosed(self, *widget):
846 self.orientation.set_mode(self.config.getOrientation())
847 self.checkAutoUpdate()
849 def checkAutoUpdate(self, *widget):
850 interval = int(self.config.getUpdateInterval()*3600000)
851 if self.config.isAutoUpdateEnabled():
852 if self.autoupdate == False:
853 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
854 self.autoupdate = interval
855 elif not self.autoupdate == interval:
856 # If auto-update is enabled, but not at the right frequency
857 gobject.source_remove(self.autoupdateId)
858 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
859 self.autoupdate = interval
861 if not self.autoupdate == False:
862 gobject.source_remove(self.autoupdateId)
863 self.autoupdate = False
865 def automaticUpdate(self, *widget):
866 # Need to check for internet connection
867 # If no internet connection, try again in 10 minutes:
868 # gobject.timeout_add(int(5*3600000), self.automaticUpdate)
869 self.button_update_clicked(None, None)
874 for key in self.listing.getListOfFeeds():
875 if self.listing.getFeedNumberOfUnreadItems(key) > 0:
876 status += self.listing.getFeedTitle(key) + ": \t" + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items\n"
878 status = "No unread items"
881 if __name__ == "__main__":
882 gobject.signal_new("feed-closed", DisplayFeed, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
883 gobject.signal_new("article-closed", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
884 gobject.signal_new("article-next", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
885 gobject.signal_new("article-previous", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
886 gobject.signal_new("download-done", DownloadBar, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
887 gobject.threads_init()
888 if not isdir(CONFIGDIR):
892 print "Error: Can't create configuration directory"