Added auto-rotation, and line-wrap buttons
[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().startswith("http")) )
88         elif current != 2:
89             return False
90         else:
91             return True
92
93 class DisplayFeed(hildon.StackableWindow):
94     def __init__(self, listing, key):
95         hildon.StackableWindow.__init__(self)
96         self.listing = listing
97         self.key = key
98         
99     
100
101 class FeedingIt:
102     def __init__(self):
103         self.listing = Listing()
104         
105         # Init the windows
106         self.window = hildon.StackableWindow()
107         self.window.set_title("FeedingIt")
108         FremantleRotation("FeedingIt", main_window=self.window)
109         menu = hildon.AppMenu()
110         # Create a button and add it to the menu
111         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
112         button.set_label("Update All Feeds")
113         button.connect("clicked", self.button_update_clicked, "All")
114         menu.append(button)
115         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
116         button.set_label("Add Feed")
117         button.connect("clicked", self.button_add_clicked)
118         menu.append(button)
119         
120         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
121         button.set_label("Delete Feed")
122         #button.set_selector(self.create_selector())
123         button.connect("clicked", self.button_delete_clicked)
124         menu.append(button)
125         
126         self.window.set_app_menu(menu)
127         menu.show_all()
128         
129         self.feedWindow = hildon.StackableWindow()
130         self.articleWindow = hildon.StackableWindow()
131
132         #self.listing.downloadFeeds()
133         self.displayListing() 
134         
135         #self.window.show_all()
136         #self.displayFeed(self.listing.getFeed(0))
137         
138     def button_add_clicked(self, button):
139         wizard = AddWidgetWizard(self.window)
140         ret = wizard.run()
141         if ret == 2:
142             (title, url) = wizard.getData()
143             if (not title == '') and (not url == ''): 
144                self.listing.addFeed(title, url)
145         wizard.destroy()
146         self.displayListing()
147         
148     def button_update_clicked(self, button, key):
149         #hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
150         if key == "All":
151             self.listing.updateFeeds()
152         else:
153             self.listing.getFeed(key).updateFeed()
154             self.displayFeed(key)            
155         self.displayListing()
156         #hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
157
158     def button_delete_clicked(self, button):
159         self.pickerDialog = hildon.PickerDialog(self.window)
160         #HildonPickerDialog
161         self.pickerDialog.set_selector(self.create_selector())
162         self.pickerDialog.show_all()
163         
164     def create_selector(self):
165         selector = hildon.TouchSelector(text=True)
166         # Selection multiple
167         #selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
168         self.mapping = {}
169         selector.connect("changed", self.selection_changed)
170
171         for key in self.listing.getListOfFeeds():
172             title=self.listing.getFeedTitle(key)
173             selector.append_text(title)
174             self.mapping[title]=key
175
176         return selector
177
178     def selection_changed(self, widget, data):
179         current_selection = widget.get_current_text()
180         #print 'Current selection: %s' % current_selection
181         #print "To Delete: %s" % self.mapping[current_selection]
182         self.pickerDialog.destroy()
183         if self.show_confirmation_note(self.window, current_selection):
184             self.listing.removeFeed(self.mapping[current_selection])
185         del self.mapping
186         self.displayListing()
187
188     def show_confirmation_note(self, parent, title):
189         note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
190
191         retcode = gtk.Dialog.run(note)
192         note.destroy()
193         
194         if retcode == gtk.RESPONSE_OK:
195             return True
196         else:
197             return False
198         
199     def displayListing(self):
200         try:
201             self.window.remove(self.pannableListing)
202         except:
203             pass
204         self.vboxListing = gtk.VBox(False,10)
205         self.pannableListing = hildon.PannableArea()
206         self.pannableListing.add_with_viewport(self.vboxListing)
207
208         for key in self.listing.getListOfFeeds():
209             #button = gtk.Button(item)
210             button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
211                               hildon.BUTTON_ARRANGEMENT_VERTICAL)
212             button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key))
213             button.set_alignment(0,0,1,1)
214             #label = button.child
215             #label.modify_font(pango.FontDescription("sans 10"))
216             button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
217             self.vboxListing.pack_start(button, expand=False)
218         self.window.add(self.pannableListing)
219         self.window.show_all()
220         
221     def displayFeed(self, key):
222         # Initialize the feed panel
223         try:
224             self.feedWindow.destroy()
225         except:
226             pass
227         self.feedWindow.set_title(self.listing.getFeedTitle(key))
228         self.vboxFeed = gtk.VBox(False, 10)
229         self.pannableFeed = hildon.PannableArea()
230         self.pannableFeed.add_with_viewport(self.vboxFeed)
231         self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
232         
233         menu = hildon.AppMenu()
234         # Create a button and add it to the menu
235         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
236         button.set_label("Update Feed")
237         button.connect("clicked", self.button_update_clicked, key)
238         menu.append(button)
239         self.feedWindow.set_app_menu(menu)
240         menu.show_all()
241         
242         index = 0
243         for item in self.listing.getFeed(key).getEntries():
244             button = gtk.Button(item["title"])
245             button.set_alignment(0,0)
246             label = button.child
247             label.modify_font(pango.FontDescription("sans 16"))
248             label.set_line_wrap(True)
249             
250             label.set_size_request(self.feedWindow.get_size()[0]-50, -1)
251             button.connect("clicked", self.button_clicked, self, self.window, key, index)
252             
253             self.vboxFeed.pack_start(button, expand=False)           
254             index=index+1
255
256         self.feedWindow.add(self.pannableFeed)
257         self.feedWindow.show_all()
258      
259     def displayArticle(self, key, index):
260         self.text = self.listing.getFeed(key).getArticle(index)
261         self.articleWindow = hildon.StackableWindow()
262         self.articleWindow.set_title(self.listing.getFeedTitle(key))
263         
264         menu = hildon.AppMenu()
265         # Create a button and add it to the menu
266         button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
267         button.set_label("Display Images")
268         button.connect("clicked", self.reloadArticle)
269         menu.append(button)
270         self.articleWindow.set_app_menu(menu)
271         menu.show_all()
272
273         # Init the article display    
274         self.view = gtkhtml2.View()
275         self.pannable_article = hildon.PannableArea()
276         self.pannable_article.add(self.view)
277         self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
278         self.document = gtkhtml2.Document()
279         self.view.set_document(self.document)
280         
281         self.document.clear()
282         self.document.open_stream("text/html")
283         self.document.write_stream(self.text)
284         self.document.close_stream()
285         
286         self.articleWindow.add(self.pannable_article)
287         
288         self.articleWindow.show_all()
289         
290         self.document.connect("link_clicked", self._signal_link_clicked)
291         self.document.connect("request-url", self._signal_request_url)
292         #self.document.connect("ReloadArticle", self.reloadArticle)
293         
294     def reloadArticle(self, widget):
295         self.document.open_stream("text/html")
296         self.document.write_stream(self.text)
297         self.document.close_stream()
298      
299 #    def _signal_on_url(self, object, url):
300 #        if url == None: url = ""
301 #        else: url = self._complete_url(url)
302         #self.emit("status_changed", url)
303
304     def _signal_link_clicked(self, object, link):
305         webbrowser.open(link)
306
307     def _signal_request_url(self, object, url, stream):
308         f = urllib2.urlopen(url)
309         stream.write(f.read())
310         stream.close()
311 #        
312 #    def _complete_url(self, url):
313 #        import string, urlparse, urllib
314 #        url = urllib.quote(url, safe=string.punctuation)
315 #        if urlparse.urlparse(url)[0] == '':
316 #            return urlparse.urljoin(self.location, url)
317 #        else:
318 #            return url
319 #        
320 #    def _open_url(self, url, headers=[]):
321 #        import urllib2
322 #        opener = urllib2.build_opener()
323 #        opener.addheaders = [('User-agent', 'Wikitin')]+headers
324 #        return opener.open(url)
325 #
326 #    def _fetch_url(self, url, headers=[]):
327 #        return self._open_url(url, headers).read()
328         
329     def button_clicked(widget, button, app, window, key, index):
330         app.displayArticle(key, index)
331         #app.document.emit("ReloadArticle")
332     
333     def buttonFeedClicked(widget, button, app, window, key):
334         app.displayFeed(key)
335      
336     def run(self):
337         self.window.connect("destroy", gtk.main_quit)
338         gtk.main()
339
340 if __name__ == "__main__":
341     #gobject.signal_new("ReloadArticle", gtkhtml2.Document,
342     #                   gobject.SIGNAL_RUN_LAST,
343     #                   gobject.TYPE_NONE, ())
344     if not isdir(CONFIGDIR):
345         try:
346             mkdir(CONFIGDIR)
347         except:
348             print "Error: Can't create configuration directory"
349             sys.exit(1)
350     app = FeedingIt()
351     app.run()