9a19a87650cb07be5e55588b0fe0e6defbfe0c6f
[feedingit] / src / feedingit_widget.py
1 #!/usr/bin/env python2.5
2
3
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.
9 #
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.
14 #
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/>.
17 #
18
19 # ============================================================================
20 # Name        : FeedingIt.py
21 # Author      : Yves Marcoz
22 # Version     : 0.6.0
23 # Description : Simple RSS Reader
24 # ============================================================================
25 #import sys
26
27 import gtk, pickle, gobject, dbus
28 import hildondesktop, hildon
29 #from rss import Listing
30
31 # Create a session bus.
32 import dbus
33 from dbus.mainloop.glib import DBusGMainLoop
34 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
35 #bus = dbus.SessionBus()
36
37 from os import environ
38 bus = dbus.bus.BusConnection(environ["DBUS_SESSION_BUS_ADDRESS"])
39
40 color_style = gtk.rc_get_style_by_paths(gtk.settings_get_default() , 'GtkButton', 'osso-logical-colors', gtk.Button)
41 active_color = color_style.lookup_color('ActiveTextColor')
42 default_color = color_style.lookup_color('DefaultTextColor')
43 del color_style
44
45 CONFIGDIR="/home/user/.feedingit/"
46
47 #DBusConnection *hd_home_plugin_item_get_dbus_connection ( HDHomePluginItem *item, DBusBusType type, DBusError *error);
48 #import ctypes
49 #libc = ctypes.CDLL('libc.so.6')
50 #libc.printf('Hello world!')
51
52 class FeedingItHomePlugin(hildondesktop.HomePluginItem):
53     def __init__(self):
54       try:
55         hildondesktop.HomePluginItem.__init__(self)
56         self.set_settings(True)
57         self.connect("show-settings", self.show_settings)
58         self.feed_list = {}
59         self.total = 0
60         self.autoupdateID=False
61         
62         vbox = gtk.VBox(False, 0)
63         
64         #self.button = gtk.Button()
65         self.button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
66         self.button.set_text("FeedingIt","")
67         self.button.set_sensitive(False)
68         self.label1 = self.button.child.child.get_children()[0].get_children()[0]
69         self.label2 = self.button.child.child.get_children()[0].get_children()[1]
70         self.label1.modify_fg(gtk.STATE_INSENSITIVE, default_color)
71         self.label2.modify_fg(gtk.STATE_INSENSITIVE, active_color)
72         icon_theme = gtk.icon_theme_get_default()
73         pixbuf = icon_theme.load_icon("feedingit", 48, gtk.ICON_LOOKUP_USE_BUILTIN )
74         image = gtk.Image()
75         image.set_from_pixbuf(pixbuf)
76         self.button.set_image(image)
77         self.button.set_image_position(gtk.POS_LEFT)
78
79         #button = gtk.Button("Update")
80         self.button.connect("clicked", self.button_clicked)
81         #button.show_all()
82         vbox.pack_start(self.button, expand=False)        
83         
84         #for feed in ["Slashdot", "Engadget", "Cheez"]:
85         #    self.treestore.append([feed, "0"])
86         self.treeview = gtk.TreeView()
87         self.update_list()
88         self.treeview.append_column(gtk.TreeViewColumn('Feed Name', gtk.CellRendererText(), text = 0))
89         self.treeview.append_column(gtk.TreeViewColumn('Unread Items', gtk.CellRendererText(), text = 1))
90         #self.treeview.get_selection().set_mode(gtk.SELECTION_NONE)
91         #hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview, gtk.HILDON_UI_MODE_NORMAL)
92         
93         vbox.pack_start(self.treeview)
94         
95         self.add(vbox)
96         self.treeview.connect("row-activated", self.row_activated)
97         vbox.show_all()
98         self.setupDbus()
99         #gobject.timeout_add_seconds(30*60, self.update_list)
100       except:
101           import traceback
102           file = open("/home/user/.feedingit/feedingit_widget.log", "a")
103           traceback.print_exc(file=file)
104           file.close()
105
106     def button_clicked(self, *widget):
107         self.button.set_sensitive(False)
108         self.label1.modify_fg(gtk.STATE_NORMAL, default_color)
109         self.label2.modify_fg(gtk.STATE_NORMAL, active_color)
110         self.update_label("Stopping")
111         remote_object = bus.get_object("org.marcoz.feedingit", # Connection name
112                                "/org/marcoz/feedingit/update" # Object's path
113                               )
114         iface = dbus.Interface(remote_object, 'org.marcoz.feedingit')
115         iface.StopUpdate()
116         
117     def update_label(self, title, value=None):
118         self.button.set_title(title)
119         if value != None:
120             self.button.set_value(value)
121         else:
122             self.button.set_value("")
123
124     def row_activated(self, treeview, treepath, column):
125         (model, iter) = self.treeview.get_selection().get_selected()
126         key = model.get_value(iter, 2)
127         # Create an object that will proxy for a particular remote object.
128         remote_object = bus.get_object("org.maemo.feedingit", # Connection name
129                                "/org/maemo/feedingit" # Object's path
130                               )
131         iface = dbus.Interface(remote_object, 'org.maemo.feedingit')
132         iface.OpenFeed(key)
133
134     def update_list(self, *widget):
135         #listing = Listing(CONFIGDIR)
136         treestore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
137         
138         if self.feed_list == {}:
139             self.load_config()
140
141         list = []
142         oldtotal = self.total
143         self.total = 0
144         #for key in listOfFeeds["feedingit-order"]:
145         for key in self.feed_list.keys():
146             try:
147                 file = open(CONFIGDIR+key+".d/unread", "r")
148                 readItems = pickle.load( file )
149                 file.close()
150                 countUnread = 0
151                 for id in readItems.keys():
152                     if readItems[id]==False:
153                         countUnread = countUnread + 1
154                 list.append([self.feed_list[key], countUnread, key])
155                 self.total += countUnread
156             except:
157                 pass
158         list = sorted(list, key=lambda item: item[1], reverse=True)
159         for item in list[0:8]:
160             treestore.append(item)
161         self.treeview.set_model(treestore)
162         self.treeview.get_selection().unselect_all()
163         if self.total > oldtotal:
164             self.update_label("%s Unread" %str(self.total), "%s more articles" %str(self.total-oldtotal))
165         else:
166             self.update_label("%s Unread" %str(self.total))
167         return True
168
169     def create_selector(self, choices, setting):
170         #self.pickerDialog = hildon.PickerDialog(self.parent)
171         selector = hildon.TouchSelector(text=True)
172         index = 0
173         for item in choices:
174             iter = selector.append_text(str(item))
175             if str(self.autoupdate) == str(item): 
176                 selector.set_active(0, index)
177             index += 1
178         selector.connect("changed", self.selection_changed, setting)
179         #self.pickerDialog.set_selector(selector)
180         return selector
181         
182     def selection_changed(self, selector, button, setting):
183         tmp = selector.get_current_text()
184         if tmp == "Disabled":
185             self.autoupdate = 0
186         else:
187             self.autoupdate = tmp
188         #current_selection = selector.get_current_text()
189         #if current_selection:
190         #    self.config[setting] = current_selection
191         #gobject.idle_add(self.updateButton, setting)
192         #self.saveConfig()
193         
194     def create_autoupdate_picker(self):
195             picker = hildon.PickerButton(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
196             selector = self.create_selector(["Disabled", 0.02, 0.5, 1, 2, 4, 12, 24], "autoupdate")
197             picker.set_selector(selector)
198             picker.set_title("Frequency of updates from the widget")
199             picker.set_text("Setup Feed Auto-updates","Update every %s hours" %str(self.autoupdate) )
200             picker.set_name('HildonButton-finger')
201             picker.set_alignment(0,0,1,1)
202             #self.buttons[setting] = picker
203             #vbox.pack_start(picker, expand=False)
204             return picker
205         
206     def show_settings(self, widget):
207         file = open(CONFIGDIR+"feeds.pickle")
208         listOfFeeds = pickle.load(file)
209         file.close()
210         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))
211
212         self.pannableArea = hildon.PannableArea()
213         
214         #self.treestore_settings = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
215         self.treeview_settings = gtk.TreeView()
216         
217         self.treeview_settings.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
218         hildon.hildon_gtk_tree_view_set_ui_mode(self.treeview_settings, gtk.HILDON_UI_MODE_EDIT)
219         dialog.vbox.pack_start(self.pannableArea)
220         
221         self.treeview_settings.append_column(gtk.TreeViewColumn('Feed Name', gtk.CellRendererText(), text = 0))
222         self.treestore_settings = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
223         self.treeview_settings.set_model(self.treestore_settings)
224         
225         for key in listOfFeeds["feedingit-order"]:
226             title = listOfFeeds[key]["title"]
227             item = self.treestore_settings.append([title, key])
228             if key in self.feed_list:
229                 self.treeview_settings.get_selection().select_iter(item)
230             
231         self.pannableArea.add(self.treeview_settings)
232         self.pannableArea.show_all()
233         dialog.set_default_size(-1, 600)
234         
235         dialog.action_area.pack_start(self.create_autoupdate_picker())
236         
237         dialog.show_all()
238         response = dialog.run()
239
240         if response == gtk.RESPONSE_ACCEPT:
241             self.feed_list = self.getItems()
242         dialog.destroy()
243         self.save_config()
244         self.update_list()
245         #self.treeview_settings.get_selection().select_all()
246         
247     def getItems(self):
248         list = {}
249         treeselection = self.treeview_settings.get_selection()
250         (model, pathlist) = treeselection.get_selected_rows()
251         for path in pathlist:
252             list[model.get_value(model.get_iter(path),1)] = model.get_value(model.get_iter(path),0)
253         return list
254         
255     def setupDbus(self):
256         bus.add_signal_receiver(self.update_list, dbus_interface="org.marcoz.feedingit",
257                         signal_name="ArticleCountUpdated", path="/org/marcoz/feedingit/update")
258         bus.add_signal_receiver(self.update_started, dbus_interface="org.marcoz.feedingit",
259                         signal_name="UpdateStarted", path="/org/marcoz/feedingit/update")
260         bus.add_signal_receiver(self.update_finished, dbus_interface="org.marcoz.feedingit",
261                         signal_name="UpdateFinished", path="/org/marcoz/feedingit/update")
262
263     def update_started(self, *widget):
264         self.button.set_sensitive(True)
265         self.update_label("Updating...", "Click to stop update")
266
267     def update_finished(self, *widget):
268         self.button.set_sensitive(False)
269         self.update_label("Update done")
270         
271     def start_update(self):
272         try:
273             if self.autoupdate >0:
274                 file = open("/home/user/.feedingit/feedingit_widget.log", "a")
275                 from time import localtime, strftime
276                 file.write("Widget: %s\n" % strftime("%a, %d %b %Y %H:%M:%S +0000", localtime()))
277                 file.close()
278                 remote_object = bus.get_object("org.marcoz.feedingit", # Connection name
279                               "/org/marcoz/feedingit/update" # Object's path
280                               )
281                 iface = dbus.Interface(remote_object, 'org.marcoz.feedingit')
282                 iface.UpdateAll()
283             return True
284         except:
285             import traceback
286             file = open("/home/user/.feedingit/feedingit_widget.log", "a")
287             traceback.print_exc(file=file)
288             file.close()
289
290     def save_config(self):
291             from os.path import isdir
292             if not isdir(CONFIGDIR):
293                 from os import mkdir
294                 mkdir(CONFIGDIR)
295             file = open(CONFIGDIR+"widget", "w")
296             pickle.dump(self.feed_list, file )
297             pickle.dump(self.autoupdate, file)
298             file.close()
299             self.setup_autoupdate()
300
301     def setup_autoupdate(self):
302         if (float(self.autoupdate) > 0):
303             if (not self.autoupdateID==False):
304                 gobject.disconnect(self.autoupdateId)
305             self.autoupdateId = gobject.timeout_add_seconds(int(float(self.autoupdate)*3600), self.start_update)
306         else:
307             if (not self.autoupdateID==False):
308                 gobject.disconnect(self.autoupdateId)
309                 self.autoupdateID=False
310
311     def load_config(self):
312             try:
313                 file = open(CONFIGDIR+"widget", "r")
314                 self.feed_list = pickle.load( file )
315                 self.autoupdate = pickle.load( file )
316                 file.close()
317                 self.setup_autoupdate()
318             except:
319                 file = open(CONFIGDIR+"feeds.pickle")
320                 listOfFeeds = pickle.load(file)
321                 file.close()
322             
323                 #self.feed_list = listOfFeeds["feedingit-order"]
324                 for key in listOfFeeds["feedingit-order"]:
325                     self.feed_list[key] = listOfFeeds[key]["title"]
326                 del listOfFeeds
327                 self.autoupdate = 0
328
329
330 hd_plugin_type = FeedingItHomePlugin
331
332 # The code below is just for testing purposes.
333 # It allows to run the widget as a standalone process.
334 if __name__ == "__main__":
335     import gobject
336     gobject.type_register(hd_plugin_type)
337     obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
338     obj.show_all()
339     gtk.main()