#
# Copyright (c) 2007-2008 INdT.
+# Copyright (c) 2011 Neal H. Walfield
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
from updatedbus import UpdateServerObject, get_lock
from config import Config
from cgi import escape
+import weakref
from rss_sqlite import Listing
from opml import GetOpmlData, ExportOpmlData
-from urllib2 import install_opener, build_opener
+import mainthread
+from jobmanager import JobManager
from socket import setdefaulttimeout
timeout = 5
def getData(self):
return self.nameEntry.get_text()
-class Download(Thread):
- def __init__(self, listing, key, config):
- Thread.__init__(self)
- self.listing = listing
- self.key = key
- self.config = config
-
- def run (self):
- (use_proxy, proxy) = self.config.getProxy()
- key_lock = get_lock(self.key)
- if key_lock != None:
- if use_proxy:
- self.listing.updateFeed(self.key, self.config.getExpiry(), proxy=proxy, imageCache=self.config.getImageCache() )
- else:
- self.listing.updateFeed(self.key, self.config.getExpiry(), imageCache=self.config.getImageCache() )
- del key_lock
-
-
class DownloadBar(gtk.ProgressBar):
- def __init__(self, parent, listing, listOfKeys, config, single=False):
-
- update_lock = get_lock("update_lock")
- if update_lock != None:
- gtk.ProgressBar.__init__(self)
- self.listOfKeys = listOfKeys[:]
- self.listing = listing
- self.total = len(self.listOfKeys)
- self.config = config
- self.current = 0
- self.single = single
- (use_proxy, proxy) = self.config.getProxy()
- if use_proxy:
- opener = build_opener(proxy)
- else:
- opener = build_opener()
-
- opener.addheaders = [('User-agent', USER_AGENT)]
- install_opener(opener)
-
- if self.total>0:
- # In preparation for i18n/l10n
- def N_(a, b, n):
- return (a if n == 1 else b)
-
- self.set_text(N_('Updating %d feed', 'Updating %d feeds', self.total) % self.total)
-
- self.fraction = 0
- self.set_fraction(self.fraction)
- self.show_all()
- # Create a timeout
- self.timeout_handler_id = gobject.timeout_add(50, self.update_progress_bar)
-
- def update_progress_bar(self):
- #self.progress_bar.pulse()
- if activeCount() < 4:
- x = activeCount() - 1
- k = len(self.listOfKeys)
- fin = self.total - k - x
- fraction = float(fin)/float(self.total) + float(x)/(self.total*2.)
- #print x, k, fin, fraction
- self.set_fraction(fraction)
-
- if len(self.listOfKeys)>0:
- self.current = self.current+1
- key = self.listOfKeys.pop()
- #if self.single == True:
- # Check if the feed is being displayed
- download = Download(self.listing, key, self.config)
- download.start()
- return True
- elif activeCount() > 1:
- return True
+ @classmethod
+ def class_init(cls):
+ if hasattr (cls, 'class_init_done'):
+ return
+
+ jm = JobManager ()
+ jm.stats_hook_register (cls.update_progress,
+ run_in_main_thread=True)
+
+ cls.downloadbars = []
+ # Total number of jobs we are monitoring.
+ cls.total = 0
+ # Number of jobs complete (of those that we are monitoring).
+ cls.done = 0
+ # Percent complete.
+ cls.progress = 0
+
+ cls.class_init_done = True
+
+ def __init__(self, parent):
+ self.class_init ()
+
+ gtk.ProgressBar.__init__(self)
+
+ self.downloadbars.append(weakref.ref (self))
+ self.set_fraction(0)
+ self.__class__.update_bars()
+ self.show_all()
+
+ @classmethod
+ def downloading(cls):
+ return hasattr (cls, 'jobs_at_start')
+
+ @classmethod
+ def update_progress(cls, jm, old_stats, new_stats, updated_feed):
+ if not cls.downloading():
+ cls.jobs_at_start = old_stats['jobs-completed']
+
+ if not cls.downloadbars:
+ return
+
+ if new_stats['jobs-in-progress'] + new_stats['jobs-queued'] == 0:
+ del cls.jobs_at_start
+ for ref in cls.downloadbars:
+ bar = ref ()
+ if bar is None:
+ # The download bar disappeared.
+ cls.downloadbars.remove (ref)
+ else:
+ bar.emit("download-done", None)
+ return
+
+ # This should never be called if new_stats['jobs'] is 0, but
+ # just in case...
+ cls.total = max (1, new_stats['jobs'] - cls.jobs_at_start)
+ cls.done = new_stats['jobs-completed'] - cls.jobs_at_start
+ cls.progress = 1 - (new_stats['jobs-in-progress'] / 2.
+ + new_stats['jobs-queued']) / cls.total
+ cls.update_bars()
+
+ if updated_feed:
+ for ref in cls.downloadbars:
+ bar = ref ()
+ if bar is None:
+ # The download bar disappeared.
+ cls.downloadbars.remove (ref)
+ else:
+ bar.emit("download-done", updated_feed)
+
+ @classmethod
+ def update_bars(cls):
+ # In preparation for i18n/l10n
+ def N_(a, b, n):
+ return (a if n == 1 else b)
+
+ text = (N_('Updated %d of %d feeds ', 'Updated %d of %d feeds',
+ cls.total)
+ % (cls.done, cls.total))
+
+ for ref in cls.downloadbars:
+ bar = ref ()
+ if bar is None:
+ # The download bar disappeared.
+ cls.downloadbars.remove (ref)
else:
- #self.waitingWindow.destroy()
- #self.destroy()
- try:
- del self.update_lock
- except:
- pass
- self.emit("download-done", "success")
- return False
- return True
-
-
+ bar.set_text(text)
+ bar.set_fraction(cls.progress)
+
class SortList(hildon.StackableWindow):
def __init__(self, parent, listing, feedingit, after_closing, category=None):
hildon.StackableWindow.__init__(self)
self.set_app_menu(menu)
menu.show_all()
+ self.main_vbox = gtk.VBox(False, 0)
+ self.add(self.main_vbox)
+
+ self.pannableFeed = None
self.displayFeed()
+
+ if DownloadBar.downloading ():
+ self.show_download_bar ()
self.connect('configure-event', self.on_configure_event)
self.connect("destroy", self.destroyWindow)
return escape(unescape(title).replace("<em>","").replace("</em>","").replace("<nobr>","").replace("</nobr>","").replace("<wbr>",""))
def displayFeed(self):
+ if self.pannableFeed:
+ self.pannableFeed.destroy()
+
self.pannableFeed = hildon.PannableArea()
self.pannableFeed.set_property('hscrollbar-policy', gtk.POLICY_NEVER)
markup = ENTRY_TEMPLATE % (self.config.getFontSize(), "No Articles To Display")
self.feedItems.append((markup, ""))
- self.add(self.pannableFeed)
+ self.main_vbox.pack_start(self.pannableFeed)
self.show_all()
def clear(self):
self.displayFeed()
def button_update_clicked(self, button):
- #bar = DownloadBar(self, self.listing, [self.key,], self.config )
+ self.listing.updateFeed (self.key, priority=-1)
+
+ def show_download_bar(self):
if not type(self.downloadDialog).__name__=="DownloadBar":
- self.pannableFeed.destroy()
- self.vbox = gtk.VBox(False, 10)
- self.downloadDialog = DownloadBar(self.window, self.listing, [self.key,], self.config, single=True )
- self.downloadDialog.connect("download-done", self.onDownloadsDone)
- self.vbox.pack_start(self.downloadDialog, expand=False, fill=False)
- self.add(self.vbox)
+ self.downloadDialog = DownloadBar(self.window)
+ self.downloadDialog.connect("download-done", self.onDownloadDone)
+ self.main_vbox.pack_end(self.downloadDialog,
+ expand=False, fill=False)
self.show_all()
-
- def onDownloadsDone(self, *widget):
- self.vbox.destroy()
- self.feed = self.listing.getFeed(self.key)
- self.displayFeed()
- self.updateDbusHandler.ArticleCountUpdated()
+ def onDownloadDone(self, widget, feed):
+ if feed == self.feed or feed is None:
+ self.downloadDialog.destroy()
+ self.downloadDialog = False
+ self.feed = self.listing.getFeed(self.key)
+ self.displayFeed()
+ self.updateDbusHandler.ArticleCountUpdated()
+
def buttonReadAllClicked(self, button):
#self.clear()
self.feed.markAllAsRead()
self.stopButton.destroy()
except:
pass
- self.listing = Listing(CONFIGDIR)
-
+ self.listing = Listing(self.config, CONFIGDIR)
+
self.downloadDialog = False
try:
self.orientation = FremantleRotation(__appname__, main_window=self.window, app=self)
self.checkAutoUpdate()
hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
- gobject.idle_add(self.enableDbus)
+ gobject.idle_add(self.late_init)
+ def job_manager_update(self, jm, old_stats, new_stats, updated_feed):
+ if (not self.downloadDialog
+ and new_stats['jobs-in-progress'] + new_stats['jobs-queued'] > 0):
+ self.updateDbusHandler.UpdateStarted()
+
+ self.downloadDialog = DownloadBar(self.window)
+ self.downloadDialog.connect("download-done", self.onDownloadDone)
+ self.mainVbox.pack_end(self.downloadDialog, expand=False, fill=False)
+ self.mainVbox.show_all()
+
+ if self.__dict__.get ('disp', None):
+ self.disp.show_download_bar ()
+
+ def onDownloadDone(self, widget, feed):
+ if feed is None:
+ self.downloadDialog.destroy()
+ self.downloadDialog = False
+ self.displayListing()
+ self.updateDbusHandler.UpdateFinished()
+ self.updateDbusHandler.ArticleCountUpdated()
+
def stop_running_update(self, button):
self.stopButton.set_sensitive(False)
import dbus
iface = dbus.Interface(remote_object, 'org.marcoz.feedingit')
iface.StopUpdate()
- def enableDbus(self):
+ def late_init(self):
self.dbusHandler = ServerObject(self)
self.updateDbusHandler = UpdateServerObject(self)
+ jm = JobManager()
+ jm.stats_hook_register (self.job_manager_update,
+ run_in_main_thread=True)
+ JobManager(True)
+
def button_markAll(self, button):
for key in self.listing.getListOfFeeds():
feed = self.listing.getFeed(key)
SortList(self.window, self.listing, self, after_closing)
def button_update_clicked(self, button, key):
- if not type(self.downloadDialog).__name__=="DownloadBar":
- self.updateDbusHandler.UpdateStarted()
- self.downloadDialog = DownloadBar(self.window, self.listing, self.listing.getListOfFeeds(), self.config )
- self.downloadDialog.connect("download-done", self.onDownloadsDone)
- self.mainVbox.pack_end(self.downloadDialog, expand=False, fill=False)
- self.mainVbox.show_all()
+ for k in self.listing.getListOfFeeds():
+ self.listing.updateFeed (k)
#self.displayListing()
def onDownloadsDone(self, *widget):
def onFeedClosedTimeout(self):
del self.feed_lock
self.updateDbusHandler.ArticleCountUpdated()
-
+
+ def quit(self, *args):
+ self.window.hide()
+
+ if hasattr (self, 'app_lock'):
+ del self.app_lock
+
+ # Wait until all slave threads have properly exited before
+ # terminating the mainloop.
+ jm = JobManager()
+ jm.quit ()
+ stats = jm.stats()
+ if stats['jobs-in-progress'] == 0 and stats['jobs-queued'] == 0:
+ gtk.main_quit ()
+ else:
+ gobject.timeout_add(500, self.quit)
+
+ return False
+
def run(self):
- self.window.connect("destroy", gtk.main_quit)
+ self.window.connect("destroy", self.quit)
gtk.main()
- del self.app_lock
def prefsClosed(self, *widget):
try:
def stopUpdate(self):
# Not implemented in the app (see update_feeds.py)
try:
- self.downloadDialog.listOfKeys = []
+ JobManager().cancel ()
except:
pass
return status
if __name__ == "__main__":
+ mainthread.init ()
+
gobject.signal_new("feed-closed", DisplayFeed, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
gobject.signal_new("article-closed", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
gobject.signal_new("article-deleted", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))