follow recent timer changes
[enigma2-plugins.git] / simplerss / src / RSSScreens.py
1 from enigma import eTimer
2
3 from Screens.Screen import Screen
4 from Screens.MessageBox import MessageBox
5 from Screens.ChoiceBox import ChoiceBox
6
7 from Components.Scanner import openList
8
9 from Components.ActionMap import ActionMap
10 from Components.Label import Label
11 from Components.ScrollLabel import ScrollLabel
12
13 from RSSList import RSSFeedList, RSSEntryList
14 from RSSSetup import RSSSetup
15
16 class RSSBaseView(Screen):
17         """Base Screen for all Screens used in SimpleRSS"""
18
19         def __init__(self, session, poller, parent=None):
20                 Screen.__init__(self, session)
21                 self.rssPoller = poller
22                 self.pollDialog = None
23                 self.parent = parent
24
25         def errorPolling(self, errmsg = ""):
26                 # An error occured while polling
27                 self.session.open(
28                         MessageBox,
29                         _("Error while parsing Feed, this usually means there is something wrong with it."),
30                         type = MessageBox.TYPE_ERROR,
31                         timeout = 3
32                 )
33
34                 # Don't show "we're updating"-dialog any longer
35                 if self.pollDialog:
36                         self.pollDialog.close()
37                         self.pollDialog = None
38
39         def singleUpdate(self, feedid, errback = None):
40                 # Don't do anything if we have no poller
41                 if self.rssPoller is None:
42                         return
43
44                 # Default errorback to self.errorPolling
45                 # If an empty errorback is wanted the Screen needs to provide it
46                 if errback is None:
47                         errback = self.errorPolling
48
49                 # Tell Poller to poll
50                 self.rssPoller.singlePoll(feedid, callback=True, errorback=errback)
51
52                 # Open Dialog and save locally
53                 self.pollDialog = self.session.open(
54                         MessageBox,
55                         _("Update is being done in Background.\nContents will automatically be updated when it's done."),
56                         type = MessageBox.TYPE_INFO,
57                         timeout = 5
58                 )
59
60         def selectEnclosure(self, enclosures):
61                 # Empty List
62                 if enclosures is None:
63                         return
64
65                 if not openList(self.session, enclosures):
66                         self.session.open(
67                                 MessageBox,
68                                 _("Found no Enclosure we can display."),
69                                 type = MessageBox.TYPE_INFO, 
70                                 timeout = 5
71                         )
72
73 class RSSEntryView(RSSBaseView):
74         """Shows a RSS Item"""
75         skin = """
76                 <screen position="100,100" size="460,420" title="Simple RSS Reader" >
77                         <widget name="info" position="0,0" size="460, 20" halign="right" font="Regular; 18" />
78                         <widget name="content" position="0,20" size="460,400" font="Regular; 22" />
79                 </screen>"""
80
81         def __init__(self, session, data, feedTitle="", cur_idx=None, entries=None, parent=None):
82                 RSSBaseView.__init__(self, session, None, parent)
83
84                 self.data = data
85                 self.feedTitle = feedTitle
86                 self.cur_idx = cur_idx
87                 self.entries = entries
88
89                 if cur_idx is not None and entries is not None:
90                         self["info"] = Label(_("Entry %s/%s") % (cur_idx+1, entries))
91                 else:
92                         self["info"] = Label()
93
94                 if data is not None:
95                         self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
96                 else:
97                         self["content"] = ScrollLabel()
98
99                 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "ColorActions", "DirectionActions" ],
100                 {
101                         "cancel": self.close,
102                         "ok": self.selectEnclosure,
103                         "yellow": self.selectEnclosure,
104                         "up": self.up,
105                         "down": self.down,
106                         "right": self.next,
107                         "left": self.previous,
108                         "nextBouquet": self.nextFeed,
109                         "prevBouquet": self.previousFeed,
110                 })
111
112                 self.onLayoutFinish.append(self.setConditionalTitle)
113
114         def setConditionalTitle(self):
115                 self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
116
117         def up(self):
118                 self["content"].pageUp()
119
120         def down(self):
121                 self["content"].pageDown()
122
123         def next(self):
124                 if self.parent is not None:
125                         (self.data, self.cur_idx, self.entries) = self.parent.nextEntry()
126                         self.setContent()
127
128         def previous(self):
129                 if self.parent is not None:
130                         (self.data, self.cur_idx, self.entries) = self.parent.previousEntry()
131                         self.setContent()
132
133         def nextFeed(self):
134                 # Show next Feed
135                 if self.parent is not None:
136                         result = self.parent.next()
137                         self.feedTitle = result[0]
138                         self.entries = len(result[1])
139                         if self.entries:
140                                 self.cur_idx = 0
141                                 self.data = result[1][0]
142                         else:
143                                 self.cur_idx = None
144                                 self.data = None
145                         self.setConditionalTitle()
146                         self.setContent()
147
148         def previousFeed(self):
149                 # Show previous Feed
150                 if self.parent is not None:
151                         result = self.parent.previous()
152                         self.feedTitle = result[0]
153                         self.entries = len(result[1])
154                         if self.entries:
155                                 self.cur_idx = 0
156                                 self.data = result[1][0]
157                         else:
158                                 self.cur_idx = None
159                                 self.data = None
160                         self.setConditionalTitle()
161                         self.setContent()
162
163         def setContent(self):
164                 if self.cur_idx is not None and self.entries is not None:
165                         self["info"].setText(_("Entry %s/%s") % (self.cur_idx+1, self.entries))
166                 else:
167                         self["info"].setText("")
168                 if self.data is not None:
169                         self["content"].setText("\n\n".join([self.data[0], self.data[2], " ".join([str(len(self.data[3])), _("Enclosures")])]))
170                 else:
171                         self["content"].setText(_("No such Item."))
172
173         def selectEnclosure(self):
174                 if self.data is not None:
175                         RSSBaseView.selectEnclosure(self, self.data[3])
176
177 class RSSFeedView(RSSBaseView):
178         """Shows a RSS-Feed"""
179         skin = """
180                 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
181                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
182                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
183                         <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
184                 </screen>"""
185
186         def __init__(self, session, feed=None, newItems=False, parent=None, rssPoller=None,id=None):
187                 RSSBaseView.__init__(self, session, rssPoller, parent)
188
189                 self.feed = feed
190                 self.newItems = newItems
191                 self.id = id
192
193                 self["content"] = RSSEntryList(self.feed.history)
194                 self["summary"] = Label()
195                 self["info"] = Label()
196
197                 if not newItems:
198                         self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions", "ColorActions" ], 
199                         {
200                                 "ok": self.showCurrentEntry,
201                                 "cancel": self.close,
202                                 "nextBouquet": self.next,
203                                 "prevBouquet": self.previous,
204                                 "menu": self.menu,
205                                 "yellow": self.selectEnclosure,
206                         })
207                         self.onLayoutFinish.append(self.__show)
208                         self.onClose.append(self.__close)
209
210                         self.timer = None
211                 else:
212                         self["actions"] = ActionMap([ "OkCancelActions" ], 
213                         {
214                                 "cancel": self.close,
215                         })
216
217                         self.timer = eTimer()
218                         self.timer.callback.append(self.timerTick)
219                         self.onExecBegin.append(self.startTimer)
220
221                 self["content"].connectSelChanged(self.updateInfo)
222                 self.onLayoutFinish.extend([self.updateInfo, self.setConditionalTitle])
223
224         def startTimer(self):
225                 self.timer.startLongTimer(5)
226
227         def timerTick(self):
228                 self.timer.callback.remove(self.timerTick)
229                 self.timer = None
230
231                 self.close()
232
233         def __show(self):
234                 self.rssPoller.addCallback(self.pollCallback)
235
236         def __close(self):
237                 if self.timer is not None:
238                         self.timer.callback.remove(self.timerTick)
239                         self.timer = None
240                 self.rssPoller.removeCallback(self.pollCallback)
241
242         def pollCallback(self, id = None):
243                 print "[SimpleRSS] SimpleRSSFeed called back"
244
245                 if id is None or id+1 == self.id:
246                         # TODO: do we really need this?
247                         current_entry = self["content"].getCurrentEntry()
248                         self["content"].moveToEntry(current_entry)
249
250                         self["content"].invalidate()
251                         self.setConditionalTitle()
252                         self.updateInfo()
253
254         def setConditionalTitle(self):
255                 self.setTitle(': '.join(["Simple RSS Reader", self.feed.title]))
256
257         def updateInfo(self):
258                 current_entry = self["content"].getCurrentEntry()
259                 if current_entry:
260                         self["summary"].setText(current_entry[2])
261
262                         cur_idx = self["content"].getCurrentIndex()
263                         self["info"].setText(_("Entry %s/%s") % (cur_idx+1, len(self.feed.history)))
264                 else:
265                         self["summary"].setText(_("Feed is empty."))
266                         self["info"].setText("")
267
268         def menu(self):
269                 if self.id > 0:
270                         self.singleUpdate(self.id-1)
271
272         def nextEntry(self):
273                 self["content"].moveDown()
274                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.feed.history))
275
276         def previousEntry(self):
277                 self["content"].moveUp()
278                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.feed.history))
279
280         # TODO: Fix moving back to previously marked entry (same goes for self.previous)
281         def next(self):
282                 # Show next Feed
283                 if self.parent is not None:
284                         (self.feed, self.id) = self.parent.nextFeed()
285                         #current_entry = self["content"].getCurrentEntry()
286                         self["content"].l.setList(self.feed.history) # Update list
287                         self["content"].moveToIndex(0)
288                         #self["content"].moveToEntry(current_entry)
289                         self.updateInfo() # In case entry is no longer in history
290                         self.setConditionalTitle() # Update title
291                         return (self.feed.title, self.feed.history, self.id)
292                 return (self.feed.title, self.feed.history, self.id)
293
294         def previous(self):
295                 # Show previous Feed
296                 if self.parent is not None:
297                         (self.feed, self.id) = self.parent.previousFeed()
298                         #current_entry = self["content"].getCurrentEntry()
299                         self["content"].l.setList(self.feed.history) # Update list
300                         self["content"].moveToIndex(0)
301                         #self["content"].moveToEntry(current_entry)
302                         self.updateInfo() # In case entry is no longer in history
303                         self.setConditionalTitle() # Update title
304                         return (self.feed.title, self.feed.history, self.id)
305                 return (self.feed.title, self.feed.history, self.id)
306
307         def checkEmpty(self):
308                 if self.id > 0 and not len(self.feed.history):
309                         self.singleUpdate(self.id-1)
310
311         def showCurrentEntry(self):
312                 current_entry = self["content"].getCurrentEntry()
313                 if current_entry is None: # empty list
314                         return
315
316                 self.session.openWithCallback(
317                         self.updateInfo,
318                         RSSEntryView,
319                         current_entry,
320                         cur_idx=self["content"].getCurrentIndex(),
321                         entries=len(self.feed.history),
322                         feedTitle=self.feed.title,
323                         parent=self
324                 )
325
326         def selectEnclosure(self):
327                 current_entry = self["content"].getCurrentEntry()
328                 if current_entry is None: # empty list
329                         return
330
331                 RSSBaseView.selectEnclosure(self, current_entry[3])
332
333 class RSSOverview(RSSBaseView):
334         """Shows an Overview over all RSS-Feeds known to rssPoller"""
335         skin = """
336                 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
337                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
338                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
339                         <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
340                 </screen>"""
341
342         def __init__(self, session, poller):
343                 RSSBaseView.__init__(self, session, poller)
344
345                 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ], 
346                 {
347                         "ok": self.showCurrentEntry,
348                         "cancel": self.close,
349                         "menu": self.menu,
350                         "yellow": self.selectEnclosure,
351                 })
352
353                 self.fillFeeds()
354
355                 # We always have at least "New Items"-Feed
356                 self["content"] = RSSFeedList(self.feeds)
357                 self["summary"] = Label(' '.join([str(len(self.feeds[0][0].history)), _("Entries")]))
358                 self["info"] = Label(_("Feed %s/%s") % (1, len(self.feeds)))
359
360                 self["content"].connectSelChanged(self.updateInfo)
361                 self.onLayoutFinish.append(self.__show)
362                 self.onClose.append(self.__close)
363
364         def __show(self):
365                 self.rssPoller.addCallback(self.pollCallback)
366
367         def __close(self):
368                 self.rssPoller.removeCallback(self.pollCallback)
369
370         def fillFeeds(self):
371                 # Feedlist contains our virtual Feed and all real ones
372                 self.feeds = [(self.rssPoller.newItemFeed,)]
373                 self.feeds.extend([(feed,) for feed in self.rssPoller.feeds])
374
375         def pollCallback(self, id = None):
376                 print "[SimpleRSS] SimpleRSS called back"
377                 self.updateInfo()
378                 self["content"].invalidate()
379
380         def updateInfo(self):
381                 current_entry = self["content"].getCurrentEntry()
382                 self["summary"].setText(' '.join([str(len(current_entry.history)), _("Entries")]))
383                 self["info"].setText(_("Feed %s/%s") % (self["content"].getCurrentIndex()+1, len(self.feeds)))
384
385         def menu(self):
386                 cur_idx = self["content"].getCurrentIndex()
387                 if cur_idx > 0:
388                         possible_actions = [
389                                 (_("Update Feed"), "update"),
390                                 (_("Setup"), "setup"),
391                                 (_("Close"), "close")
392                         ]
393                 else:
394                         possible_actions = [
395                                 (_("Setup"), "setup"),
396                                 (_("Close"), "close")
397                         ]
398                 self.session.openWithCallback(
399                         self.menuChoice,
400                         ChoiceBox,
401                         "What to do?",
402                         possible_actions
403                 )
404
405         def menuChoice(self, result):
406                 if result:
407                         if result[1] == "update":
408                                 cur_idx = self["content"].getCurrentIndex()
409                                 if cur_idx > 0:
410                                         self.singleUpdate(cur_idx-1)
411                         elif result[1] == "setup":
412                                 self.session.openWithCallback(
413                                         self.refresh,
414                                         RSSSetup,
415                                         rssPoller=self.rssPoller
416                                 )
417                         elif result[1] == "close":
418                                 self.close()
419
420         def refresh(self):
421                 current_entry = self["content"].getCurrentEntry()
422
423                 self.fillFeeds()
424                 self["content"].l.setList(self.feeds)
425
426                 self["content"].moveToEntry(current_entry)
427                 self.updateInfo()
428
429         def nextFeed(self):
430                 self["content"].moveUp()
431                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex())
432
433         def previousFeed(self):
434                 self["content"].moveDown()
435                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex())
436
437         def showCurrentEntry(self):
438                 current_entry = self["content"].getCurrentEntry()
439                 self.session.openWithCallback(
440                         self.updateInfo,
441                         RSSFeedView,
442                         feed=current_entry,
443                         parent=self,
444                         rssPoller=self.rssPoller,
445                         id=self["content"].getCurrentIndex()
446                 )
447
448         def selectEnclosure(self):
449                 # Build a list of all enclosures in this feed
450                 enclosures = []
451                 for entry in self["content"].getCurrentEntry().history:
452                                 enclosures.extend(entry[3])
453                 RSSBaseView.selectEnclosure(self, enclosures)