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
50 from cgi import escape
53 from opml import GetOpmlData, ExportOpmlData
57 socket.setdefaulttimeout(timeout)
59 color_style = gtk.rc_get_style_by_paths(gtk.settings_get_default() , 'GtkButton', 'osso-logical-colors', gtk.Button)
60 unread_color = color_style.lookup_color('ActiveTextColor')
61 read_color = color_style.lookup_color('DefaultTextColor')
64 CONFIGDIR="/home/user/.feedingit/"
66 class AddWidgetWizard(hildon.WizardDialog):
68 def __init__(self, parent, urlIn, titleIn=None):
70 self.notebook = gtk.Notebook()
72 self.nameEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
73 self.nameEntry.set_placeholder("Enter Feed Name")
74 vbox = gtk.VBox(False,10)
75 label = gtk.Label("Enter Feed Name:")
76 vbox.pack_start(label)
77 vbox.pack_start(self.nameEntry)
78 if not titleIn == None:
79 self.nameEntry.set_text(titleIn)
80 self.notebook.append_page(vbox, None)
82 self.urlEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
83 self.urlEntry.set_placeholder("Enter a URL")
84 self.urlEntry.set_text(urlIn)
85 self.urlEntry.select_region(0,-1)
87 vbox = gtk.VBox(False,10)
88 label = gtk.Label("Enter Feed URL:")
89 vbox.pack_start(label)
90 vbox.pack_start(self.urlEntry)
91 self.notebook.append_page(vbox, None)
93 labelEnd = gtk.Label("Success")
95 self.notebook.append_page(labelEnd, None)
97 hildon.WizardDialog.__init__(self, parent, "Add Feed", self.notebook)
99 # Set a handler for "switch-page" signal
100 #self.notebook.connect("switch_page", self.on_page_switch, self)
102 # Set a function to decide if user can go to next page
103 self.set_forward_page_func(self.some_page_func)
108 return (self.nameEntry.get_text(), self.urlEntry.get_text())
110 def on_page_switch(self, notebook, page, num, dialog):
113 def some_page_func(self, nb, current, userdata):
114 # Validate data for 1st page
116 return len(self.nameEntry.get_text()) != 0
118 # Check the url is not null, and starts with http
119 return ( (len(self.urlEntry.get_text()) != 0) and (self.urlEntry.get_text().lower().startswith("http")) )
125 #class GetImage(threading.Thread):
126 # def __init__(self, url, stream):
127 # threading.Thread.__init__(self)
129 # self.stream = stream
132 # f = urllib2.urlopen(self.url)
135 # self.stream.write(data)
136 # self.stream.close()
138 #class ImageDownloader():
139 # def __init__(self):
141 # self.downloading = False
143 # def queueImage(self, url, stream):
144 # self.images.append((url, stream))
145 # if not self.downloading:
146 # self.downloading = True
147 # gobject.timeout_add(50, self.checkQueue)
149 # def checkQueue(self):
150 # for i in range(4-threading.activeCount()):
151 # if len(self.images) > 0:
152 # (url, stream) = self.images.pop()
153 # GetImage(url, stream).start()
154 # if len(self.images)>0:
155 # gobject.timeout_add(200, self.checkQueue)
157 # self.downloading=False
163 class Download(threading.Thread):
164 def __init__(self, listing, key, config):
165 threading.Thread.__init__(self)
166 self.listing = listing
171 (use_proxy, proxy) = self.config.getProxy()
173 self.listing.updateFeed(self.key, self.config.getExpiry(), proxy=proxy, imageCache=self.config.getImageCache() )
175 self.listing.updateFeed(self.key, self.config.getExpiry(), imageCache=self.config.getImageCache() )
178 class DownloadBar(gtk.ProgressBar):
179 def __init__(self, parent, listing, listOfKeys, config, single=False):
180 gtk.ProgressBar.__init__(self)
181 self.listOfKeys = listOfKeys[:]
182 self.listing = listing
183 self.total = len(self.listOfKeys)
189 #self.progress = gtk.ProgressBar()
190 #self.waitingWindow = hildon.Note("cancel", parent, "Downloading",
191 # progressbar=self.progress)
192 self.set_text("Updating...")
194 self.set_fraction(self.fraction)
197 self.timeout_handler_id = gobject.timeout_add(50, self.update_progress_bar)
198 #self.waitingWindow.show_all()
199 #response = self.waitingWindow.run()
200 #self.listOfKeys = []
201 #while threading.activeCount() > 1:
202 # Wait for current downloads to finish
204 #self.waitingWindow.destroy()
206 def update_progress_bar(self):
207 #self.progress_bar.pulse()
208 if threading.activeCount() < 4:
209 x = threading.activeCount() - 1
210 k = len(self.listOfKeys)
211 fin = self.total - k - x
212 fraction = float(fin)/float(self.total) + float(x)/(self.total*2.)
213 #print x, k, fin, fraction
214 self.set_fraction(fraction)
216 if len(self.listOfKeys)>0:
217 self.current = self.current+1
218 key = self.listOfKeys.pop()
219 if (not self.listing.getCurrentlyDisplayedFeed() == key) or (self.single == True):
220 # Check if the feed is being displayed
221 download = Download(self.listing, key, self.config)
224 elif threading.activeCount() > 1:
227 #self.waitingWindow.destroy()
229 self.emit("download-done", "success")
234 class SortList(gtk.Dialog):
235 def __init__(self, parent, listing):
236 gtk.Dialog.__init__(self, "Organizer", parent)
237 self.listing = listing
239 self.vbox2 = gtk.VBox(False, 10)
241 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
242 button.set_label("Move Up")
243 button.connect("clicked", self.buttonUp)
244 self.vbox2.pack_start(button, expand=False, fill=False)
246 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
247 button.set_label("Move Down")
248 button.connect("clicked", self.buttonDown)
249 self.vbox2.pack_start(button, expand=False, fill=False)
251 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
252 button.set_label("Add Feed")
253 button.connect("clicked", self.buttonAdd)
254 self.vbox2.pack_start(button, expand=False, fill=False)
256 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
257 button.set_label("Edit Feed")
258 button.connect("clicked", self.buttonEdit)
259 self.vbox2.pack_start(button, expand=False, fill=False)
261 button = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
262 button.set_label("Delete")
263 button.connect("clicked", self.buttonDelete)
264 self.vbox2.pack_start(button, expand=False, fill=False)
266 #button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
267 #button.set_label("Done")
268 #button.connect("clicked", self.buttonDone)
269 #self.vbox.pack_start(button)
270 self.hbox2= gtk.HBox(False, 10)
271 self.pannableArea = hildon.PannableArea()
272 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
273 self.treeview = gtk.TreeView(self.treestore)
274 self.hbox2.pack_start(self.pannableArea, expand=True)
276 self.hbox2.pack_end(self.vbox2, expand=False)
277 self.set_default_size(-1, 600)
278 self.vbox.pack_start(self.hbox2)
281 #self.connect("destroy", self.buttonDone)
283 def displayFeeds(self):
284 self.treeview.destroy()
285 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
286 self.treeview = gtk.TreeView()
288 self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
289 hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview, gtk.HILDON_UI_MODE_EDIT)
291 self.treeview.append_column(gtk.TreeViewColumn('Feed Name', gtk.CellRendererText(), text = 0))
293 self.pannableArea.add(self.treeview)
297 def refreshList(self, selected=None, offset=0):
298 #rect = self.treeview.get_visible_rect()
299 #y = rect.y+rect.height
300 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
301 for key in self.listing.getListOfFeeds():
302 item = self.treestore.append([self.listing.getFeedTitle(key), key])
305 self.treeview.set_model(self.treestore)
306 if not selected == None:
307 self.treeview.get_selection().select_iter(selectedItem)
308 self.treeview.scroll_to_cell(self.treeview.get_model().get_path(selectedItem))
309 self.pannableArea.show_all()
311 def getSelectedItem(self):
312 (model, iter) = self.treeview.get_selection().get_selected()
315 return model.get_value(iter, 1)
317 def findIndex(self, key):
321 for row in self.treestore:
323 return (before, row.iter)
324 if key == list(row)[0]:
328 return (before, None)
330 def buttonUp(self, button):
331 key = self.getSelectedItem()
333 self.listing.moveUp(key)
334 self.refreshList(key, -10)
336 def buttonDown(self, button):
337 key = self.getSelectedItem()
339 self.listing.moveDown(key)
340 self.refreshList(key, 10)
342 def buttonDelete(self, button):
343 key = self.getSelectedItem()
345 self.listing.removeFeed(key)
348 def buttonEdit(self, button):
349 key = self.getSelectedItem()
351 wizard = AddWidgetWizard(self, self.listing.getFeedUrl(key), self.listing.getFeedTitle(key))
354 (title, url) = wizard.getData()
355 if (not title == '') and (not url == ''):
356 self.listing.editFeed(key, title, url)
360 def buttonDone(self, *args):
363 def buttonAdd(self, button, urlIn="http://"):
364 wizard = AddWidgetWizard(self, urlIn)
367 (title, url) = wizard.getData()
368 if (not title == '') and (not url == ''):
369 self.listing.addFeed(title, url)
374 class DisplayArticle(hildon.StackableWindow):
375 def __init__(self, feed, id, key, config, listing):
376 hildon.StackableWindow.__init__(self)
377 #self.imageDownloader = ImageDownloader()
382 #self.set_title(feed.getTitle(id))
383 self.set_title(self.listing.getFeedTitle(key))
386 # Init the article display
387 #if self.config.getWebkitSupport():
388 self.view = webkit.WebView()
389 #self.view.set_editable(False)
392 # self.view = gtkhtml2.View()
393 # self.document = gtkhtml2.Document()
394 # self.view.set_document(self.document)
395 # self.document.connect("link_clicked", self._signal_link_clicked)
396 self.pannable_article = hildon.PannableArea()
397 self.pannable_article.add(self.view)
398 #self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
399 #self.gestureId = self.pannable_article.connect('horizontal-movement', self.gesture)
401 #if self.config.getWebkitSupport():
402 contentLink = self.feed.getContentLink(self.id)
403 self.feed.setEntryRead(self.id)
404 #if key=="ArchivedArticles":
405 self.view.open("file://" + contentLink)
406 self.view.connect("motion-notify-event", lambda w,ev: True)
407 self.view.connect('load-started', self.load_started)
408 self.view.connect('load-finished', self.load_finished)
411 #self.view.load_html_string(self.text, contentLink) # "text/html", "utf-8", self.link)
412 self.view.set_zoom_level(float(config.getArtFontSize())/10.)
414 # if not key == "ArchivedArticles":
415 # Do not download images if the feed is "Archived Articles"
416 # self.document.connect("request-url", self._signal_request_url)
418 # self.document.clear()
419 # self.document.open_stream("text/html")
420 # self.document.write_stream(self.text)
421 # self.document.close_stream()
423 menu = hildon.AppMenu()
424 # Create a button and add it to the menu
425 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
426 button.set_label("Allow Horizontal Scrolling")
427 button.connect("clicked", self.horiz_scrolling_button)
430 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
431 button.set_label("Open in Browser")
432 button.connect("clicked", self._signal_link_clicked, self.feed.getExternalLink(self.id))
435 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
436 button.set_label("Add to Archived Articles")
437 button.connect("clicked", self.archive_button)
440 self.set_app_menu(menu)
443 #self.event_box = gtk.EventBox()
444 #self.event_box.add(self.pannable_article)
445 self.add(self.pannable_article)
448 self.pannable_article.show_all()
450 self.destroyId = self.connect("destroy", self.destroyWindow)
452 self.view.connect("button_press_event", self.button_pressed)
453 self.gestureId = self.view.connect("button_release_event", self.button_released)
454 #self.timeout_handler_id = gobject.timeout_add(300, self.reloadArticle)
456 def load_started(self, *widget):
457 hildon.hildon_gtk_window_set_progress_indicator(self, 1)
459 def load_finished(self, *widget):
460 hildon.hildon_gtk_window_set_progress_indicator(self, 0)
462 def button_pressed(self, window, event):
463 #print event.x, event.y
464 self.coords = (event.x, event.y)
466 def button_released(self, window, event):
467 x = self.coords[0] - event.x
468 y = self.coords[1] - event.y
470 if (2*abs(y) < abs(x)):
472 self.emit("article-previous", self.id)
474 self.emit("article-next", self.id)
478 #def gesture(self, widget, direction, startx, starty):
479 # if (direction == 3):
480 # self.emit("article-next", self.index)
481 # if (direction == 2):
482 # self.emit("article-previous", self.index)
483 #print startx, starty
484 #self.timeout_handler_id = gobject.timeout_add(200, self.destroyWindow)
486 def destroyWindow(self, *args):
487 self.disconnect(self.destroyId)
488 self.emit("article-closed", self.id)
489 #self.imageDownloader.stopAll()
492 def horiz_scrolling_button(self, *widget):
493 self.pannable_article.disconnect(self.gestureId)
494 self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
496 def archive_button(self, *widget):
497 # Call the listing.addArchivedArticle
498 self.listing.addArchivedArticle(self.key, self.id)
500 #def reloadArticle(self, *widget):
501 # if threading.activeCount() > 1:
502 # Image thread are still running, come back in a bit
505 # for (stream, imageThread) in self.images:
507 # stream.write(imageThread.data)
512 def _signal_link_clicked(self, object, link):
513 bus = dbus.SessionBus()
514 proxy = bus.get_object("com.nokia.osso_browser", "/com/nokia/osso_browser/request")
515 iface = dbus.Interface(proxy, 'com.nokia.osso_browser')
516 iface.open_new_window(link)
518 #def _signal_request_url(self, object, url, stream):
520 # self.imageDownloader.queueImage(url, stream)
521 #imageThread = GetImage(url)
523 #self.images.append((stream, imageThread))
526 class DisplayFeed(hildon.StackableWindow):
527 def __init__(self, listing, feed, title, key, config):
528 hildon.StackableWindow.__init__(self)
529 self.listing = listing
531 self.feedTitle = title
532 self.set_title(title)
536 self.downloadDialog = False
538 self.listing.setCurrentlyDisplayedFeed(self.key)
542 menu = hildon.AppMenu()
543 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
544 button.set_label("Update Feed")
545 button.connect("clicked", self.button_update_clicked)
548 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
549 button.set_label("Mark All As Read")
550 button.connect("clicked", self.buttonReadAllClicked)
552 self.set_app_menu(menu)
557 self.connect("destroy", self.destroyWindow)
559 def destroyWindow(self, *args):
560 self.feed.saveUnread(CONFIGDIR)
561 self.listing.updateUnread(self.key, self.feed.getNumberOfUnreadItems())
562 self.emit("feed-closed", self.key)
564 #gobject.idle_add(self.feed.saveFeed, CONFIGDIR)
565 self.listing.closeCurrentlyDisplayedFeed()
567 def displayFeed(self):
568 self.vboxFeed = gtk.VBox(False, 10)
569 self.pannableFeed = hildon.PannableArea()
570 self.pannableFeed.add_with_viewport(self.vboxFeed)
571 self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
573 for id in self.feed.getIds():
574 title = self.feed.getTitle(id)
575 esc_title = title.replace("<em>","").replace("</em>","").replace("&","&").replace("—", "-").replace("’", "'")
576 button = gtk.Button(esc_title)
577 button.set_alignment(0,0)
579 if self.feed.isEntryRead(id):
580 #label.modify_font(pango.FontDescription("sans 16"))
581 label.modify_font(pango.FontDescription(self.config.getReadFont()))
582 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
584 #print self.listing.getFont() + " bold"
585 label.modify_font(pango.FontDescription(self.config.getUnreadFont()))
586 label.modify_fg(gtk.STATE_NORMAL, unread_color)
587 label.set_line_wrap(True)
589 label.set_size_request(self.get_size()[0]-50, -1)
590 button.connect("clicked", self.button_clicked, id)
591 self.buttons[id] = button
593 self.vboxFeed.pack_start(button, expand=False)
595 self.add(self.pannableFeed)
599 self.pannableFeed.destroy()
600 #self.remove(self.pannableFeed)
602 def button_clicked(self, button, index, previous=False, next=False):
603 #newDisp = DisplayArticle(self.feedTitle, self.feed.getArticle(index), self.feed.getLink(index), index, self.key, self.listing, self.config)
604 newDisp = DisplayArticle(self.feed, index, self.key, self.config, self.listing)
605 stack = hildon.WindowStack.get_default()
608 stack.pop_and_push(1, newDisp, tmp)
610 gobject.timeout_add(200, self.destroyArticle, tmp)
615 if type(self.disp).__name__ == "DisplayArticle":
616 gobject.timeout_add(200, self.destroyArticle, self.disp)
623 self.ids.append(self.disp.connect("article-closed", self.onArticleClosed))
624 self.ids.append(self.disp.connect("article-next", self.nextArticle))
625 self.ids.append(self.disp.connect("article-previous", self.previousArticle))
627 def destroyArticle(self, handle):
628 handle.destroyWindow()
630 def nextArticle(self, object, index):
631 label = self.buttons[index].child
632 label.modify_font(pango.FontDescription(self.config.getReadFont()))
633 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
634 id = self.feed.getNextId(index)
635 self.button_clicked(object, id, next=True)
637 def previousArticle(self, object, index):
638 label = self.buttons[index].child
639 label.modify_font(pango.FontDescription(self.config.getReadFont()))
640 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
641 id = self.feed.getPreviousId(index)
642 self.button_clicked(object, id, previous=True)
644 def onArticleClosed(self, object, index):
645 label = self.buttons[index].child
646 label.modify_font(pango.FontDescription(self.config.getReadFont()))
647 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
648 self.buttons[index].show()
650 def button_update_clicked(self, button):
651 #bar = DownloadBar(self, self.listing, [self.key,], self.config )
652 if not type(self.downloadDialog).__name__=="DownloadBar":
653 self.pannableFeed.destroy()
654 self.vbox = gtk.VBox(False, 10)
655 self.downloadDialog = DownloadBar(self.window, self.listing, [self.key,], self.config, single=True )
656 self.downloadDialog.connect("download-done", self.onDownloadsDone)
657 self.vbox.pack_start(self.downloadDialog, expand=False, fill=False)
661 def onDownloadsDone(self, *widget):
663 self.feed = self.listing.getFeed(self.key)
666 def buttonReadAllClicked(self, button):
667 for index in self.feed.getIds():
668 self.feed.setEntryRead(index)
669 label = self.buttons[index].child
670 label.modify_font(pango.FontDescription(self.config.getReadFont()))
671 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
672 self.buttons[index].show()
678 self.window = hildon.StackableWindow()
679 self.window.set_title("FeedingIt")
680 hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
681 self.mainVbox = gtk.VBox(False,10)
682 self.pannableListing = gtk.Label("Loading...")
683 self.mainVbox.pack_start(self.pannableListing)
684 self.window.add(self.mainVbox)
685 self.window.show_all()
686 self.config = Config(self.window, CONFIGDIR+"config.ini")
687 gobject.idle_add(self.createWindow)
689 def createWindow(self):
690 self.listing = Listing(CONFIGDIR)
692 self.downloadDialog = False
693 self.orientation = FremantleRotation("FeedingIt", main_window=self.window, app=self)
694 self.orientation.set_mode(self.config.getOrientation())
696 menu = hildon.AppMenu()
697 # Create a button and add it to the menu
698 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
699 button.set_label("Update All Feeds")
700 button.connect("clicked", self.button_update_clicked, "All")
703 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
704 button.set_label("Mark All As Read")
705 button.connect("clicked", self.button_markAll)
708 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
709 button.set_label("Organize Feeds")
710 button.connect("clicked", self.button_organize_clicked)
713 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
714 button.set_label("Preferences")
715 button.connect("clicked", self.button_preferences_clicked)
718 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
719 button.set_label("Import Feeds")
720 button.connect("clicked", self.button_import_clicked)
723 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
724 button.set_label("Export Feeds")
725 button.connect("clicked", self.button_export_clicked)
728 self.window.set_app_menu(menu)
731 self.feedWindow = hildon.StackableWindow()
732 self.articleWindow = hildon.StackableWindow()
734 self.displayListing()
735 self.autoupdate = False
736 self.checkAutoUpdate()
737 hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
738 gobject.idle_add(self.enableDbus)
740 def enableDbus(self):
741 self.dbusHandler = ServerObject(self)
743 def button_markAll(self, button):
744 for key in self.listing.getListOfFeeds():
745 feed = self.listing.getFeed(key)
746 for id in feed.getIds():
747 feed.setEntryRead(id)
748 feed.saveUnread(CONFIGDIR)
749 self.listing.updateUnread(key, feed.getNumberOfUnreadItems())
752 def button_export_clicked(self, button):
753 opml = ExportOpmlData(self.window, self.listing)
755 def button_import_clicked(self, button):
756 opml = GetOpmlData(self.window)
757 feeds = opml.getData()
758 for (title, url) in feeds:
759 self.listing.addFeed(title, url)
760 self.displayListing()
762 def addFeed(self, urlIn="http://"):
763 wizard = AddWidgetWizard(self.window, urlIn)
766 (title, url) = wizard.getData()
767 if (not title == '') and (not url == ''):
768 self.listing.addFeed(title, url)
770 self.displayListing()
772 def button_organize_clicked(self, button):
773 org = SortList(self.window, self.listing)
776 self.listing.saveConfig()
777 self.displayListing()
779 def button_update_clicked(self, button, key):
780 if not type(self.downloadDialog).__name__=="DownloadBar":
781 self.downloadDialog = DownloadBar(self.window, self.listing, self.listing.getListOfFeeds(), self.config )
782 self.downloadDialog.connect("download-done", self.onDownloadsDone)
783 self.mainVbox.pack_end(self.downloadDialog, expand=False, fill=False)
784 self.mainVbox.show_all()
785 #self.displayListing()
787 def onDownloadsDone(self, *widget):
788 self.downloadDialog.destroy()
789 self.downloadDialog = False
790 #self.displayListing()
792 self.dbusHandler.ArticleCountUpdated()
794 def button_preferences_clicked(self, button):
795 dialog = self.config.createDialog()
796 dialog.connect("destroy", self.prefsClosed)
798 def show_confirmation_note(self, parent, title):
799 note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
801 retcode = gtk.Dialog.run(note)
804 if retcode == gtk.RESPONSE_OK:
809 def displayListing(self):
811 self.mainVbox.remove(self.pannableListing)
814 self.vboxListing = gtk.VBox(False,10)
815 self.pannableListing = hildon.PannableArea()
816 self.pannableListing.add_with_viewport(self.vboxListing)
819 list = self.listing.getListOfFeeds()[:]
822 #button = gtk.Button(item)
823 unreadItems = self.listing.getFeedNumberOfUnreadItems(key)
824 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
825 hildon.BUTTON_ARRANGEMENT_VERTICAL)
826 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
827 + str(unreadItems) + " Unread Items")
828 button.set_alignment(0,0,1,1)
829 button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
830 self.vboxListing.pack_start(button, expand=False)
831 self.buttons[key] = button
833 self.mainVbox.pack_start(self.pannableListing)
834 self.window.show_all()
835 gobject.idle_add(self.refreshList)
837 def refreshList(self):
838 for key in self.listing.getListOfFeeds():
839 if self.buttons.has_key(key):
840 button = self.buttons[key]
841 unreadItems = self.listing.getFeedNumberOfUnreadItems(key)
842 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
843 + str(unreadItems) + " Unread Items")
844 label = button.child.child.get_children()[0].get_children()[1]
846 label.modify_fg(gtk.STATE_NORMAL, read_color)
848 label.modify_fg(gtk.STATE_NORMAL, unread_color)
850 self.displayListing()
853 def buttonFeedClicked(widget, button, self, window, key):
854 self.disp = DisplayFeed(self.listing, self.listing.getFeed(key), self.listing.getFeedTitle(key), key, self.config)
855 self.disp.connect("feed-closed", self.onFeedClosed)
857 def onFeedClosed(self, object, key):
858 self.listing.saveConfig()
860 self.dbusHandler.ArticleCountUpdated()
863 self.window.connect("destroy", gtk.main_quit)
865 self.listing.saveConfig()
867 def prefsClosed(self, *widget):
868 self.orientation.set_mode(self.config.getOrientation())
869 self.checkAutoUpdate()
871 def checkAutoUpdate(self, *widget):
872 interval = int(self.config.getUpdateInterval()*3600000)
873 if self.config.isAutoUpdateEnabled():
874 if self.autoupdate == False:
875 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
876 self.autoupdate = interval
877 elif not self.autoupdate == interval:
878 # If auto-update is enabled, but not at the right frequency
879 gobject.source_remove(self.autoupdateId)
880 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
881 self.autoupdate = interval
883 if not self.autoupdate == False:
884 gobject.source_remove(self.autoupdateId)
885 self.autoupdate = False
887 def automaticUpdate(self, *widget):
888 # Need to check for internet connection
889 # If no internet connection, try again in 10 minutes:
890 # gobject.timeout_add(int(5*3600000), self.automaticUpdate)
891 self.button_update_clicked(None, None)
896 for key in self.listing.getListOfFeeds():
897 if self.listing.getFeedNumberOfUnreadItems(key) > 0:
898 status += self.listing.getFeedTitle(key) + ": \t" + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items\n"
900 status = "No unread items"
903 if __name__ == "__main__":
904 gobject.signal_new("feed-closed", DisplayFeed, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
905 gobject.signal_new("article-closed", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
906 gobject.signal_new("article-next", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
907 gobject.signal_new("article-previous", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
908 gobject.signal_new("download-done", DownloadBar, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
909 gobject.threads_init()
910 if not isdir(CONFIGDIR):
914 print "Error: Can't create configuration directory"