1 from enigma import eTimer
3 from Screens.Screen import Screen
4 from Screens.MessageBox import MessageBox
5 from Screens.ChoiceBox import ChoiceBox
7 from Components.Scanner import openList
9 from Components.ActionMap import ActionMap
10 from Components.Label import Label
11 from Components.ScrollLabel import ScrollLabel
12 from Components.Pixmap import Pixmap
14 from RSSFeed import BaseFeed
15 from RSSList import RSSFeedList, RSSEntryList
16 from RSSSetup import RSSSetup
18 class RSSBaseView(Screen):
19 """Base Screen for all Screens used in SimpleRSS"""
21 def __init__(self, session, poller):
22 Screen.__init__(self, session)
23 self.rssPoller = poller
24 self.pollDialog = None
26 def errorPolling(self, errmsg = ""):
27 # An error occured while polling
30 "Error while parsing Feed, this usually means there is something wrong with it.",
31 type = MessageBox.TYPE_ERROR,
35 # Don't show "we're updating"-dialog any longer
37 self.pollDialog.close()
38 self.pollDialog = None
40 def singleUpdate(self, feedid, errback = None):
41 # Don't do anything if we have no poller
42 if self.rssPoller is None:
45 # Default errorback to self.errorPolling
46 # If an empty errorback is wanted the Screen needs to provide it
48 errback = self.errorPolling
51 self.rssPoller.singlePoll(feedid, callback=True, errorback=errback)
53 # Open Dialog and save locally
54 self.pollDialog = self.session.open(
56 "Update is being done in Background.\nContents will automatically be updated when it's done.",
57 type = MessageBox.TYPE_INFO,
61 def selectEnclosure(self, enclosures):
63 if enclosures is None:
66 if not openList(self.session, enclosures):
69 "Found no Enclosure we can display.",
70 type = MessageBox.TYPE_INFO,
74 class RSSEntryView(RSSBaseView):
75 """Shows a RSS Item"""
77 <screen position="100,100" size="460,420" title="Simple RSS Reader" >
78 <widget name="info" position="0,0" size="460, 20" halign="right" font="Regular; 18" />
79 <widget name="content" position="0,20" size="460,400" font="Regular; 22" />
82 def __init__(self, session, data, feedTitle="", cur_idx=None, entries=None, nextEntryCB=None, previousEntryCB=None, nextFeedCB=None, previousFeedCB=None):
83 RSSBaseView.__init__(self, session, None)
86 self.feedTitle = feedTitle
87 self.nextEntryCB = nextEntryCB
88 self.previousEntryCB = previousEntryCB
89 self.nextFeedCB = nextFeedCB
90 self.previousFeedCB = previousFeedCB
91 self.cur_idx = cur_idx
92 self.entries = entries
94 if cur_idx is not None and entries is not None:
95 self["info"] = Label("Entry %s/%s" % (cur_idx+1, entries))
97 self["info"] = Label()
100 self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
102 self["content"] = ScrollLabel()
104 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "ColorActions", "DirectionActions" ],
106 "cancel": self.close,
107 "ok": self.selectEnclosure,
108 "yellow": self.selectEnclosure,
112 "left": self.previous,
113 "nextBouquet": self.nextFeed,
114 "prevBouquet": self.previousFeed,
117 self.onLayoutFinish.append(self.setConditionalTitle)
119 def setConditionalTitle(self):
120 self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
123 self["content"].pageUp()
126 self["content"].pageDown()
129 if self.nextEntryCB is not None:
130 (self.data, self.cur_idx, self.entries) = self.nextEntryCB()
134 if self.previousEntryCB is not None:
135 (self.data, self.cur_idx, self.entries) = self.previousEntryCB()
140 if self.nextFeedCB is not None:
141 result = self.nextFeedCB()
142 self.feedTitle = result[0]
143 self.entries = len(result[1])
146 self.data = result[1][0]
150 self.setConditionalTitle()
153 def previousFeed(self):
155 if self.previousFeedCB is not None:
156 result = self.previousFeedCB()
157 self.feedTitle = result[0]
158 self.entries = len(result[1])
161 self.data = result[1][0]
165 self.setConditionalTitle()
168 def setContent(self):
169 if self.cur_idx is not None and self.entries is not None:
170 self["info"].setText("Entry %s/%s" % (self.cur_idx+1, self.entries))
172 self["info"].setText("")
173 if self.data is not None:
174 self["content"].setText("\n\n".join([self.data[0], self.data[2], " ".join([str(len(self.data[3])), "Enclosures"])]))
176 self["content"].setText("No such Item.")
178 def selectEnclosure(self):
179 if self.data is not None:
180 RSSBaseView.selectEnclosure(self, self.data[3])
182 class RSSFeedView(RSSBaseView):
183 """Shows a RSS-Feed"""
185 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
186 <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
187 <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
188 <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
191 def __init__(self, session, data, feedTitle = "", newItems=False, nextFeedCB=None, previousFeedCB=None, rssPoller=None, id = None):
192 RSSBaseView.__init__(self, session, rssPoller)
195 self.feedTitle = feedTitle
196 self.newItems = newItems
198 self.nextFeedCB=nextFeedCB
199 self.previousFeedCB=previousFeedCB
201 self["content"] = RSSEntryList(data)
202 self["summary"] = Label()
203 self["info"] = Label()
206 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions", "ColorActions" ],
208 "ok": self.showCurrentEntry,
209 "cancel": self.close,
210 "nextBouquet": self.next,
211 "prevBouquet": self.previous,
213 "yellow": self.selectEnclosure,
215 self.onShown.append(self.__show)
216 self.onClose.append(self.__close)
220 self["actions"] = ActionMap([ "OkCancelActions" ],
222 "cancel": self.close,
225 self.timer = eTimer()
226 self.timer.timeout.get().append(self.timerTick)
227 self.onExecBegin.append(self.startTimer)
229 self["content"].connectSelChanged(self.updateInfo)
230 self.onLayoutFinish.extend([self.updateInfo, self.setConditionalTitle])
232 def startTimer(self):
233 self.timer.startLongTimer(5)
239 self.rssPoller.addCallback(self.pollCallback)
242 if self.timer is not None:
243 self.timer.timeout.get().remove(self.timerTick)
245 self.rssPoller.removeCallback(self.pollCallback)
247 def pollCallback(self, id = None):
248 print "[SimpleRSS] SimpleRSSFeed called back"
249 current_entry = self["content"].getCurrentEntry()
251 if id is not None and self.id == id+1:
252 print "[SimpleRSS] pollCallback recieved local feed", self.id
253 self.feedTitle = self.rssPoller.feeds[id].title
254 self.data = self.rssPoller.feeds[id].history
256 print "[SimpleRSS] pollCallback recieved all or non-local feed, updating active view (new_items)"
257 self.data = self.rssPoller.new_items
259 print "[SimpleRSS] pollCallback recieved all or non-local feed, updating", self.id
260 self.feedTitle = self.rssPoller.feeds[self.id-1].title
261 self.data = self.rssPoller.feeds[self-id-1].history
263 self["content"].l.setList(self.data)
264 self["content"].moveToEntry(current_entry)
266 self.setConditionalTitle()
269 def setConditionalTitle(self):
270 if not self.newItems:
271 self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
273 self.setTitle("Simple RSS Reader: New Items")
275 def updateInfo(self):
276 current_entry = self["content"].getCurrentEntry()
278 self["summary"].setText(current_entry[2])
280 cur_idx = self["content"].getCurrentIndex()
281 self["info"].setText("Entry %s/%s" % (cur_idx+1, len(self.data)))
283 self["summary"].setText("Feed is empty.")
284 self["info"].setText("")
288 self.singleUpdate(self.id-1)
290 def nextEntryCB(self):
291 self["content"].moveDown()
292 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
294 def previousEntryCB(self):
295 self["content"].moveUp()
296 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
298 # TODO: Fix moving back to previously marked entry (same goes for self.previous)
301 if self.nextFeedCB is not None:
302 result = self.nextFeedCB()
303 (self.feedTitle, self.data, self.id) = result
304 #current_entry = self["content"].getCurrentEntry()
305 self["content"].l.setList(self.data) # Update list
306 self["content"].moveToIndex(0)
307 #self["content"].moveToEntry(current_entry)
308 self.updateInfo() # In case entry is no longer in history
309 self.setConditionalTitle() # Update title
311 return (self.feedTitle, self.data, self.id)
315 if self.previousFeedCB is not None:
316 result = self.previousFeedCB()
317 (self.feedTitle, self.data, self.id) = result
318 #current_entry = self["content"].getCurrentEntry()
319 self["content"].l.setList(self.data) # Update list
320 self["content"].moveToIndex(0)
321 #self["content"].moveToEntry(current_entry)
322 self.updateInfo() # In case entry is no longer in history
323 self.setConditionalTitle() # Update title
325 return (self.feedTitle, self.data, self.id)
327 def checkEmpty(self):
328 if self.id > 0 and not len(self.data):
329 self.singleUpdate(self.id-1)
331 def showCurrentEntry(self):
332 current_entry = self["content"].getCurrentEntry()
333 if current_entry is None: # empty list
336 self.session.openWithCallback(
340 cur_idx=self["content"].getCurrentIndex(),
341 entries=len(self.data),
342 feedTitle=self.feedTitle,
343 nextEntryCB=self.nextEntryCB,
344 previousEntryCB=self.previousEntryCB,
345 nextFeedCB=self.next,
346 previousFeedCB=self.previous
349 def selectEnclosure(self):
350 current_entry = self["content"].getCurrentEntry()
351 if current_entry is None: # empty list
354 RSSBaseView.selectEnclosure(self, current_entry[3])
356 class RSSOverview(RSSBaseView):
357 """Shows an Overview over all RSS-Feeds known to rssPoller"""
359 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
360 <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
361 <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
362 <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
365 def __init__(self, session, poller):
366 RSSBaseView.__init__(self, session, poller)
368 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ],
370 "ok": self.showCurrentEntry,
371 "cancel": self.close,
373 "yellow": self.selectEnclosure,
378 # We always have at least "New Items"-Feed
379 self["content"] = RSSFeedList(self.feeds)
380 self["summary"] = Label(' '.join([str(len(self.feeds[0][0].history)), "Entries"]))
381 self["info"] = Label("Feed 1/%s" % len(self.feeds))
383 self["content"].connectSelChanged(self.updateInfo)
384 self.onShown.append(self.__show)
385 self.onClose.append(self.__close)
388 self.rssPoller.addCallback(self.pollCallback)
391 self.rssPoller.removeCallback(self.pollCallback)
394 # Build virtual "new item"-Feed
395 newItemFeed = BaseFeed("", False)
396 newItemFeed.title = "New Items"
397 newItemFeed.description = "New Items since last Auto-Update"
398 newItemFeed.history = self.rssPoller.new_items
400 # Feedlist contains our virtual Feed and all real ones
401 self.feeds = [(newItemFeed,)]
402 self.feeds.extend([(feed,) for feed in self.rssPoller.feeds])
404 def pollCallback(self, id = None):
405 print "[SimpleRSS] SimpleRSS called back"
406 current_entry = self["content"].getCurrentEntry()
409 print "[SimpleRSS] pollCallback updating feed", id
411 print "[SimpleRSS] pollCallback updating all feeds"
414 self["content"].l.setList(self.feeds)
415 self["content"].moveToEntry(current_entry)
419 def updateInfo(self):
420 current_entry = self["content"].getCurrentEntry()
422 self["summary"].setText(' '.join([str(len(current_entry.history)), "Entries"]))
423 self["info"].setText("Feed %s/%s" % (self["content"].getCurrentIndex()+1, len(self.feeds)))
424 # Should never happen
426 self["summary"].setText("")
427 self["info"].setText("")
430 cur_idx = self["content"].getCurrentIndex()
433 (_("Update Feed"), "update"),
434 (_("Setup"), "setup"),
435 (_("Close"), "close")
439 (_("Setup"), "setup"),
440 (_("Close"), "close")
442 self.session.openWithCallback(
449 def menuChoice(self, result):
451 if result[1] == "update":
452 cur_idx = self["content"].getCurrentIndex()
454 self.singleUpdate(cur_idx-1)
455 elif result[1] == "setup":
456 self.session.openWithCallback(self.refresh, RSSSetup, rssPoller=self.rssPoller)
457 elif result[1] == "close":
461 current_entry = self["content"].getCurrentEntry()
464 self["content"].l.setList(self.feeds)
466 self["content"].moveToEntry(current_entry)
469 def nextFeedCB(self):
470 self["content"].moveUp()
471 current_entry = self["content"].getCurrentEntry()
472 return (current_entry.title, current_entry.history, self["content"].getCurrentIndex())
474 def previousFeedCB(self):
475 self["content"].moveDown()
476 current_entry = self["content"].getCurrentEntry()
477 return (current_entry.title, current_entry.history, self["content"].getCurrentIndex())
479 def showCurrentEntry(self):
480 current_entry = self["content"].getCurrentEntry()
481 if current_entry is None: # empty list
484 self.session.openWithCallback(
487 current_entry.history,
488 feedTitle=current_entry.title,
489 nextFeedCB=self.nextFeedCB,
490 previousFeedCB=self.previousFeedCB,
491 rssPoller=self.rssPoller,
492 id=self["content"].getCurrentIndex()
495 def selectEnclosure(self):
496 current_entry = self["content"].getCurrentEntry()
497 if current_entry is None: # empty list
500 # Build a list of all enclosures in this feed
502 for entry in current_entry.history:
503 enclosures.extend(entry[3])
504 RSSBaseView.selectEnclosure(self, enclosures)