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 : PyGtk Example
24 # ============================================================================
34 from os.path import isfile, isdir
39 from portrait import FremantleRotation
43 class AddWidgetWizard(hildon.WizardDialog):
45 def __init__(self, parent):
47 self.notebook = gtk.Notebook()
49 self.nameEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
50 self.nameEntry.set_placeholder("Enter Feed Name")
52 self.urlEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
54 self.urlEntry.set_placeholder("Enter a URL")
56 labelEnd = gtk.Label("Success")
58 self.notebook.append_page(self.nameEntry, None)
59 self.notebook.append_page(self.urlEntry, None)
60 self.notebook.append_page(labelEnd, None)
62 hildon.WizardDialog.__init__(self, parent, "Add Feed", self.notebook)
64 # Set a handler for "switch-page" signal
65 #self.notebook.connect("switch_page", self.on_page_switch, self)
67 # Set a function to decide if user can go to next page
68 self.set_forward_page_func(self.some_page_func)
73 return (self.nameEntry.get_text(), self.urlEntry.get_text())
75 def on_page_switch(self, notebook, page, num, dialog):
78 def some_page_func(self, nb, current, userdata):
79 # Validate data for 1st page
81 entry = nb.get_nth_page(current)
82 # Check the name is not null
83 return len(entry.get_text()) != 0
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")) )
93 class DisplayArticle(hildon.StackableWindow):
94 def __init__(self, title, text, index):
95 hildon.StackableWindow.__init__(self)
100 # Init the article display
101 self.view = gtkhtml2.View()
102 self.pannable_article = hildon.PannableArea()
103 self.pannable_article.add(self.view)
104 self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
105 self.document = gtkhtml2.Document()
106 self.view.set_document(self.document)
108 self.document.clear()
109 self.document.open_stream("text/html")
110 self.document.write_stream(self.text)
111 self.document.close_stream()
113 menu = hildon.AppMenu()
114 # Create a button and add it to the menu
115 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
116 button.set_label("Display Images")
117 button.connect("clicked", self.reloadArticle)
119 self.set_app_menu(menu)
122 self.add(self.pannable_article)
126 self.document.connect("link_clicked", self._signal_link_clicked)
127 self.document.connect("request-url", self._signal_request_url)
128 self.connect("destroy", self.destroyWindow)
130 def destroyWindow(self, *args):
131 self.emit("article-closed", self.index)
134 def reloadArticle(self, widget):
135 self.document.open_stream("text/html")
136 self.document.write_stream(self.text)
137 self.document.close_stream()
139 def _signal_link_clicked(self, object, link):
140 webbrowser.open(link)
142 def _signal_request_url(self, object, url, stream):
143 f = urllib2.urlopen(url)
144 stream.write(f.read())
148 class DisplayFeed(hildon.StackableWindow):
149 def __init__(self, feed, title, key):
150 hildon.StackableWindow.__init__(self)
152 self.feedTitle = title
153 self.set_title(title)
156 menu = hildon.AppMenu()
157 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
158 button.set_label("Update Feed")
159 button.connect("clicked", self.button_update_clicked)
161 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
162 button.set_label("Mark All As Read")
163 button.connect("clicked", self.buttonReadAllClicked)
165 self.set_app_menu(menu)
170 self.connect("destroy", self.destroyWindow)
172 def destroyWindow(self, *args):
174 self.emit("feed-closed", self.key)
177 def displayFeed(self):
178 self.vboxFeed = gtk.VBox(False, 10)
179 self.pannableFeed = hildon.PannableArea()
180 self.pannableFeed.add_with_viewport(self.vboxFeed)
181 self.pannableFeed.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
183 for index in range(self.feed.getNumberOfEntries()):
184 button = gtk.Button(self.feed.getTitle(index))
185 button.set_alignment(0,0)
187 if self.feed.isEntryRead(index):
188 label.modify_font(pango.FontDescription("sans 16"))
190 label.modify_font(pango.FontDescription("sans bold 16"))
191 label.set_line_wrap(True)
193 label.set_size_request(self.get_size()[0]-50, -1)
194 button.connect("clicked", self.button_clicked, index)
195 self.buttons.append(button)
197 self.vboxFeed.pack_start(button, expand=False)
200 self.add(self.pannableFeed)
204 self.remove(self.pannableFeed)
206 def button_clicked(self, button, index):
207 disp = DisplayArticle(self.feedTitle, self.feed.getArticle(index), index)
208 disp.connect("article-closed", self.onArticleClosed)
210 def onArticleClosed(self, object, index):
211 label = self.buttons[index].child
212 label.modify_font(pango.FontDescription("sans 16"))
213 self.buttons[index].show()
215 def button_update_clicked(self, button):
216 self.listing.getFeed(key).updateFeed()
218 self.displayFeed(key)
220 def buttonReadAllClicked(self, button):
221 for index in range(self.feed.getNumberOfEntries()):
222 self.feed.setEntryRead(index)
223 label = self.buttons[index].child
224 label.modify_font(pango.FontDescription("sans 16"))
225 self.buttons[index].show()
230 self.listing = Listing()
233 self.window = hildon.StackableWindow()
234 self.window.set_title("FeedingIt")
235 FremantleRotation("FeedingIt", main_window=self.window)
236 menu = hildon.AppMenu()
237 # Create a button and add it to the menu
238 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
239 button.set_label("Update All Feeds")
240 button.connect("clicked", self.button_update_clicked, "All")
242 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
243 button.set_label("Add Feed")
244 button.connect("clicked", self.button_add_clicked)
247 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
248 button.set_label("Delete Feed")
249 button.connect("clicked", self.button_delete_clicked)
252 self.window.set_app_menu(menu)
255 self.feedWindow = hildon.StackableWindow()
256 self.articleWindow = hildon.StackableWindow()
258 self.displayListing()
260 def button_add_clicked(self, button):
261 wizard = AddWidgetWizard(self.window)
264 (title, url) = wizard.getData()
265 if (not title == '') and (not url == ''):
266 self.listing.addFeed(title, url)
268 self.displayListing()
270 def button_update_clicked(self, button, key):
271 #hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
273 self.listing.updateFeeds()
275 self.listing.getFeed(key).updateFeed()
276 self.displayFeed(key)
277 self.displayListing()
278 #hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
280 def button_delete_clicked(self, button):
281 self.pickerDialog = hildon.PickerDialog(self.window)
283 self.pickerDialog.set_selector(self.create_selector())
284 self.pickerDialog.show_all()
286 def create_selector(self):
287 selector = hildon.TouchSelector(text=True)
289 #selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
291 selector.connect("changed", self.selection_changed)
293 for key in self.listing.getListOfFeeds():
294 title=self.listing.getFeedTitle(key)
295 selector.append_text(title)
296 self.mapping[title]=key
300 def selection_changed(self, widget, data):
301 current_selection = widget.get_current_text()
302 #print 'Current selection: %s' % current_selection
303 #print "To Delete: %s" % self.mapping[current_selection]
304 self.pickerDialog.destroy()
305 if self.show_confirmation_note(self.window, current_selection):
306 self.listing.removeFeed(self.mapping[current_selection])
308 self.displayListing()
310 def show_confirmation_note(self, parent, title):
311 note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
313 retcode = gtk.Dialog.run(note)
316 if retcode == gtk.RESPONSE_OK:
321 def displayListing(self):
323 self.window.remove(self.pannableListing)
326 self.vboxListing = gtk.VBox(False,10)
327 self.pannableListing = hildon.PannableArea()
328 self.pannableListing.add_with_viewport(self.vboxListing)
331 for key in self.listing.getListOfFeeds():
332 #button = gtk.Button(item)
333 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
334 hildon.BUTTON_ARRANGEMENT_VERTICAL)
335 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
336 + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items / " + str(self.listing.getFeed(key).getNumberOfEntries()))
337 button.set_alignment(0,0,1,1)
338 button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
339 self.vboxListing.pack_start(button, expand=False)
340 self.buttons[key] = button
341 self.window.add(self.pannableListing)
342 self.window.show_all()
344 def buttonFeedClicked(widget, button, self, window, key):
345 disp = DisplayFeed(self.listing.getFeed(key), self.listing.getFeedTitle(key), key)
346 disp.connect("feed-closed", self.onFeedClosed)
348 def onFeedClosed(self, object, key):
349 self.buttons[key].set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
350 + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
351 self.buttons[key].show()
354 self.window.connect("destroy", gtk.main_quit)
358 if __name__ == "__main__":
359 gobject.signal_new("feed-closed", DisplayFeed, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
360 gobject.signal_new("article-closed", DisplayArticle, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
362 if not isdir(CONFIGDIR):
366 print "Error: Can't create configuration directory"