initial checkin of MerlinSkinThemes
[enigma2-plugins.git] / simplerss / src / RSSScreens.py
1 from __future__ import print_function
2
3 from enigma import eTimer
4
5 from Screens.Screen import Screen
6 from Screens.MessageBox import MessageBox
7
8 from Components.ActionMap import ActionMap
9 from Components.ScrollLabel import ScrollLabel
10 from Components.Sources.List import List
11 from Components.Sources.StaticText import StaticText
12
13 from RSSList import RSSFeedList
14
15 class RSSSummary(Screen):
16         skin = """
17         <screen position="0,0" size="132,64">
18                 <widget source="parent.Title" render="Label" position="6,4" size="120,21" font="Regular;18" />
19                 <widget source="entry" render="Label" position="6,25" size="120,21" font="Regular;16" />
20                 <widget source="global.CurrentTime" render="Label" position="56,46" size="82,18" font="Regular;16" >
21                         <convert type="ClockToText">WithSeconds</convert>
22                 </widget>
23         </screen>"""
24
25         def __init__(self, session, parent):
26                 Screen.__init__(self, session, parent = parent)
27                 self["entry"] = StaticText("")
28                 parent.onChangedEntry.append(self.selectionChanged)
29                 self.onShow.append(parent.updateInfo)
30                 self.onClose.append(self.removeWatcher)
31
32         def removeWatcher(self):
33                 self.parent.onChangedEntry.remove(self.selectionChanged)
34
35         def selectionChanged(self, text):
36                 self["entry"].text = text
37
38 class RSSBaseView(Screen):
39         """Base Screen for all Screens used in SimpleRSS"""
40
41         def __init__(self, session, poller, parent = None):
42                 Screen.__init__(self, session, parent)
43                 self.onChangedEntry = []
44                 self.rssPoller = poller
45                 self.pollDialog = None
46
47         def createSummary(self):
48                 return RSSSummary
49
50         def errorPolling(self, errmsg = ""):
51                 # An error occured while polling
52                 self.session.open(
53                         MessageBox,
54                         _("Error while parsing Feed, this usually means there is something wrong with it."),
55                         type = MessageBox.TYPE_ERROR,
56                         timeout = 3
57                 )
58
59                 # Don't show "we're updating"-dialog any longer
60                 if self.pollDialog:
61                         self.pollDialog.close()
62                         self.pollDialog = None
63
64         def singleUpdate(self, feedid, errback = None):
65                 # Don't do anything if we have no poller
66                 if self.rssPoller is None:
67                         return
68
69                 # Default errorback to self.errorPolling
70                 # If an empty errorback is wanted the Screen needs to provide it
71                 if errback is None:
72                         errback = self.errorPolling
73
74                 # Tell Poller to poll
75                 self.rssPoller.singlePoll(feedid, callback=True, errorback=errback)
76
77                 # Open Dialog and save locally
78                 self.pollDialog = self.session.open(
79                         MessageBox,
80                         _("Update is being done in Background.\nContents will automatically be updated when it's done."),
81                         type = MessageBox.TYPE_INFO,
82                         timeout = 5
83                 )
84
85         def selectEnclosure(self, enclosures):
86                 # Empty List
87                 if enclosures is None:
88                         return
89
90                 from Components.Scanner import openList
91
92                 if not openList(self.session, enclosures):
93                         self.session.open(
94                                 MessageBox,
95                                 _("Found no Enclosure we can display."),
96                                 type = MessageBox.TYPE_INFO,
97                                 timeout = 5
98                         )
99
100 class RSSEntryView(RSSBaseView):
101         """Shows a RSS Item"""
102
103         skin = """
104                 <screen position="center,center" size="460,420" title="Simple RSS Reader" >
105                         <widget source="info" render="Label" position="0,0" size="460, 20" halign="right" font="Regular; 18" />
106                         <widget name="content" position="0,20" size="460,400" font="Regular; 22" />
107                 </screen>"""
108
109         def __init__(self, session, data, feedTitle="", cur_idx=None, entries=None, parent=None):
110                 RSSBaseView.__init__(self, session, None, parent)
111
112                 self.data = data
113                 self.feedTitle = feedTitle
114                 self.cur_idx = cur_idx
115                 self.entries = entries
116
117                 if cur_idx is not None and entries is not None:
118                         self["info"] = StaticText(_("Entry %s/%s") % (cur_idx+1, entries))
119                 else:
120                         self["info"] = StaticText()
121
122                 if data:
123                         self["content"] = ScrollLabel(''.join((data[0], '\n\n', data[2], '\n\n', str(len(data[3])), ' ',  _("Enclosures"))))
124                 else:
125                         self["content"] = ScrollLabel()
126
127                 self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "ColorActions", "DirectionActions" ],
128                 {
129                         "cancel": self.close,
130                         "ok": self.selectEnclosure,
131                         "yellow": self.selectEnclosure,
132                         "up": self.up,
133                         "down": self.down,
134                         "right": self.next,
135                         "left": self.previous,
136                         "nextBouquet": self.nextFeed,
137                         "prevBouquet": self.previousFeed,
138                 })
139
140                 self.onLayoutFinish.append(self.setConditionalTitle)
141
142         def setConditionalTitle(self):
143                 self.setTitle(_("Simple RSS Reader: %s") % (self.feedTitle))
144
145         def updateInfo(self):
146                 if self.data:
147                         text = self.data[0]
148                 else:
149                         text = _("No such Item.")
150
151                 for x in self.onChangedEntry:
152                         try:
153                                 x(text)
154                         except Exception:
155                                 pass
156
157         def up(self):
158                 self["content"].pageUp()
159
160         def down(self):
161                 self["content"].pageDown()
162
163         def next(self):
164                 if self.parent is not None:
165                         (self.data, self.cur_idx, self.entries) = self.parent.nextEntry()
166                         self.setContent()
167
168         def previous(self):
169                 if self.parent is not None:
170                         (self.data, self.cur_idx, self.entries) = self.parent.previousEntry()
171                         self.setContent()
172
173         def nextFeed(self):
174                 # Show next Feed
175                 if self.parent is not None:
176                         result = self.parent.next()
177                         self.feedTitle = result[0]
178                         self.entries = len(result[1])
179                         if self.entries:
180                                 self.cur_idx = 0
181                                 self.data = result[1][0]
182                         else:
183                                 self.cur_idx = None
184                                 self.data = None
185                         self.setConditionalTitle()
186                         self.setContent()
187
188         def previousFeed(self):
189                 # Show previous Feed
190                 if self.parent is not None:
191                         result = self.parent.previous()
192                         self.feedTitle = result[0]
193                         self.entries = len(result[1])
194                         if self.entries:
195                                 self.cur_idx = 0
196                                 self.data = result[1][0]
197                         else:
198                                 self.cur_idx = None
199                                 self.data = None
200                         self.setConditionalTitle()
201                         self.setContent()
202
203         def setContent(self):
204                 if self.cur_idx is not None and self.entries is not None:
205                         self["info"].text = _("Entry %s/%s") % (self.cur_idx+1, self.entries)
206                 else:
207                         self["info"].text = ""
208                 data = self.data
209                 if data:
210                         self["content"].setText(''.join((data[0], '\n\n', data[2], '\n\n', str(len(data[3])), ' ',  _("Enclosures"))))
211                 else:
212                         self["content"].setText(_("No such Item."))
213                 self.updateInfo()
214
215         def selectEnclosure(self):
216                 if self.data is not None:
217                         RSSBaseView.selectEnclosure(self, self.data[3])
218
219 class RSSFeedView(RSSBaseView):
220         """Shows a RSS-Feed"""
221
222         skin = """
223                 <screen position="center,center" size="460,415" title="Simple RSS Reader" >
224                         <widget source="info" render="Label" position="0,0" size="460,20" halign="right" font="Regular; 18" />
225                         <widget source="content" render="Listbox" position="0,20" size="460,300" scrollbarMode="showOnDemand">
226                                 <convert type="TemplatedMultiContent">
227                                         {"template": [
228                                                         MultiContentEntryText(pos=(0, 3), size=(460, 294), font=0, flags = RT_HALIGN_LEFT|RT_WRAP, text = 0)
229                                                 ],
230                                          "fonts": [gFont("Regular", 22)],
231                                          "itemHeight": 50
232                                         }
233                                 </convert>
234                         </widget>
235                         <widget source="summary" render="Label" position="0,320" size="460,95" font="Regular;16" />
236                 </screen>"""
237
238         def __init__(self, session, feed=None, newItems=False, parent=None, rssPoller=None,id=None):
239                 RSSBaseView.__init__(self, session, rssPoller, parent)
240
241                 self.feed = feed
242                 self.newItems = newItems
243                 self.id = id
244
245                 self["content"] = List(self.feed.history)
246                 self["summary"] = StaticText()
247                 self["info"] = StaticText()
248
249                 if not newItems:
250                         self["actions"] = ActionMap([ "OkCancelActions", "ChannelSelectBaseActions", "MenuActions", "ColorActions" ],
251                         {
252                                 "ok": self.showCurrentEntry,
253                                 "cancel": self.close,
254                                 "nextBouquet": self.next,
255                                 "prevBouquet": self.previous,
256                                 "menu": self.menu,
257                                 "yellow": self.selectEnclosure,
258                         })
259                         self.onLayoutFinish.append(self.__show)
260                         self.onClose.append(self.__close)
261
262                         self.timer = None
263                 else:
264                         self["actions"] = ActionMap([ "OkCancelActions" ],
265                         {
266                                 "cancel": self.close,
267                         })
268
269                         self.timer = eTimer()
270                         self.timer_conn = self.timer.timeout.connect(self.timerTick)
271                         self.onExecBegin.append(self.startTimer)
272
273                 self["content"].onSelectionChanged.append(self.updateInfo)
274                 self.onLayoutFinish.extend((
275                         self.updateInfo,
276                         self.setConditionalTitle
277                 ))
278
279         def startTimer(self):
280                 self.timer.startLongTimer(5)
281
282         def timerTick(self):
283                 self.timer_conn = None
284
285                 self.close()
286
287         def __show(self):
288                 self.rssPoller.addCallback(self.pollCallback)
289
290         def __close(self):
291                 if self.timer is not None:
292                         self.timer_conn = None
293                         self.timer = None
294                 self.rssPoller.removeCallback(self.pollCallback)
295
296         def pollCallback(self, id = None):
297                 print("[SimpleRSS] SimpleRSSFeed called back")
298
299                 if id is None or id+1 == self.id:
300                         self["content"].updateList(self.feed.history)
301                         self.setConditionalTitle()
302                         self.updateInfo()
303
304         def setConditionalTitle(self):
305                 self.setTitle(_("Simple RSS Reader: %s") % (self.feed.title))
306
307         def updateInfo(self):
308                 current_entry = self["content"].current
309                 if current_entry:
310                         self["summary"].text = current_entry[2]
311
312                         cur_idx = self["content"].index
313                         self["info"].text = _("Entry %s/%s") % (cur_idx+1, len(self.feed.history))
314                         summary_text = current_entry[0]
315                 else:
316                         self["summary"].text = _("Feed is empty.")
317                         self["info"].text = ""
318                         summary_text = _("Feed is empty.")
319
320                 for x in self.onChangedEntry:
321                         try:
322                                 x(summary_text)
323                         except Exception:
324                                 pass
325
326         def menu(self):
327                 if self.id > 0:
328                         self.singleUpdate(self.id-1)
329
330         def nextEntry(self):
331                 self["content"].selectNext()
332                 return (self["content"].current, self["content"].index, len(self.feed.history))
333
334         def previousEntry(self):
335                 self["content"].selectPrevious()
336                 return (self["content"].current, self["content"].index, len(self.feed.history))
337
338         def next(self):
339                 # Show next Feed
340                 if self.parent is not None:
341                         (self.feed, self.id) = self.parent.nextFeed()
342                         self["content"].list = self.feed.history
343                         self["content"].index = 0
344                         self.updateInfo()
345                         self.setConditionalTitle() # Update title
346                         return (self.feed.title, self.feed.history, self.id)
347                 return (self.feed.title, self.feed.history, self.id)
348
349         def previous(self):
350                 # Show previous Feed
351                 if self.parent is not None:
352                         (self.feed, self.id) = self.parent.previousFeed()
353                         self["content"].list = self.feed.history
354                         self["content"].index = 0
355                         self.updateInfo()
356                         self.setConditionalTitle() # Update title
357                         return (self.feed.title, self.feed.history, self.id)
358                 return (self.feed.title, self.feed.history, self.id)
359
360         def checkEmpty(self):
361                 if self.id > 0 and not len(self.feed.history):
362                         self.singleUpdate(self.id-1)
363
364         def showCurrentEntry(self):
365                 current_entry = self["content"].current
366                 if not current_entry: # empty list
367                         return
368
369                 self.session.openWithCallback(
370                         self.updateInfo,
371                         RSSEntryView,
372                         current_entry,
373                         cur_idx = self["content"].index,
374                         entries = len(self.feed.history),
375                         feedTitle = self.feed.title,
376                         parent = self
377                 )
378
379         def selectEnclosure(self):
380                 current_entry = self["content"].current
381                 if not current_entry: # empty list
382                         return
383
384                 RSSBaseView.selectEnclosure(self, current_entry[3])
385
386 class RSSOverview(RSSBaseView):
387         """Shows an Overview over all RSS-Feeds known to rssPoller"""
388
389         skin = """
390                 <screen position="center,center" size="460,415" title="Simple RSS Reader" >
391                         <widget source="info" render="Label" position="0,0" size="460,20" halign="right" font="Regular; 18" />
392                         <widget name="content" position="0,20" size="460,300" scrollbarMode="showOnDemand" />
393                         <widget source="summary" render="Label" position="0,320" size="460,95" font="Regular;16" />
394                 </screen>"""
395
396         def __init__(self, session, poller):
397                 RSSBaseView.__init__(self, session, poller)
398
399                 self["actions"] = ActionMap([ "OkCancelActions", "MenuActions", "ColorActions" ],
400                 {
401                         "ok": self.showCurrentEntry,
402                         "cancel": self.close,
403                         "menu": self.menu,
404                         "yellow": self.selectEnclosure,
405                 })
406
407                 self.fillFeeds()
408
409                 # We always have at least "New Items"-Feed
410                 self["content"] = RSSFeedList(self.feeds)
411                 self["summary"] = StaticText(' '.join((str(len(self.feeds[0][0].history)), _("Entries"))))
412                 self["info"] = StaticText(_("Feed %s/%s") % (1, len(self.feeds)))
413
414                 self["content"].connectSelChanged(self.updateInfo)
415                 self.onLayoutFinish.append(self.__show)
416                 self.onClose.append(self.__close)
417
418         def __show(self):
419                 self.rssPoller.addCallback(self.pollCallback)
420                 self.setTitle(_("Simple RSS Reader"))
421
422         def __close(self):
423                 self.rssPoller.removeCallback(self.pollCallback)
424
425         def fillFeeds(self):
426                 # Feedlist contains our virtual Feed and all real ones
427                 self.feeds = [(self.rssPoller.newItemFeed,)]
428                 self.feeds.extend([(feed,) for feed in self.rssPoller.feeds])
429
430         def pollCallback(self, id = None):
431                 print("[SimpleRSS] SimpleRSS called back")
432                 self.fillFeeds()
433                 self["content"].setList(self.feeds)
434                 self.updateInfo()
435                 self["content"].invalidate()
436
437         def updateInfo(self):
438                 current_entry = self["content"].getCurrent()
439                 self["summary"].text = ' '.join((str(len(current_entry.history)), _("Entries")))
440                 self["info"].text = _("Feed %s/%s") % (self["content"].getSelectedIndex()+1, len(self.feeds))
441                 summary_text = current_entry.title
442
443                 for x in self.onChangedEntry:
444                         try:
445                                 x(summary_text)
446                         except Exception:
447                                 pass
448
449         def menu(self):
450                 from Screens.ChoiceBox import ChoiceBox
451
452                 cur_idx = self["content"].getSelectedIndex()
453                 if cur_idx > 0:
454                         possible_actions = (
455                                 (_("Update Feed"), "update"),
456                                 (_("Setup"), "setup"),
457                                 (_("Close"), "close")
458                         )
459                 else:
460                         possible_actions = (
461                                 (_("Setup"), "setup"),
462                                 (_("Close"), "close")
463                         )
464
465                 self.session.openWithCallback(
466                         self.menuChoice,
467                         ChoiceBox,
468                         _("What to do?"),
469                         possible_actions
470                 )
471
472         def menuChoice(self, result):
473                 if result:
474                         if result[1] == "update":
475                                 cur_idx = self["content"].getSelectedIndex()
476                                 if cur_idx > 0:
477                                         self.singleUpdate(cur_idx-1)
478                         elif result[1] == "setup":
479                                 from RSSSetup import RSSSetup
480
481                                 self.session.openWithCallback(
482                                         self.refresh,
483                                         RSSSetup,
484                                         rssPoller=self.rssPoller
485                                 )
486                         elif result[1] == "close":
487                                 self.close()
488
489         def refresh(self):
490                 current_entry = self["content"].getCurrent()
491
492                 self.fillFeeds()
493                 self["content"].setList(self.feeds)
494
495                 self["content"].moveToEntry(current_entry)
496                 self.updateInfo()
497
498         def nextFeed(self):
499                 self["content"].up()
500                 return (self["content"].getCurrent(), self["content"].getSelectedIndex())
501
502         def previousFeed(self):
503                 self["content"].down()
504                 return (self["content"].getCurrent(), self["content"].getSelectedIndex())
505
506         def showCurrentEntry(self):
507                 current_entry = self["content"].getCurrent()
508                 self.session.openWithCallback(
509                         self.updateInfo,
510                         RSSFeedView,
511                         feed=current_entry,
512                         parent=self,
513                         rssPoller=self.rssPoller,
514                         id=self["content"].getSelectedIndex()
515                 )
516
517         def selectEnclosure(self):
518                 # Build a list of all enclosures in this feed
519                 enclosures = []
520                 for entry in self["content"].getCurrent().history:
521                                 enclosures.extend(entry[3])
522                 RSSBaseView.selectEnclosure(self, enclosures)
523