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 fg_colog = color_style.lookup_color('ActiveTextColor')
62 CONFIGDIR="/home/user/.feedingit/"
64 class AddWidgetWizard(hildon.WizardDialog):
66 def __init__(self, parent, urlIn, titleIn=None):
68 self.notebook = gtk.Notebook()
70 self.nameEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
71 self.nameEntry.set_placeholder("Enter Feed Name")
72 vbox = gtk.VBox(False,10)
73 label = gtk.Label("Enter Feed Name:")
74 vbox.pack_start(label)
75 vbox.pack_start(self.nameEntry)
76 if not titleIn == None:
77 self.nameEntry.set_text(titleIn)
78 self.notebook.append_page(vbox, None)
80 self.urlEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
81 self.urlEntry.set_placeholder("Enter a URL")
82 self.urlEntry.set_text(urlIn)
83 self.urlEntry.select_region(0,-1)
85 vbox = gtk.VBox(False,10)
86 label = gtk.Label("Enter Feed URL:")
87 vbox.pack_start(label)
88 vbox.pack_start(self.urlEntry)
89 self.notebook.append_page(vbox, None)
91 labelEnd = gtk.Label("Success")
93 self.notebook.append_page(labelEnd, None)
95 hildon.WizardDialog.__init__(self, parent, "Add Feed", self.notebook)
97 # Set a handler for "switch-page" signal
98 #self.notebook.connect("switch_page", self.on_page_switch, self)
100 # Set a function to decide if user can go to next page
101 self.set_forward_page_func(self.some_page_func)
106 return (self.nameEntry.get_text(), self.urlEntry.get_text())
108 def on_page_switch(self, notebook, page, num, dialog):
111 def some_page_func(self, nb, current, userdata):
112 # Validate data for 1st page
114 return len(self.nameEntry.get_text()) != 0
116 # Check the url is not null, and starts with http
117 return ( (len(self.urlEntry.get_text()) != 0) and (self.urlEntry.get_text().lower().startswith("http")) )
123 class GetImage(threading.Thread):
124 def __init__(self, url, stream):
125 threading.Thread.__init__(self)
130 f = urllib2.urlopen(self.url)
133 self.stream.write(data)
136 class ImageDownloader():
139 self.downloading = False
141 def queueImage(self, url, stream):
142 self.images.append((url, stream))
143 if not self.downloading:
144 self.downloading = True
145 gobject.timeout_add(50, self.checkQueue)
147 def checkQueue(self):
148 for i in range(4-threading.activeCount()):
149 if len(self.images) > 0:
150 (url, stream) = self.images.pop()
151 GetImage(url, stream).start()
152 if len(self.images)>0:
153 gobject.timeout_add(200, self.checkQueue)
155 self.downloading=False
161 class Download(threading.Thread):
162 def __init__(self, listing, key, config):
163 threading.Thread.__init__(self)
164 self.listing = listing
169 (use_proxy, proxy) = self.config.getProxy()
171 self.listing.updateFeed(self.key, self.config.getExpiry(), proxy=proxy)
173 self.listing.updateFeed(self.key, self.config.getExpiry())
176 class DownloadBar(gtk.ProgressBar):
177 def __init__(self, parent, listing, listOfKeys, config, single=False):
178 gtk.ProgressBar.__init__(self)
179 self.listOfKeys = listOfKeys[:]
180 self.listing = listing
181 self.total = len(self.listOfKeys)
187 #self.progress = gtk.ProgressBar()
188 #self.waitingWindow = hildon.Note("cancel", parent, "Downloading",
189 # progressbar=self.progress)
190 self.set_text("Updating...")
192 self.set_fraction(self.fraction)
195 self.timeout_handler_id = gobject.timeout_add(50, self.update_progress_bar)
196 #self.waitingWindow.show_all()
197 #response = self.waitingWindow.run()
198 #self.listOfKeys = []
199 #while threading.activeCount() > 1:
200 # Wait for current downloads to finish
202 #self.waitingWindow.destroy()
204 def update_progress_bar(self):
205 #self.progress_bar.pulse()
206 if threading.activeCount() < 4:
207 x = threading.activeCount() - 1
208 k = len(self.listOfKeys)
209 fin = self.total - k - x
210 fraction = float(fin)/float(self.total) + float(x)/(self.total*2.)
211 #print x, k, fin, fraction
212 self.set_fraction(fraction)
214 if len(self.listOfKeys)>0:
215 self.current = self.current+1
216 key = self.listOfKeys.pop()
217 if (not self.listing.getCurrentlyDisplayedFeed() == key) or (self.single == True):
218 # Check if the feed is being displayed
219 download = Download(self.listing, key, self.config)
222 elif threading.activeCount() > 1:
225 #self.waitingWindow.destroy()
227 self.emit("download-done", "success")
232 class SortList(gtk.Dialog):
233 def __init__(self, parent, listing):
234 gtk.Dialog.__init__(self, "Organizer", parent)
235 self.listing = listing
237 self.vbox2 = gtk.VBox(False, 10)
239 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
240 button.set_label("Move Up")
241 button.connect("clicked", self.buttonUp)
242 self.vbox2.pack_start(button, expand=False, fill=False)
244 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
245 button.set_label("Move Down")
246 button.connect("clicked", self.buttonDown)
247 self.vbox2.pack_start(button, expand=False, fill=False)
249 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
250 button.set_label("Add Feed")
251 button.connect("clicked", self.buttonAdd)
252 self.vbox2.pack_start(button, expand=False, fill=False)
254 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
255 button.set_label("Edit Feed")
256 button.connect("clicked", self.buttonEdit)
257 self.vbox2.pack_start(button, expand=False, fill=False)
259 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
260 button.set_label("Delete")
261 button.connect("clicked", self.buttonDelete)
262 self.vbox2.pack_start(button, expand=False, fill=False)
264 #button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
265 #button.set_label("Done")
266 #button.connect("clicked", self.buttonDone)
267 #self.vbox.pack_start(button)
268 self.hbox2= gtk.HBox(False, 10)
269 self.pannableArea = hildon.PannableArea()
270 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
271 self.treeview = gtk.TreeView(self.treestore)
272 self.hbox2.pack_start(self.pannableArea, expand=True)
274 self.hbox2.pack_end(self.vbox2, expand=False)
275 self.set_default_size(-1, 600)
276 self.vbox.pack_start(self.hbox2)
279 #self.connect("destroy", self.buttonDone)
281 def displayFeeds(self):
282 self.treeview.destroy()
283 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
284 self.treeview = gtk.TreeView()
286 self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
287 hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview, gtk.HILDON_UI_MODE_EDIT)
289 self.treeview.append_column(gtk.TreeViewColumn('Feed Name', gtk.CellRendererText(), text = 0))
291 self.pannableArea.add(self.treeview)
295 def refreshList(self, selected=None, offset=0):
296 rect = self.treeview.get_visible_rect()
297 y = rect.y+rect.height
298 self.treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
299 for key in self.listing.getListOfFeeds():
300 item = self.treestore.append([self.listing.getFeedTitle(key), key])
303 self.treeview.set_model(self.treestore)
304 if not selected == None:
305 self.treeview.get_selection().select_iter(selectedItem)
306 self.treeview.scroll_to_cell(self.treeview.get_model().get_path(selectedItem))
307 self.pannableArea.show_all()
309 def getSelectedItem(self):
310 (model, iter) = self.treeview.get_selection().get_selected()
313 return model.get_value(iter, 1)
315 def findIndex(self, key):
319 for row in self.treestore:
321 return (before, row.iter)
322 if key == list(row)[0]:
326 return (before, None)
328 def buttonUp(self, button):
329 key = self.getSelectedItem()
331 self.listing.moveUp(key)
332 self.refreshList(key, -10)
334 def buttonDown(self, button):
335 key = self.getSelectedItem()
337 self.listing.moveDown(key)
338 self.refreshList(key, 10)
340 def buttonDelete(self, button):
341 key = self.getSelectedItem()
343 self.listing.removeFeed(key)
346 def buttonEdit(self, button):
347 key = self.getSelectedItem()
349 wizard = AddWidgetWizard(self, self.listing.getFeedUrl(key), self.listing.getFeedTitle(key))
352 (title, url) = wizard.getData()
353 if (not title == '') and (not url == ''):
354 self.listing.editFeed(key, title, url)
358 def buttonDone(self, *args):
361 def buttonAdd(self, button, urlIn="http://"):
362 wizard = AddWidgetWizard(self, urlIn)
365 (title, url) = wizard.getData()
366 if (not title == '') and (not url == ''):
367 self.listing.addFeed(title, url)
372 class DisplayArticle(hildon.StackableWindow):
373 def __init__(self, title, text, link, index, key, listing, config):
374 hildon.StackableWindow.__init__(self)
375 self.imageDownloader = ImageDownloader()
381 self.set_title(title)
385 # Init the article display
386 if self.config.getWebkitSupport():
387 self.view = webkit.WebView()
388 #self.view.set_editable(False)
391 self.view = gtkhtml2.View()
392 self.document = gtkhtml2.Document()
393 self.view.set_document(self.document)
394 self.document.connect("link_clicked", self._signal_link_clicked)
395 self.pannable_article = hildon.PannableArea()
396 self.pannable_article.add(self.view)
397 #self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
398 #self.gestureId = self.pannable_article.connect('horizontal-movement', self.gesture)
400 if self.config.getWebkitSupport():
401 if key=="ArchivedArticles":
402 self.view.open("file://" + self.link)
404 self.view.load_html_string(self.text, self.link) # "text/html", "utf-8", self.link)
405 self.view.set_zoom_level(float(config.getArtFontSize())/10.)
407 if not key == "ArchivedArticles":
408 # Do not download images if the feed is "Archived Articles"
409 self.document.connect("request-url", self._signal_request_url)
411 self.document.clear()
412 self.document.open_stream("text/html")
413 self.document.write_stream(self.text)
414 self.document.close_stream()
416 menu = hildon.AppMenu()
417 # Create a button and add it to the menu
418 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
419 button.set_label("Allow Horizontal Scrolling")
420 button.connect("clicked", self.horiz_scrolling_button)
423 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
424 button.set_label("Open in Browser")
425 button.connect("clicked", self._signal_link_clicked, self.link)
428 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
429 button.set_label("Add to Archived Articles")
430 button.connect("clicked", self.archive_button)
433 self.set_app_menu(menu)
436 #self.event_box = gtk.EventBox()
437 #self.event_box.add(self.pannable_article)
438 self.add(self.pannable_article)
441 self.pannable_article.show_all()
443 self.destroyId = self.connect("destroy", self.destroyWindow)
445 self.view.connect("button_press_event", self.button_pressed)
446 self.gestureId = self.view.connect("button_release_event", self.button_released)
447 #self.timeout_handler_id = gobject.timeout_add(300, self.reloadArticle)
449 def button_pressed(self, window, event):
450 #print event.x, event.y
451 self.coords = (event.x, event.y)
453 def button_released(self, window, event):
454 x = self.coords[0] - event.x
455 y = self.coords[1] - event.y
459 self.emit("article-previous", self.index)
461 self.emit("article-next", self.index)
465 #def gesture(self, widget, direction, startx, starty):
466 # if (direction == 3):
467 # self.emit("article-next", self.index)
468 # if (direction == 2):
469 # self.emit("article-previous", self.index)
470 #print startx, starty
471 #self.timeout_handler_id = gobject.timeout_add(200, self.destroyWindow)
473 def destroyWindow(self, *args):
474 self.disconnect(self.destroyId)
475 self.emit("article-closed", self.index)
476 self.imageDownloader.stopAll()
479 def horiz_scrolling_button(self, *widget):
480 self.pannable_article.disconnect(self.gestureId)
481 self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
483 def archive_button(self, *widget):
484 # Call the listing.addArchivedArticle
485 self.listing.addArchivedArticle(self.key, self.index)
487 #def reloadArticle(self, *widget):
488 # if threading.activeCount() > 1:
489 # Image thread are still running, come back in a bit
492 # for (stream, imageThread) in self.images:
494 # stream.write(imageThread.data)
499 def _signal_link_clicked(self, object, link):
500 bus = dbus.SystemBus()
501 proxy = bus.get_object("com.nokia.osso_browser", "/com/nokia/osso_browser/request")
502 iface = dbus.Interface(proxy, 'com.nokia.osso_browser')
505 def _signal_request_url(self, object, url, stream):
507 self.imageDownloader.queueImage(url, stream)
508 #imageThread = GetImage(url)
510 #self.images.append((stream, imageThread))
513 class DisplayFeed(hildon.StackableWindow):
514 def __init__(self, listing, feed, title, key, config):
515 hildon.StackableWindow.__init__(self)
516 self.listing = listing
518 self.feedTitle = title
519 self.set_title(title)
523 self.downloadDialog = False
525 self.listing.setCurrentlyDisplayedFeed(self.key)
529 menu = hildon.AppMenu()
530 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
531 button.set_label("Update Feed")
532 button.connect("clicked", self.button_update_clicked)
535 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
536 button.set_label("Mark All As Read")
537 button.connect("clicked", self.buttonReadAllClicked)
539 self.set_app_menu(menu)
544 self.connect("destroy", self.destroyWindow)
546 def destroyWindow(self, *args):
547 self.feed.saveUnread(CONFIGDIR)
548 self.listing.updateUnread(self.key, self.feed.getNumberOfUnreadItems())
549 self.emit("feed-closed", self.key)
551 #gobject.idle_add(self.feed.saveFeed, CONFIGDIR)
552 self.listing.closeCurrentlyDisplayedFeed()
554 def displayFeed(self):
555 self.vboxFeed = gtk.VBox(False, 10)
556 self.pannableFeed = hildon.PannableArea()
557 self.pannableFeed.add_with_viewport(self.vboxFeed)
558 self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
560 for id in self.feed.getIds():
561 button = gtk.Button(self.feed.getTitle(id))
562 button.set_alignment(0,0)
564 if self.feed.isEntryRead(id):
565 #label.modify_font(pango.FontDescription("sans 16"))
566 label.modify_font(pango.FontDescription(self.config.getReadFont()))
567 label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
569 #print self.listing.getFont() + " bold"
570 label.modify_font(pango.FontDescription(self.config.getUnreadFont()))
571 #label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("SkyBlue"))
572 #fg_color = fg_button.child.get_children()[0].get_children()[0].get_children()[1].get_style().fg[gtk.STATE_NORMAL]
573 label.modify_fg(gtk.STATE_NORMAL, fg_color)
574 #label.modify_font(pango.FontDescription("sans bold 23"))
576 label.set_line_wrap(True)
578 label.set_size_request(self.get_size()[0]-50, -1)
579 button.connect("clicked", self.button_clicked, id)
580 self.buttons[id] = button
582 self.vboxFeed.pack_start(button, expand=False)
584 self.add(self.pannableFeed)
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 stack = hildon.WindowStack.get_default()
595 stack.pop_and_push(1, newDisp, tmp)
597 gobject.timeout_add(200, self.destroyArticle, tmp)
602 #if not self.disp == False:
603 # self.disp.destroyWindow()
605 #print type(self.disp).__name__
607 #self.disp.destroyWindow()
608 #stack.pop_and_push(1,newDisp)
610 # stack.push(newDisp)
613 if type(self.disp).__name__ == "DisplayArticle":
614 gobject.timeout_add(200, self.destroyArticle, self.disp)
616 #self.disp.show_all()
617 #if not self.disp == False:
618 # self.disp.destroyWindow()
624 self.ids.append(self.disp.connect("article-closed", self.onArticleClosed))
625 self.ids.append(self.disp.connect("article-next", self.nextArticle))
626 self.ids.append(self.disp.connect("article-previous", self.previousArticle))
628 def destroyArticle(self, handle):
629 handle.destroyWindow()
631 def nextArticle(self, object, index):
632 label = self.buttons[index].child
633 label.modify_font(pango.FontDescription(self.config.getReadFont()))
634 label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
635 id = self.feed.getNextId(index)
636 self.button_clicked(object, id, next=True)
638 def previousArticle(self, object, index):
639 label = self.buttons[index].child
640 label.modify_font(pango.FontDescription(self.config.getReadFont()))
641 label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
642 id = self.feed.getPreviousId(index)
643 self.button_clicked(object, id, previous=True)
645 def onArticleClosed(self, object, index):
646 label = self.buttons[index].child
647 label.modify_font(pango.FontDescription(self.config.getReadFont()))
648 label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
649 self.buttons[index].show()
651 def button_update_clicked(self, button):
652 #bar = DownloadBar(self, self.listing, [self.key,], self.config )
653 if not type(self.downloadDialog).__name__=="DownloadBar":
654 self.pannableFeed.destroy()
655 self.vbox = gtk.VBox(False, 10)
656 self.downloadDialog = DownloadBar(self.window, self.listing, [self.key,], self.config, single=True )
657 self.downloadDialog.connect("download-done", self.onDownloadsDone)
658 self.vbox.pack_start(self.downloadDialog, expand=False, fill=False)
662 def onDownloadsDone(self, *widget):
664 self.feed = self.listing.getFeed(self.key)
666 #self.feed.updateFeed()
670 def buttonReadAllClicked(self, button):
671 for index in self.feed.getIds():
672 self.feed.setEntryRead(index)
673 label = self.buttons[index].child
674 label.modify_font(pango.FontDescription(self.config.getReadFont()))
675 label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
676 self.buttons[index].show()
682 self.window = hildon.StackableWindow()
683 self.window.set_title("FeedingIt")
684 hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
685 self.mainVbox = gtk.VBox(False,10)
686 self.pannableListing = gtk.Label("Loading...")
687 self.mainVbox.pack_start(self.pannableListing)
688 self.window.add(self.mainVbox)
689 self.window.show_all()
690 self.config = Config(self.window, CONFIGDIR+"config.ini", has_webkit)
691 gobject.idle_add(self.createWindow)
693 def createWindow(self):
694 self.listing = Listing(CONFIGDIR)
696 self.downloadDialog = False
697 self.orientation = FremantleRotation("FeedingIt", main_window=self.window)
698 self.orientation.set_mode(self.config.getOrientation())
700 menu = hildon.AppMenu()
701 # Create a button and add it to the menu
702 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
703 button.set_label("Update All Feeds")
704 button.connect("clicked", self.button_update_clicked, "All")
707 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
708 button.set_label("Mark All As Read")
709 button.connect("clicked", self.button_markAll)
712 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
713 button.set_label("Organize Feeds")
714 button.connect("clicked", self.button_organize_clicked)
717 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
718 button.set_label("Preferences")
719 button.connect("clicked", self.button_preferences_clicked)
722 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
723 button.set_label("Import Feeds")
724 button.connect("clicked", self.button_import_clicked)
727 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
728 button.set_label("Export Feeds")
729 button.connect("clicked", self.button_export_clicked)
732 self.window.set_app_menu(menu)
735 #self.feedWindow = hildon.StackableWindow()
736 #self.articleWindow = hildon.StackableWindow()
738 self.displayListing()
739 self.autoupdate = False
740 self.checkAutoUpdate()
741 hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
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)
750 def button_export_clicked(self, button):
751 opml = ExportOpmlData(self.window, self.listing)
753 def button_import_clicked(self, button):
754 opml = GetOpmlData(self.window)
755 feeds = opml.getData()
756 for (title, url) in feeds:
757 self.listing.addFeed(title, url)
758 self.displayListing()
760 def addFeed(self, urlIn="http://"):
761 wizard = AddWidgetWizard(self.window, urlIn)
764 (title, url) = wizard.getData()
765 if (not title == '') and (not url == ''):
766 self.listing.addFeed(title, url)
768 self.displayListing()
770 def button_organize_clicked(self, button):
771 org = SortList(self.window, self.listing)
774 self.listing.saveConfig()
775 self.displayListing()
777 def button_update_clicked(self, button, key):
778 if not type(self.downloadDialog).__name__=="DownloadBar":
779 self.downloadDialog = DownloadBar(self.window, self.listing, self.listing.getListOfFeeds(), self.config )
780 self.downloadDialog.connect("download-done", self.onDownloadsDone)
781 self.mainVbox.pack_end(self.downloadDialog, expand=False, fill=False)
782 self.mainVbox.show_all()
783 #self.displayListing()
785 def onDownloadsDone(self, *widget):
786 self.downloadDialog.destroy()
787 self.downloadDialog = False
788 #self.displayListing()
791 def button_preferences_clicked(self, button):
792 dialog = self.config.createDialog()
793 dialog.connect("destroy", self.prefsClosed)
795 def show_confirmation_note(self, parent, title):
796 note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
798 retcode = gtk.Dialog.run(note)
801 if retcode == gtk.RESPONSE_OK:
806 def displayListing(self):
808 self.mainVbox.remove(self.pannableListing)
811 self.vboxListing = gtk.VBox(False,10)
812 self.pannableListing = hildon.PannableArea()
813 self.pannableListing.add_with_viewport(self.vboxListing)
816 list = self.listing.getListOfFeeds()[:]
819 #button = gtk.Button(item)
820 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
821 hildon.BUTTON_ARRANGEMENT_VERTICAL)
822 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
823 + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
824 button.set_alignment(0,0,1,1)
825 button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
826 self.vboxListing.pack_start(button, expand=False)
827 self.buttons[key] = button
829 self.mainVbox.pack_start(self.pannableListing)
830 self.window.show_all()
832 def refreshList(self):
833 for key in self.listing.getListOfFeeds():
834 if self.buttons.has_key(key):
835 button = self.buttons[key]
836 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
837 + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
839 self.displayListing()
842 def buttonFeedClicked(widget, button, self, window, key):
843 disp = DisplayFeed(self.listing, self.listing.getFeed(key), self.listing.getFeedTitle(key), key, self.config)
844 disp.connect("feed-closed", self.onFeedClosed)
846 def onFeedClosed(self, object, key):
847 #self.displayListing()
848 self.listing.saveConfig()
850 #self.buttons[key].set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
851 # + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
852 #self.buttons[key].show()
855 self.window.connect("destroy", gtk.main_quit)
857 #for key in self.listing.getListOfFeeds():
858 # self.listing.getFeed(key).saveFeed(CONFIGDIR)
859 self.listing.saveConfig()
861 def prefsClosed(self, *widget):
862 self.orientation.set_mode(self.config.getOrientation())
863 self.checkAutoUpdate()
865 def checkAutoUpdate(self, *widget):
866 interval = int(self.config.getUpdateInterval()*3600000)
867 if self.config.isAutoUpdateEnabled():
868 if self.autoupdate == False:
869 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
870 self.autoupdate = interval
871 elif not self.autoupdate == interval:
872 # If auto-update is enabled, but not at the right frequency
873 gobject.source_remove(self.autoupdateId)
874 self.autoupdateId = gobject.timeout_add(interval, self.automaticUpdate)
875 self.autoupdate = interval
877 if not self.autoupdate == False:
878 gobject.source_remove(self.autoupdateId)
879 self.autoupdate = False
881 def automaticUpdate(self, *widget):
882 # Need to check for internet connection
883 # If no internet connection, try again in 10 minutes:
884 # gobject.timeout_add(int(5*3600000), self.automaticUpdate)
885 self.button_update_clicked(None, None)
890 for key in self.listing.getListOfFeeds():
891 if self.listing.getFeedNumberOfUnreadItems(key) > 0:
892 status += self.listing.getFeedTitle(key) + ": \t" + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items\n"
894 status = "No unread items"
897 if __name__ == "__main__":
898 gobject.signal_new("feed-closed", DisplayFeed, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
899 gobject.signal_new("article-closed", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
900 gobject.signal_new("article-next", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
901 gobject.signal_new("article-previous", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
902 gobject.signal_new("download-done", DownloadBar, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
903 gobject.threads_init()
904 if not isdir(CONFIGDIR):
908 print "Error: Can't create configuration directory"
911 dbusHandler = ServerObject(app)