Always save new items in a BaseFeed,
[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 RSSFeedList, RSSEntryList
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, parent=None):
21                 Screen.__init__(self, session)
22                 self.rssPoller = poller
23                 self.pollDialog = None
24                 self.parent = parent
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, parent=None):
83                 RSSBaseView.__init__(self, session, None, parent)
84
85                 self.data = data
86                 self.feedTitle = feedTitle
87                 self.cur_idx = cur_idx
88                 self.entries = entries
89
90                 if cur_idx is not None and entries is not None:
91                         self["info"] = Label(_("Entry %s/%s") % (cur_idx+1, entries))
92                 else:
93                         self["info"] = Label()
94
95                 if data is not None:
96                         self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
97                 else:
98                         self["content"] = ScrollLabel()
99
100                 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "ColorActions", "DirectionActions" ],
101                 {
102                         "cancel": self.close,
103                         "ok": self.selectEnclosure,
104                         "yellow": self.selectEnclosure,
105                         "up": self.up,
106                         "down": self.down,
107                         "right": self.next,
108                         "left": self.previous,
109                         "nextBouquet": self.nextFeed,
110                         "prevBouquet": self.previousFeed,
111                 })
112
113                 self.onLayoutFinish.append(self.setConditionalTitle)
114
115         def setConditionalTitle(self):
116                 self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
117
118         def up(self):
119                 self["content"].pageUp()
120
121         def down(self):
122                 self["content"].pageDown()
123
124         def next(self):
125                 if self.parent is not None:
126                         (self.data, self.cur_idx, self.entries) = self.parent.nextEntry()
127                         self.setContent()
128
129         def previous(self):
130                 if self.parent is not None:
131                         (self.data, self.cur_idx, self.entries) = self.parent.previousEntry()
132                         self.setContent()
133
134         def nextFeed(self):
135                 # Show next Feed
136                 if self.parent is not None:
137                         result = self.parent.next()
138                         self.feedTitle = result[0]
139                         self.entries = len(result[1])
140                         if self.entries:
141                                 self.cur_idx = 0
142                                 self.data = result[1][0]
143                         else:
144                                 self.cur_idx = None
145                                 self.data = None
146                         self.setConditionalTitle()
147                         self.setContent()
148
149         def previousFeed(self):
150                 # Show previous Feed
151                 if self.parent is not None:
152                         result = self.parent.previous()
153                         self.feedTitle = result[0]
154                         self.entries = len(result[1])
155                         if self.entries:
156                                 self.cur_idx = 0
157                                 self.data = result[1][0]
158                         else:
159                                 self.cur_idx = None
160                                 self.data = None
161                         self.setConditionalTitle()
162                         self.setContent()
163
164         def setContent(self):
165                 if self.cur_idx is not None and self.entries is not None:
166                         self["info"].setText(_("Entry %s/%s") % (self.cur_idx+1, self.entries))
167                 else:
168                         self["info"].setText("")
169                 if self.data is not None:
170                         self["content"].setText("\n\n".join([self.data[0], self.data[2], " ".join([str(len(self.data[3])), _("Enclosures")])]))
171                 else:
172                         self["content"].setText(_("No such Item."))
173
174         def selectEnclosure(self):
175                 if self.data is not None:
176                         RSSBaseView.selectEnclosure(self, self.data[3])
177
178 class RSSFeedView(RSSBaseView):
179         """Shows a RSS-Feed"""
180         skin = """
181                 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
182                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
183                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
184                         <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
185                 </screen>"""
186
187         def __init__(self, session, feed=None, newItems=False, parent=None, rssPoller=None,id=None):
188                 RSSBaseView.__init__(self, session, rssPoller, parent)
189
190                 self.feed = feed
191                 self.newItems = newItems
192                 self.id = id
193
194                 self["content"] = RSSEntryList(self.feed.history)
195                 self["summary"] = Label()
196                 self["info"] = Label()
197
198                 if not newItems:
199                         self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions", "ColorActions" ], 
200                         {
201                                 "ok": self.showCurrentEntry,
202                                 "cancel": self.close,
203                                 "nextBouquet": self.next,
204                                 "prevBouquet": self.previous,
205                                 "menu": self.menu,
206                                 "yellow": self.selectEnclosure,
207                         })
208                         self.onLayoutFinish.append(self.__show)
209                         self.onClose.append(self.__close)
210
211                         self.timer = None
212                 else:
213                         self["actions"] = ActionMap([ "OkCancelActions" ], 
214                         {
215                                 "cancel": self.close,
216                         })
217
218                         self.timer = eTimer()
219                         self.timer.timeout.get().append(self.timerTick)
220                         self.onExecBegin.append(self.startTimer)
221
222                 self["content"].connectSelChanged(self.updateInfo)
223                 self.onLayoutFinish.extend([self.updateInfo, self.setConditionalTitle])
224
225         def startTimer(self):
226                 self.timer.startLongTimer(5)
227
228         def timerTick(self):
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.timeout.get().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"].getCurrentEntry()
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"].getCurrentEntry()
257                 if current_entry:
258                         self["summary"].setText(current_entry[2])
259
260                         cur_idx = self["content"].getCurrentIndex()
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"].moveDown()
272                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.feed.history))
273
274         def previousEntry(self):
275                 self["content"].moveUp()
276                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), 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"].getCurrentEntry()
284                         self["content"].l.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"].getCurrentEntry()
297                         self["content"].l.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"].getCurrentEntry()
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"].getCurrentIndex(),
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"].getCurrentEntry()
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         skin = """
334                 <screen position="100,100" size="460,415" title="Simple RSS Reader" >
335                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
336                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
337                         <widget name="summary" position="0,320" size="460,95" font="Regular;16" />
338                 </screen>"""
339
340         def __init__(self, session, poller):
341                 RSSBaseView.__init__(self, session, poller)
342
343                 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ], 
344                 {
345                         "ok": self.showCurrentEntry,
346                         "cancel": self.close,
347                         "menu": self.menu,
348                         "yellow": self.selectEnclosure,
349                 })
350
351                 self.fillFeeds()
352
353                 # We always have at least "New Items"-Feed
354                 self["content"] = RSSFeedList(self.feeds)
355                 self["summary"] = Label(' '.join([str(len(self.feeds[0][0].history)), _("Entries")]))
356                 self["info"] = Label(_("Feed %s/%s") % (1, len(self.feeds)))
357
358                 self["content"].connectSelChanged(self.updateInfo)
359                 self.onLayoutFinish.append(self.__show)
360                 self.onClose.append(self.__close)
361
362         def __show(self):
363                 self.rssPoller.addCallback(self.pollCallback)
364
365         def __close(self):
366                 self.rssPoller.removeCallback(self.pollCallback)
367
368         def fillFeeds(self):
369                 # Feedlist contains our virtual Feed and all real ones
370                 self.feeds = [(self.rssPoller.newItemFeed,)]
371                 self.feeds.extend([(feed,) for feed in self.rssPoller.feeds])
372
373         def pollCallback(self, id = None):
374                 print "[SimpleRSS] SimpleRSS called back"
375                 self.updateInfo()
376                 self["content"].invalidate()
377
378         def updateInfo(self):
379                 current_entry = self["content"].getCurrentEntry()
380                 self["summary"].setText(' '.join([str(len(current_entry.history)), _("Entries")]))
381                 self["info"].setText(_("Feed %s/%s") % (self["content"].getCurrentIndex()+1, len(self.feeds)))
382
383         def menu(self):
384                 cur_idx = self["content"].getCurrentIndex()
385                 if cur_idx > 0:
386                         possible_actions = [
387                                 (_("Update Feed"), "update"),
388                                 (_("Setup"), "setup"),
389                                 (_("Close"), "close")
390                         ]
391                 else:
392                         possible_actions = [
393                                 (_("Setup"), "setup"),
394                                 (_("Close"), "close")
395                         ]
396                 self.session.openWithCallback(
397                         self.menuChoice,
398                         ChoiceBox,
399                         "What to do?",
400                         possible_actions
401                 )
402
403         def menuChoice(self, result):
404                 if result:
405                         if result[1] == "update":
406                                 cur_idx = self["content"].getCurrentIndex()
407                                 if cur_idx > 0:
408                                         self.singleUpdate(cur_idx-1)
409                         elif result[1] == "setup":
410                                 self.session.openWithCallback(
411                                         self.refresh,
412                                         RSSSetup,
413                                         rssPoller=self.rssPoller
414                                 )
415                         elif result[1] == "close":
416                                 self.close()
417
418         def refresh(self):
419                 current_entry = self["content"].getCurrentEntry()
420
421                 self.fillFeeds()
422                 self["content"].l.setList(self.feeds)
423
424                 self["content"].moveToEntry(current_entry)
425                 self.updateInfo()
426
427         def nextFeed(self):
428                 self["content"].moveUp()
429                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex())
430
431         def previousFeed(self):
432                 self["content"].moveDown()
433                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex())
434
435         def showCurrentEntry(self):
436                 current_entry = self["content"].getCurrentEntry()
437                 self.session.openWithCallback(
438                         self.updateInfo,
439                         RSSFeedView,
440                         feed=current_entry,
441                         parent=self,
442                         rssPoller=self.rssPoller,
443                         id=self["content"].getCurrentIndex()
444                 )
445
446         def selectEnclosure(self):
447                 # Build a list of all enclosures in this feed
448                 enclosures = []
449                 for entry in self["content"].getCurrentEntry().history:
450                                 enclosures.extend(entry[3])
451                 RSSBaseView.selectEnclosure(self, enclosures)