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