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