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 CONFIGDIR="/home/user/.feedingit/"
42 return md5.new(string).hexdigest()
45 # Contains all the info about a single feed (articles, ...), and expose the data
46 def __init__(self, name, url):
50 self.updateTime = "Never"
51 #self.feed=feedparser.parse(url)
54 self.feed=feedparser.parse(self.url)
55 self.updateTime = time.asctime()
56 file = open(CONFIGDIR+getId(self.name), "w")
57 pickle.dump(self, file )
60 def getUpdateTime(self):
61 return self.updateTime
65 return self.feed["entries"]
69 def getItem(self, index):
71 return self.feed["entries"][index]
75 def getArticle(self, index):
76 entry = self.feed["entries"][index]
77 text = "<h4><a href=\"" + entry["link"] + "\">" + entry["title"] + "</a></h4>"
78 text = text + "<small><i>Date: " + time.strftime("%a, %d %b %Y %H:%M:%S",entry["updated_parsed"]) + "</i></small>"
79 text = text + "<BR />"
80 text = text + entry["summary"]
84 # Lists all the feeds in a dictionary, and expose the data
86 def updateFeeds(self):
87 for key in self.listOfFeeds.keys():
88 self.feeds[key].updateFeed()
90 def getFeed(self, key):
91 return self.feeds[key]
93 def getFeedUpdateTime(self, key):
94 return self.feeds[key].getUpdateTime()
96 def getFeedTitle(self, key):
97 return self.listOfFeeds[key]["title"]
99 def getFeedUrl(self, key):
100 return self.listOfFeeds[key]["url"]
102 def getListOfFeeds(self):
103 return self.listOfFeeds.keys()
105 def addFeed(self, title, url):
106 self.listOfFeeds[getId(title)] = {"title":title, "url":url}
108 self.feeds[getId(title)] = Feed(title, url)
110 def saveConfig(self):
111 file = open(CONFIGDIR+"feeds.pickle", "w")
112 pickle.dump(self.listOfFeeds, file)
117 if isfile(CONFIGDIR+"feeds.pickle"):
118 file = open(CONFIGDIR+"feeds.pickle")
119 self.listOfFeeds = pickle.load(file)
122 self.listOfFeeds = {getId("Slashdot"):{"title":"Slashdot", "url":"http://rss.slashdot.org/Slashdot/slashdot"}, }
123 for key in self.listOfFeeds.keys():
124 if isfile(CONFIGDIR+key):
125 file = open(CONFIGDIR+key)
126 self.feeds[key] = pickle.load(file)
129 self.feeds[key] = Feed(self.listOfFeeds[key]["title"], self.listOfFeeds[key]["url"])
133 class AddWidgetWizard(hildon.WizardDialog):
135 def __init__(self, parent):
137 self.notebook = gtk.Notebook()
139 self.nameEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
140 self.nameEntry.set_placeholder("Enter Feed Name")
142 self.urlEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO)
144 self.urlEntry.set_placeholder("Enter a URL")
146 labelEnd = gtk.Label("Success")
148 self.notebook.append_page(self.nameEntry, None)
149 self.notebook.append_page(self.urlEntry, None)
150 self.notebook.append_page(labelEnd, None)
152 hildon.WizardDialog.__init__(self, parent, "Add Feed", self.notebook)
154 # Set a handler for "switch-page" signal
155 #self.notebook.connect("switch_page", self.on_page_switch, self)
157 # Set a function to decide if user can go to next page
158 self.set_forward_page_func(self.some_page_func)
164 return (self.nameEntry.get_text(), self.urlEntry.get_text())
166 def on_page_switch(self, notebook, page, num, dialog):
167 print >>sys.stderr, "Page %d" % num
170 def some_page_func(self, nb, current, userdata):
171 # Validate data for 1st page
174 entry = nb.get_nth_page(current)
175 # Check the name is not null
176 return len(entry.get_text()) != 0
178 entry = nb.get_nth_page(current)
179 # Check the url is not null, and starts with http
180 print ( (len(entry.get_text()) != 0) and (entry.get_text().startswith("http")) )
181 return ( (len(entry.get_text()) != 0) and (entry.get_text().startswith("http")) )
190 self.window = hildon.StackableWindow()
191 menu = hildon.AppMenu()
192 # Create a button and add it to the menu
193 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
194 button.set_label("Update All Feeds")
195 button.connect("clicked", self.button_update_clicked, "All")
197 button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
198 button.set_label("Add Feed")
199 button.connect("clicked", self.button_add_clicked)
202 self.window.set_app_menu(menu)
205 self.feedWindow = hildon.StackableWindow()
206 self.articleWindow = hildon.StackableWindow()
208 self.listing = Listing()
209 #self.listing.downloadFeeds()
210 self.displayListing()
212 #self.window.show_all()
213 #self.displayFeed(self.listing.getFeed(0))
215 def button_add_clicked(self, button):
216 wizard = AddWidgetWizard(self.window)
219 (title, url) = wizard.getData()
220 if (not title == '') and (not url == ''):
221 self.listing.addFeed(title, url)
223 self.displayListing()
225 def button_update_clicked(self, button, key):
226 hildon.hildon_gtk_window_set_progress_indicator(self.window, 1)
228 self.listing.updateFeeds()
230 self.listing.getFeed(key).updateFeed()
231 self.displayListing()
232 hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
234 def displayListing(self):
236 self.window.remove(self.pannableListing)
239 self.vboxListing = gtk.VBox(False,10)
240 self.pannableListing = hildon.PannableArea()
241 self.pannableListing.add_with_viewport(self.vboxListing)
243 for key in self.listing.getListOfFeeds():
244 #button = gtk.Button(item)
245 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
246 hildon.BUTTON_ARRANGEMENT_VERTICAL)
247 button.set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key))
248 button.set_alignment(0,0,1,1)
249 #label = button.child
250 #label.modify_font(pango.FontDescription("sans 10"))
251 button.connect("clicked", self.buttonFeedClicked, self, self.window, key)
252 self.vboxListing.pack_start(button, expand=False)
253 self.window.add(self.pannableListing)
254 self.window.show_all()
256 def displayFeed(self, key):
257 # Initialize the feed panel
258 self.vboxFeed = gtk.VBox(False, 10)
259 self.pannableFeed = hildon.PannableArea()
260 self.pannableFeed.add_with_viewport(self.vboxFeed)
263 for item in self.listing.getFeed(key).getEntries():
264 #button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT,
265 # hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
266 #button.set_text(item["title"], time.strftime("%a, %d %b %Y %H:%M:%S",item["updated_parsed"]))
267 #button.set_text(item["title"], time.asctime(item["updated_parsed"]))
268 #button.set_text(item["title"],"")
269 #button.set_alignment(0,0,1,1)
270 #button.set_markup(True)
271 button = gtk.Button(item["title"])
272 button.set_alignment(0,0)
274 #label.set_markup(item["title"])
275 label.modify_font(pango.FontDescription("sans 16"))
276 button.connect("clicked", self.button_clicked, self, self.window, key, index)
277 self.vboxFeed.pack_start(button, expand=False)
280 self.feedWindow.add(self.pannableFeed)
281 self.feedWindow.show_all()
283 def displayArticle(self, key, index):
284 text = self.listing.getFeed(key).getArticle(index)
285 self.articleWindow = hildon.StackableWindow()
286 # Init the article display
287 self.view = gtkhtml2.View()
288 self.document = gtkhtml2.Document()
289 self.view.set_document(self.document)
290 self.pannable_article = hildon.PannableArea()
292 #self.view.connect("on_url", self._signal_on_url)
293 self.document.connect("link_clicked", self._signal_link_clicked)
294 #self.document.connect("request-url", self._signal_request_url)
296 self.document.clear()
297 self.document.open_stream("text/html")
298 self.document.write_stream(text)
299 self.document.close_stream()
301 self.pannable_article.add_with_viewport(self.view)
302 self.articleWindow.add(self.pannable_article)
303 self.articleWindow.show_all()
305 # def _signal_on_url(self, object, url):
306 # if url == None: url = ""
307 # else: url = self._complete_url(url)
308 #self.emit("status_changed", url)
310 def _signal_link_clicked(self, object, link):
311 #self.emit("open_uri", self._complete_url(link))
312 #os.spawnl(os.P_NOWAIT, '/usr/bin/browser', '/usr/bin/browser', '--url', link)
313 webbrowser.open(link)
315 # def _signal_request_url(self, object, url, stream):
316 # stream.write(self._fetch_url(self._complete_url(url)))
318 # def _complete_url(self, url):
319 # import string, urlparse, urllib
320 # url = urllib.quote(url, safe=string.punctuation)
321 # if urlparse.urlparse(url)[0] == '':
322 # return urlparse.urljoin(self.location, url)
326 # def _open_url(self, url, headers=[]):
328 # opener = urllib2.build_opener()
329 # opener.addheaders = [('User-agent', 'Wikitin')]+headers
330 # return opener.open(url)
332 # def _fetch_url(self, url, headers=[]):
333 # return self._open_url(url, headers).read()
336 def button_clicked(widget, button, app, window, key, index):
337 app.displayArticle(key, index)
339 def buttonFeedClicked(widget, button, app, window, key):
343 self.window.connect("destroy", gtk.main_quit)
344 #self.window.show_all()
347 if __name__ == "__main__":
348 if not isdir(CONFIGDIR):
352 print "Error: Can't create configuration directory"