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)
409 #self.view.load_html_string(self.text, contentLink) # "text/html", "utf-8", self.link)
410 self.view.set_zoom_level(float(config.getArtFontSize())/10.)
412 # if not key == "ArchivedArticles":
413 # Do not download images if the feed is "Archived Articles"
414 # self.document.connect("request-url", self._signal_request_url)
416 # self.document.clear()
417 # self.document.open_stream("text/html")
418 # self.document.write_stream(self.text)
419 # self.document.close_stream()
421 menu = hildon.AppMenu()
422 # Create a button and add it to the menu
423 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
424 button.set_label("Allow Horizontal Scrolling")
425 button.connect("clicked", self.horiz_scrolling_button)
428 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
429 button.set_label("Open in Browser")
430 button.connect("clicked", self._signal_link_clicked, self.feed.getExternalLink(self.id))
433 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
434 button.set_label("Add to Archived Articles")
435 button.connect("clicked", self.archive_button)
438 self.set_app_menu(menu)
441 #self.event_box = gtk.EventBox()
442 #self.event_box.add(self.pannable_article)
443 self.add(self.pannable_article)
446 self.pannable_article.show_all()
448 self.destroyId = self.connect("destroy", self.destroyWindow)
450 self.view.connect("button_press_event", self.button_pressed)
451 self.gestureId = self.view.connect("button_release_event", self.button_released)
452 #self.timeout_handler_id = gobject.timeout_add(300, self.reloadArticle)
454 def button_pressed(self, window, event):
455 #print event.x, event.y
456 self.coords = (event.x, event.y)
458 def button_released(self, window, event):
459 x = self.coords[0] - event.x
460 y = self.coords[1] - event.y
462 if (2*abs(y) < abs(x)):
464 self.emit("article-previous", self.id)
466 self.emit("article-next", self.id)
470 #def gesture(self, widget, direction, startx, starty):
471 # if (direction == 3):
472 # self.emit("article-next", self.index)
473 # if (direction == 2):
474 # self.emit("article-previous", self.index)
475 #print startx, starty
476 #self.timeout_handler_id = gobject.timeout_add(200, self.destroyWindow)
478 def destroyWindow(self, *args):
479 self.disconnect(self.destroyId)
480 self.emit("article-closed", self.id)
481 #self.imageDownloader.stopAll()
484 def horiz_scrolling_button(self, *widget):
485 self.pannable_article.disconnect(self.gestureId)
486 self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
488 def archive_button(self, *widget):
489 # Call the listing.addArchivedArticle
490 self.listing.addArchivedArticle(self.key, self.id)
492 #def reloadArticle(self, *widget):
493 # if threading.activeCount() > 1:
494 # Image thread are still running, come back in a bit
497 # for (stream, imageThread) in self.images:
499 # stream.write(imageThread.data)
504 def _signal_link_clicked(self, object, link):
505 bus = dbus.SessionBus()
506 proxy = bus.get_object("com.nokia.osso_browser", "/com/nokia/osso_browser/request")
507 iface = dbus.Interface(proxy, 'com.nokia.osso_browser')
508 iface.open_new_window(link)
510 #def _signal_request_url(self, object, url, stream):
512 # self.imageDownloader.queueImage(url, stream)
513 #imageThread = GetImage(url)
515 #self.images.append((stream, imageThread))
518 class DisplayFeed(hildon.StackableWindow):
519 def __init__(self, listing, feed, title, key, config):
520 hildon.StackableWindow.__init__(self)
521 self.listing = listing
523 self.feedTitle = title
524 self.set_title(title)
528 self.downloadDialog = False
530 self.listing.setCurrentlyDisplayedFeed(self.key)
534 menu = hildon.AppMenu()
535 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
536 button.set_label("Update Feed")
537 button.connect("clicked", self.button_update_clicked)
540 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
541 button.set_label("Mark All As Read")
542 button.connect("clicked", self.buttonReadAllClicked)
544 self.set_app_menu(menu)
549 self.connect("destroy", self.destroyWindow)
551 def destroyWindow(self, *args):
552 self.feed.saveUnread(CONFIGDIR)
553 self.listing.updateUnread(self.key, self.feed.getNumberOfUnreadItems())
554 self.emit("feed-closed", self.key)
556 #gobject.idle_add(self.feed.saveFeed, CONFIGDIR)
557 self.listing.closeCurrentlyDisplayedFeed()
559 def displayFeed(self):
560 self.vboxFeed = gtk.VBox(False, 10)
561 self.pannableFeed = hildon.PannableArea()
562 self.pannableFeed.add_with_viewport(self.vboxFeed)
563 self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
565 for id in self.feed.getIds():
566 title = self.feed.getTitle(id)
567 esc_title = title.replace("<em>","").replace("</em>","").replace("&","&")
568 button = gtk.Button(esc_title)
569 button.set_alignment(0,0)
571 if self.feed.isEntryRead(id):
572 #label.modify_font(pango.FontDescription("sans 16"))
573 label.modify_font(pango.FontDescription(self.config.getReadFont()))
574 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
576 #print self.listing.getFont() + " bold"
577 label.modify_font(pango.FontDescription(self.config.getUnreadFont()))
578 label.modify_fg(gtk.STATE_NORMAL, unread_color)
579 label.set_line_wrap(True)
581 label.set_size_request(self.get_size()[0]-50, -1)
582 button.connect("clicked", self.button_clicked, id)
583 self.buttons[id] = button
585 self.vboxFeed.pack_start(button, expand=False)
587 self.add(self.pannableFeed)
591 self.pannableFeed.destroy()
592 #self.remove(self.pannableFeed)
594 def button_clicked(self, button, index, previous=False, next=False):
595 #newDisp = DisplayArticle(self.feedTitle, self.feed.getArticle(index), self.feed.getLink(index), index, self.key, self.listing, self.config)
596 newDisp = DisplayArticle(self.feed, index, self.key, self.config, self.listing)
597 stack = hildon.WindowStack.get_default()
600 stack.pop_and_push(1, newDisp, tmp)
602 gobject.timeout_add(200, self.destroyArticle, tmp)
607 if type(self.disp).__name__ == "DisplayArticle":
608 gobject.timeout_add(200, self.destroyArticle, self.disp)
615 self.ids.append(self.disp.connect("article-closed", self.onArticleClosed))
616 self.ids.append(self.disp.connect("article-next", self.nextArticle))
617 self.ids.append(self.disp.connect("article-previous", self.previousArticle))
619 def destroyArticle(self, handle):
620 handle.destroyWindow()
622 def nextArticle(self, object, index):
623 label = self.buttons[index].child
624 label.modify_font(pango.FontDescription(self.config.getReadFont()))
625 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
626 id = self.feed.getNextId(index)
627 self.button_clicked(object, id, next=True)
629 def previousArticle(self, object, index):
630 label = self.buttons[index].child
631 label.modify_font(pango.FontDescription(self.config.getReadFont()))
632 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
633 id = self.feed.getPreviousId(index)
634 self.button_clicked(object, id, previous=True)
636 def onArticleClosed(self, object, index):
637 label = self.buttons[index].child
638 label.modify_font(pango.FontDescription(self.config.getReadFont()))
639 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
640 self.buttons[index].show()
642 def button_update_clicked(self, button):
643 #bar = DownloadBar(self, self.listing, [self.key,], self.config )
644 if not type(self.downloadDialog).__name__=="DownloadBar":
645 self.pannableFeed.destroy()
646 self.vbox = gtk.VBox(False, 10)
647 self.downloadDialog = DownloadBar(self.window, self.listing, [self.key,], self.config, single=True )
648 self.downloadDialog.connect("download-done", self.onDownloadsDone)
649 self.vbox.pack_start(self.downloadDialog, expand=False, fill=False)
653 def onDownloadsDone(self, *widget):
655 self.feed = self.listing.getFeed(self.key)
658 def buttonReadAllClicked(self, button):
659 for index in self.feed.getIds():
660 self.feed.setEntryRead(index)
661 label = self.buttons[index].child
662 label.modify_font(pango.FontDescription(self.config.getReadFont()))
663 label.modify_fg(gtk.STATE_NORMAL, read_color) # gtk.gdk.color_parse("white"))
664 self.buttons[index].show()
670 self.window = hildon.StackableWindow()
671 self.window.set_title("FeedingIt")
672 hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
673 self.mainVbox = gtk.VBox(False,10)
674 self.pannableListing = gtk.Label("Loading...")
675 self.mainVbox.pack_start(self.pannableListing)
676 self.window.add(self.mainVbox)
677 self.window.show_all()
678 self.config = Config(self.window, CONFIGDIR+"config.ini")
679 gobject.idle_add(self.createWindow)
681 def createWindow(self):
682 self.listing = Listing(CONFIGDIR)
684 self.downloadDialog = False
685 self.orientation = FremantleRotation("FeedingIt", main_window=self.window, app=self)
686 self.orientation.set_mode(self.config.getOrientation())
688 menu = hildon.AppMenu()
689 # Create a button and add it to the menu
690 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
691 button.set_label("Update All Feeds")
692 button.connect("clicked", self.button_update_clicked, "All")
695 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
696 button.set_label("Mark All As Read")
697 button.connect("clicked", self.button_markAll)
700 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
701 button.set_label("Organize Feeds")
702 button.connect("clicked", self.button_organize_clicked)
705 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
706 button.set_label("Preferences")
707 button.connect("clicked", self.button_preferences_clicked)
710 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
711 button.set_label("Import Feeds")
712 button.connect("clicked", self.button_import_clicked)
715 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
716 button.set_label("Export Feeds")
717 button.connect("clicked", self.button_export_clicked)
720 self.window.set_app_menu(menu)
723 self.feedWindow = hildon.StackableWindow()
724 self.articleWindow = hildon.StackableWindow()
726 self.displayListing()
727 self.autoupdate = False
728 self.checkAutoUpdate()
729 hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
730 gobject.idle_add(self.enableDbus)
732 def enableDbus(self):
733 dbusHandler = ServerObject(self)
735 def button_markAll(self, button):
736 for key in self.listing.getListOfFeeds():
737 feed = self.listing.getFeed(key)
738 for id in feed.getIds():
739 feed.setEntryRead(id)
740 feed.saveUnread(CONFIGDIR)
741 self.listing.updateUnread(key, feed.getNumberOfUnreadItems())
744 def button_export_clicked(self, button):
745 opml = ExportOpmlData(self.window, self.listing)
747 def button_import_clicked(self, button):
748 opml = GetOpmlData(self.window)
749 feeds = opml.getData()
750 for (title, url) in feeds:
751 self.listing.addFeed(title, url)
752 self.displayListing()
754 def addFeed(self, urlIn="http://"):
755 wizard = AddWidgetWizard(self.window, urlIn)
758 (title, url) = wizard.getData()
759 if (not title == '') and (not url == ''):
760 self.listing.addFeed(title, url)
762 self.displayListing()
764 def button_organize_clicked(self, button):
765 org = SortList(self.window, self.listing)
768 self.listing.saveConfig()
769 self.displayListing()
771 def button_update_clicked(self, button, key):
772 if not type(self.downloadDialog).__name__=="DownloadBar":
773 self.downloadDialog = DownloadBar(self.window, self.listing, self.listing.getListOfFeeds(), self.config )
774 self.downloadDialog.connect("download-done", self.onDownloadsDone)
775 self.mainVbox.pack_end(self.downloadDialog, expand=False, fill=False)
776 self.mainVbox.show_all()
777 #self.displayListing()
779 def onDownloadsDone(self, *widget):
780 self.downloadDialog.destroy()
781 self.downloadDialog = False
782 #self.displayListing()
785 def button_preferences_clicked(self, button):
786 dialog = self.config.createDialog()
787 dialog.connect("destroy", self.prefsClosed)
789 def show_confirmation_note(self, parent, title):
790 note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
792 retcode = gtk.Dialog.run(note)
795 if retcode == gtk.RESPONSE_OK:
800 def displayListing(self):
802 self.mainVbox.remove(self.pannableListing)
805 self.vboxListing = gtk.VBox(False,10)
806 self.pannableListing = hildon.PannableArea()
807 self.pannableListing.add_with_viewport(self.vboxListing)
810 list = self.listing.getListOfFeeds()[:]
813 #button = gtk.Button(item)
814 unreadItems = self.listing.getFeedNumberOfUnreadItems(key)
815 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
816 hildon.BUTTON_ARRANGEMENT_VERTICAL)
817 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
818 + str(unreadItems) + " Unread Items")
819 button.set_alignment(0,0,1,1)
820 button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
821 self.vboxListing.pack_start(button, expand=False)
822 self.buttons[key] = button
824 self.mainVbox.pack_start(self.pannableListing)
825 self.window.show_all()
826 gobject.idle_add(self.refreshList)
828 def refreshList(self):
829 for key in self.listing.getListOfFeeds():
830 if self.buttons.has_key(key):
831 button = self.buttons[key]
832 unreadItems = self.listing.getFeedNumberOfUnreadItems(key)
833 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
834 + str(unreadItems) + " Unread Items")
835 label = button.child.child.get_children()[0].get_children()[1]
837 label.modify_fg(gtk.STATE_NORMAL, read_color)
839 label.modify_fg(gtk.STATE_NORMAL, unread_color)
841 self.displayListing()
844 def buttonFeedClicked(widget, button, self, window, key):
845 self.disp = DisplayFeed(self.listing, self.listing.getFeed(key), self.listing.getFeedTitle(key), key, self.config)
846 self.disp.connect("feed-closed", self.onFeedClosed)
848 def onFeedClosed(self, object, key):
849 self.listing.saveConfig()
853 self.window.connect("destroy", gtk.main_quit)
855 self.listing.saveConfig()
857 def prefsClosed(self, *widget):
858 self.orientation.set_mode(self.config.getOrientation())
859 self.checkAutoUpdate()
861 def checkAutoUpdate(self, *widget):
862 interval = int(self.config.getUpdateInterval()*3600000)
863 if self.config.isAutoUpdateEnabled():
864 if self.autoupdate == False:
865 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
866 self.autoupdate = interval
867 elif not self.autoupdate == interval:
868 # If auto-update is enabled, but not at the right frequency
869 gobject.source_remove(self.autoupdateId)
870 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
871 self.autoupdate = interval
873 if not self.autoupdate == False:
874 gobject.source_remove(self.autoupdateId)
875 self.autoupdate = False
877 def automaticUpdate(self, *widget):
878 # Need to check for internet connection
879 # If no internet connection, try again in 10 minutes:
880 # gobject.timeout_add(int(5*3600000), self.automaticUpdate)
881 self.button_update_clicked(None, None)
886 for key in self.listing.getListOfFeeds():
887 if self.listing.getFeedNumberOfUnreadItems(key) > 0:
888 status += self.listing.getFeedTitle(key) + ": \t" + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items\n"
890 status = "No unread items"
893 if __name__ == "__main__":
894 gobject.signal_new("feed-closed", DisplayFeed, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
895 gobject.signal_new("article-closed", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
896 gobject.signal_new("article-next", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
897 gobject.signal_new("article-previous", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
898 gobject.signal_new("download-done", DownloadBar, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
899 gobject.threads_init()
900 if not isdir(CONFIGDIR):
904 print "Error: Can't create configuration directory"