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 # ============================================================================
27 import gtk, pickle, gobject, dbus
28 import hildondesktop, hildon
29 #from rss import Listing
31 # Create a session bus.
33 from dbus.mainloop.glib import DBusGMainLoop
34 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
35 #bus = dbus.SessionBus()
37 from os import environ, remove
38 bus = dbus.bus.BusConnection(environ["DBUS_SESSION_BUS_ADDRESS"])
39 from os.path import isfile
41 settings = gtk.settings_get_default()
42 color_style = gtk.rc_get_style_by_paths( settings, 'GtkButton', 'osso-logical-colors', gtk.Button)
43 active_color = color_style.lookup_color('ActiveTextColor')
44 default_color = color_style.lookup_color('DefaultTextColor')
45 font_desc = gtk.rc_get_style_by_paths(settings, 'HomeSystemFont', None, None).font_desc
49 CONFIGDIR="/home/user/.feedingit/"
50 SOURCE=CONFIGDIR + "source"
52 #DBusConnection *hd_home_plugin_item_get_dbus_connection ( HDHomePluginItem *item, DBusBusType type, DBusError *error);
54 #libc = ctypes.CDLL('libc.so.6')
55 #libc.printf('Hello world!')
57 def get_font_desc(logicalfontname):
58 settings = gtk.settings_get_default()
59 font_style = gtk.rc_get_style_by_paths(settings, logicalfontname, \
61 font_desc = font_style.font_desc
64 class FeedingItHomePlugin(hildondesktop.HomePluginItem):
67 'destroy' : 'override'
71 hildondesktop.HomePluginItem.__init__(self)
72 self.set_settings(True)
73 self.connect("show-settings", self.show_settings)
78 self.autoupdateId = int(file.read())
81 self.autoupdateId=False
83 vbox = gtk.VBox(False, 0)
85 #self.button = gtk.Button()
86 self.button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
87 self.button.set_text("FeedingIt","")
88 self.button.set_sensitive(False)
89 self.label1 = self.button.child.child.get_children()[0].get_children()[0]
90 self.label2 = self.button.child.child.get_children()[0].get_children()[1]
91 self.label1.modify_fg(gtk.STATE_INSENSITIVE, default_color)
92 self.label1.modify_font(font_desc)
93 self.label2.modify_fg(gtk.STATE_INSENSITIVE, active_color)
94 icon_theme = gtk.icon_theme_get_default()
95 pixbuf = icon_theme.load_icon("feedingit", 32, gtk.ICON_LOOKUP_USE_BUILTIN )
97 image.set_from_pixbuf(pixbuf)
98 self.button.set_image(image)
99 self.button.set_image_position(gtk.POS_LEFT)
101 #button = gtk.Button("Update")
102 self.button.connect("clicked", self.button_clicked)
104 vbox.pack_start(self.button, expand=False)
106 #for feed in ["Slashdot", "Engadget", "Cheez"]:
107 # self.treestore.append([feed, "0"])
108 self.treeview = gtk.TreeView()
110 name_renderer = gtk.CellRendererText()
111 name_renderer.set_property("font-desc", font_desc)
112 unread_renderer = gtk.CellRendererText()
113 unread_renderer.set_property("font-desc", font_desc)
114 self.treeview.append_column(gtk.TreeViewColumn('Feed Name', name_renderer, text = 0))
115 self.treeview.append_column(gtk.TreeViewColumn('Unread Items', unread_renderer, text = 1))
116 #selection = self.treeview.get_selection()
117 #selection.set_mode(gtk.SELECTION_NONE)
118 #self.treeview.get_selection().set_mode(gtk.SELECTION_NONE)
119 #hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview, gtk.HILDON_UI_MODE_NORMAL)
121 vbox.pack_start(self.treeview)
124 self.treeview.connect("hildon-row-tapped", self.row_activated)
125 #self.treeview.connect("cursor-changed", self.cursor_changed)
128 #gobject.timeout_add_seconds(30*60, self.update_list)
131 file = open("/home/user/feedingit_widget.log", "a")
132 traceback.print_exc(file=file)
135 def do_destroy(self):
136 #file = open("/home/user/.feedingit/feedingit_widget.log", "a")
137 #file.write("Do_destroy: ")
138 if (not self.autoupdateId==False):
139 gobject.source_remove(self.autoupdateId)
140 self.autoupdateId=False
141 #file.write("Destroyed %s\n" %self.autoupdateId)
143 hildondesktop.HomePluginItem.do_destroy(self)
144 #file.write("End destroy\n")
147 def button_clicked(self, *widget):
148 self.button.set_sensitive(False)
149 self.label1.modify_fg(gtk.STATE_NORMAL, default_color)
150 self.label2.modify_fg(gtk.STATE_NORMAL, active_color)
151 self.update_label("Stopping")
152 remote_object = bus.get_object("org.marcoz.feedingit", # Connection name
153 "/org/marcoz/feedingit/update" # Object's path
155 iface = dbus.Interface(remote_object, 'org.marcoz.feedingit')
158 def update_label(self, title, value=None):
159 self.button.set_title(title)
161 self.button.set_value(value)
163 self.button.set_value("")
165 def row_activated(self, treeview, treepath): #, column):
166 (model, iter) = self.treeview.get_selection().get_selected()
167 key = model.get_value(iter, 2)
168 # Create an object that will proxy for a particular remote object.
169 remote_object = bus.get_object("org.maemo.feedingit", # Connection name
170 "/org/maemo/feedingit" # Object's path
172 iface = dbus.Interface(remote_object, 'org.maemo.feedingit')
175 def update_list(self, *widget):
176 #listing = Listing(CONFIGDIR)
177 treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
179 if self.feed_list == {}:
182 if self.feed_list == None:
183 treestore.append(["Start Application", "", None])
184 self.update_label("No feeds added yet")
185 self.treeview.set_model(treestore)
189 oldtotal = self.total
191 #for key in listOfFeeds["feedingit-order"]:
192 for key in self.feed_list.keys():
194 file = open(CONFIGDIR+key+".d/unread", "r")
195 readItems = pickle.load( file )
198 for id in readItems.keys():
199 if readItems[id]==False:
200 countUnread = countUnread + 1
201 list.append([self.feed_list[key][0:18], countUnread, key])
202 self.total += countUnread
205 list = sorted(list, key=lambda item: item[1], reverse=True)
206 for item in list[0:8]:
207 treestore.append(item)
208 self.treeview.set_model(treestore)
209 if self.total > oldtotal:
210 self.update_label("%s Unread" %str(self.total), "%s more articles" %str(self.total-oldtotal))
212 self.update_label("%s Unread" %str(self.total))
215 def create_selector(self, choices, setting):
216 #self.pickerDialog = hildon.PickerDialog(self.parent)
217 selector = hildon.TouchSelector(text=True)
220 iter = selector.append_text(str(item))
221 if str(self.autoupdate) == str(item):
222 selector.set_active(0, index)
224 selector.connect("changed", self.selection_changed, setting)
225 #self.pickerDialog.set_selector(selector)
228 def selection_changed(self, selector, button, setting):
229 tmp = selector.get_current_text()
230 if tmp == "Disabled":
233 self.autoupdate = tmp
234 #current_selection = selector.get_current_text()
235 #if current_selection:
236 # self.config[setting] = current_selection
237 #gobject.idle_add(self.updateButton, setting)
240 def create_autoupdate_picker(self):
241 picker = hildon.PickerButton(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
242 selector = self.create_selector(["Disabled", 0.5, 1, 2, 4, 12, 24], "autoupdate")
243 picker.set_selector(selector)
244 picker.set_title("Frequency of updates from the widget")
245 picker.set_text("Setup Feed Auto-updates","Update every %s hours" %str(self.autoupdate) )
246 picker.set_name('HildonButton-finger')
247 picker.set_alignment(0,0,1,1)
248 #self.buttons[setting] = picker
249 #vbox.pack_start(picker, expand=False)
252 def show_settings(self, widget):
253 if isfile(CONFIGDIR+"feeds.pickle"):
254 file = open(CONFIGDIR+"feeds.pickle")
255 listOfFeeds = pickle.load(file)
257 dialog = gtk.Dialog("Choose feeds to display", None, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
259 self.pannableArea = hildon.PannableArea()
261 #self.treestore_settings = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
262 self.treeview_settings = gtk.TreeView()
264 self.treeview_settings.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
265 hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview_settings, gtk.HILDON_UI_MODE_EDIT)
266 dialog.vbox.pack_start(self.pannableArea)
268 self.treeview_settings.append_column(gtk.TreeViewColumn('Feed Name', gtk.CellRendererText(), text = 0))
269 self.treestore_settings = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
270 self.treeview_settings.set_model(self.treestore_settings)
272 for key in listOfFeeds["feedingit-order"]:
273 title = listOfFeeds[key]["title"]
274 item = self.treestore_settings.append([title, key])
275 if key in self.feed_list:
276 self.treeview_settings.get_selection().select_iter(item)
278 self.pannableArea.add(self.treeview_settings)
279 self.pannableArea.show_all()
280 dialog.set_default_size(-1, 600)
282 dialog.action_area.pack_start(self.create_autoupdate_picker())
285 response = dialog.run()
287 if response == gtk.RESPONSE_ACCEPT:
288 self.feed_list = self.getItems()
293 dialog = gtk.Dialog("Please add feeds first", None, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
294 label = gtk.Label("Please add feeds through the main application")
295 dialog.vbox.pack_start(label)
297 response = dialog.run()
299 #self.treeview_settings.get_selection().select_all()
303 treeselection = self.treeview_settings.get_selection()
304 (model, pathlist) = treeselection.get_selected_rows()
305 for path in pathlist:
306 list[model.get_value(model.get_iter(path),1)] = model.get_value(model.get_iter(path),0)
310 bus.add_signal_receiver(self.update_list, dbus_interface="org.marcoz.feedingit",
311 signal_name="ArticleCountUpdated", path="/org/marcoz/feedingit/update")
312 bus.add_signal_receiver(self.update_started, dbus_interface="org.marcoz.feedingit",
313 signal_name="UpdateStarted", path="/org/marcoz/feedingit/update")
314 bus.add_signal_receiver(self.update_finished, dbus_interface="org.marcoz.feedingit",
315 signal_name="UpdateFinished", path="/org/marcoz/feedingit/update")
317 def update_started(self, *widget):
318 self.button.set_sensitive(True)
319 self.update_label("Updating...", "Click to stop update")
321 def update_finished(self, *widget):
322 self.button.set_sensitive(False)
323 self.update_label("Update done")
325 def start_update(self):
327 if self.autoupdate >0:
328 #file = open("/home/user/.feedingit/feedingit_widget.log", "a")
329 #from time import localtime, strftime
331 #file.write("Widget: pid:%s ppid:%s time:%s\n" % (os.getpid(), os.getppid(), strftime("%a, %d %b %Y %H:%M:%S +0000", localtime())))
333 remote_object = bus.get_object("org.marcoz.feedingit", # Connection name
334 "/org/marcoz/feedingit/update" # Object's path
336 iface = dbus.Interface(remote_object, 'org.marcoz.feedingit')
341 file = open("/home/user/.feedingit/feedingit_widget.log", "a")
342 traceback.print_exc(file=file)
345 def save_config(self):
346 from os.path import isdir
347 if not isdir(CONFIGDIR):
350 file = open(CONFIGDIR+"widget", "w")
351 pickle.dump(self.feed_list, file )
352 pickle.dump(self.autoupdate, file)
354 self.setup_autoupdate()
356 def setup_autoupdate(self):
357 if (float(self.autoupdate) > 0):
358 if (not self.autoupdateId==False):
359 #file = open("/home/user/.feedingit/feedingit_widget.log", "a")
360 #file.write("Disabling %s\n" % self.autoupdateId)
362 gobject.source_remove(self.autoupdateId)
364 self.autoupdateId = gobject.timeout_add_seconds(int(float(self.autoupdate)*3600), self.start_update)
365 file = open(SOURCE, "w")
366 file.write(str(self.autoupdateId))
368 #file = open("/home/user/.feedingit/feedingit_widget.log", "a")
369 #file.write("Started %s\n" % self.autoupdateId)
372 if (not self.autoupdateId==False):
373 gobject.source_remove(self.autoupdateId)
374 self.autoupdateId=False
377 def load_config(self):
378 if isfile(CONFIGDIR+"widget"):
379 file = open(CONFIGDIR+"widget", "r")
380 self.feed_list = pickle.load( file )
381 self.autoupdate = pickle.load( file )
383 self.setup_autoupdate()
384 elif isfile(CONFIGDIR+"feeds.pickle"):
385 file = open(CONFIGDIR+"feeds.pickle")
386 listOfFeeds = pickle.load(file)
389 #self.feed_list = listOfFeeds["feedingit-order"]
390 for key in listOfFeeds["feedingit-order"]:
391 self.feed_list[key] = listOfFeeds[key]["title"]
395 self.feed_list = None
398 hd_plugin_type = FeedingItHomePlugin
400 # The code below is just for testing purposes.
401 # It allows to run the widget as a standalone process.
402 if __name__ == "__main__":
404 gobject.type_register(hd_plugin_type)
405 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")