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 CONFIGDIR="/home/user/.feedingit/"
60 class AddWidgetWizard(hildon.WizardDialog):
62 def __init__(self, parent, urlIn, titleIn=None):
64 self.notebook = gtk.Notebook()
66 self.nameEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
67 self.nameEntry.set_placeholder("Enter Feed Name")
68 vbox = gtk.VBox(False,10)
69 label = gtk.Label("Enter Feed Name:")
70 vbox.pack_start(label)
71 vbox.pack_start(self.nameEntry)
72 if not titleIn == None:
73 self.nameEntry.set_text(titleIn)
74 self.notebook.append_page(vbox, None)
76 self.urlEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
77 self.urlEntry.set_placeholder("Enter a URL")
78 self.urlEntry.set_text(urlIn)
79 self.urlEntry.select_region(0,-1)
81 vbox = gtk.VBox(False,10)
82 label = gtk.Label("Enter Feed URL:")
83 vbox.pack_start(label)
84 vbox.pack_start(self.urlEntry)
85 self.notebook.append_page(vbox, None)
87 labelEnd = gtk.Label("Success")
89 self.notebook.append_page(labelEnd, None)
91 hildon.WizardDialog.__init__(self, parent, "Add Feed", self.notebook)
93 # Set a handler for "switch-page" signal
94 #self.notebook.connect("switch_page", self.on_page_switch, self)
96 # Set a function to decide if user can go to next page
97 self.set_forward_page_func(self.some_page_func)
102 return (self.nameEntry.get_text(), self.urlEntry.get_text())
104 def on_page_switch(self, notebook, page, num, dialog):
107 def some_page_func(self, nb, current, userdata):
108 # Validate data for 1st page
110 return len(self.nameEntry.get_text()) != 0
112 # Check the url is not null, and starts with http
113 return ( (len(self.urlEntry.get_text()) != 0) and (self.urlEntry.get_text().lower().startswith("http")) )
119 class GetImage(threading.Thread):
120 def __init__(self, url, stream):
121 threading.Thread.__init__(self)
126 f = urllib2.urlopen(self.url)
129 self.stream.write(data)
132 class ImageDownloader():
135 self.downloading = False
137 def queueImage(self, url, stream):
138 self.images.append((url, stream))
139 if not self.downloading:
140 self.downloading = True
141 gobject.timeout_add(50, self.checkQueue)
143 def checkQueue(self):
144 for i in range(4-threading.activeCount()):
145 if len(self.images) > 0:
146 (url, stream) = self.images.pop()
147 GetImage(url, stream).start()
148 if len(self.images)>0:
149 gobject.timeout_add(200, self.checkQueue)
151 self.downloading=False
157 class Download(threading.Thread):
158 def __init__(self, listing, key, config):
159 threading.Thread.__init__(self)
160 self.listing = listing
165 (use_proxy, proxy) = self.config.getProxy()
167 self.listing.updateFeed(self.key, self.config.getExpiry(), proxy=proxy)
169 self.listing.updateFeed(self.key, self.config.getExpiry())
172 class DownloadBar(gtk.ProgressBar):
173 def __init__(self, parent, listing, listOfKeys, config, single=False):
174 gtk.ProgressBar.__init__(self)
175 self.listOfKeys = listOfKeys[:]
176 self.listing = listing
177 self.total = len(self.listOfKeys)
183 #self.progress = gtk.ProgressBar()
184 #self.waitingWindow = hildon.Note("cancel", parent, "Downloading",
185 # progressbar=self.progress)
186 self.set_text("Updating...")
188 self.set_fraction(self.fraction)
191 self.timeout_handler_id = gobject.timeout_add(50, self.update_progress_bar)
192 #self.waitingWindow.show_all()
193 #response = self.waitingWindow.run()
194 #self.listOfKeys = []
195 #while threading.activeCount() > 1:
196 # Wait for current downloads to finish
198 #self.waitingWindow.destroy()
200 def update_progress_bar(self):
201 #self.progress_bar.pulse()
202 if threading.activeCount() < 4:
203 x = threading.activeCount() - 1
204 k = len(self.listOfKeys)
205 fin = self.total - k - x
206 fraction = float(fin)/float(self.total) + float(x)/(self.total*2.)
207 #print x, k, fin, fraction
208 self.set_fraction(fraction)
210 if len(self.listOfKeys)>0:
211 self.current = self.current+1
212 key = self.listOfKeys.pop()
213 if (not self.listing.getCurrentlyDisplayedFeed() == key) or (self.single == True):
214 # Check if the feed is being displayed
215 download = Download(self.listing, key, self.config)
218 elif threading.activeCount() > 1:
221 #self.waitingWindow.destroy()
223 self.emit("download-done", "success")
228 class SortList(gtk.Dialog):
229 def __init__(self, parent, listing):
230 gtk.Dialog.__init__(self, "Organizer", parent)
231 self.listing = listing
233 self.vbox2 = gtk.VBox(False, 10)
235 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
236 button.set_label("Move Up")
237 button.connect("clicked", self.buttonUp)
238 self.vbox2.pack_start(button, expand=False, fill=False)
240 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
241 button.set_label("Move Down")
242 button.connect("clicked", self.buttonDown)
243 self.vbox2.pack_start(button, expand=False, fill=False)
245 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
246 button.set_label("Add Feed")
247 button.connect("clicked", self.buttonAdd)
248 self.vbox2.pack_start(button, expand=False, fill=False)
250 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
251 button.set_label("Edit Feed")
252 button.connect("clicked", self.buttonEdit)
253 self.vbox2.pack_start(button, expand=False, fill=False)
255 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
256 button.set_label("Delete")
257 button.connect("clicked", self.buttonDelete)
258 self.vbox2.pack_start(button, expand=False, fill=False)
260 #button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
261 #button.set_label("Done")
262 #button.connect("clicked", self.buttonDone)
263 #self.vbox.pack_start(button)
264 self.hbox2= gtk.HBox(False, 10)
265 self.pannableArea = hildon.PannableArea()
266 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
267 self.treeview = gtk.TreeView(self.treestore)
268 self.hbox2.pack_start(self.pannableArea, expand=True)
270 self.hbox2.pack_end(self.vbox2, expand=False)
271 self.set_default_size(-1, 600)
272 self.vbox.pack_start(self.hbox2)
275 #self.connect("destroy", self.buttonDone)
277 def displayFeeds(self):
278 self.treeview.destroy()
279 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
280 self.treeview = gtk.TreeView()
282 self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
283 hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview, gtk.HILDON_UI_MODE_EDIT)
285 self.treeview.append_column(gtk.TreeViewColumn('Feed Name', gtk.CellRendererText(), text = 0))
287 self.pannableArea.add(self.treeview)
291 def refreshList(self, selected=None, offset=0):
292 rect = self.treeview.get_visible_rect()
293 y = rect.y+rect.height
294 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
295 for key in self.listing.getListOfFeeds():
296 item = self.treestore.append([self.listing.getFeedTitle(key), key])
299 self.treeview.set_model(self.treestore)
300 if not selected == None:
301 self.treeview.get_selection().select_iter(selectedItem)
302 self.treeview.scroll_to_cell(self.treeview.get_model().get_path(selectedItem))
303 self.pannableArea.show_all()
305 def getSelectedItem(self):
306 (model, iter) = self.treeview.get_selection().get_selected()
309 return model.get_value(iter, 1)
311 def findIndex(self, key):
315 for row in self.treestore:
317 return (before, row.iter)
318 if key == list(row)[0]:
322 return (before, None)
324 def buttonUp(self, button):
325 key = self.getSelectedItem()
327 self.listing.moveUp(key)
328 self.refreshList(key, -10)
330 def buttonDown(self, button):
331 key = self.getSelectedItem()
333 self.listing.moveDown(key)
334 self.refreshList(key, 10)
336 def buttonDelete(self, button):
337 key = self.getSelectedItem()
339 self.listing.removeFeed(key)
342 def buttonEdit(self, button):
343 key = self.getSelectedItem()
345 wizard = AddWidgetWizard(self, self.listing.getFeedUrl(key), self.listing.getFeedTitle(key))
348 (title, url) = wizard.getData()
349 if (not title == '') and (not url == ''):
350 self.listing.editFeed(key, title, url)
354 def buttonDone(self, *args):
357 def buttonAdd(self, button, urlIn="http://"):
358 wizard = AddWidgetWizard(self, urlIn)
361 (title, url) = wizard.getData()
362 if (not title == '') and (not url == ''):
363 self.listing.addFeed(title, url)
368 class DisplayArticle(hildon.StackableWindow):
369 def __init__(self, title, text, link, index, key, listing, config):
370 hildon.StackableWindow.__init__(self)
371 self.imageDownloader = ImageDownloader()
377 self.set_title(title)
381 # Init the article display
382 if self.config.getWebkitSupport():
383 self.view = webkit.WebView()
384 #self.view.set_editable(False)
387 self.view = gtkhtml2.View()
388 self.document = gtkhtml2.Document()
389 self.view.set_document(self.document)
390 self.document.connect("link_clicked", self._signal_link_clicked)
391 self.pannable_article = hildon.PannableArea()
392 self.pannable_article.add(self.view)
393 #self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
394 #self.gestureId = self.pannable_article.connect('horizontal-movement', self.gesture)
396 if self.config.getWebkitSupport():
397 if key=="ArchivedArticles":
398 self.view.open("file://" + self.link)
400 self.view.load_html_string(self.text, self.link) # "text/html", "utf-8", self.link)
401 self.view.set_zoom_level(float(config.getArtFontSize())/10.)
403 if not key == "ArchivedArticles":
404 # Do not download images if the feed is "Archived Articles"
405 self.document.connect("request-url", self._signal_request_url)
407 self.document.clear()
408 self.document.open_stream("text/html")
409 self.document.write_stream(self.text)
410 self.document.close_stream()
412 menu = hildon.AppMenu()
413 # Create a button and add it to the menu
414 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
415 button.set_label("Allow Horizontal Scrolling")
416 button.connect("clicked", self.horiz_scrolling_button)
419 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
420 button.set_label("Open in Browser")
421 button.connect("clicked", self._signal_link_clicked, self.link)
424 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
425 button.set_label("Add to Archived Articles")
426 button.connect("clicked", self.archive_button)
429 self.set_app_menu(menu)
432 #self.event_box = gtk.EventBox()
433 #self.event_box.add(self.pannable_article)
434 self.add(self.pannable_article)
437 self.pannable_article.show_all()
439 self.destroyId = self.connect("destroy", self.destroyWindow)
441 self.view.connect("button_press_event", self.button_pressed)
442 self.gestureId = self.view.connect("button_release_event", self.button_released)
443 #self.timeout_handler_id = gobject.timeout_add(300, self.reloadArticle)
445 def button_pressed(self, window, event):
446 #print event.x, event.y
447 self.coords = (event.x, event.y)
449 def button_released(self, window, event):
450 x = self.coords[0] - event.x
451 y = self.coords[1] - event.y
455 self.emit("article-previous", self.index)
457 self.emit("article-next", self.index)
461 #def gesture(self, widget, direction, startx, starty):
462 # if (direction == 3):
463 # self.emit("article-next", self.index)
464 # if (direction == 2):
465 # self.emit("article-previous", self.index)
466 #print startx, starty
467 #self.timeout_handler_id = gobject.timeout_add(200, self.destroyWindow)
469 def destroyWindow(self, *args):
470 self.disconnect(self.destroyId)
471 self.emit("article-closed", self.index)
472 self.imageDownloader.stopAll()
475 def horiz_scrolling_button(self, *widget):
476 self.pannable_article.disconnect(self.gestureId)
477 self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
479 def archive_button(self, *widget):
480 # Call the listing.addArchivedArticle
481 self.listing.addArchivedArticle(self.key, self.index)
483 #def reloadArticle(self, *widget):
484 # if threading.activeCount() > 1:
485 # Image thread are still running, come back in a bit
488 # for (stream, imageThread) in self.images:
490 # stream.write(imageThread.data)
495 def _signal_link_clicked(self, object, link):
496 bus = dbus.SystemBus()
497 proxy = bus.get_object("com.nokia.osso_browser", "/com/nokia/osso_browser/request")
498 iface = dbus.Interface(proxy, 'com.nokia.osso_browser')
501 def _signal_request_url(self, object, url, stream):
503 self.imageDownloader.queueImage(url, stream)
504 #imageThread = GetImage(url)
506 #self.images.append((stream, imageThread))
509 class DisplayFeed(hildon.StackableWindow):
510 def __init__(self, listing, feed, title, key, config):
511 hildon.StackableWindow.__init__(self)
512 self.listing = listing
514 self.feedTitle = title
515 self.set_title(title)
519 self.downloadDialog = False
521 self.listing.setCurrentlyDisplayedFeed(self.key)
525 menu = hildon.AppMenu()
526 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
527 button.set_label("Update Feed")
528 button.connect("clicked", self.button_update_clicked)
531 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
532 button.set_label("Mark All As Read")
533 button.connect("clicked", self.buttonReadAllClicked)
535 self.set_app_menu(menu)
540 self.connect("destroy", self.destroyWindow)
542 def destroyWindow(self, *args):
543 self.feed.saveUnread(CONFIGDIR)
544 self.listing.updateUnread(self.key, self.feed.getNumberOfUnreadItems())
545 self.emit("feed-closed", self.key)
547 #gobject.idle_add(self.feed.saveFeed, CONFIGDIR)
548 self.listing.closeCurrentlyDisplayedFeed()
550 def displayFeed(self):
551 self.vboxFeed = gtk.VBox(False, 10)
552 self.pannableFeed = hildon.PannableArea()
553 self.pannableFeed.add_with_viewport(self.vboxFeed)
554 self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
556 for id in self.feed.getIds():
557 button = gtk.Button(self.feed.getTitle(id))
558 button.set_alignment(0,0)
560 if self.feed.isEntryRead(id):
561 #label.modify_font(pango.FontDescription("sans 16"))
562 label.modify_font(pango.FontDescription(self.config.getReadFont()))
563 label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
565 #print self.listing.getFont() + " bold"
566 label.modify_font(pango.FontDescription(self.config.getUnreadFont()))
567 #label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("SkyBlue"))
568 fg_color = fg_button.child.get_children()[0].get_children()[0].get_children()[1].get_style().fg[gtk.STATE_NORMAL]
569 label.modify_fg(gtk.STATE_NORMAL, fg_color)
570 #label.modify_font(pango.FontDescription("sans bold 23"))
572 label.set_line_wrap(True)
574 label.set_size_request(self.get_size()[0]-50, -1)
575 button.connect("clicked", self.button_clicked, id)
576 self.buttons[id] = button
578 self.vboxFeed.pack_start(button, expand=False)
580 self.add(self.pannableFeed)
584 self.remove(self.pannableFeed)
586 def button_clicked(self, button, index, previous=False, next=False):
587 newDisp = DisplayArticle(self.feedTitle, self.feed.getArticle(index), self.feed.getLink(index), index, self.key, self.listing, self.config)
588 stack = hildon.WindowStack.get_default()
591 stack.pop_and_push(1, newDisp, tmp)
593 gobject.timeout_add(200, self.destroyArticle, tmp)
598 #if not self.disp == False:
599 # self.disp.destroyWindow()
601 #print type(self.disp).__name__
603 #self.disp.destroyWindow()
604 #stack.pop_and_push(1,newDisp)
606 # stack.push(newDisp)
609 if type(self.disp).__name__ == "DisplayArticle":
610 gobject.timeout_add(200, self.destroyArticle, self.disp)
612 #self.disp.show_all()
613 #if not self.disp == False:
614 # self.disp.destroyWindow()
620 self.ids.append(self.disp.connect("article-closed", self.onArticleClosed))
621 self.ids.append(self.disp.connect("article-next", self.nextArticle))
622 self.ids.append(self.disp.connect("article-previous", self.previousArticle))
624 def destroyArticle(self, handle):
625 handle.destroyWindow()
627 def nextArticle(self, object, index):
628 label = self.buttons[index].child
629 label.modify_font(pango.FontDescription(self.config.getReadFont()))
630 label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
631 id = self.feed.getNextId(index)
632 self.button_clicked(object, id, next=True)
634 def previousArticle(self, object, index):
635 label = self.buttons[index].child
636 label.modify_font(pango.FontDescription(self.config.getReadFont()))
637 label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
638 id = self.feed.getPreviousId(index)
639 self.button_clicked(object, id, previous=True)
641 def onArticleClosed(self, object, index):
642 label = self.buttons[index].child
643 label.modify_font(pango.FontDescription(self.config.getReadFont()))
644 label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
645 self.buttons[index].show()
647 def button_update_clicked(self, button):
648 #bar = DownloadBar(self, self.listing, [self.key,], self.config )
649 if not type(self.downloadDialog).__name__=="DownloadBar":
650 self.pannableFeed.destroy()
651 self.vbox = gtk.VBox(False, 10)
652 self.downloadDialog = DownloadBar(self.window, self.listing, [self.key,], self.config, single=True )
653 self.downloadDialog.connect("download-done", self.onDownloadsDone)
654 self.vbox.pack_start(self.downloadDialog, expand=False, fill=False)
658 def onDownloadsDone(self, *widget):
660 self.feed = self.listing.getFeed(self.key)
662 #self.feed.updateFeed()
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, 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", has_webkit)
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)
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)
739 def button_markAll(self, button):
740 for key in self.listing.getListOfFeeds():
741 feed = self.listing.getFeed(key)
742 for index in range(feed.getNumberOfEntries()):
743 feed.setEntryRead(index)
746 def button_export_clicked(self, button):
747 opml = ExportOpmlData(self.window, self.listing)
749 def button_import_clicked(self, button):
750 opml = GetOpmlData(self.window)
751 feeds = opml.getData()
752 for (title, url) in feeds:
753 self.listing.addFeed(title, url)
754 self.displayListing()
756 def addFeed(self, urlIn="http://"):
757 wizard = AddWidgetWizard(self.window, urlIn)
760 (title, url) = wizard.getData()
761 if (not title == '') and (not url == ''):
762 self.listing.addFeed(title, url)
764 self.displayListing()
766 def button_organize_clicked(self, button):
767 org = SortList(self.window, self.listing)
770 self.listing.saveConfig()
771 self.displayListing()
773 def button_update_clicked(self, button, key):
774 if not type(self.downloadDialog).__name__=="DownloadBar":
775 self.downloadDialog = DownloadBar(self.window, self.listing, self.listing.getListOfFeeds(), self.config )
776 self.downloadDialog.connect("download-done", self.onDownloadsDone)
777 self.mainVbox.pack_end(self.downloadDialog, expand=False, fill=False)
778 self.mainVbox.show_all()
779 #self.displayListing()
781 def onDownloadsDone(self, *widget):
782 self.downloadDialog.destroy()
783 self.downloadDialog = False
784 #self.displayListing()
787 def button_preferences_clicked(self, button):
788 dialog = self.config.createDialog()
789 dialog.connect("destroy", self.prefsClosed)
791 def show_confirmation_note(self, parent, title):
792 note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
794 retcode = gtk.Dialog.run(note)
797 if retcode == gtk.RESPONSE_OK:
802 def displayListing(self):
804 self.mainVbox.remove(self.pannableListing)
807 self.vboxListing = gtk.VBox(False,10)
808 self.pannableListing = hildon.PannableArea()
809 self.pannableListing.add_with_viewport(self.vboxListing)
812 list = self.listing.getListOfFeeds()[:]
815 #button = gtk.Button(item)
816 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
817 hildon.BUTTON_ARRANGEMENT_VERTICAL)
818 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
819 + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
820 button.set_alignment(0,0,1,1)
821 button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
822 self.vboxListing.pack_start(button, expand=False)
823 self.buttons[key] = button
826 #fg_color = button.child.get_children()[0].get_children()[0].get_children()[1].get_style().fg[gtk.STATE_NORMAL]
828 #if type(self.downloadDialog).__name__=="DownloadBar":
829 # self.vboxListing.pack_start(self.downloadDialog)
830 self.mainVbox.pack_start(self.pannableListing)
831 self.window.show_all()
833 def refreshList(self):
834 for key in self.listing.getListOfFeeds():
835 if self.buttons.has_key(key):
836 button = self.buttons[key]
837 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
838 + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
840 self.displayListing()
843 def buttonFeedClicked(widget, button, self, window, key):
844 disp = DisplayFeed(self.listing, self.listing.getFeed(key), self.listing.getFeedTitle(key), key, self.config)
845 disp.connect("feed-closed", self.onFeedClosed)
847 def onFeedClosed(self, object, key):
848 #self.displayListing()
849 self.listing.saveConfig()
851 #self.buttons[key].set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
852 # + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
853 #self.buttons[key].show()
856 self.window.connect("destroy", gtk.main_quit)
858 #for key in self.listing.getListOfFeeds():
859 # self.listing.getFeed(key).saveFeed(CONFIGDIR)
860 self.listing.saveConfig()
862 def prefsClosed(self, *widget):
863 self.orientation.set_mode(self.config.getOrientation())
864 self.checkAutoUpdate()
866 def checkAutoUpdate(self, *widget):
867 interval = int(self.config.getUpdateInterval()*3600000)
868 if self.config.isAutoUpdateEnabled():
869 if self.autoupdate == False:
870 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
871 self.autoupdate = interval
872 elif not self.autoupdate == interval:
873 # If auto-update is enabled, but not at the right frequency
874 gobject.source_remove(self.autoupdateId)
875 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
876 self.autoupdate = interval
878 if not self.autoupdate == False:
879 gobject.source_remove(self.autoupdateId)
880 self.autoupdate = False
882 def automaticUpdate(self, *widget):
883 # Need to check for internet connection
884 # If no internet connection, try again in 10 minutes:
885 # gobject.timeout_add(int(5*3600000), self.automaticUpdate)
886 self.button_update_clicked(None, None)
891 for key in self.listing.getListOfFeeds():
892 if self.listing.getFeedNumberOfUnreadItems(key) > 0:
893 status += self.listing.getFeedTitle(key) + ": \t" + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items\n"
895 status = "No unread items"
898 if __name__ == "__main__":
899 gobject.signal_new("feed-closed", DisplayFeed, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
900 gobject.signal_new("article-closed", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
901 gobject.signal_new("article-next", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
902 gobject.signal_new("article-previous", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
903 gobject.signal_new("download-done", DownloadBar, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
904 gobject.threads_init()
905 if not isdir(CONFIGDIR):
909 print "Error: Can't create configuration directory"
912 dbusHandler = ServerObject(app)