Code cleanup
[feedingit] / src / FeedingIt.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.1.1
23 # Description : PyGtk Example 
24 # ============================================================================
25
26 import gtk
27 import feedparser
28 import pango
29 import hildon
30 import gtkhtml2
31 import time
32 import webbrowser
33 import pickle
34 from os.path import isfile, isdir
35 from os import mkdir
36 import sys   
37 import urllib2
38 import gobject
39 from portrait import FremantleRotation
40
41 from rss import *
42    
43 class AddWidgetWizard(hildon.WizardDialog):
44     
45     def __init__(self, parent):
46         # Create a Notebook
47         self.notebook = gtk.Notebook()
48
49         self.nameEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
50         self.nameEntry.set_placeholder("Enter Feed Name")
51         
52         self.urlEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
53         
54         self.urlEntry.set_placeholder("Enter a URL")
55             
56         labelEnd = gtk.Label("Success")
57         
58         self.notebook.append_page(self.nameEntry, None)
59         self.notebook.append_page(self.urlEntry, None) 
60         self.notebook.append_page(labelEnd, None)      
61
62         hildon.WizardDialog.__init__(self, parent, "Add Feed", self.notebook)
63    
64         # Set a handler for "switch-page" signal
65         #self.notebook.connect("switch_page", self.on_page_switch, self)
66    
67         # Set a function to decide if user can go to next page
68         self.set_forward_page_func(self.some_page_func)
69    
70         self.show_all()
71         
72     def getData(self):
73         return (self.nameEntry.get_text(), self.urlEntry.get_text())
74         
75     def on_page_switch(self, notebook, page, num, dialog):
76         return True
77    
78     def some_page_func(self, nb, current, userdata):
79         # Validate data for 1st page
80         if current == 0:
81             entry = nb.get_nth_page(current)
82             # Check the name is not null
83             return len(entry.get_text()) != 0
84         elif current == 1:
85             entry = nb.get_nth_page(current)
86             # Check the url is not null, and starts with http
87             return ( (len(entry.get_text()) != 0) and (entry.get_text().lower().startswith("http")) )
88         elif current != 2:
89             return False
90         else:
91             return True
92
93 class DisplayArticle(hildon.StackableWindow):
94     def __init__(self, title, text):
95         hildon.StackableWindow.__init__(self)
96         self.text = text
97         self.set_title(title)
98
99         # Init the article display    
100         self.view = gtkhtml2.View()
101         self.pannable_article = hildon.PannableArea()
102         self.pannable_article.add(self.view)
103         self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
104         self.document = gtkhtml2.Document()
105         self.view.set_document(self.document)
106         
107         self.document.clear()
108         self.document.open_stream("text/html")
109         self.document.write_stream(self.text)
110         self.document.close_stream()
111         
112         menu = hildon.AppMenu()
113         # Create a button and add it to the menu
114         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
115         button.set_label("Display Images")
116         button.connect("clicked", self.reloadArticle)
117         menu.append(button)
118         self.set_app_menu(menu)
119         menu.show_all()
120         
121         self.add(self.pannable_article)
122         
123         self.show_all()
124         
125         self.document.connect("link_clicked", self._signal_link_clicked)
126         self.document.connect("request-url", self._signal_request_url)
127         #self.document.connect("ReloadArticle", self.reloadArticle)
128         self.connect("destroy", self.destroyWindow)
129         
130     def destroyWindow(self, *args):
131         self.destroy()
132         
133     def reloadArticle(self, widget):
134         self.document.open_stream("text/html")
135         self.document.write_stream(self.text)
136         self.document.close_stream()
137
138     def _signal_link_clicked(self, object, link):
139         webbrowser.open(link)
140
141     def _signal_request_url(self, object, url, stream):
142         f = urllib2.urlopen(url)
143         stream.write(f.read())
144         stream.close()
145
146
147 class DisplayFeed(hildon.StackableWindow):
148     def __init__(self, feed, title):
149         hildon.StackableWindow.__init__(self)
150         self.feed = feed
151         self.feedTitle = title
152         self.set_title(title)
153         
154         menu = hildon.AppMenu()
155         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
156         button.set_label("Update Feed")
157         button.connect("clicked", self.button_update_clicked)
158         menu.append(button)
159         self.set_app_menu(menu)
160         menu.show_all()
161         
162         self.displayFeed()
163         
164         self.connect("destroy", self.destroyWindow)
165         
166     def destroyWindow(self, *args):
167         self.destroy()
168
169     def displayFeed(self):
170         self.vboxFeed = gtk.VBox(False, 10)
171         self.pannableFeed = hildon.PannableArea()
172         self.pannableFeed.add_with_viewport(self.vboxFeed)
173         self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
174         
175         index = 0
176         for item in self.feed.getEntries():
177             button = gtk.Button(item["title"])
178             button.set_alignment(0,0)
179             label = button.child
180             label.modify_font(pango.FontDescription("sans 18"))
181             label.set_line_wrap(True)
182             
183             label.set_size_request(self.get_size()[0]-50, -1)
184             button.connect("clicked", self.button_clicked, index)
185             
186             self.vboxFeed.pack_start(button, expand=False)           
187             index=index+1
188
189         self.add(self.pannableFeed)
190         self.show_all()
191         
192     def clear(self):
193         self.remove(self.pannableFeed)
194         
195     def button_clicked(self, button, index):
196         disp = DisplayArticle(self.feedTitle, self.feed.getArticle(index))
197
198     def button_update_clicked(self, button):
199         self.listing.getFeed(key).updateFeed()
200         self.clear()
201         self.displayFeed(key)
202
203
204 class FeedingIt:
205     def __init__(self):
206         self.listing = Listing()
207         
208         # Init the windows
209         self.window = hildon.StackableWindow()
210         self.window.set_title("FeedingIt")
211         FremantleRotation("FeedingIt", main_window=self.window)
212         menu = hildon.AppMenu()
213         # Create a button and add it to the menu
214         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
215         button.set_label("Update All Feeds")
216         button.connect("clicked", self.button_update_clicked, "All")
217         menu.append(button)
218         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
219         button.set_label("Add Feed")
220         button.connect("clicked", self.button_add_clicked)
221         menu.append(button)
222         
223         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
224         button.set_label("Delete Feed")
225         button.connect("clicked", self.button_delete_clicked)
226         menu.append(button)
227         
228         self.window.set_app_menu(menu)
229         menu.show_all()
230         
231         self.feedWindow = hildon.StackableWindow()
232         self.articleWindow = hildon.StackableWindow()
233
234         self.displayListing() 
235         
236     def button_add_clicked(self, button):
237         wizard = AddWidgetWizard(self.window)
238         ret = wizard.run()
239         if ret == 2:
240             (title, url) = wizard.getData()
241             if (not title == '') and (not url == ''): 
242                self.listing.addFeed(title, url)
243         wizard.destroy()
244         self.displayListing()
245         
246     def button_update_clicked(self, button, key):
247         #hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
248         if key == "All":
249             self.listing.updateFeeds()
250         else:
251             self.listing.getFeed(key).updateFeed()
252             self.displayFeed(key)            
253         self.displayListing()
254         #hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
255
256     def button_delete_clicked(self, button):
257         self.pickerDialog = hildon.PickerDialog(self.window)
258         #HildonPickerDialog
259         self.pickerDialog.set_selector(self.create_selector())
260         self.pickerDialog.show_all()
261         
262     def create_selector(self):
263         selector = hildon.TouchSelector(text=True)
264         # Selection multiple
265         #selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
266         self.mapping = {}
267         selector.connect("changed", self.selection_changed)
268
269         for key in self.listing.getListOfFeeds():
270             title=self.listing.getFeedTitle(key)
271             selector.append_text(title)
272             self.mapping[title]=key
273
274         return selector
275
276     def selection_changed(self, widget, data):
277         current_selection = widget.get_current_text()
278         #print 'Current selection: %s' % current_selection
279         #print "To Delete: %s" % self.mapping[current_selection]
280         self.pickerDialog.destroy()
281         if self.show_confirmation_note(self.window, current_selection):
282             self.listing.removeFeed(self.mapping[current_selection])
283         del self.mapping
284         self.displayListing()
285
286     def show_confirmation_note(self, parent, title):
287         note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
288
289         retcode = gtk.Dialog.run(note)
290         note.destroy()
291         
292         if retcode == gtk.RESPONSE_OK:
293             return True
294         else:
295             return False
296         
297     def displayListing(self):
298         try:
299             self.window.remove(self.pannableListing)
300         except:
301             pass
302         self.vboxListing = gtk.VBox(False,10)
303         self.pannableListing = hildon.PannableArea()
304         self.pannableListing.add_with_viewport(self.vboxListing)
305
306         for key in self.listing.getListOfFeeds():
307             #button = gtk.Button(item)
308             button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
309                               hildon.BUTTON_ARRANGEMENT_VERTICAL)
310             button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key))
311             button.set_alignment(0,0,1,1)
312             #label = button.child
313             #label.modify_font(pango.FontDescription("sans 10"))
314             button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
315             self.vboxListing.pack_start(button, expand=False)
316         self.window.add(self.pannableListing)
317         self.window.show_all()
318     
319     def buttonFeedClicked(widget, button, self, window, key):
320         disp = DisplayFeed(self.listing.getFeed(key), self.listing.getFeedTitle(key))
321      
322     def run(self):
323         self.window.connect("destroy", gtk.main_quit)
324         gtk.main()
325
326
327 if __name__ == "__main__":
328     if not isdir(CONFIGDIR):
329         try:
330             mkdir(CONFIGDIR)
331         except:
332             print "Error: Can't create configuration directory"
333             sys.exit(1)
334     app = FeedingIt()
335     app.run()