sort plugins by name, give some ChoiceBoxes individual names
[vuplus_dvbapp] / lib / python / Screens / InfoBarGenerics.py
1 from ChannelSelection import ChannelSelection, BouquetSelector
2
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.Harddisk import harddiskmanager
6 from Components.Input import Input
7 from Components.Label import Label
8 from Components.PluginComponent import plugins
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Components.Sources.Boolean import Boolean
11 from Components.config import config, ConfigBoolean, ConfigClock
12 from Components.SystemInfo import SystemInfo
13 from EpgSelection import EPGSelection
14 from Plugins.Plugin import PluginDescriptor
15
16 from Screen import Screen
17 from Screens.ChoiceBox import ChoiceBox
18 from Screens.Dish import Dish
19 from Screens.EventView import EventViewEPGSelect, EventViewSimple
20 from Screens.InputBox import InputBox
21 from Screens.MessageBox import MessageBox
22 from Screens.MinuteInput import MinuteInput
23 from Screens.TimerSelection import TimerSelection
24 from Screens.PictureInPicture import PictureInPicture
25 from Screens.SubtitleDisplay import SubtitleDisplay
26 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
27 from Screens.SleepTimerEdit import SleepTimerEdit
28 from Screens.TimeDateInput import TimeDateInput
29 from ServiceReference import ServiceReference
30
31 from Tools import Notifications
32 from Tools.Directories import SCOPE_HDD, resolveFilename, fileExists
33
34 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
35         iPlayableService, eServiceReference, eEPGCache
36
37 from time import time, localtime, strftime
38 from os import stat as os_stat
39 from bisect import insort
40
41 from RecordTimer import RecordTimerEntry, RecordTimer
42
43 # hack alert!
44 from Menu import MainMenu, mdom
45
46 class InfoBarDish:
47         def __init__(self):
48                 self.dishDialog = self.session.instantiateDialog(Dish)
49
50 class InfoBarShowHide:
51         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
52         fancy animations. """
53         STATE_HIDDEN = 0
54         STATE_HIDING = 1
55         STATE_SHOWING = 2
56         STATE_SHOWN = 3
57
58         def __init__(self):
59                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
60                         {
61                                 "toggleShow": self.toggleShow,
62                                 "hide": self.hide,
63                         }, 1) # lower prio to make it possible to override ok and cancel..
64
65                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
66                         {
67                                 iPlayableService.evStart: self.serviceStarted,
68                         })
69
70                 self.__state = self.STATE_SHOWN
71                 self.__locked = 0
72
73                 self.hideTimer = eTimer()
74                 self.hideTimer.callback.append(self.doTimerHide)
75                 self.hideTimer.start(5000, True)
76
77                 self.onShow.append(self.__onShow)
78                 self.onHide.append(self.__onHide)
79
80         def serviceStarted(self):
81                 if self.execing:
82                         if config.usage.show_infobar_on_zap.value:
83                                 self.doShow()
84
85         def __onShow(self):
86                 self.__state = self.STATE_SHOWN
87                 self.startHideTimer()
88
89         def startHideTimer(self):
90                 if self.__state == self.STATE_SHOWN and not self.__locked:
91                         idx = config.usage.infobar_timeout.index
92                         if idx:
93                                 self.hideTimer.start(idx*1000, True)
94
95         def __onHide(self):
96                 self.__state = self.STATE_HIDDEN
97
98         def doShow(self):
99                 self.show()
100                 self.startHideTimer()
101
102         def doTimerHide(self):
103                 self.hideTimer.stop()
104                 if self.__state == self.STATE_SHOWN:
105                         self.hide()
106
107         def toggleShow(self):
108                 if self.__state == self.STATE_SHOWN:
109                         self.hide()
110                         self.hideTimer.stop()
111                 elif self.__state == self.STATE_HIDDEN:
112                         self.show()
113
114         def lockShow(self):
115                 self.__locked = self.__locked + 1
116                 if self.execing:
117                         self.show()
118                         self.hideTimer.stop()
119
120         def unlockShow(self):
121                 self.__locked = self.__locked - 1
122                 if self.execing:
123                         self.startHideTimer()
124
125 #       def startShow(self):
126 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
127 #               self.__state = self.STATE_SHOWN
128 #
129 #       def startHide(self):
130 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
131 #               self.__state = self.STATE_HIDDEN
132
133 class NumberZap(Screen):
134         def quit(self):
135                 self.Timer.stop()
136                 self.close(0)
137
138         def keyOK(self):
139                 self.Timer.stop()
140                 self.close(int(self["number"].getText()))
141
142         def keyNumberGlobal(self, number):
143                 self.Timer.start(3000, True)            #reset timer
144                 self.field = self.field + str(number)
145                 self["number"].setText(self.field)
146                 if len(self.field) >= 4:
147                         self.keyOK()
148
149         def __init__(self, session, number):
150                 Screen.__init__(self, session)
151                 self.field = str(number)
152
153                 self["channel"] = Label(_("Channel:"))
154
155                 self["number"] = Label(self.field)
156
157                 self["actions"] = NumberActionMap( [ "SetupActions" ],
158                         {
159                                 "cancel": self.quit,
160                                 "ok": self.keyOK,
161                                 "1": self.keyNumberGlobal,
162                                 "2": self.keyNumberGlobal,
163                                 "3": self.keyNumberGlobal,
164                                 "4": self.keyNumberGlobal,
165                                 "5": self.keyNumberGlobal,
166                                 "6": self.keyNumberGlobal,
167                                 "7": self.keyNumberGlobal,
168                                 "8": self.keyNumberGlobal,
169                                 "9": self.keyNumberGlobal,
170                                 "0": self.keyNumberGlobal
171                         })
172
173                 self.Timer = eTimer()
174                 self.Timer.callback.append(self.keyOK)
175                 self.Timer.start(3000, True)
176
177 class InfoBarNumberZap:
178         """ Handles an initial number for NumberZapping """
179         def __init__(self):
180                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
181                         {
182                                 "1": self.keyNumberGlobal,
183                                 "2": self.keyNumberGlobal,
184                                 "3": self.keyNumberGlobal,
185                                 "4": self.keyNumberGlobal,
186                                 "5": self.keyNumberGlobal,
187                                 "6": self.keyNumberGlobal,
188                                 "7": self.keyNumberGlobal,
189                                 "8": self.keyNumberGlobal,
190                                 "9": self.keyNumberGlobal,
191                                 "0": self.keyNumberGlobal,
192                         })
193
194         def keyNumberGlobal(self, number):
195 #               print "You pressed number " + str(number)
196                 if number == 0:
197                         if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
198                                 self.pipDoHandle0Action()
199                         else:
200                                 self.servicelist.recallPrevService()
201                 else:
202                         if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
203                                 self.session.openWithCallback(self.numberEntered, NumberZap, number)
204
205         def numberEntered(self, retval):
206 #               print self.servicelist
207                 if retval > 0:
208                         self.zapToNumber(retval)
209
210         def searchNumberHelper(self, serviceHandler, num, bouquet):
211                 servicelist = serviceHandler.list(bouquet)
212                 if not servicelist is None:
213                         while num:
214                                 serviceIterator = servicelist.getNext()
215                                 if not serviceIterator.valid(): #check end of list
216                                         break
217                                 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
218                                 if playable:
219                                         num -= 1;
220                         if not num: #found service with searched number ?
221                                 return serviceIterator, 0
222                 return None, num
223
224         def zapToNumber(self, number):
225                 bouquet = self.servicelist.bouquet_root
226                 service = None
227                 serviceHandler = eServiceCenter.getInstance()
228                 if not config.usage.multibouquet.value:
229                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
230                 else:
231                         bouquetlist = serviceHandler.list(bouquet)
232                         if not bouquetlist is None:
233                                 while number:
234                                         bouquet = bouquetlist.getNext()
235                                         if not bouquet.valid(): #check end of list
236                                                 break
237                                         if bouquet.flags & eServiceReference.isDirectory:
238                                                 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
239                 if not service is None:
240                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
241                                 self.servicelist.clearPath()
242                                 if self.servicelist.bouquet_root != bouquet:
243                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
244                                 self.servicelist.enterPath(bouquet)
245                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
246                         self.servicelist.zap()
247
248 config.misc.initialchannelselection = ConfigBoolean(default = True)
249
250 class InfoBarChannelSelection:
251         """ ChannelSelection - handles the channelSelection dialog and the initial
252         channelChange actions which open the channelSelection dialog """
253         def __init__(self):
254                 #instantiate forever
255                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
256
257                 if config.misc.initialchannelselection.value:
258                         self.onShown.append(self.firstRun)
259
260                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
261                         {
262                                 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
263                                 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
264                                 "zapUp": (self.zapUp, _("previous channel")),
265                                 "zapDown": (self.zapDown, _("next channel")),
266                                 "historyBack": (self.historyBack, _("previous channel in history")),
267                                 "historyNext": (self.historyNext, _("next channel in history")),
268                                 "openServiceList": (self.openServiceList, _("open servicelist")),
269                         })
270
271         def showTvChannelList(self, zap=False):
272                 self.servicelist.setModeTv()
273                 if zap:
274                         self.servicelist.zap()
275                 self.session.execDialog(self.servicelist)
276
277         def showRadioChannelList(self, zap=False):
278                 self.servicelist.setModeRadio()
279                 if zap:
280                         self.servicelist.zap()
281                 self.session.execDialog(self.servicelist)
282
283         def firstRun(self):
284                 self.onShown.remove(self.firstRun)
285                 config.misc.initialchannelselection.value = False
286                 config.misc.initialchannelselection.save()
287                 self.switchChannelDown()
288
289         def historyBack(self):
290                 self.servicelist.historyBack()
291
292         def historyNext(self):
293                 self.servicelist.historyNext()
294
295         def switchChannelUp(self):
296                 self.servicelist.moveUp()
297                 self.session.execDialog(self.servicelist)
298
299         def switchChannelDown(self):
300                 self.servicelist.moveDown()
301                 self.session.execDialog(self.servicelist)
302
303         def openServiceList(self):
304                 self.session.execDialog(self.servicelist)
305
306         def zapUp(self):
307                 if self.servicelist.inBouquet():
308                         prev = self.servicelist.getCurrentSelection()
309                         if prev:
310                                 prev = prev.toString()
311                                 while True:
312                                         if config.usage.quickzap_bouquet_change.value:
313                                                 if self.servicelist.atBegin():
314                                                         self.servicelist.prevBouquet()
315                                         self.servicelist.moveUp()
316                                         cur = self.servicelist.getCurrentSelection()
317                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
318                                                 break
319                 else:
320                         self.servicelist.moveUp()
321                 self.servicelist.zap()
322
323         def zapDown(self):
324                 if self.servicelist.inBouquet():
325                         prev = self.servicelist.getCurrentSelection()
326                         if prev:
327                                 prev = prev.toString()
328                                 while True:
329                                         if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
330                                                 self.servicelist.nextBouquet()
331                                         else:
332                                                 self.servicelist.moveDown()
333                                         cur = self.servicelist.getCurrentSelection()
334                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
335                                                 break
336                 else:
337                         self.servicelist.moveDown()
338                 self.servicelist.zap()
339
340 class InfoBarMenu:
341         """ Handles a menu action, to open the (main) menu """
342         def __init__(self):
343                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
344                         {
345                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
346                         })
347                 self.session.infobar = None
348
349         def mainMenu(self):
350                 print "loading mainmenu XML..."
351                 menu = mdom.getroot()
352                 assert menu.tag == "menu", "root element in menu must be 'menu'!"
353
354                 self.session.infobar = self
355                 # so we can access the currently active infobar from screens opened from within the mainmenu
356                 # at the moment used from the SubserviceSelection
357
358                 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
359
360         def mainMenuClosed(self, *val):
361                 self.session.infobar = None
362
363 class InfoBarSimpleEventView:
364         """ Opens the Eventview for now/next """
365         def __init__(self):
366                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
367                         {
368                                 "showEventInfo": (self.openEventView, _("show event details")),
369                         })
370
371         def openEventView(self):
372                 epglist = [ ]
373                 self.epglist = epglist
374                 service = self.session.nav.getCurrentService()
375                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
376                 info = service.info()
377                 ptr=info.getEvent(0)
378                 if ptr:
379                         epglist.append(ptr)
380                 ptr=info.getEvent(1)
381                 if ptr:
382                         epglist.append(ptr)
383                 if epglist:
384                         self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
385
386         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
387                 epglist = self.epglist
388                 if len(epglist) > 1:
389                         tmp = epglist[0]
390                         epglist[0] = epglist[1]
391                         epglist[1] = tmp
392                         setEvent(epglist[0])
393
394 class SimpleServicelist:
395         def __init__(self, services):
396                 self.services = services
397                 self.length = len(services)
398                 self.current = 0
399
400         def selectService(self, service):
401                 self.current = 0
402                 while self.services[self.current].ref != service:
403                         self.current += 1
404
405         def nextService(self):
406                 if self.current+1 < self.length:
407                         self.current += 1
408                 else:
409                         self.current = 0
410
411         def prevService(self):
412                 if self.current-1 > -1:
413                         self.current -= 1
414                 else:
415                         self.current = self.length - 1
416
417         def currentService(self):
418                 return self.services[self.current]
419
420 class InfoBarEPG:
421         """ EPG - Opens an EPG list when the showEPGList action fires """
422         def __init__(self):
423                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
424                         {
425                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
426                         })
427
428                 self.is_now_next = False
429                 self.dlg_stack = [ ]
430                 self.bouquetSel = None
431                 self.eventView = None
432                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
433                         {
434                                 "showEventInfo": (self.openEventView, _("show EPG...")),
435                                 "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
436                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
437                         })
438
439         def showEventInfoWhenNotVisible(self):
440                 if self.shown:
441                         self.openEventView()
442                 else:
443                         self.toggleShow()
444                         return 1
445
446         def zapToService(self, service):
447                 if not service is None:
448                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
449                                 self.servicelist.clearPath()
450                                 if self.servicelist.bouquet_root != self.epg_bouquet:
451                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
452                                 self.servicelist.enterPath(self.epg_bouquet)
453                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
454                         self.servicelist.zap()
455
456         def getBouquetServices(self, bouquet):
457                 services = [ ]
458                 servicelist = eServiceCenter.getInstance().list(bouquet)
459                 if not servicelist is None:
460                         while True:
461                                 service = servicelist.getNext()
462                                 if not service.valid(): #check if end of list
463                                         break
464                                 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
465                                         continue
466                                 services.append(ServiceReference(service))
467                 return services
468
469         def openBouquetEPG(self, bouquet, withCallback=True):
470                 services = self.getBouquetServices(bouquet)
471                 if services:
472                         self.epg_bouquet = bouquet
473                         if withCallback:
474                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
475                         else:
476                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
477
478         def changeBouquetCB(self, direction, epg):
479                 if self.bouquetSel:
480                         if direction > 0:
481                                 self.bouquetSel.down()
482                         else:
483                                 self.bouquetSel.up()
484                         bouquet = self.bouquetSel.getCurrent()
485                         services = self.getBouquetServices(bouquet)
486                         if services:
487                                 self.epg_bouquet = bouquet
488                                 epg.setServices(services)
489
490         def closed(self, ret=False):
491                 closedScreen = self.dlg_stack.pop()
492                 if self.bouquetSel and closedScreen == self.bouquetSel:
493                         self.bouquetSel = None
494                 elif self.eventView and closedScreen == self.eventView:
495                         self.eventView = None
496                 if ret:
497                         dlgs=len(self.dlg_stack)
498                         if dlgs > 0:
499                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
500
501         def openMultiServiceEPG(self, withCallback=True):
502                 bouquets = self.servicelist.getBouquetList()
503                 if bouquets is None:
504                         cnt = 0
505                 else:
506                         cnt = len(bouquets)
507                 if cnt > 1: # show bouquet list
508                         if withCallback:
509                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
510                                 self.dlg_stack.append(self.bouquetSel)
511                         else:
512                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
513                 elif cnt == 1:
514                         self.openBouquetEPG(bouquets[0][1], withCallback)
515
516         def changeServiceCB(self, direction, epg):
517                 if self.serviceSel:
518                         if direction > 0:
519                                 self.serviceSel.nextService()
520                         else:
521                                 self.serviceSel.prevService()
522                         epg.setService(self.serviceSel.currentService())
523
524         def SingleServiceEPGClosed(self, ret=False):
525                 self.serviceSel = None
526
527         def openSingleServiceEPG(self):
528                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
529                 if ref:
530                         if self.servicelist.getMutableList() is not None: # bouquet in channellist
531                                 current_path = self.servicelist.getRoot()
532                                 services = self.getBouquetServices(current_path)
533                                 self.serviceSel = SimpleServicelist(services)
534                                 self.serviceSel.selectService(ref)
535                                 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
536                         else:
537                                 self.session.open(EPGSelection, ref)
538
539         def showEventInfoPlugins(self):
540                 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
541
542                 if list:
543                         list.append((_("show single service EPG..."), self.openSingleServiceEPG))
544                         self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
545                 else:
546                         self.openSingleServiceEPG()
547                         
548         def runPlugin(self, plugin):
549                 plugin(session = self.session, servicelist = self.servicelist)
550                 
551         def EventInfoPluginChosen(self, answer):
552                 if answer is not None:
553                         answer[1]()
554
555         def openSimilarList(self, eventid, refstr):
556                 self.session.open(EPGSelection, refstr, None, eventid)
557
558         def getNowNext(self):
559                 epglist = [ ]
560                 service = self.session.nav.getCurrentService()
561                 info = service and service.info()
562                 ptr = info and info.getEvent(0)
563                 if ptr:
564                         epglist.append(ptr)
565                 ptr = info and info.getEvent(1)
566                 if ptr:
567                         epglist.append(ptr)
568                 self.epglist = epglist
569
570         def __evEventInfoChanged(self):
571                 if self.is_now_next and len(self.dlg_stack) == 1:
572                         self.getNowNext()
573                         assert self.eventView
574                         if self.epglist:
575                                 self.eventView.setEvent(self.epglist[0])
576
577         def openEventView(self):
578                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
579                 self.getNowNext()
580                 epglist = self.epglist
581                 if not epglist:
582                         self.is_now_next = False
583                         epg = eEPGCache.getInstance()
584                         ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
585                         if ptr:
586                                 epglist.append(ptr)
587                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
588                                 if ptr:
589                                         epglist.append(ptr)
590                 else:
591                         self.is_now_next = True
592                 if epglist:
593                         self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
594                         self.dlg_stack.append(self.eventView)
595                 else:
596                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
597                         self.openMultiServiceEPG(False)
598
599         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
600                 epglist = self.epglist
601                 if len(epglist) > 1:
602                         tmp = epglist[0]
603                         epglist[0]=epglist[1]
604                         epglist[1]=tmp
605                         setEvent(epglist[0])
606
607 class InfoBarRdsDecoder:
608         """provides RDS and Rass support/display"""
609         def __init__(self):
610                 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
611                 self.rass_interactive = None
612
613                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
614                         {
615                                 iPlayableService.evEnd: self.__serviceStopped,
616                                 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
617                         })
618
619                 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
620                 {
621                         "startRassInteractive": self.startRassInteractive
622                 },-1)
623
624                 self["RdsActions"].setEnabled(False)
625
626                 self.onLayoutFinish.append(self.rds_display.show)
627                 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
628
629         def RassInteractivePossibilityChanged(self, state):
630                 self["RdsActions"].setEnabled(state)
631
632         def RassSlidePicChanged(self):
633                 if not self.rass_interactive:
634                         service = self.session.nav.getCurrentService()
635                         decoder = service and service.rdsDecoder()
636                         if decoder:
637                                 decoder.showRassSlidePicture()
638
639         def __serviceStopped(self):
640                 if self.rass_interactive is not None:
641                         rass_interactive = self.rass_interactive
642                         self.rass_interactive = None
643                         rass_interactive.close()
644
645         def startRassInteractive(self):
646                 self.rds_display.hide()
647                 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
648
649         def RassInteractiveClosed(self, *val):
650                 if self.rass_interactive is not None:
651                         self.rass_interactive = None
652                         self.RassSlidePicChanged()
653                 self.rds_display.show()
654
655 class InfoBarSeek:
656         """handles actions like seeking, pause"""
657
658         SEEK_STATE_PLAY = (0, 0, 0, ">")
659         SEEK_STATE_PAUSE = (1, 0, 0, "||")
660         SEEK_STATE_EOF = (1, 0, 0, "END")
661
662         def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True):
663                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
664                         {
665                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
666                                 iPlayableService.evStart: self.__serviceStarted,
667
668                                 iPlayableService.evEOF: self.__evEOF,
669                                 iPlayableService.evSOF: self.__evSOF,
670                         })
671
672                 self.minSpeedBackward = useSeekBackHack and 16 or 0
673
674                 class InfoBarSeekActionMap(HelpableActionMap):
675                         def __init__(self, screen, *args, **kwargs):
676                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
677                                 self.screen = screen
678
679                         def action(self, contexts, action):
680                                 print "action:", action
681                                 if action[:5] == "seek:":
682                                         time = int(action[5:])
683                                         self.screen.doSeekRelative(time * 90000)
684                                         return 1
685                                 elif action[:8] == "seekdef:":
686                                         key = int(action[8:])
687                                         time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
688                                                 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
689                                                 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
690                                         self.screen.doSeekRelative(time * 90000)
691                                         return 1                                        
692                                 else:
693                                         return HelpableActionMap.action(self, contexts, action)
694
695                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
696                         {
697                                 "playpauseService": self.playpauseService,
698                                 "pauseService": (self.pauseService, _("pause")),
699                                 "unPauseService": (self.unPauseService, _("continue")),
700
701                                 "seekFwd": (self.seekFwd, _("skip forward")),
702                                 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
703                                 "seekBack": (self.seekBack, _("skip backward")),
704                                 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
705                         }, prio=-1)
706                         # give them a little more priority to win over color buttons
707
708                 self["SeekActions"].setEnabled(False)
709
710                 self.seekstate = self.SEEK_STATE_PLAY
711                 self.lastseekstate = self.SEEK_STATE_PLAY
712
713                 self.onPlayStateChanged = [ ]
714
715                 self.lockedBecauseOfSkipping = False
716
717                 self.__seekableStatusChanged()
718
719         def makeStateForward(self, n):
720                 minspeed = config.seek.stepwise_minspeed.value
721                 repeat = int(config.seek.stepwise_repeat.value)
722                 if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
723                         return (0, n * repeat, repeat, ">> %dx" % n)
724                 else:
725                         return (0, n, 0, ">> %dx" % n)
726
727         def makeStateBackward(self, n):
728                 minspeed = config.seek.stepwise_minspeed.value
729                 repeat = int(config.seek.stepwise_repeat.value)
730                 if self.minSpeedBackward and n < self.minSpeedBackward:
731                         r = (self.minSpeedBackward - 1)/ n + 1
732                         if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
733                                 r = max(r, repeat)
734                         return (0, -n * r, r, "<< %dx" % n)
735                 elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
736                         return (0, -n * repeat, repeat, "<< %dx" % n)
737                 else:
738                         return (0, -n, 0, "<< %dx" % n)
739
740         def makeStateSlowMotion(self, n):
741                 return (0, 0, n, "/%d" % n)
742
743         def isStateForward(self, state):
744                 return state[1] > 1
745
746         def isStateBackward(self, state):
747                 return state[1] < 0
748
749         def isStateSlowMotion(self, state):
750                 return state[1] == 0 and state[2] > 1
751
752         def getHigher(self, n, lst):
753                 for x in lst:
754                         if x > n:
755                                 return x
756                 return False
757
758         def getLower(self, n, lst):
759                 lst = lst[:]
760                 lst.reverse()
761                 for x in lst:
762                         if x < n:
763                                 return x
764                 return False
765
766         def showAfterSeek(self):
767                 if isinstance(self, InfoBarShowHide):
768                         self.doShow()
769
770         def up(self):
771                 pass
772
773         def down(self):
774                 pass
775
776         def getSeek(self):
777                 service = self.session.nav.getCurrentService()
778                 if service is None:
779                         return None
780
781                 seek = service.seek()
782
783                 if seek is None or not seek.isCurrentlySeekable():
784                         return None
785
786                 return seek
787
788         def isSeekable(self):
789                 if self.getSeek() is None:
790                         return False
791                 return True
792
793         def __seekableStatusChanged(self):
794 #               print "seekable status changed!"
795                 if not self.isSeekable():
796                         self["SeekActions"].setEnabled(False)
797 #                       print "not seekable, return to play"
798                         self.setSeekState(self.SEEK_STATE_PLAY)
799                 else:
800                         self["SeekActions"].setEnabled(True)
801 #                       print "seekable"
802
803         def __serviceStarted(self):
804                 self.seekstate = self.SEEK_STATE_PLAY
805                 self.__seekableStatusChanged()
806
807         def setSeekState(self, state):
808                 service = self.session.nav.getCurrentService()
809
810                 if service is None:
811                         return False
812
813                 if not self.isSeekable():
814                         if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
815                                 state = self.SEEK_STATE_PLAY
816
817                 pauseable = service.pause()
818
819                 if pauseable is None:
820                         print "not pauseable."
821                         state = self.SEEK_STATE_PLAY
822
823                 oldstate = self.seekstate
824                 self.seekstate = state
825
826                 for i in (0, 1, 2):
827                         if oldstate[i] != self.seekstate[i]:
828                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
829
830                 for c in self.onPlayStateChanged:
831                         c(self.seekstate)
832
833                 self.checkSkipShowHideLock()
834
835                 return True
836
837         def playpauseService(self):
838                 if self.seekstate != self.SEEK_STATE_PLAY:
839                         self.unPauseService()
840                 else:
841                         self.pauseService()
842
843         def pauseService(self):
844                 if self.seekstate == self.SEEK_STATE_PAUSE:
845                         if config.seek.on_pause.value == "play":
846                                 self.unPauseService()
847                         elif config.seek.on_pause.value == "step":
848                                 self.doSeekRelative(0)
849                         elif config.seek.on_pause.value == "last":
850                                 self.setSeekState(self.lastseekstate)
851                                 self.lastseekstate = self.SEEK_STATE_PLAY
852                 else:
853                         if self.seekstate != self.SEEK_STATE_EOF:
854                                 self.lastseekstate = self.seekstate
855                         self.setSeekState(self.SEEK_STATE_PAUSE);
856
857         def unPauseService(self):
858                 print "unpause"
859                 if self.seekstate == self.SEEK_STATE_PLAY:
860                         return 0
861                 self.setSeekState(self.SEEK_STATE_PLAY)
862
863         def doSeek(self, pts):
864                 seekable = self.getSeek()
865                 if seekable is None:
866                         return
867                 seekable.seekTo(pts)
868
869         def doSeekRelative(self, pts):
870                 seekable = self.getSeek()
871                 if seekable is None:
872                         return
873                 prevstate = self.seekstate
874
875                 if self.seekstate == self.SEEK_STATE_EOF:
876                         if prevstate == self.SEEK_STATE_PAUSE:
877                                 self.setSeekState(self.SEEK_STATE_PAUSE)
878                         else:
879                                 self.setSeekState(self.SEEK_STATE_PLAY)
880                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
881                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
882                         self.showAfterSeek()
883
884         def seekFwd(self):
885                 if self.seekstate == self.SEEK_STATE_PLAY:
886                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
887                 elif self.seekstate == self.SEEK_STATE_PAUSE:
888                         if len(config.seek.speeds_slowmotion.value):
889                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
890                         else:
891                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
892                 elif self.seekstate == self.SEEK_STATE_EOF:
893                         pass
894                 elif self.isStateForward(self.seekstate):
895                         speed = self.seekstate[1]
896                         if self.seekstate[2]:
897                                 speed /= self.seekstate[2]
898                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
899                         self.setSeekState(self.makeStateForward(speed))
900                 elif self.isStateBackward(self.seekstate):
901                         speed = -self.seekstate[1]
902                         if self.seekstate[2]:
903                                 speed /= self.seekstate[2]
904                         speed = self.getLower(speed, config.seek.speeds_backward.value)
905                         if speed:
906                                 self.setSeekState(self.makeStateBackward(speed))
907                         else:
908                                 self.setSeekState(self.SEEK_STATE_PLAY)
909                 elif self.isStateSlowMotion(self.seekstate):
910                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
911                         self.setSeekState(self.makeStateSlowMotion(speed))
912
913         def seekBack(self):
914                 seekstate = self.seekstate
915                 if seekstate == self.SEEK_STATE_PLAY:
916                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
917                 elif seekstate == self.SEEK_STATE_EOF:
918                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
919                         self.doSeekRelative(-6)
920                 elif seekstate == self.SEEK_STATE_PAUSE:
921                         self.doSeekRelative(-3)
922                 elif self.isStateForward(seekstate):
923                         speed = seekstate[1]
924                         if seekstate[2]:
925                                 speed /= seekstate[2]
926                         speed = self.getLower(speed, config.seek.speeds_forward.value)
927                         if speed:
928                                 self.setSeekState(self.makeStateForward(speed))
929                         else:
930                                 self.setSeekState(self.SEEK_STATE_PLAY)
931                 elif self.isStateBackward(seekstate):
932                         speed = -seekstate[1]
933                         if seekstate[2]:
934                                 speed /= seekstate[2]
935                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
936                         self.setSeekState(self.makeStateBackward(speed))
937                 elif self.isStateSlowMotion(seekstate):
938                         speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
939                         if speed:
940                                 self.setSeekState(self.makeStateSlowMotion(speed))
941                         else:
942                                 self.setSeekState(self.SEEK_STATE_PAUSE)
943
944         def seekFwdManual(self):
945                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
946
947         def fwdSeekTo(self, minutes):
948                 print "Seek", minutes, "minutes forward"
949                 self.doSeekRelative(minutes * 60 * 90000)
950
951         def seekBackManual(self):
952                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
953
954         def rwdSeekTo(self, minutes):
955                 print "rwdSeekTo"
956                 self.doSeekRelative(-minutes * 60 * 90000)
957
958         def checkSkipShowHideLock(self):
959                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
960
961                 if config.usage.show_infobar_on_skip.value:
962                         if self.lockedBecauseOfSkipping and not wantlock:
963                                 self.unlockShow()
964                                 self.lockedBecauseOfSkipping = False
965
966                         if wantlock and not self.lockedBecauseOfSkipping:
967                                 self.lockShow()
968                                 self.lockedBecauseOfSkipping = True
969
970         def calcRemainingTime(self):
971                 seekable = self.getSeek()
972                 if seekable is not None:
973                         len = seekable.getLength()
974                         try:
975                                 tmp = self.cueGetEndCutPosition()
976                                 if tmp:
977                                         len = [False, tmp]
978                         except:
979                                 pass
980                         pos = seekable.getPlayPosition()
981                         speednom = self.seekstate[1] or 1
982                         speedden = self.seekstate[2] or 1
983                         if not len[0] and not pos[0]:
984                                 if len[1] <= pos[1]:
985                                         return 0
986                                 time = (len[1] - pos[1])*speedden/(90*speednom)
987                                 return time
988                 return False
989                 
990         def __evEOF(self):
991                 if self.seekstate == self.SEEK_STATE_EOF:
992                         return
993
994                 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
995                 seekstate = self.seekstate
996                 if self.seekstate != self.SEEK_STATE_PAUSE:
997                         self.setSeekState(self.SEEK_STATE_EOF)
998
999                 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1000                         seekable = self.getSeek()
1001                         if seekable is not None:
1002                                 seekable.seekTo(-1)
1003                 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1004                         self.doEofInternal(True)
1005                 else:
1006                         self.doEofInternal(False)
1007
1008         def doEofInternal(self, playing):
1009                 pass            # Defined in subclasses
1010
1011         def __evSOF(self):
1012                 self.setSeekState(self.SEEK_STATE_PLAY)
1013                 self.doSeek(0)
1014
1015 from Screens.PVRState import PVRState, TimeshiftState
1016
1017 class InfoBarPVRState:
1018         def __init__(self, screen=PVRState, force_show = False):
1019                 self.onPlayStateChanged.append(self.__playStateChanged)
1020                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1021                 self.onShow.append(self._mayShow)
1022                 self.onHide.append(self.pvrStateDialog.hide)
1023                 self.force_show = force_show
1024
1025         def _mayShow(self):
1026                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1027                         self.pvrStateDialog.show()
1028
1029         def __playStateChanged(self, state):
1030                 playstateString = state[3]
1031                 self.pvrStateDialog["state"].setText(playstateString)
1032                 
1033                 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1034                 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1035                         self.pvrStateDialog.hide()
1036                 else:
1037                         self._mayShow()
1038                         
1039
1040 class InfoBarTimeshiftState(InfoBarPVRState):
1041         def __init__(self):
1042                 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1043
1044         def _mayShow(self):
1045                 if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
1046                         self.pvrStateDialog.show()
1047
1048 class InfoBarShowMovies:
1049
1050         # i don't really like this class.
1051         # it calls a not further specified "movie list" on up/down/movieList,
1052         # so this is not more than an action map
1053         def __init__(self):
1054                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1055                         {
1056                                 "movieList": (self.showMovies, _("movie list")),
1057                                 "up": (self.showMovies, _("movie list")),
1058                                 "down": (self.showMovies, _("movie list"))
1059                         })
1060
1061 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1062
1063 # Hrmf.
1064 #
1065 # Timeshift works the following way:
1066 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1067 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1068 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1069 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1070 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1071 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1072 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1073 #
1074
1075 # in other words:
1076 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1077 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1078 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1079 # - the user can now PVR around
1080 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1081 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1082 # after!
1083 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1084 # - if the user rewinds, or press pause, timeshift will be activated again
1085
1086 # note that a timeshift can be enabled ("recording") and
1087 # activated (currently time-shifting).
1088
1089 class InfoBarTimeshift:
1090         def __init__(self):
1091                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1092                         {
1093                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
1094                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
1095                         }, prio=1)
1096                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1097                         {
1098                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1099                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1100                         }, prio=-1) # priority over record
1101
1102                 self.timeshift_enabled = 0
1103                 self.timeshift_state = 0
1104                 self.ts_rewind_timer = eTimer()
1105                 self.ts_rewind_timer.callback.append(self.rewindService)
1106
1107                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1108                         {
1109                                 iPlayableService.evStart: self.__serviceStarted,
1110                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1111                         })
1112
1113         def getTimeshift(self):
1114                 service = self.session.nav.getCurrentService()
1115                 return service and service.timeshift()
1116
1117         def startTimeshift(self):
1118                 print "enable timeshift"
1119                 ts = self.getTimeshift()
1120                 if ts is None:
1121                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1122                         print "no ts interface"
1123                         return 0
1124
1125                 if self.timeshift_enabled:
1126                         print "hu, timeshift already enabled?"
1127                 else:
1128                         if not ts.startTimeshift():
1129                                 self.timeshift_enabled = 1
1130
1131                                 # we remove the "relative time" for now.
1132                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1133
1134                                 # PAUSE.
1135                                 #self.setSeekState(self.SEEK_STATE_PAUSE)
1136                                 self.activateTimeshiftEnd(False)
1137
1138                                 # enable the "TimeshiftEnableActions", which will override
1139                                 # the startTimeshift actions
1140                                 self.__seekableStatusChanged()
1141                         else:
1142                                 print "timeshift failed"
1143
1144         def stopTimeshift(self):
1145                 if not self.timeshift_enabled:
1146                         return 0
1147                 print "disable timeshift"
1148                 ts = self.getTimeshift()
1149                 if ts is None:
1150                         return 0
1151                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1152
1153         def stopTimeshiftConfirmed(self, confirmed):
1154                 if not confirmed:
1155                         return
1156
1157                 ts = self.getTimeshift()
1158                 if ts is None:
1159                         return
1160
1161                 ts.stopTimeshift()
1162                 self.timeshift_enabled = 0
1163
1164                 # disable actions
1165                 self.__seekableStatusChanged()
1166
1167         # activates timeshift, and seeks to (almost) the end
1168         def activateTimeshiftEnd(self, back = True):
1169                 ts = self.getTimeshift()
1170                 print "activateTimeshiftEnd"
1171
1172                 if ts is None:
1173                         return
1174
1175                 if ts.isTimeshiftActive():
1176                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1177                         self.pauseService()
1178                 else:
1179                         print "play, ..."
1180                         ts.activateTimeshift() # activate timeshift will automatically pause
1181                         self.setSeekState(self.SEEK_STATE_PAUSE)
1182
1183                 if back:
1184                         self.doSeek(-5) # seek some gops before end
1185                         self.ts_rewind_timer.start(200, 1)
1186                 else:
1187                         self.doSeek(-1) # seek 1 gop before end
1188
1189         def rewindService(self):
1190                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1191
1192         # same as activateTimeshiftEnd, but pauses afterwards.
1193         def activateTimeshiftEndAndPause(self):
1194                 print "activateTimeshiftEndAndPause"
1195                 #state = self.seekstate
1196                 self.activateTimeshiftEnd(False)
1197
1198         def __seekableStatusChanged(self):
1199                 enabled = False
1200
1201 #               print "self.isSeekable", self.isSeekable()
1202 #               print "self.timeshift_enabled", self.timeshift_enabled
1203
1204                 # when this service is not seekable, but timeshift
1205                 # is enabled, this means we can activate
1206                 # the timeshift
1207                 if not self.isSeekable() and self.timeshift_enabled:
1208                         enabled = True
1209
1210 #               print "timeshift activate:", enabled
1211                 self["TimeshiftActivateActions"].setEnabled(enabled)
1212
1213         def __serviceStarted(self):
1214                 self.timeshift_enabled = False
1215                 self.__seekableStatusChanged()
1216
1217 from Screens.PiPSetup import PiPSetup
1218
1219 class InfoBarExtensions:
1220         EXTENSION_SINGLE = 0
1221         EXTENSION_LIST = 1
1222
1223         def __init__(self):
1224                 self.list = []
1225
1226                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1227                         {
1228                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1229                         }, 1) # lower priority
1230
1231         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1232                 self.list.append((type, extension, key))
1233
1234         def updateExtension(self, extension, key = None):
1235                 self.extensionsList.append(extension)
1236                 if key is not None:
1237                         if self.extensionKeys.has_key(key):
1238                                 key = None
1239
1240                 if key is None:
1241                         for x in self.availableKeys:
1242                                 if not self.extensionKeys.has_key(x):
1243                                         key = x
1244                                         break
1245
1246                 if key is not None:
1247                         self.extensionKeys[key] = len(self.extensionsList) - 1
1248
1249         def updateExtensions(self):
1250                 self.extensionsList = []
1251                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1252                 self.extensionKeys = {}
1253                 for x in self.list:
1254                         if x[0] == self.EXTENSION_SINGLE:
1255                                 self.updateExtension(x[1], x[2])
1256                         else:
1257                                 for y in x[1]():
1258                                         self.updateExtension(y[0], y[1])
1259
1260
1261         def showExtensionSelection(self):
1262                 self.updateExtensions()
1263                 extensionsList = self.extensionsList[:]
1264                 keys = []
1265                 list = []
1266                 for x in self.availableKeys:
1267                         if self.extensionKeys.has_key(x):
1268                                 entry = self.extensionKeys[x]
1269                                 extension = self.extensionsList[entry]
1270                                 if extension[2]():
1271                                         name = str(extension[0]())
1272                                         list.append((extension[0](), extension))
1273                                         keys.append(x)
1274                                         extensionsList.remove(extension)
1275                                 else:
1276                                         extensionsList.remove(extension)
1277                 list.extend([(x[0](), x) for x in extensionsList])
1278
1279                 keys += [""] * len(extensionsList)
1280                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1281
1282         def extensionCallback(self, answer):
1283                 if answer is not None:
1284                         answer[1][1]()
1285
1286 from Tools.BoundFunction import boundFunction
1287
1288 # depends on InfoBarExtensions
1289
1290 class InfoBarPlugins:
1291         def __init__(self):
1292                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1293
1294         def getPluginName(self, name):
1295                 return name
1296
1297         def getPluginList(self):
1298                 list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)]
1299                 list.sort(key = lambda e: e[2]) # sort by name
1300                 return list
1301
1302         def runPlugin(self, plugin):
1303                 if isinstance(self, InfoBarChannelSelection):
1304                         plugin(session = self.session, servicelist = self.servicelist)
1305                 else:
1306                         plugin(session = self.session)
1307
1308 from Components.Task import job_manager
1309 class InfoBarJobman:
1310         def __init__(self):
1311                 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1312
1313         def getJobList(self):
1314                 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1315
1316         def getJobName(self, job):
1317                 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1318
1319         def showJobView(self, job):
1320                 from Screens.TaskView import JobView
1321                 job_manager.in_background = False
1322                 self.session.openWithCallback(self.JobViewCB, JobView, job)
1323         
1324         def JobViewCB(self, in_background):
1325                 job_manager.in_background = in_background
1326
1327 # depends on InfoBarExtensions
1328 class InfoBarSleepTimer:
1329         def __init__(self):
1330                 self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, lambda: True), "1")
1331
1332         def getSleepTimerName(self):
1333                 return _("Sleep Timer")
1334
1335         def showSleepTimerSetup(self):
1336                 self.session.open(SleepTimerEdit)
1337
1338 # depends on InfoBarExtensions
1339 class InfoBarPiP:
1340         def __init__(self):
1341                 self.session.pipshown = False
1342                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1343                         self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1344                         self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1345                         self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1346
1347         def pipShown(self):
1348                 return self.session.pipshown
1349
1350         def pipHandles0Action(self):
1351                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1352
1353         def getShowHideName(self):
1354                 if self.session.pipshown:
1355                         return _("Disable Picture in Picture")
1356                 else:
1357                         return _("Activate Picture in Picture")
1358
1359         def getSwapName(self):
1360                 return _("Swap Services")
1361
1362         def getMoveName(self):
1363                 return _("Move Picture in Picture")
1364
1365         def showPiP(self):
1366                 if self.session.pipshown:
1367                         del self.session.pip
1368                         self.session.pipshown = False
1369                 else:
1370                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1371                         self.session.pip.show()
1372                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1373                         if self.session.pip.playService(newservice):
1374                                 self.session.pipshown = True
1375                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1376                         else:
1377                                 self.session.pipshown = False
1378                                 del self.session.pip
1379                         self.session.nav.playService(newservice)
1380
1381         def swapPiP(self):
1382                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1383                 if self.session.pip.servicePath:
1384                         servicepath = self.servicelist.getCurrentServicePath()
1385                         ref=servicepath[len(servicepath)-1]
1386                         pipref=self.session.pip.getCurrentService()
1387                         self.session.pip.playService(swapservice)
1388                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1389                         if pipref.toString() != ref.toString(): # is a subservice ?
1390                                 self.session.nav.stopService() # stop portal
1391                                 self.session.nav.playService(pipref) # start subservice
1392                         self.session.pip.servicePath=servicepath
1393
1394         def movePiP(self):
1395                 self.session.open(PiPSetup, pip = self.session.pip)
1396
1397         def pipDoHandle0Action(self):
1398                 use = config.usage.pip_zero_button.value
1399                 if "swap" == use:
1400                         self.swapPiP()
1401                 elif "swapstop" == use:
1402                         self.swapPiP()
1403                         self.showPiP()
1404                 elif "stop" == use:
1405                         self.showPiP()
1406
1407 from RecordTimer import parseEvent, RecordTimerEntry
1408
1409 class InfoBarInstantRecord:
1410         """Instant Record - handles the instantRecord action in order to
1411         start/stop instant records"""
1412         def __init__(self):
1413                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1414                         {
1415                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1416                         })
1417                 self.recording = []
1418
1419         def stopCurrentRecording(self, entry = -1):
1420                 if entry is not None and entry != -1:
1421                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1422                         self.recording.remove(self.recording[entry])
1423
1424         def startInstantRecording(self, limitEvent = False):
1425                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1426
1427                 # try to get event info
1428                 event = None
1429                 try:
1430                         service = self.session.nav.getCurrentService()
1431                         epg = eEPGCache.getInstance()
1432                         event = epg.lookupEventTime(serviceref, -1, 0)
1433                         if event is None:
1434                                 info = service.info()
1435                                 ev = info.getEvent(0)
1436                                 event = ev
1437                 except:
1438                         pass
1439
1440                 begin = int(time())
1441                 end = begin + 3600      # dummy
1442                 name = "instant record"
1443                 description = ""
1444                 eventid = None
1445
1446                 if event is not None:
1447                         curEvent = parseEvent(event)
1448                         name = curEvent[2]
1449                         description = curEvent[3]
1450                         eventid = curEvent[4]
1451                         if limitEvent:
1452                                 end = curEvent[1]
1453                 else:
1454                         if limitEvent:
1455                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1456
1457                 if isinstance(serviceref, eServiceReference):
1458                         serviceref = ServiceReference(serviceref)
1459
1460                 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
1461                 recording.dontSave = True
1462                 
1463                 if event is None or limitEvent == False:
1464                         recording.autoincrease = True
1465                         if recording.setAutoincreaseEnd():
1466                                 self.session.nav.RecordTimer.record(recording)
1467                                 self.recording.append(recording)
1468                 else:
1469                                 simulTimerList = self.session.nav.RecordTimer.record(recording)
1470                                 if simulTimerList is not None:  # conflict with other recording
1471                                         name = simulTimerList[1].name
1472                                         name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1473                                         print "[TIMER] conflicts with", name_date
1474                                         recording.autoincrease = True   # start with max available length, then increment
1475                                         if recording.setAutoincreaseEnd():
1476                                                 self.session.nav.RecordTimer.record(recording)
1477                                                 self.recording.append(recording)
1478                                                 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1479                                         else:
1480                                                 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1481                                         recording.autoincrease = False
1482                                 else:
1483                                         self.recording.append(recording)
1484
1485         def isInstantRecordRunning(self):
1486                 print "self.recording:", self.recording
1487                 if self.recording:
1488                         for x in self.recording:
1489                                 if x.isRunning():
1490                                         return True
1491                 return False
1492
1493         def recordQuestionCallback(self, answer):
1494                 print "pre:\n", self.recording
1495
1496                 if answer is None or answer[1] == "no":
1497                         return
1498                 list = []
1499                 recording = self.recording[:]
1500                 for x in recording:
1501                         if not x in self.session.nav.RecordTimer.timer_list:
1502                                 self.recording.remove(x)
1503                         elif x.dontSave and x.isRunning():
1504                                 list.append((x, False))
1505
1506                 if answer[1] == "changeduration":
1507                         if len(self.recording) == 1:
1508                                 self.changeDuration(0)
1509                         else:
1510                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1511                 elif answer[1] == "changeendtime":
1512                         if len(self.recording) == 1:
1513                                 self.setEndtime(0)
1514                         else:
1515                                 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1516                 elif answer[1] == "stop":
1517                         if len(self.recording) == 1:
1518                                 self.stopCurrentRecording(0)
1519                         else:
1520                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1521                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1522                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1523                         if answer[1] == "manualduration":
1524                                 self.changeDuration(len(self.recording)-1)
1525                         elif answer[1] == "manualendtime":
1526                                 self.setEndtime(len(self.recording)-1)
1527                 print "after:\n", self.recording
1528
1529         def setEndtime(self, entry):
1530                 if entry is not None and entry >= 0:
1531                         self.selectedEntry = entry
1532                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1533                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1534                         dlg.setTitle(_("Please change recording endtime"))
1535
1536         def TimeDateInputClosed(self, ret):
1537                 if len(ret) > 1:
1538                         if ret[0]:
1539                                 localendtime = localtime(ret[1])
1540                                 print "stopping recording at", strftime("%c", localendtime)
1541                                 if self.recording[self.selectedEntry].end != ret[1]:
1542                                         self.recording[self.selectedEntry].autoincrease = False
1543                                 self.recording[self.selectedEntry].end = ret[1]
1544                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1545
1546         def changeDuration(self, entry):
1547                 if entry is not None and entry >= 0:
1548                         self.selectedEntry = entry
1549                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1550
1551         def inputCallback(self, value):
1552                 if value is not None:
1553                         print "stopping recording after", int(value), "minutes."
1554                         entry = self.recording[self.selectedEntry]
1555                         if int(value) != 0:
1556                                 entry.autoincrease = False
1557                         entry.end = int(time()) + 60 * int(value)
1558                         self.session.nav.RecordTimer.timeChanged(entry)
1559
1560         def instantRecord(self):
1561                 dir = config.movielist.last_videodir.value
1562                 if not fileExists(dir, 'w'):
1563                         dir = resolveFilename(SCOPE_HDD)
1564                 try:
1565                         stat = os_stat(dir)
1566                 except:
1567                         # XXX: this message is a little odd as we might be recording to a remote device
1568                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1569                         return
1570
1571                 if self.isInstantRecordRunning():
1572                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1573                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1574                                 list=((_("add recording (stop after current event)"), "event"), \
1575                                 (_("add recording (enter recording duration)"), "manualduration"), \
1576                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1577                                 (_("add recording (indefinitely)"), "indefinitely"), \
1578                                 (_("change recording (duration)"), "changeduration"), \
1579                                 (_("change recording (endtime)"), "changeendtime"), \
1580                                 (_("stop recording"), "stop"), \
1581                                 (_("do nothing"), "no")))
1582                 else:
1583                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1584                                 title=_("Start recording?"), \
1585                                 list=((_("add recording (stop after current event)"), "event"), \
1586                                 (_("add recording (enter recording duration)"), "manualduration"), \
1587                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1588                                 (_("add recording (indefinitely)"), "indefinitely"), \
1589                                 (_("don't record"), "no")))
1590
1591 from Tools.ISO639 import LanguageCodes
1592
1593 class InfoBarAudioSelection:
1594         def __init__(self):
1595                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1596                         {
1597                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1598                         })
1599
1600         def audioSelection(self):
1601                 service = self.session.nav.getCurrentService()
1602                 self.audioTracks = audio = service and service.audioTracks()
1603                 n = audio and audio.getNumberOfTracks() or 0
1604                 tlist = []
1605                 if n > 0:
1606                         self.audioChannel = service.audioChannel()
1607
1608                         idx = 0
1609                         while idx < n:
1610                                 i = audio.getTrackInfo(idx)
1611                                 language = i.getLanguage()
1612                                 description = i.getDescription()
1613
1614                                 if LanguageCodes.has_key(language):
1615                                         language = LanguageCodes[language][0]
1616
1617                                 if len(description):
1618                                         description += " (" + language + ")"
1619                                 else:
1620                                         description = language
1621
1622                                 tlist.append((description, idx))
1623                                 idx += 1
1624
1625                         tlist.sort(key=lambda x: x[0])
1626
1627                         selectedAudio = self.audioTracks.getCurrentTrack()
1628
1629                         selection = 0
1630
1631                         for x in tlist:
1632                                 if x[1] != selectedAudio:
1633                                         selection += 1
1634                                 else:
1635                                         break
1636
1637                         if SystemInfo["CanDownmixAC3"]:
1638                                 tlist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
1639                                         ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"),
1640                                         ("--", "")] + tlist
1641                                 keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1642                                 selection += 3
1643                         else:
1644                                 tlist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1645                                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1646                                 selection += 2
1647                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection")
1648                 else:
1649                         del self.audioTracks
1650
1651         def changeAC3Downmix(self, arg):
1652                 choicelist = self.session.current_dialog["list"]
1653                 list = choicelist.list
1654                 t = list[0][1]
1655                 list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
1656                         _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0])
1657                 choicelist.setList(list)
1658                 if config.av.downmix_ac3.value:
1659                         config.av.downmix_ac3.value = False
1660                 else:
1661                         config.av.downmix_ac3.value = True
1662                 config.av.downmix_ac3.save()
1663
1664         def audioSelected(self, audio):
1665                 if audio is not None:
1666                         if isinstance(audio[1], str):
1667                                 if audio[1] == "mode":
1668                                         keys = ["red", "green", "yellow"]
1669                                         selection = self.audioChannel.getCurrentChannel()
1670                                         tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2))
1671                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection")
1672                         else:
1673                                 del self.audioChannel
1674                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1675                                         self.audioTracks.selectTrack(audio[1])
1676                 else:
1677                         del self.audioChannel
1678                 del self.audioTracks
1679
1680         def modeSelected(self, mode):
1681                 if mode is not None:
1682                         self.audioChannel.selectChannel(mode[1])
1683                 del self.audioChannel
1684
1685 class InfoBarSubserviceSelection:
1686         def __init__(self):
1687                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1688                         {
1689                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1690                         })
1691
1692                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1693                         {
1694                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1695                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1696                         }, -1)
1697                 self["SubserviceQuickzapAction"].setEnabled(False)
1698
1699                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1700                         {
1701                                 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1702                         })
1703
1704                 self.bsel = None
1705
1706         def checkSubservicesAvail(self):
1707                 service = self.session.nav.getCurrentService()
1708                 subservices = service and service.subServices()
1709                 if not subservices or subservices.getNumberOfSubservices() == 0:
1710                         self["SubserviceQuickzapAction"].setEnabled(False)
1711
1712         def nextSubservice(self):
1713                 self.changeSubservice(+1)
1714
1715         def prevSubservice(self):
1716                 self.changeSubservice(-1)
1717
1718         def changeSubservice(self, direction):
1719                 service = self.session.nav.getCurrentService()
1720                 subservices = service and service.subServices()
1721                 n = subservices and subservices.getNumberOfSubservices()
1722                 if n and n > 0:
1723                         selection = -1
1724                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1725                         idx = 0
1726                         while idx < n:
1727                                 if subservices.getSubservice(idx).toString() == ref.toString():
1728                                         selection = idx
1729                                         break
1730                                 idx += 1
1731                         if selection != -1:
1732                                 selection += direction
1733                                 if selection >= n:
1734                                         selection=0
1735                                 elif selection < 0:
1736                                         selection=n-1
1737                                 newservice = subservices.getSubservice(selection)
1738                                 if newservice.valid():
1739                                         del subservices
1740                                         del service
1741                                         self.session.nav.playService(newservice, False)
1742
1743         def subserviceSelection(self):
1744                 service = self.session.nav.getCurrentService()
1745                 subservices = service and service.subServices()
1746                 self.bouquets = self.servicelist.getBouquetList()
1747                 n = subservices and subservices.getNumberOfSubservices()
1748                 selection = 0
1749                 if n and n > 0:
1750                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1751                         tlist = []
1752                         idx = 0
1753                         while idx < n:
1754                                 i = subservices.getSubservice(idx)
1755                                 if i.toString() == ref.toString():
1756                                         selection = idx
1757                                 tlist.append((i.getName(), i))
1758                                 idx += 1
1759
1760                         if self.bouquets and len(self.bouquets):
1761                                 keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1762                                 if config.usage.multibouquet.value:
1763                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1764                                 else:
1765                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1766                                 selection += 3
1767                         else:
1768                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1769                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1770                                 selection += 2
1771
1772                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1773
1774         def subserviceSelected(self, service):
1775                 del self.bouquets
1776                 if not service is None:
1777                         if isinstance(service[1], str):
1778                                 if service[1] == "quickzap":
1779                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1780                                         self.session.open(SubservicesQuickzap, service[2])
1781                         else:
1782                                 self["SubserviceQuickzapAction"].setEnabled(True)
1783                                 self.session.nav.playService(service[1], False)
1784
1785         def addSubserviceToBouquetCallback(self, service):
1786                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1787                         self.selectedSubservice = service
1788                         if self.bouquets is None:
1789                                 cnt = 0
1790                         else:
1791                                 cnt = len(self.bouquets)
1792                         if cnt > 1: # show bouquet list
1793                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1794                         elif cnt == 1: # add to only one existing bouquet
1795                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1796                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1797
1798         def bouquetSelClosed(self, confirmed):
1799                 self.bsel = None
1800                 del self.selectedSubservice
1801                 if confirmed:
1802                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1803
1804         def addSubserviceToBouquet(self, dest):
1805                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1806                 if self.bsel:
1807                         self.bsel.close(True)
1808                 else:
1809                         del self.selectedSubservice
1810
1811 class InfoBarAdditionalInfo:
1812         def __init__(self):
1813
1814                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1815                 self["TimeshiftPossible"] = self["RecordingPossible"]
1816                 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1817                 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1818                 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1819                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1820
1821 class InfoBarNotifications:
1822         def __init__(self):
1823                 self.onExecBegin.append(self.checkNotifications)
1824                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1825                 self.onClose.append(self.__removeNotification)
1826
1827         def __removeNotification(self):
1828                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1829
1830         def checkNotificationsIfExecing(self):
1831                 if self.execing:
1832                         self.checkNotifications()
1833
1834         def checkNotifications(self):
1835                 notifications = Notifications.notifications
1836                 if notifications:
1837                         n = notifications[0]
1838
1839                         del notifications[0]
1840                         cb = n[0]
1841
1842                         if n[3].has_key("onSessionOpenCallback"):
1843                                 n[3]["onSessionOpenCallback"]()
1844                                 del n[3]["onSessionOpenCallback"]
1845
1846                         if cb is not None:
1847                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1848                         else:
1849                                 dlg = self.session.open(n[1], *n[2], **n[3])
1850
1851                         # remember that this notification is currently active
1852                         d = (n[4], dlg)
1853                         Notifications.current_notifications.append(d)
1854                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1855
1856         def __notificationClosed(self, d):
1857                 Notifications.current_notifications.remove(d)
1858
1859 class InfoBarServiceNotifications:
1860         def __init__(self):
1861                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1862                         {
1863                                 iPlayableService.evEnd: self.serviceHasEnded
1864                         })
1865
1866         def serviceHasEnded(self):
1867                 print "service end!"
1868
1869                 try:
1870                         self.setSeekState(self.SEEK_STATE_PLAY)
1871                 except:
1872                         pass
1873
1874 class InfoBarCueSheetSupport:
1875         CUT_TYPE_IN = 0
1876         CUT_TYPE_OUT = 1
1877         CUT_TYPE_MARK = 2
1878         CUT_TYPE_LAST = 3
1879
1880         ENABLE_RESUME_SUPPORT = False
1881
1882         def __init__(self, actionmap = "InfobarCueSheetActions"):
1883                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1884                         {
1885                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1886                                 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1887                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1888                         }, prio=1)
1889
1890                 self.cut_list = [ ]
1891                 self.is_closing = False
1892                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1893                         {
1894                                 iPlayableService.evStart: self.__serviceStarted,
1895                         })
1896
1897         def __serviceStarted(self):
1898                 if self.is_closing:
1899                         return
1900                 print "new service started! trying to download cuts!"
1901                 self.downloadCuesheet()
1902
1903                 if self.ENABLE_RESUME_SUPPORT:
1904                         last = None
1905
1906                         for (pts, what) in self.cut_list:
1907                                 if what == self.CUT_TYPE_LAST:
1908                                         last = pts
1909
1910                         if last is not None:
1911                                 self.resume_point = last
1912                                 if config.usage.on_movie_start.value == "ask":
1913                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1914                                 elif config.usage.on_movie_start.value == "resume":
1915 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1916 # TRANSLATORS: at the start of a movie, when the user has selected
1917 # TRANSLATORS: "Resume from last position" as start behavior.
1918 # TRANSLATORS: The purpose is to notify the user that the movie starts
1919 # TRANSLATORS: in the middle somewhere and not from the beginning.
1920 # TRANSLATORS: (Some translators seem to have interpreted it as a
1921 # TRANSLATORS: question or a choice, but it is a statement.)
1922                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1923
1924         def playLastCB(self, answer):
1925                 if answer == True:
1926                         self.doSeek(self.resume_point)
1927                 self.hideAfterResume()
1928
1929         def hideAfterResume(self):
1930                 if isinstance(self, InfoBarShowHide):
1931                         self.hide()
1932
1933         def __getSeekable(self):
1934                 service = self.session.nav.getCurrentService()
1935                 if service is None:
1936                         return None
1937                 return service.seek()
1938
1939         def cueGetCurrentPosition(self):
1940                 seek = self.__getSeekable()
1941                 if seek is None:
1942                         return None
1943                 r = seek.getPlayPosition()
1944                 if r[0]:
1945                         return None
1946                 return long(r[1])
1947
1948         def cueGetEndCutPosition(self):
1949                 ret = False
1950                 isin = True
1951                 for cp in self.cut_list:
1952                         if cp[1] == self.CUT_TYPE_OUT:
1953                                 if isin:
1954                                         isin = False
1955                                         ret = cp[0]
1956                         elif cp[1] == self.CUT_TYPE_IN:
1957                                 isin = True
1958                 return ret
1959                 
1960         def jumpPreviousNextMark(self, cmp, start=False):
1961                 current_pos = self.cueGetCurrentPosition()
1962                 if current_pos is None:
1963                         return False
1964                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
1965                 if mark is not None:
1966                         pts = mark[0]
1967                 else:
1968                         return False
1969
1970                 self.doSeek(pts)
1971                 return True
1972
1973         def jumpPreviousMark(self):
1974                 # we add 2 seconds, so if the play position is <2s after
1975                 # the mark, the mark before will be used
1976                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
1977
1978         def jumpNextMark(self):
1979                 if not self.jumpPreviousNextMark(lambda x: x):
1980                         self.doSeek(-1)
1981
1982         def getNearestCutPoint(self, pts, cmp=abs, start=False):
1983                 # can be optimized
1984                 beforecut = False
1985                 nearest = None
1986                 if start:
1987                         beforecut = True
1988                         bestdiff = cmp(0 - pts)
1989                         if bestdiff >= 0:
1990                                 nearest = [0, False]
1991                 for cp in self.cut_list:
1992                         if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
1993                                 beforecut = False
1994                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
1995                                         diff = cmp(cp[0] - pts)
1996                                         if diff >= 0:
1997                                                 nearest = cp
1998                                                 bestdiff = diff
1999                                         else:
2000                                                 nearest = None
2001                         if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2002                                 diff = cmp(cp[0] - pts)
2003                                 if diff >= 0 and (nearest is None or bestdiff > diff):
2004                                         nearest = cp
2005                                         bestdiff = diff
2006                 return nearest
2007
2008         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2009                 current_pos = self.cueGetCurrentPosition()
2010                 if current_pos is None:
2011                         print "not seekable"
2012                         return
2013
2014                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2015
2016                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2017                         if onlyreturn:
2018                                 return nearest_cutpoint
2019                         if not onlyadd:
2020                                 self.removeMark(nearest_cutpoint)
2021                 elif not onlyremove and not onlyreturn:
2022                         self.addMark((current_pos, self.CUT_TYPE_MARK))
2023
2024                 if onlyreturn:
2025                         return None
2026
2027         def addMark(self, point):
2028                 insort(self.cut_list, point)
2029                 self.uploadCuesheet()
2030                 self.showAfterCuesheetOperation()
2031
2032         def removeMark(self, point):
2033                 self.cut_list.remove(point)
2034                 self.uploadCuesheet()
2035                 self.showAfterCuesheetOperation()
2036
2037         def showAfterCuesheetOperation(self):
2038                 if isinstance(self, InfoBarShowHide):
2039                         self.doShow()
2040
2041         def __getCuesheet(self):
2042                 service = self.session.nav.getCurrentService()
2043                 if service is None:
2044                         return None
2045                 return service.cueSheet()
2046
2047         def uploadCuesheet(self):
2048                 cue = self.__getCuesheet()
2049
2050                 if cue is None:
2051                         print "upload failed, no cuesheet interface"
2052                         return
2053                 cue.setCutList(self.cut_list)
2054
2055         def downloadCuesheet(self):
2056                 cue = self.__getCuesheet()
2057
2058                 if cue is None:
2059                         print "download failed, no cuesheet interface"
2060                         self.cut_list = [ ]
2061                 else:
2062                         self.cut_list = cue.getCutList()
2063
2064 class InfoBarSummary(Screen):
2065         skin = """
2066         <screen position="0,0" size="132,64">
2067                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2068                         <convert type="ClockToText">WithSeconds</convert>
2069                 </widget>
2070                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2071                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2072                         <convert type="ConditionalShowHide">Blink</convert>
2073                 </widget>
2074                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2075                         <convert type="ServiceName">Name</convert>
2076                 </widget>
2077                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2078                         <convert type="EventTime">Progress</convert>
2079                 </widget>
2080         </screen>"""
2081
2082 # for picon:  (path="piconlcd" will use LCD picons)
2083 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2084 #                       <convert type="ServiceName">Reference</convert>
2085 #               </widget>
2086
2087 class InfoBarSummarySupport:
2088         def __init__(self):
2089                 pass
2090
2091         def createSummary(self):
2092                 return InfoBarSummary
2093
2094 class InfoBarMoviePlayerSummary(Screen):
2095         skin = """
2096         <screen position="0,0" size="132,64">
2097                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2098                         <convert type="ClockToText">WithSeconds</convert>
2099                 </widget>
2100                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2101                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2102                         <convert type="ConditionalShowHide">Blink</convert>
2103                 </widget>
2104                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2105                         <convert type="ServiceName">Name</convert>
2106                 </widget>
2107                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2108                         <convert type="ServicePosition">Position</convert>
2109                 </widget>
2110         </screen>"""
2111
2112 class InfoBarMoviePlayerSummarySupport:
2113         def __init__(self):
2114                 pass
2115
2116         def createSummary(self):
2117                 return InfoBarMoviePlayerSummary
2118
2119 class InfoBarTeletextPlugin:
2120         def __init__(self):
2121                 self.teletext_plugin = None
2122
2123                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2124                         self.teletext_plugin = p
2125
2126                 if self.teletext_plugin is not None:
2127                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2128                                 {
2129                                         "startTeletext": (self.startTeletext, _("View teletext..."))
2130                                 })
2131                 else:
2132                         print "no teletext plugin found!"
2133
2134         def startTeletext(self):
2135                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2136
2137 class InfoBarSubtitleSupport(object):
2138         def __init__(self):
2139                 object.__init__(self)
2140                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2141                 self.__subtitles_enabled = False
2142
2143                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2144                         {
2145                                 iPlayableService.evEnd: self.__serviceStopped,
2146                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
2147                         })
2148                 self.cached_subtitle_checked = False
2149                 self.__selected_subtitle = None
2150
2151         def __serviceStopped(self):
2152                 self.subtitle_window.hide()
2153                 self.__subtitles_enabled = False
2154                 self.cached_subtitle_checked = False
2155
2156         def __updatedInfo(self):
2157                 if not self.cached_subtitle_checked:
2158                         subtitle = self.getCurrentServiceSubtitle()
2159                         self.cached_subtitle_checked = True
2160                         self.__selected_subtitle = subtitle and subtitle.getCachedSubtitle()
2161                         if self.__selected_subtitle:
2162                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2163                                 self.subtitle_window.show()
2164                                 self.__subtitles_enabled = True
2165
2166         def getCurrentServiceSubtitle(self):
2167                 service = self.session.nav.getCurrentService()
2168                 return service and service.subtitle()
2169
2170         def setSubtitlesEnable(self, enable=True):
2171                 subtitle = self.getCurrentServiceSubtitle()
2172                 if enable and self.__selected_subtitle is not None:
2173                         if subtitle and not self.__subtitles_enabled:
2174                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2175                                 self.subtitle_window.show()
2176                                 self.__subtitles_enabled = True
2177                 else:
2178                         if subtitle:
2179                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2180                         self.__subtitles_enabled = False
2181                         self.subtitle_window.hide()
2182
2183         def setSelectedSubtitle(self, subtitle):
2184                 self.__selected_subtitle = subtitle
2185
2186         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2187         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2188
2189 class InfoBarServiceErrorPopupSupport:
2190         def __init__(self):
2191                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2192                         {
2193                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2194                                 iPlayableService.evStart: self.__serviceStarted
2195                         })
2196                 self.__serviceStarted()
2197
2198         def __serviceStarted(self):
2199                 self.last_error = None
2200                 Notifications.RemovePopup(id = "ZapError")
2201
2202         def __tuneFailed(self):
2203                 service = self.session.nav.getCurrentService()
2204                 info = service and service.info()
2205                 error = info and info.getInfo(iServiceInformation.sDVBState)
2206
2207                 if error == self.last_error:
2208                         error = None
2209                 else:
2210                         self.last_error = error
2211
2212                 error = {
2213                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2214                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2215                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2216                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2217                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2218                         eDVBServicePMTHandler.eventNewProgramInfo: None,
2219                         eDVBServicePMTHandler.eventTuned: None,
2220                         eDVBServicePMTHandler.eventSOF: None,
2221                         eDVBServicePMTHandler.eventEOF: None,
2222                         eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2223                 }.get(error) #this returns None when the key not exist in the dict
2224
2225                 if error is not None:
2226                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2227                 else:
2228                         Notifications.RemovePopup(id = "ZapError")