fix crash on "new items" notification
[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
230                 self.close()
231
232         def __show(self):
233                 self.rssPoller.addCallback(self.pollCallback)
234
235         def __close(self):
236                 if self.timer is not None:
237                         self.timer.callback.remove(self.timerTick)
238                         self.timer = None
239                 self.rssPoller.removeCallback(self.pollCallback)
240
241         def pollCallback(self, id = None):
242                 print "[SimpleRSS] SimpleRSSFeed called back"
243
244                 if id is None or id+1 == self.id:
245                         # TODO: do we really need this?
246                         current_entry = self["content"].getCurrentEntry()
247                         self["content"].moveToEntry(current_entry)
248
249                         self["content"].invalidate()
250                         self.setConditionalTitle()
251                         self.updateInfo()
252
253         def setConditionalTitle(self):
254                 self.setTitle(': '.join(["Simple RSS Reader", self.feed.title]))
255
256         def updateInfo(self):
257                 current_entry = self["content"].getCurrentEntry()
258                 if current_entry:
259                         self["summary"].setText(current_entry[2])
260
261                         cur_idx = self["content"].getCurrentIndex()
262                         self["info"].setText(_("Entry %s/%s") % (cur_idx+1, len(self.feed.history)))
263                 else:
264                         self["summary"].setText(_("Feed is empty."))
265                         self["info"].setText("")
266
267         def menu(self):
268                 if self.id > 0:
269                         self.singleUpdate(self.id-1)
270
271         def nextEntry(self):
272                 self["content"].moveDown()
273                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.feed.history))
274
275         def previousEntry(self):
276                 self["content"].moveUp()
277                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.feed.history))
278
279         # TODO: Fix moving back to previously marked entry (same goes for self.previous)
280         def next(self):
281                 # Show next Feed
282                 if self.parent is not None:
283                         (self.feed, self.id) = self.parent.nextFeed()
284                         #current_entry = self["content"].getCurrentEntry()
285                         self["content"].l.setList(self.feed.history) # Update list
286                         self["content"].moveToIndex(0)
287                         #self["content"].moveToEntry(current_entry)
288                         self.updateInfo() # In case entry is no longer in history
289                         self.setConditionalTitle() # Update title
290                         return (self.feed.title, self.feed.history, self.id)
291                 return (self.feed.title, self.feed.history, self.id)
292
293         def previous(self):
294                 # Show previous Feed
295                 if self.parent is not None:
296                         (self.feed, self.id) = self.parent.previousFeed()
297                         #current_entry = self["content"].getCurrentEntry()
298                         self["content"].l.setList(self.feed.history) # Update list
299                         self["content"].moveToIndex(0)
300                         #self["content"].moveToEntry(current_entry)
301                         self.updateInfo() # In case entry is no longer in history
302                         self.setConditionalTitle() # Update title
303                         return (self.feed.title, self.feed.history, self.id)
304                 return (self.feed.title, self.feed.history, self.id)
305
306         def checkEmpty(self):
307                 if self.id > 0 and not len(self.feed.history):
308                         self.singleUpdate(self.id-1)
309
310         def showCurrentEntry(self):
311                 current_entry = self["content"].getCurrentEntry()
312                 if current_entry is None: # empty list
313                         return
314
315                 self.session.openWithCallback(
316                         self.updateInfo,
317                         RSSEntryView,
318                         current_entry,
319                         cur_idx=self["content"].getCurrentIndex(),
320                         entries=len(self.feed.history),
321                         feedTitle=self.feed.title,
322                         parent=self
323                 )
324
325         def selectEnclosure(self):
326                 current_entry = self["content"].getCurrentEntry()
327                 if current_entry is None: # empty list
328                         return
329
330                 RSSBaseView.selectEnclosure(self, current_entry[3])
331
332 class RSSOverview(RSSBaseView):
333         """Shows an Overview over all RSS-Feeds known to rssPoller"""
334         skin = """
335                 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
336                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
337                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
338                         <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
339                 </screen>"""
340
341         def __init__(self, session, poller):
342                 RSSBaseView.__init__(self, session, poller)
343
344                 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ], 
345                 {
346                         "ok": self.showCurrentEntry,
347                         "cancel": self.close,
348                         "menu": self.menu,
349                         "yellow": self.selectEnclosure,
350                 })
351
352                 self.fillFeeds()
353
354                 # We always have at least "New Items"-Feed
355                 self["content"] = RSSFeedList(self.feeds)
356                 self["summary"] = Label(' '.join([str(len(self.feeds[0][0].history)), _("Entries")]))
357                 self["info"] = Label(_("Feed %s/%s") % (1, len(self.feeds)))
358
359                 self["content"].connectSelChanged(self.updateInfo)
360                 self.onLayoutFinish.append(self.__show)
361                 self.onClose.append(self.__close)
362
363         def __show(self):
364                 self.rssPoller.addCallback(self.pollCallback)
365
366         def __close(self):
367                 self.rssPoller.removeCallback(self.pollCallback)
368
369         def fillFeeds(self):
370                 # Feedlist contains our virtual Feed and all real ones
371                 self.feeds = [(self.rssPoller.newItemFeed,)]
372                 self.feeds.extend([(feed,) for feed in self.rssPoller.feeds])
373
374         def pollCallback(self, id = None):
375                 print "[SimpleRSS] SimpleRSS called back"
376                 self.updateInfo()
377                 self["content"].invalidate()
378
379         def updateInfo(self):
380                 current_entry = self["content"].getCurrentEntry()
381                 self["summary"].setText(' '.join([str(len(current_entry.history)), _("Entries")]))
382                 self["info"].setText(_("Feed %s/%s") % (self["content"].getCurrentIndex()+1, len(self.feeds)))
383
384         def menu(self):
385                 cur_idx = self["content"].getCurrentIndex()
386                 if cur_idx > 0:
387                         possible_actions = [
388                                 (_("Update Feed"), "update"),
389                                 (_("Setup"), "setup"),
390                                 (_("Close"), "close")
391                         ]
392                 else:
393                         possible_actions = [
394                                 (_("Setup"), "setup"),
395                                 (_("Close"), "close")
396                         ]
397                 self.session.openWithCallback(
398                         self.menuChoice,
399                         ChoiceBox,
400                         "What to do?",
401                         possible_actions
402                 )
403
404         def menuChoice(self, result):
405                 if result:
406                         if result[1] == "update":
407                                 cur_idx = self["content"].getCurrentIndex()
408                                 if cur_idx > 0:
409                                         self.singleUpdate(cur_idx-1)
410                         elif result[1] == "setup":
411                                 self.session.openWithCallback(
412                                         self.refresh,
413                                         RSSSetup,
414                                         rssPoller=self.rssPoller
415                                 )
416                         elif result[1] == "close":
417                                 self.close()
418
419         def refresh(self):
420                 current_entry = self["content"].getCurrentEntry()
421
422                 self.fillFeeds()
423                 self["content"].l.setList(self.feeds)
424
425                 self["content"].moveToEntry(current_entry)
426                 self.updateInfo()
427
428         def nextFeed(self):
429                 self["content"].moveUp()
430                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex())
431
432         def previousFeed(self):
433                 self["content"].moveDown()
434                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex())
435
436         def showCurrentEntry(self):
437                 current_entry = self["content"].getCurrentEntry()
438                 self.session.openWithCallback(
439                         self.updateInfo,
440                         RSSFeedView,
441                         feed=current_entry,
442                         parent=self,
443                         rssPoller=self.rssPoller,
444                         id=self["content"].getCurrentIndex()
445                 )
446
447         def selectEnclosure(self):
448                 # Build a list of all enclosures in this feed
449                 enclosures = []
450                 for entry in self["content"].getCurrentEntry().history:
451                                 enclosures.extend(entry[3])
452                 RSSBaseView.selectEnclosure(self, enclosures)