Possibility to not autostart and to shutdown poller when inactive,whitespace cleanup
[enigma2-plugins.git] / simplerss / src / RSSScreens.py
1 from Screens.Screen import Screen
2 from Screens.MessageBox import MessageBox
3 from Screens.ChoiceBox import ChoiceBox
4 from Components.ActionMap import ActionMap
5 from Components.Label import Label
6 from Components.ScrollLabel import ScrollLabel
7 from Components.Pixmap import Pixmap
8
9 from RSSList import RSSList
10 from RSSSetup import RSSSetup
11
12 class PictureView(Screen):
13         """Downloads a Picture, shows it and delete the temporary file"""
14
15         skin = """
16                 <screen position="100,100" size="460,400" title="Simple RSS Reader" >
17                         <widget name="content" position="0,0" size="460,400" alphatest="on"/>
18                 </screen>"""
19
20         filename = '/tmp/simplerss_enclosure'
21
22         def __init__(self, session, url):
23                 Screen.__init__(self, session)
24
25                 self.url = url
26
27                 self["actions"] = ActionMap([ "OkCancelActions" ], 
28                 {
29                         "ok": self.close,
30                         "cancel": self.close,
31                 })
32
33                 self["content"] = Pixmap()
34
35                 self.onLayoutFinish.append(self.fetchFile)
36
37         def fetchFile(self):
38                 # Fetch file
39                 from httpclient import getFile
40                 getFile(self.filename, self.url, callback=self.gotFile, errorback=self.error)
41
42         def gotFile(self, data = ""):
43                 # Determine Aspect
44                 from Components.AVSwitch import AVSwitch
45                 aspect = AVSwitch().getAspectRatioSetting()/2
46
47                 # Load Picture
48                 from enigma import loadPic
49                 ptr = loadPic(self.filename, 460, 400, aspect)
50
51                 # Show Picture
52                 self["content"].instance.setPixmap(ptr)
53
54                 # Remove Temporary File
55                 from os import unlink
56                 unlink(self.filename)
57
58         def error(self):
59                 self.session.open(
60                         MessageBox,
61                         "Error while loading Picture.",
62                         type = MessageBox.TYPE_ERROR,
63                         timeout = 3
64                 )
65                 self.close()
66
67 class RSSBaseView(Screen):
68         """Base Screen for all Screens used in SimpleRSS"""
69
70         def __init__(self, session):
71                 Screen.__init__(self, session)
72
73         def errorPolling(self, errmsg = ""):
74                 self.session.open(
75                         MessageBox,
76                         "Error while parsing Feed, this usually means there is something wrong with it.",
77                         type = MessageBox.TYPE_ERROR,
78                         timeout = 3
79                 )
80
81         def singleUpdate(self, feedid, errback = None):
82                 # Default errorback to self.errorPolling
83                 # If an empty errorback is wanted the Screen needs to provide it
84                 if errback is None:
85                         errback = self.errorPolling
86                 self.rssPoller.singlePoll(feedid, callback=True, errorback=errback)
87                 self.session.open(
88                         MessageBox,
89                         "Update is being done in Background.\nContents will automatically be updated when it's done.",
90                         type = MessageBox.TYPE_INFO,
91                         timeout = 5
92                 )
93
94         def selectEnclosure(self, enclosures):
95                 # Empty List
96                 if enclosures is None:
97                         return
98
99                 count = len(enclosures)
100                 # Select stream in ChoiceBox if more than one present
101                 if count > 1:
102                         self.session.openWithCallback(
103                                 self.enclosureSelected,
104                                 ChoiceBox,
105                                 "Select enclosure to play",
106                                 [(x[0][x[0].rfind("/")+1:].replace('%20', ' '), x) for x in enclosures]
107                         )
108                 # Play if one present
109                 elif count:
110                         self.enclosureSelected((None, enclosures[0]))
111
112         def enclosureSelected(self, enclosure):
113                 if enclosure:
114                         (url, type) = enclosure[1]
115
116                         print "[SimpleRSS] Trying to play back enclosure: url=%s, type=%s" % (url, type)
117
118                         if type in ["video/mpeg", "audio/mpeg"]:
119                                 # We should launch a Player here, but the MediaPlayer gets angry about our non-local sources
120                                 from enigma import eServiceReference
121                                 self.session.nav.playService(eServiceReference(4097, 0, url))
122                         elif type in ["image/jpeg", "image/png", "image/gif", "image/bmp"]:
123                                 self.session.open(PictureView, url)
124
125 class RSSEntryView(RSSBaseView):
126         """Shows a RSS Item"""
127         skin = """
128                 <screen position="100,100" size="460,420" title="Simple RSS Reader" >
129                         <widget name="info" position="0,0" size="460, 20" halign="right" font="Regular; 18" />
130                         <widget name="content" position="0,20" size="460,420" font="Regular; 22" />
131                 </screen>"""
132
133         def __init__(self, session, data, feedTitle="", cur_idx=None, entries=None, nextEntryCB=None, previousEntryCB=None, nextFeedCB=None, previousFeedCB=None):
134                 RSSBaseView.__init__(self, session)
135
136                 self.data = data
137                 self.feedTitle = feedTitle
138                 self.nextEntryCB = nextEntryCB
139                 self.previousEntryCB = previousEntryCB
140                 self.nextFeedCB = nextFeedCB
141                 self.previousFeedCB = previousFeedCB
142                 self.cur_idx = cur_idx
143                 self.entries = entries
144
145                 if cur_idx is not None and entries is not None:
146                         self["info"] = Label("Entry %s/%s" % (cur_idx+1, entries))
147                 else:
148                         self["info"] = Label()
149
150                 if data is not None:
151                         self["content"] = ScrollLabel("\n\n".join([data[0], data[2], " ".join([str(len(data[3])), "Enclosures"])]))
152                 else:
153                         self["content"] = ScrollLabel()
154
155                 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "ColorActions", "DirectionActions" ],
156                 {
157                         "cancel": self.close,
158                         "ok": self.selectEnclosure,
159                         "yellow": self.selectEnclosure,
160                         "up": self.up,
161                         "down": self.down,
162                         "right": self.next,
163                         "left": self.previous,
164                         "nextBouquet": self.nextFeed,
165                         "prevBouquet": self.previousFeed,
166                 })
167
168                 self.onLayoutFinish.append(self.setConditionalTitle)
169
170         def setConditionalTitle(self):
171                 self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
172
173         def up(self):
174                 self["content"].pageUp()
175
176         def down(self):
177                 self["content"].pageDown()
178
179         def next(self):
180                 if self.nextEntryCB is not None:
181                         (self.data, self.cur_idx, self.entries) = self.nextEntryCB()
182                         self.setContent()
183
184         def previous(self):
185                 if self.previousEntryCB is not None:
186                         (self.data, self.cur_idx, self.entries) = self.previousEntryCB()
187                         self.setContent()
188
189         def nextFeed(self):
190                 # Show next Feed
191                 if self.nextFeedCB is not None:
192                         result = self.nextFeedCB()
193                         self.feedTitle = result[0]
194                         self.entries = len(result[1])
195                         if self.entries:
196                                 self.cur_idx = 0
197                                 self.data = result[1][0]
198                         else:
199                                 self.cur_idx = None
200                                 self.data = None
201                         self.setConditionalTitle()
202                         self.setContent()
203
204         def previousFeed(self):
205                 # Show previous Feed
206                 if self.previousFeedCB is not None:
207                         result = self.previousFeedCB()
208                         self.feedTitle = result[0]
209                         self.entries = len(result[1])
210                         if self.entries:
211                                 self.cur_idx = 0
212                                 self.data = result[1][0]
213                         else:
214                                 self.cur_idx = None
215                                 self.data = None
216                         self.setConditionalTitle()
217                         self.setContent()
218
219         def setContent(self):
220                 if self.cur_idx is not None and self.entries is not None:
221                         self["info"].setText("Entry %s/%s" % (self.cur_idx+1, self.entries))
222                 else:
223                         self["info"].setText("")
224                 if self.data is not None:
225                         self["content"].setText("\n\n".join([self.data[0], self.data[2], " ".join([str(len(self.data[3])), "Enclosures"])]))
226                 else:
227                         self["content"].setText("No such Item.")
228
229         def selectEnclosure(self):
230                 if self.data is not None:
231                         RSSBaseView.selectEnclosure(self, self.data[3])
232
233 class RSSFeedView(RSSBaseView):
234         """Shows a RSS-Feed"""
235         skin = """
236                 <screen position="100,100" size="460,420" title="Simple RSS Reader" >
237                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
238                         <widget name="content" position="0,20" size="460,324" scrollbarMode="showOnDemand" />
239                         <widget name="summary" position="0,325" size="460,95" font="Regular;16" />
240                 </screen>"""
241
242         def __init__(self, session, data, feedTitle = "", newItems=False, nextFeedCB=None, previousFeedCB=None, rssPoller=None, id = None):
243                 RSSBaseView.__init__(self, session)
244
245                 self.data = data
246                 self.feedTitle = feedTitle
247                 self.newItems = newItems
248                 self.id = id
249                 self.nextFeedCB=nextFeedCB
250                 self.previousFeedCB=previousFeedCB
251                 self.rssPoller=rssPoller
252
253                 self["content"] = RSSList(data)
254                 self["summary"] = Label()
255                 self["info"] = Label()
256
257                 if not newItems:
258                         self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions", "ColorActions" ], 
259                         {
260                                 "ok": self.showCurrentEntry,
261                                 "cancel": self.close,
262                                 "nextBouquet": self.next,
263                                 "prevBouquet": self.previous,
264                                 "menu": self.menu,
265                                 "yellow": self.selectEnclosure,
266                         })
267                         self.onShown.append(self.__show)
268                         self.onClose.append(self.__close)
269                 
270                 self["content"].connectSelChanged(self.updateInfo)
271                 self.onLayoutFinish.extend([self.updateInfo, self.setConditionalTitle])
272
273         def __show(self):
274                 self.rssPoller.addCallback(self.pollCallback)
275
276         def __close(self):
277                 self.rssPoller.removeCallback(self.pollCallback)
278
279         def pollCallback(self, id = None):
280                 print "[SimpleRSS] SimpleRSSFeed called back"
281                 current_entry = self["content"].getCurrentEntry()
282
283                 if id is not None and self.id == id+1:
284                         print "[SimpleRSS] pollCallback recieved local feed", self.id
285                         self.feedTitle = self.rssPoller.feeds[id].title
286                         self.data = self.rssPoller.feeds[id].history
287                 elif self.id == 0:
288                         print "[SimpleRSS] pollCallback recieved all or non-local feed, updating active view (new_items)"
289                         self.data = self.rssPoller.new_items
290                 else:
291                         print "[SimpleRSS] pollCallback recieved all or non-local feed, updating", self.id
292                         self.feedTitle = self.rssPoller.feeds[self.id-1].title
293                         self.data = self.rssPoller.feeds[self-id-1].history
294
295                 self["content"].l.setList(self.data)
296                 self["content"].moveToEntry(current_entry)
297
298                 self.setConditionalTitle()
299                 self.updateInfo()
300
301         def setConditionalTitle(self):
302                 if not self.newItems:
303                         self.setTitle(': '.join(["Simple RSS Reader", self.feedTitle]))
304                 else:
305                         self.setTitle("Simple RSS Reader: New Items")
306
307         def updateInfo(self):
308                 current_entry = self["content"].getCurrentEntry()
309                 if current_entry:
310                         self["summary"].setText(current_entry[2])
311
312                         cur_idx = self["content"].getCurrentIndex()
313                         self["info"].setText("Entry %s/%s" % (cur_idx+1, len(self.data)))
314                 else:
315                         self["summary"].setText("Feed is empty.")
316                         self["info"].setText("")
317
318         def menu(self):
319                 if self.id > 0:
320                         self.singleUpdate(self.id-1)
321
322         def nextEntryCB(self):
323                 self["content"].moveDown()
324                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
325
326         def previousEntryCB(self):
327                 self["content"].moveUp()
328                 return (self["content"].getCurrentEntry(), self["content"].getCurrentIndex(), len(self.data))
329
330         # TODO: Fix moving back to previously marked entry (same goes for self.previous)
331         def next(self):
332                 # Show next Feed
333                 if self.nextFeedCB is not None:
334                         result = self.nextFeedCB()
335                         (self.feedTitle, self.data, self.id) = result
336                         #current_entry = self["content"].getCurrentEntry()
337                         self["content"].l.setList(self.data) # Update list
338                         self["content"].moveToIndex(0)
339                         #self["content"].moveToEntry(current_entry)
340                         self.updateInfo() # In case entry is no longer in history
341                         self.setConditionalTitle() # Update title
342                         return result
343                 return (self.feedTitle, self.data, self.id)
344
345         def previous(self):
346                 # Show previous Feed
347                 if self.previousFeedCB is not None:
348                         result = self.previousFeedCB()
349                         (self.feedTitle, self.data, self.id) = result
350                         #current_entry = self["content"].getCurrentEntry()
351                         self["content"].l.setList(self.data) # Update list
352                         self["content"].moveToIndex(0)
353                         #self["content"].moveToEntry(current_entry)
354                         self.updateInfo() # In case entry is no longer in history
355                         self.setConditionalTitle() # Update title
356                         return result
357                 return (self.feedTitle, self.data, self.id)
358
359         def showCurrentEntry(self):
360                 current_entry = self["content"].getCurrentEntry()
361                 if current_entry is None: # empty list
362                         return
363
364                 self.session.openWithCallback(
365                         self.updateInfo,
366                         RSSEntryView,
367                         current_entry,
368                         cur_idx=self["content"].getCurrentIndex(),
369                         entries=len(self.data),
370                         feedTitle=self.feedTitle,
371                         nextEntryCB=self.nextEntryCB,
372                         previousEntryCB=self.previousEntryCB,
373                         nextFeedCB=self.next,
374                         previousFeedCB=self.previous
375                 )
376
377         def selectEnclosure(self):
378                 current_entry = self["content"].getCurrentEntry()
379                 if current_entry is None: # empty list
380                         return
381
382                 RSSBaseView.selectEnclosure(self, current_entry[3])
383
384 class RSSOverview(RSSBaseView):
385         """Shows an Overview over all RSS-Feeds known to rssPoller"""
386         skin = """
387                 <screen position="100,100" size="460,420" title="Simple RSS Reader" >
388                         <widget name="info" position="0,0" size="460,20" halign="right" font="Regular; 18" />
389                         <widget name="content" position="0,20" size="460,304" scrollbarMode="showOnDemand" />
390                         <widget name="summary" position="0,325" size="460,95" font="Regular;16" />
391                 </screen>"""
392
393         def __init__(self, session, poller):
394                 RSSBaseView.__init__(self, session)
395
396                 self.rssPoller = poller
397                 
398                 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ], 
399                 {
400                         "ok": self.showCurrentEntry,
401                         "cancel": self.close,
402                         "menu": self.menu,
403                         "yellow": self.selectEnclosure,
404                 })
405
406                 self.fillFeeds()
407
408                 # We always have at least "New Items"-Feed
409                 self["content"] = RSSList(self.feeds)
410                 self["summary"] = Label(self.feeds[0][2])
411                 self["info"] = Label("Feed 1/%s" % len(self.feeds))
412
413                 self["content"].connectSelChanged(self.updateInfo)
414                 self.onShown.append(self.__show)
415                 self.onClose.append(self.__close)
416
417         def __show(self):
418                 self.rssPoller.addCallback(self.pollCallback)
419
420         def __close(self):
421                 self.rssPoller.removeCallback(self.pollCallback)
422
423         def fillFeeds(self):
424                 self.feeds = [(
425                         "New Items",
426                         "New Items since last Auto-Update",
427                         ' '.join([str(len(self.rssPoller.new_items)), "Entries"]),
428                         self.rssPoller.new_items
429                 )]
430                 self.feeds.extend([
431                         (
432                                 feed.title,
433                                 feed.description,
434                                 ' '.join([str(len(feed.history)), "Entries"]),
435                                 feed.history
436                         )
437                                 for feed in self.rssPoller.feeds
438                 ])
439
440         def pollCallback(self, id = None):
441                 print "[SimpleRSS] SimpleRSS called back"
442                 current_entry = self["content"].getCurrentEntry()
443
444                 if id is not None:
445                         print "[SimpleRSS] pollCallback updating feed", id
446                         self.feeds[id+1] = (
447                                 self.rssPoller.feeds[id].title,
448                                 self.rssPoller.feeds[id].description,
449                                 ' '.join([str(len(self.rssPoller.feeds[id].history)), "Entries"]),
450                                 self.rssPoller.feeds[id].history
451                         )
452                 else:
453                         print "[SimpleRSS] pollCallback updating all feeds"
454                         self.fillFeeds()
455
456                 self["content"].l.setList(self.feeds)
457                 self["content"].moveToEntry(current_entry)
458
459                 self.updateInfo()
460
461         def updateInfo(self):
462                 current_entry = self["content"].getCurrentEntry()
463                 if current_entry:
464                         self["summary"].setText(current_entry[2])
465                         self["info"].setText("Feed %s/%s" % (self["content"].getCurrentIndex()+1, len(self.feeds)))
466                 # Should never happen
467                 else:
468                         self["summary"].setText("")
469                         self["info"].setText("")
470
471         def menu(self):
472                 cur_idx = self["content"].getCurrentIndex()
473                 if cur_idx > 0:
474                         possible_actions = [
475                                 (_("Update Feed"), "update"),
476                                 (_("Setup"), "setup"),
477                                 (_("Close"), "close")
478                         ]
479                 else:
480                         possible_actions = [
481                                 (_("Setup"), "setup"),
482                                 (_("Close"), "close")
483                         ]
484                 self.session.openWithCallback(
485                         self.menuChoice,
486                         ChoiceBox,
487                         "What to do?",
488                         possible_actions
489                 )
490
491         def menuChoice(self, result):
492                 if result:
493                         if result[1] == "update":
494                                 cur_idx = self["content"].getCurrentIndex()
495                                 if cur_idx > 0:
496                                         self.singleUpdate(cur_idx-1)
497                         elif result[1] == "setup":
498                                 self.session.openWithCallback(self.refresh, RSSSetup, rssPoller=self.rssPoller)
499                         elif result[1] == "close":
500                                 self.close()
501
502         def refresh(self):
503                 current_entry = self["content"].getCurrentEntry()
504
505                 self.fillFeeds()
506                 self["content"].l.setList(self.feeds)
507
508                 self["content"].moveToEntry(current_entry)
509                 self.updateInfo()
510
511         def nextFeedCB(self):
512                 self["content"].moveUp()
513                 current_entry = self["content"].getCurrentEntry()
514                 return (current_entry[0], current_entry[3], self["content"].getCurrentIndex())
515
516         def previousFeedCB(self):
517                 self["content"].moveDown()
518                 current_entry = self["content"].getCurrentEntry()
519                 return (current_entry[0], current_entry[3], self["content"].getCurrentIndex())
520
521         def showCurrentEntry(self):
522                 current_entry = self["content"].getCurrentEntry()
523                 if current_entry is None: # empty list
524                         return
525
526                 self.session.openWithCallback(
527                         self.refresh,
528                         RSSFeedView,
529                         current_entry[3],
530                         feedTitle=current_entry[0],
531                         nextFeedCB=self.nextFeedCB,
532                         previousFeedCB=self.previousFeedCB,
533                         rssPoller=self.rssPoller,
534                         id=self["content"].getCurrentIndex()
535                 )
536
537         def selectEnclosure(self):
538                 current_entry = self["content"].getCurrentEntry()
539                 if current_entry is None: # empty list
540                         return
541
542                 # Build a list of all enclosures in this feed
543                 enclosures = []
544                 for entry in current_entry[3]:
545                                 enclosures.extend(entry[3])
546                 RSSBaseView.selectEnclosure(self, enclosures)