Version 0.1.1 - Portrait mode finalized. Placed articles and feed windows in own...
[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.set_selector(self.create_selector())
226         button.connect("clicked", self.button_delete_clicked)
227         menu.append(button)
228         
229         self.window.set_app_menu(menu)
230         menu.show_all()
231         
232         self.feedWindow = hildon.StackableWindow()
233         self.articleWindow = hildon.StackableWindow()
234
235         #self.listing.downloadFeeds()
236         self.displayListing() 
237         
238         #self.window.show_all()
239         #self.displayFeed(self.listing.getFeed(0))
240         
241     def button_add_clicked(self, button):
242         wizard = AddWidgetWizard(self.window)
243         ret = wizard.run()
244         if ret == 2:
245             (title, url) = wizard.getData()
246             if (not title == '') and (not url == ''): 
247                self.listing.addFeed(title, url)
248         wizard.destroy()
249         self.displayListing()
250         
251     def button_update_clicked(self, button, key):
252         #hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
253         if key == "All":
254             self.listing.updateFeeds()
255         else:
256             self.listing.getFeed(key).updateFeed()
257             self.displayFeed(key)            
258         self.displayListing()
259         #hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
260
261     def button_delete_clicked(self, button):
262         self.pickerDialog = hildon.PickerDialog(self.window)
263         #HildonPickerDialog
264         self.pickerDialog.set_selector(self.create_selector())
265         self.pickerDialog.show_all()
266         
267     def create_selector(self):
268         selector = hildon.TouchSelector(text=True)
269         # Selection multiple
270         #selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
271         self.mapping = {}
272         selector.connect("changed", self.selection_changed)
273
274         for key in self.listing.getListOfFeeds():
275             title=self.listing.getFeedTitle(key)
276             selector.append_text(title)
277             self.mapping[title]=key
278
279         return selector
280
281     def selection_changed(self, widget, data):
282         current_selection = widget.get_current_text()
283         #print 'Current selection: %s' % current_selection
284         #print "To Delete: %s" % self.mapping[current_selection]
285         self.pickerDialog.destroy()
286         if self.show_confirmation_note(self.window, current_selection):
287             self.listing.removeFeed(self.mapping[current_selection])
288         del self.mapping
289         self.displayListing()
290
291     def show_confirmation_note(self, parent, title):
292         note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
293
294         retcode = gtk.Dialog.run(note)
295         note.destroy()
296         
297         if retcode == gtk.RESPONSE_OK:
298             return True
299         else:
300             return False
301         
302     def displayListing(self):
303         try:
304             self.window.remove(self.pannableListing)
305         except:
306             pass
307         self.vboxListing = gtk.VBox(False,10)
308         self.pannableListing = hildon.PannableArea()
309         self.pannableListing.add_with_viewport(self.vboxListing)
310
311         for key in self.listing.getListOfFeeds():
312             #button = gtk.Button(item)
313             button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
314                               hildon.BUTTON_ARRANGEMENT_VERTICAL)
315             button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key))
316             button.set_alignment(0,0,1,1)
317             #label = button.child
318             #label.modify_font(pango.FontDescription("sans 10"))
319             button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
320             self.vboxListing.pack_start(button, expand=False)
321         self.window.add(self.pannableListing)
322         self.window.show_all()
323     
324     def buttonFeedClicked(widget, button, self, window, key):
325         disp = DisplayFeed(self.listing.getFeed(key), self.listing.getFeedTitle(key))
326      
327     def run(self):
328         self.window.connect("destroy", gtk.main_quit)
329         gtk.main()
330
331
332 if __name__ == "__main__":
333     #gobject.signal_new("ReloadArticle", gtkhtml2.Document,
334     #                   gobject.SIGNAL_RUN_LAST,
335     #                   gobject.TYPE_NONE, ())
336     if not isdir(CONFIGDIR):
337         try:
338             mkdir(CONFIGDIR)
339         except:
340             print "Error: Can't create configuration directory"
341             sys.exit(1)
342     app = FeedingIt()
343     app.run()