Use Components.Scanner.openList to open enclosures,
[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 from Components.Pixmap import Pixmap
13
14 from RSSList import RSSList
15 from RSSSetup import RSSSetup
16
17 class RSSBaseView(Screen):
18         """Base Screen for all Screens used in SimpleRSS"""
19
20         def __init__(self, session, poller):
21                 Screen.__init__(self, session)
22                 self.rssPoller = poller
23                 self.pollDialog = None
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,420" font="Regular; 22" />
79                 </screen>"""
80
81         def __init__(self, session, data, feedTitle="", cur_idx=None, entries=None, nextEntryCB=None, previousEntryCB=None, nextFeedCB=None, previousFeedCB=None):
82                 RSSBaseView.__init__(self, session, None)
83
84                 self.data = data
85                 self.feedTitle = feedTitle
86                 self.nextEntryCB = nextEntryCB
87                 self.previousEntryCB = previousEntryCB
88                 self.nextFeedCB = nextFeedCB
89                 self.previousFeedCB = previousFeedCB
90                 self.cur_idx = cur_idx
91                 self.entries = entries
92
93                 if cur_idx is not None and entries is not None:
94                         self["info"] = Label("Entry %s/%s" % (cur_idx+1, entries))
95                 else:
96                         self["info"] = Label()
97
98                 if data is not None:
99                         self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
100                 else:
101                         self["content"] = ScrollLabel()
102
103                 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "ColorActions", "DirectionActions" ],
104                 {
105                         "cancel": self.close,
106                         "ok": self.selectEnclosure,
107                         "yellow": self.selectEnclosure,
108                         "up": self.up,
109                         "down": self.down,
110                         "right": self.next,
111                         "left": self.previous,
112                         "nextBouquet": self.nextFeed,
113                         "prevBouquet": self.previousFeed,
114                 })
115
116                 self.onLayoutFinish.append(self.setConditionalTitle)
117
118         def setConditionalTitle(self):
119                 self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
120
121         def up(self):
122                 self["content"].pageUp()
123
124         def down(self):
125                 self["content"].pageDown()
126
127         def next(self):
128                 if self.nextEntryCB is not None:
129                         (self.data, self.cur_idx, self.entries) = self.nextEntryCB()
130                         self.setContent()
131
132         def previous(self):
133                 if self.previousEntryCB is not None:
134                         (self.data, self.cur_idx, self.entries) = self.previousEntryCB()
135                         self.setContent()
136
137         def nextFeed(self):
138                 # Show next Feed
139                 if self.nextFeedCB is not None:
140                         result = self.nextFeedCB()
141                         self.feedTitle = result[0]
142                         self.entries = len(result[1])
143                         if self.entries:
144                                 self.cur_idx = 0
145                                 self.data = result[1][0]
146                         else:
147                                 self.cur_idx = None
148                                 self.data = None
149                         self.setConditionalTitle()
150                         self.setContent()
151
152         def previousFeed(self):
153                 # Show previous Feed
154                 if self.previousFeedCB is not None:
155                         result = self.previousFeedCB()
156                         self.feedTitle = result[0]
157                         self.entries = len(result[1])
158                         if self.entries:
159                                 self.cur_idx = 0
160                                 self.data = result[1][0]
161                         else:
162                                 self.cur_idx = None
163                                 self.data = None
164                         self.setConditionalTitle()
165                         self.setContent()
166
167         def setContent(self):
168                 if self.cur_idx is not None and self.entries is not None:
169                         self["info"].setText("Entry %s/%s" % (self.cur_idx+1, self.entries))
170                 else:
171                         self["info"].setText("")
172                 if self.data is not None:
173                         self["content"].setText("\n\n".join([self.data[0], self.data[2], " ".join([str(len(self.data[3])), "Enclosures"])]))
174                 else:
175                         self["content"].setText("No such Item.")
176
177         def selectEnclosure(self):
178                 if self.data is not None:
179                         RSSBaseView.selectEnclosure(self, self.data[3])
180
181 class RSSFeedView(RSSBaseView):
182         """Shows a RSS-Feed"""
183         skin = """
184                 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
185                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
186                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
187                         <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
188                 </screen>"""
189
190         def __init__(self, session, data, feedTitle = "", newItems=False, nextFeedCB=None, previousFeedCB=None, rssPoller=None, id = None):
191                 RSSBaseView.__init__(self, session, rssPoller)
192
193                 self.data = data
194                 self.feedTitle = feedTitle
195                 self.newItems = newItems
196                 self.id = id
197                 self.nextFeedCB=nextFeedCB
198                 self.previousFeedCB=previousFeedCB
199
200                 self["content"] = RSSList(data)
201                 self["summary"] = Label()
202                 self["info"] = Label()
203
204                 if not newItems:
205                         self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions", "ColorActions" ], 
206                         {
207                                 "ok": self.showCurrentEntry,
208                                 "cancel": self.close,
209                                 "nextBouquet": self.next,
210                                 "prevBouquet": self.previous,
211                                 "menu": self.menu,
212                                 "yellow": self.selectEnclosure,
213                         })
214                         self.onShown.append(self.__show)
215                         self.onClose.append(self.__close)
216
217                         self.timer = None
218                 else:
219                         self["actions"] = ActionMap([ "OkCancelActions" ], 
220                         {
221                                 "cancel": self.close,
222                         })
223
224                         self.timer = eTimer()
225                         self.timer.timeout.get().append(self.timerTick)
226                         self.onExecBegin.append(self.startTimer)
227
228                 self["content"].connectSelChanged(self.updateInfo)
229                 self.onLayoutFinish.extend([self.updateInfo, self.setConditionalTitle])
230
231         def startTimer(self):
232                 self.timer.startLongTimer(5)
233
234         def timerTick(self):
235                 self.close()
236
237         def __show(self):
238                 self.rssPoller.addCallback(self.pollCallback)
239
240         def __close(self):
241                 if self.timer is not None:
242                         self.timer.timeout.get().remove(self.timerTick)
243                         self.timer = None
244                 self.rssPoller.removeCallback(self.pollCallback)
245
246         def pollCallback(self, id = None):
247                 print "[SimpleRSS] SimpleRSSFeed called back"
248                 current_entry = self["content"].getCurrentEntry()
249
250                 if id is not None and self.id == id+1:
251                         print "[SimpleRSS] pollCallback recieved local feed", self.id
252                         self.feedTitle = self.rssPoller.feeds[id].title
253                         self.data = self.rssPoller.feeds[id].history
254                 elif self.id == 0:
255                         print "[SimpleRSS] pollCallback recieved all or non-local feed, updating active view (new_items)"
256                         self.data = self.rssPoller.new_items
257                 else:
258                         print "[SimpleRSS] pollCallback recieved all or non-local feed, updating", self.id
259                         self.feedTitle = self.rssPoller.feeds[self.id-1].title
260                         self.data = self.rssPoller.feeds[self-id-1].history
261
262                 self["content"].l.setList(self.data)
263                 self["content"].moveToEntry(current_entry)
264
265                 self.setConditionalTitle()
266                 self.updateInfo()
267
268         def setConditionalTitle(self):
269                 if not self.newItems:
270                         self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
271                 else:
272                         self.setTitle("Simple RSS Reader: New Items")
273
274         def updateInfo(self):
275                 current_entry = self["content"].getCurrentEntry()
276                 if current_entry:
277                         self["summary"].setText(current_entry[2])
278
279                         cur_idx = self["content"].getCurrentIndex()
280                         self["info"].setText("Entry %s/%s" % (cur_idx+1, len(self.data)))
281                 else:
282                         self["summary"].setText("Feed is empty.")
283                         self["info"].setText("")
284
285         def menu(self):
286                 if self.id > 0:
287                         self.singleUpdate(self.id-1)
288
289         def nextEntryCB(self):
290                 self["content"].moveDown()
291                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
292
293         def previousEntryCB(self):
294                 self["content"].moveUp()
295                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
296
297         # TODO: Fix moving back to previously marked entry (same goes for self.previous)
298         def next(self):
299                 # Show next Feed
300                 if self.nextFeedCB is not None:
301                         result = self.nextFeedCB()
302                         (self.feedTitle, self.data, self.id) = result
303                         #current_entry = self["content"].getCurrentEntry()
304                         self["content"].l.setList(self.data) # Update list
305                         self["content"].moveToIndex(0)
306                         #self["content"].moveToEntry(current_entry)
307                         self.updateInfo() # In case entry is no longer in history
308                         self.setConditionalTitle() # Update title
309                         return result
310                 return (self.feedTitle, self.data, self.id)
311
312         def previous(self):
313                 # Show previous Feed
314                 if self.previousFeedCB is not None:
315                         result = self.previousFeedCB()
316                         (self.feedTitle, self.data, self.id) = result
317                         #current_entry = self["content"].getCurrentEntry()
318                         self["content"].l.setList(self.data) # Update list
319                         self["content"].moveToIndex(0)
320                         #self["content"].moveToEntry(current_entry)
321                         self.updateInfo() # In case entry is no longer in history
322                         self.setConditionalTitle() # Update title
323                         return result
324                 return (self.feedTitle, self.data, self.id)
325
326         def checkEmpty(self):
327                 if self.id > 0 and not len(self.data):
328                         self.singleUpdate(self.id-1)
329
330         def showCurrentEntry(self):
331                 current_entry = self["content"].getCurrentEntry()
332                 if current_entry is None: # empty list
333                         return
334
335                 self.session.openWithCallback(
336                         self.updateInfo,
337                         RSSEntryView,
338                         current_entry,
339                         cur_idx=self["content"].getCurrentIndex(),
340                         entries=len(self.data),
341                         feedTitle=self.feedTitle,
342                         nextEntryCB=self.nextEntryCB,
343                         previousEntryCB=self.previousEntryCB,
344                         nextFeedCB=self.next,
345                         previousFeedCB=self.previous
346                 )
347
348         def selectEnclosure(self):
349                 current_entry = self["content"].getCurrentEntry()
350                 if current_entry is None: # empty list
351                         return
352
353                 RSSBaseView.selectEnclosure(self, current_entry[3])
354
355 class RSSOverview(RSSBaseView):
356         """Shows an Overview over all RSS-Feeds known to rssPoller"""
357         skin = """
358                 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
359                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
360                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
361                         <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
362                 </screen>"""
363
364         def __init__(self, session, poller):
365                 RSSBaseView.__init__(self, session, poller)
366
367                 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ], 
368                 {
369                         "ok": self.showCurrentEntry,
370                         "cancel": self.close,
371                         "menu": self.menu,
372                         "yellow": self.selectEnclosure,
373                 })
374
375                 self.fillFeeds()
376
377                 # We always have at least "New Items"-Feed
378                 self["content"] = RSSList(self.feeds)
379                 self["summary"] = Label(self.feeds[0][2])
380                 self["info"] = Label("Feed 1/%s" % len(self.feeds))
381
382                 self["content"].connectSelChanged(self.updateInfo)
383                 self.onShown.append(self.__show)
384                 self.onClose.append(self.__close)
385
386         def __show(self):
387                 self.rssPoller.addCallback(self.pollCallback)
388
389         def __close(self):
390                 self.rssPoller.removeCallback(self.pollCallback)
391
392         def fillFeeds(self):
393                 self.feeds = [(
394                         "New Items",
395                         "New Items since last Auto-Update",
396                         ' '.join([str(len(self.rssPoller.new_items)), "Entries"]),
397                         self.rssPoller.new_items
398                 )]
399                 self.feeds.extend([
400                         (
401                                 feed.title,
402                                 feed.description,
403                                 ' '.join([str(len(feed.history)), "Entries"]),
404                                 feed.history
405                         )
406                                 for feed in self.rssPoller.feeds
407                 ])
408
409         def pollCallback(self, id = None):
410                 print "[SimpleRSS] SimpleRSS called back"
411                 current_entry = self["content"].getCurrentEntry()
412
413                 if id is not None:
414                         print "[SimpleRSS] pollCallback updating feed", id
415                         self.feeds[id+1] = (
416                                 self.rssPoller.feeds[id].title,
417                                 self.rssPoller.feeds[id].description,
418                                 ' '.join([str(len(self.rssPoller.feeds[id].history)), "Entries"]),
419                                 self.rssPoller.feeds[id].history
420                         )
421                 else:
422                         print "[SimpleRSS] pollCallback updating all feeds"
423                         self.fillFeeds()
424
425                 self["content"].l.setList(self.feeds)
426                 self["content"].moveToEntry(current_entry)
427
428                 self.updateInfo()
429
430         def updateInfo(self):
431                 current_entry = self["content"].getCurrentEntry()
432                 if current_entry:
433                         self["summary"].setText(current_entry[2])
434                         self["info"].setText("Feed %s/%s" % (self["content"].getCurrentIndex()+1, len(self.feeds)))
435                 # Should never happen
436                 else:
437                         self["summary"].setText("")
438                         self["info"].setText("")
439
440         def menu(self):
441                 cur_idx = self["content"].getCurrentIndex()
442                 if cur_idx > 0:
443                         possible_actions = [
444                                 (_("Update Feed"), "update"),
445                                 (_("Setup"), "setup"),
446                                 (_("Close"), "close")
447                         ]
448                 else:
449                         possible_actions = [
450                                 (_("Setup"), "setup"),
451                                 (_("Close"), "close")
452                         ]
453                 self.session.openWithCallback(
454                         self.menuChoice,
455                         ChoiceBox,
456                         "What to do?",
457                         possible_actions
458                 )
459
460         def menuChoice(self, result):
461                 if result:
462                         if result[1] == "update":
463                                 cur_idx = self["content"].getCurrentIndex()
464                                 if cur_idx > 0:
465                                         self.singleUpdate(cur_idx-1)
466                         elif result[1] == "setup":
467                                 self.session.openWithCallback(self.refresh, RSSSetup, rssPoller=self.rssPoller)
468                         elif result[1] == "close":
469                                 self.close()
470
471         def refresh(self):
472                 current_entry = self["content"].getCurrentEntry()
473
474                 self.fillFeeds()
475                 self["content"].l.setList(self.feeds)
476
477                 self["content"].moveToEntry(current_entry)
478                 self.updateInfo()
479
480         def nextFeedCB(self):
481                 self["content"].moveUp()
482                 current_entry = self["content"].getCurrentEntry()
483                 return (current_entry[0], current_entry[3], self["content"].getCurrentIndex())
484
485         def previousFeedCB(self):
486                 self["content"].moveDown()
487                 current_entry = self["content"].getCurrentEntry()
488                 return (current_entry[0], current_entry[3], self["content"].getCurrentIndex())
489
490         def showCurrentEntry(self):
491                 current_entry = self["content"].getCurrentEntry()
492                 if current_entry is None: # empty list
493                         return
494
495                 self.session.openWithCallback(
496                         self.refresh,
497                         RSSFeedView,
498                         current_entry[3],
499                         feedTitle=current_entry[0],
500                         nextFeedCB=self.nextFeedCB,
501                         previousFeedCB=self.previousFeedCB,
502                         rssPoller=self.rssPoller,
503                         id=self["content"].getCurrentIndex()
504                 )
505
506         def selectEnclosure(self):
507                 current_entry = self["content"].getCurrentEntry()
508                 if current_entry is None: # empty list
509                         return
510
511                 # Build a list of all enclosures in this feed
512                 enclosures = []
513                 for entry in current_entry[3]:
514                                 enclosures.extend(entry[3])
515                 RSSBaseView.selectEnclosure(self, enclosures)