Don't show URL in FeedView,
[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 RSSFeed import BaseFeed
15 from RSSList import RSSFeedList, RSSEntryList
16 from RSSSetup import RSSSetup
17
18 class RSSBaseView(Screen):
19         """Base Screen for all Screens used in SimpleRSS"""
20
21         def __init__(self, session, poller):
22                 Screen.__init__(self, session)
23                 self.rssPoller = poller
24                 self.pollDialog = None
25
26         def errorPolling(self, errmsg = ""):
27                 # An error occured while polling
28                 self.session.open(
29                         MessageBox,
30                         "Error while parsing Feed, this usually means there is something wrong with it.",
31                         type = MessageBox.TYPE_ERROR,
32                         timeout = 3
33                 )
34
35                 # Don't show "we're updating"-dialog any longer
36                 if self.pollDialog:
37                         self.pollDialog.close()
38                         self.pollDialog = None
39
40         def singleUpdate(self, feedid, errback = None):
41                 # Don't do anything if we have no poller
42                 if self.rssPoller is None:
43                         return
44
45                 # Default errorback to self.errorPolling
46                 # If an empty errorback is wanted the Screen needs to provide it
47                 if errback is None:
48                         errback = self.errorPolling
49
50                 # Tell Poller to poll
51                 self.rssPoller.singlePoll(feedid, callback=True, errorback=errback)
52
53                 # Open Dialog and save locally
54                 self.pollDialog = self.session.open(
55                         MessageBox,
56                         "Update is being done in Background.\nContents will automatically be updated when it's done.",
57                         type = MessageBox.TYPE_INFO,
58                         timeout = 5
59                 )
60
61         def selectEnclosure(self, enclosures):
62                 # Empty List
63                 if enclosures is None:
64                         return
65
66                 if not openList(self.session, enclosures):
67                         self.session.open(
68                                 MessageBox,
69                                 "Found no Enclosure we can display.",
70                                 type = MessageBox.TYPE_INFO, 
71                                 timeout = 5
72                         )
73
74 class RSSEntryView(RSSBaseView):
75         """Shows a RSS Item"""
76         skin = """
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" />
80                 </screen>"""
81
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)
84
85                 self.data = data
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
93
94                 if cur_idx is not None and entries is not None:
95                         self["info"] = Label("Entry %s/%s" % (cur_idx+1, entries))
96                 else:
97                         self["info"] = Label()
98
99                 if data is not None:
100                         self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
101                 else:
102                         self["content"] = ScrollLabel()
103
104                 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "ColorActions", "DirectionActions" ],
105                 {
106                         "cancel": self.close,
107                         "ok": self.selectEnclosure,
108                         "yellow": self.selectEnclosure,
109                         "up": self.up,
110                         "down": self.down,
111                         "right": self.next,
112                         "left": self.previous,
113                         "nextBouquet": self.nextFeed,
114                         "prevBouquet": self.previousFeed,
115                 })
116
117                 self.onLayoutFinish.append(self.setConditionalTitle)
118
119         def setConditionalTitle(self):
120                 self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
121
122         def up(self):
123                 self["content"].pageUp()
124
125         def down(self):
126                 self["content"].pageDown()
127
128         def next(self):
129                 if self.nextEntryCB is not None:
130                         (self.data, self.cur_idx, self.entries) = self.nextEntryCB()
131                         self.setContent()
132
133         def previous(self):
134                 if self.previousEntryCB is not None:
135                         (self.data, self.cur_idx, self.entries) = self.previousEntryCB()
136                         self.setContent()
137
138         def nextFeed(self):
139                 # Show next Feed
140                 if self.nextFeedCB is not None:
141                         result = self.nextFeedCB()
142                         self.feedTitle = result[0]
143                         self.entries = len(result[1])
144                         if self.entries:
145                                 self.cur_idx = 0
146                                 self.data = result[1][0]
147                         else:
148                                 self.cur_idx = None
149                                 self.data = None
150                         self.setConditionalTitle()
151                         self.setContent()
152
153         def previousFeed(self):
154                 # Show previous Feed
155                 if self.previousFeedCB is not None:
156                         result = self.previousFeedCB()
157                         self.feedTitle = result[0]
158                         self.entries = len(result[1])
159                         if self.entries:
160                                 self.cur_idx = 0
161                                 self.data = result[1][0]
162                         else:
163                                 self.cur_idx = None
164                                 self.data = None
165                         self.setConditionalTitle()
166                         self.setContent()
167
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))
171                 else:
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"])]))
175                 else:
176                         self["content"].setText("No such Item.")
177
178         def selectEnclosure(self):
179                 if self.data is not None:
180                         RSSBaseView.selectEnclosure(self, self.data[3])
181
182 class RSSFeedView(RSSBaseView):
183         """Shows a RSS-Feed"""
184         skin = """
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" />
189                 </screen>"""
190
191         def __init__(self, session, data, feedTitle = "", newItems=False, nextFeedCB=None, previousFeedCB=None, rssPoller=None, id = None):
192                 RSSBaseView.__init__(self, session, rssPoller)
193
194                 self.data = data
195                 self.feedTitle = feedTitle
196                 self.newItems = newItems
197                 self.id = id
198                 self.nextFeedCB=nextFeedCB
199                 self.previousFeedCB=previousFeedCB
200
201                 self["content"] = RSSEntryList(data)
202                 self["summary"] = Label()
203                 self["info"] = Label()
204
205                 if not newItems:
206                         self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions", "ColorActions" ], 
207                         {
208                                 "ok": self.showCurrentEntry,
209                                 "cancel": self.close,
210                                 "nextBouquet": self.next,
211                                 "prevBouquet": self.previous,
212                                 "menu": self.menu,
213                                 "yellow": self.selectEnclosure,
214                         })
215                         self.onShown.append(self.__show)
216                         self.onClose.append(self.__close)
217
218                         self.timer = None
219                 else:
220                         self["actions"] = ActionMap([ "OkCancelActions" ], 
221                         {
222                                 "cancel": self.close,
223                         })
224
225                         self.timer = eTimer()
226                         self.timer.timeout.get().append(self.timerTick)
227                         self.onExecBegin.append(self.startTimer)
228
229                 self["content"].connectSelChanged(self.updateInfo)
230                 self.onLayoutFinish.extend([self.updateInfo, self.setConditionalTitle])
231
232         def startTimer(self):
233                 self.timer.startLongTimer(5)
234
235         def timerTick(self):
236                 self.close()
237
238         def __show(self):
239                 self.rssPoller.addCallback(self.pollCallback)
240
241         def __close(self):
242                 if self.timer is not None:
243                         self.timer.timeout.get().remove(self.timerTick)
244                         self.timer = None
245                 self.rssPoller.removeCallback(self.pollCallback)
246
247         def pollCallback(self, id = None):
248                 print "[SimpleRSS] SimpleRSSFeed called back"
249                 current_entry = self["content"].getCurrentEntry()
250
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
255                 elif self.id == 0:
256                         print "[SimpleRSS] pollCallback recieved all or non-local feed, updating active view (new_items)"
257                         self.data = self.rssPoller.new_items
258                 else:
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
262
263                 self["content"].l.setList(self.data)
264                 self["content"].moveToEntry(current_entry)
265
266                 self.setConditionalTitle()
267                 self.updateInfo()
268
269         def setConditionalTitle(self):
270                 if not self.newItems:
271                         self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
272                 else:
273                         self.setTitle("Simple RSS Reader: New Items")
274
275         def updateInfo(self):
276                 current_entry = self["content"].getCurrentEntry()
277                 if current_entry:
278                         self["summary"].setText(current_entry[2])
279
280                         cur_idx = self["content"].getCurrentIndex()
281                         self["info"].setText("Entry %s/%s" % (cur_idx+1, len(self.data)))
282                 else:
283                         self["summary"].setText("Feed is empty.")
284                         self["info"].setText("")
285
286         def menu(self):
287                 if self.id > 0:
288                         self.singleUpdate(self.id-1)
289
290         def nextEntryCB(self):
291                 self["content"].moveDown()
292                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
293
294         def previousEntryCB(self):
295                 self["content"].moveUp()
296                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
297
298         # TODO: Fix moving back to previously marked entry (same goes for self.previous)
299         def next(self):
300                 # Show next Feed
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
310                         return result
311                 return (self.feedTitle, self.data, self.id)
312
313         def previous(self):
314                 # Show previous Feed
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
324                         return result
325                 return (self.feedTitle, self.data, self.id)
326
327         def checkEmpty(self):
328                 if self.id > 0 and not len(self.data):
329                         self.singleUpdate(self.id-1)
330
331         def showCurrentEntry(self):
332                 current_entry = self["content"].getCurrentEntry()
333                 if current_entry is None: # empty list
334                         return
335
336                 self.session.openWithCallback(
337                         self.updateInfo,
338                         RSSEntryView,
339                         current_entry,
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
347                 )
348
349         def selectEnclosure(self):
350                 current_entry = self["content"].getCurrentEntry()
351                 if current_entry is None: # empty list
352                         return
353
354                 RSSBaseView.selectEnclosure(self, current_entry[3])
355
356 class RSSOverview(RSSBaseView):
357         """Shows an Overview over all RSS-Feeds known to rssPoller"""
358         skin = """
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" />
363                 </screen>"""
364
365         def __init__(self, session, poller):
366                 RSSBaseView.__init__(self, session, poller)
367
368                 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ], 
369                 {
370                         "ok": self.showCurrentEntry,
371                         "cancel": self.close,
372                         "menu": self.menu,
373                         "yellow": self.selectEnclosure,
374                 })
375
376                 self.fillFeeds()
377
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))
382
383                 self["content"].connectSelChanged(self.updateInfo)
384                 self.onShown.append(self.__show)
385                 self.onClose.append(self.__close)
386
387         def __show(self):
388                 self.rssPoller.addCallback(self.pollCallback)
389
390         def __close(self):
391                 self.rssPoller.removeCallback(self.pollCallback)
392
393         def fillFeeds(self):
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
399
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])
403
404         def pollCallback(self, id = None):
405                 print "[SimpleRSS] SimpleRSS called back"
406                 current_entry = self["content"].getCurrentEntry()
407
408                 if id is not None:
409                         print "[SimpleRSS] pollCallback updating feed", id
410                 else:
411                         print "[SimpleRSS] pollCallback updating all feeds"
412                         self.fillFeeds()
413
414                 self["content"].l.setList(self.feeds)
415                 self["content"].moveToEntry(current_entry)
416
417                 self.updateInfo()
418
419         def updateInfo(self):
420                 current_entry = self["content"].getCurrentEntry()
421                 if current_entry:
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
425                 else:
426                         self["summary"].setText("")
427                         self["info"].setText("")
428
429         def menu(self):
430                 cur_idx = self["content"].getCurrentIndex()
431                 if cur_idx > 0:
432                         possible_actions = [
433                                 (_("Update Feed"), "update"),
434                                 (_("Setup"), "setup"),
435                                 (_("Close"), "close")
436                         ]
437                 else:
438                         possible_actions = [
439                                 (_("Setup"), "setup"),
440                                 (_("Close"), "close")
441                         ]
442                 self.session.openWithCallback(
443                         self.menuChoice,
444                         ChoiceBox,
445                         "What to do?",
446                         possible_actions
447                 )
448
449         def menuChoice(self, result):
450                 if result:
451                         if result[1] == "update":
452                                 cur_idx = self["content"].getCurrentIndex()
453                                 if cur_idx > 0:
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":
458                                 self.close()
459
460         def refresh(self):
461                 current_entry = self["content"].getCurrentEntry()
462
463                 self.fillFeeds()
464                 self["content"].l.setList(self.feeds)
465
466                 self["content"].moveToEntry(current_entry)
467                 self.updateInfo()
468
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())
473
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())
478
479         def showCurrentEntry(self):
480                 current_entry = self["content"].getCurrentEntry()
481                 if current_entry is None: # empty list
482                         return
483
484                 self.session.openWithCallback(
485                         self.refresh,
486                         RSSFeedView,
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()
493                 )
494
495         def selectEnclosure(self):
496                 current_entry = self["content"].getCurrentEntry()
497                 if current_entry is None: # empty list
498                         return
499
500                 # Build a list of all enclosures in this feed
501                 enclosures = []
502                 for entry in current_entry.history:
503                                 enclosures.extend(entry[3])
504                 RSSBaseView.selectEnclosure(self, enclosures)