enigma2: 4.3.1r16
[enigma2.git] / usr / lib / enigma2 / python / Screens / InfoBarGenerics.py
1 from ChannelSelection import ChannelSelection, BouquetSelector, SilentBouquetSelector
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 Tools.Log import Log
12
13 try:
14         from Components.Sources.HbbtvApplication import HbbtvApplication
15         haveHbbtvApplication = True
16 except:
17         haveHbbtvApplication = False
18 from Components.config import config, ConfigBoolean, ConfigClock
19 from Components.SystemInfo import SystemInfo
20 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath, defaultStorageDevice
21 from EpgSelection import EPGSelection, OutdatedEPGSelection
22 from Plugins.Plugin import PluginDescriptor
23
24 from Screen import Screen
25 from Screens.ChoiceBox import ChoiceBox
26 from Screens.Dish import Dish
27 from Screens.EventView import EventViewEPGSelect, EventViewSimple
28 from Screens.InputBox import InputBox
29 from Screens.MessageBox import MessageBox
30 from Screens.MinuteInput import MinuteInput
31 from Screens.TimerSelection import TimerSelection
32 from Screens.PictureInPicture import PictureInPicture
33 from Screens.SubtitleDisplay import SubtitleDisplay
34 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
35 from Screens.TimeDateInput import TimeDateInput
36 from Screens.UnhandledKey import UnhandledKey
37 from ServiceReference import ServiceReference
38
39 from Tools import Notifications
40 from Tools.Directories import fileExists
41
42 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
43         iPlayableService, eServiceReference, eEPGCache, eActionMap, eServiceMP3, eSize
44
45 from time import time, localtime, strftime
46 from bisect import insort
47
48 from RecordTimer import RecordTimerEntry
49 import Screens.Standby
50
51 # hack alert!
52 from Menu import MainMenu, mdom
53
54 class InfoBarDish:
55         def __init__(self):
56                 self.dishDialog = self.session.instantiateDialog(Dish,zPosition=10000)
57                 self.dishDialog.neverAnimate()
58
59 class InfoBarUnhandledKey:
60         def __init__(self):
61                 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey,zPosition=10000)
62                 self.unhandledKeyDialog.neverAnimate()
63
64                 self.hideUnhandledKeySymbolTimer = eTimer()
65                 self.hideUnhandledKeySymbolTimer_conn = self.hideUnhandledKeySymbolTimer.timeout.connect(self.unhandledKeyDialog.hide)
66                 self.checkUnusedTimer = eTimer()
67                 self.checkUnusedTimer_conn = self.checkUnusedTimer.timeout.connect(self.checkUnused)
68                 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
69                 self.actionASlot = eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.actionA) #highest prio
70                 self.actionBSlot = eActionMap.getInstance().bindAction('', 0x7FFFFFFF, self.actionB) #lowest prio
71                 self.flags = (1<<1);
72                 self.uflags = 0;
73
74         #this function is called on every keypress!
75         def actionA(self, key, flag):
76                 if flag != 4:
77                         if self.flags & (1<<1):
78                                 self.flags = self.uflags = 0
79                         self.flags |= (1<<flag)
80                         if flag == 1: # break
81                                 self.checkUnusedTimer.start(0, True)
82                 return 0
83
84         #this function is only called when no other action has handled this key
85         def actionB(self, key, flag):
86                 if flag != 4:
87                         self.uflags |= (1<<flag)
88                 return 1
89
90         def checkUnused(self):
91                 if self.flags == self.uflags:
92                         self.unhandledKeyDialog.show()
93                         self.hideUnhandledKeySymbolTimer.start(2000, True)
94
95 class InfoBarAutoSleepTimer:
96         def __init__(self):
97                 self.inactivityTimer = eTimer()
98                 self.inactivityTimer_conn = self.inactivityTimer.timeout.connect(self.inactive)
99                 self.keypress(None, 1)
100                 self.highPrioActionSlot = eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.keypress) #highest prio
101                 if not config.usage.inactivity_shutdown_initialized.value:
102                         choicelist = [ (x[1],x[0]) for x in config.usage.inactivity_shutdown.getChoices() ] #we actually need to switch key/value for the choicebox
103                         Notifications.AddNotificationWithCallback(
104                                 self._initialAutoSleepValueSet,
105                                 ChoiceBox,
106                                 list = choicelist,
107                                 selection=3,
108                                 title=_("Please specify the amount of time the device has to be inactive (e.g. no button pressed) before shutting down automatically"),
109                                 titlebartext=_("Inactivity shutdown"))
110
111         def _initialAutoSleepValueSet(self, answer):
112                 config.usage.inactivity_shutdown_initialized.value = True
113                 config.usage.inactivity_shutdown_initialized.save()
114                 if answer != None:
115                         config.usage.inactivity_shutdown.value = answer[1]
116                         config.usage.inactivity_shutdown.save()
117
118         #this function is called on every keypress!
119         def keypress(self, key, flag):
120                 if flag == 1: # break code
121                         hours = config.usage.inactivity_shutdown.value
122                         if hours != "never":
123                                 self.inactivityTimer.startLongTimer(int(hours)*60*60)
124                         else:
125                                 self.inactivityTimer.stop()
126                 return 0
127
128         def inactive(self):
129                 print "[InfoBarAutoSleepTimer].inactive"
130                 if Screens.Standby.inStandby == None:
131                         self.session.openWithCallback(self.shutdown, MessageBox, _("The device will shutdown due to inactivity.\nDo you want to abort the shutdown?"), MessageBox.TYPE_YESNO, timeout=120, default=False, title=_("Inactivity shutdown"))
132                 else:
133                         self.shutdown(False)
134                         return
135
136         def shutdown(self, aborted):
137                 print "[InfoBarAutoSleepTimer].shutdown"
138                 if aborted or Screens.Standby.inTryQuitMainloop:
139                         print "aborted"
140                         self.keypress(None, 1) #restart the timer
141                         return
142
143                 if Screens.Standby.inStandby != None:
144                         print "RecordTimer.TryQuitMainloop"
145                         RecordTimerEntry.TryQuitMainloop(True)
146                 else:
147                         print "Screens.Standby.TryQuitMainloop"
148                         self.session.open(Screens.Standby.TryQuitMainloop, 1)
149
150 class InfoBarShowHide:
151         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
152         fancy animations. """
153         STATE_HIDDEN = 0
154         STATE_HIDING = 1
155         STATE_SHOWING = 2
156         STATE_SHOWN = 3
157
158         def __init__(self):
159                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
160                         {
161                                 "toggleShow": self.toggleShow,
162                                 "hide": self.hide,
163                         }, 1) # lower prio to make it possible to override ok and cancel..
164
165                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
166                         {
167                                 iPlayableService.evStart: self.serviceStarted,
168                         })
169
170                 self.__state = self.STATE_SHOWN
171                 self.__locked = 0
172
173                 self.hideTimer = eTimer()
174                 self.hideTimer_conn = self.hideTimer.timeout.connect(self.doTimerHide)
175                 self.hideTimer.start(5000, True)
176
177                 self.onShow.append(self.__onShow)
178                 self.onHide.append(self.__onHide)
179
180         def serviceStarted(self):
181                 if self.execing:
182                         if config.usage.show_infobar_on_zap.value:
183                                 self.doShow()
184
185         def __onShow(self):
186                 self.__state = self.STATE_SHOWN
187                 self.startHideTimer()
188
189         def startHideTimer(self):
190                 if self.__state == self.STATE_SHOWN and not self.__locked:
191                         idx = config.usage.infobar_timeout.index
192                         if idx:
193                                 self.hideTimer.start(idx*1000, True)
194
195         def __onHide(self):
196                 self.__state = self.STATE_HIDDEN
197
198         def doShow(self):
199                 self.show()
200                 self.startHideTimer()
201
202         def doTimerHide(self):
203                 self.hideTimer.stop()
204                 if self.__state == self.STATE_SHOWN:
205                         self.hide()
206
207         def toggleShow(self):
208                 if self.__state == self.STATE_SHOWN:
209                         self.hide()
210                         self.hideTimer.stop()
211                 elif self.__state == self.STATE_HIDDEN:
212                         self.show()
213
214         def lockShow(self):
215                 self.__locked = self.__locked + 1
216                 if self.execing:
217                         self.show()
218                         self.hideTimer.stop()
219
220         def unlockShow(self):
221                 self.__locked = self.__locked - 1
222                 if self.execing:
223                         self.startHideTimer()
224
225 #       def startShow(self):
226 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
227 #               self.__state = self.STATE_SHOWN
228 #
229 #       def startHide(self):
230 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
231 #               self.__state = self.STATE_HIDDEN
232
233 class NumberZap(Screen):
234         def quit(self):
235                 self.Timer.stop()
236                 self.close(0)
237
238         def keyOK(self):
239                 self.Timer.stop()
240                 self.close(int(self["number"].getText()))
241
242         def keyNumberGlobal(self, number):
243                 self.Timer.start(3000, True)            #reset timer
244                 self.field = self.field + str(number)
245                 self["number"].setText(self.field)
246                 if len(self.field) >= 4:
247                         self.keyOK()
248
249         def __init__(self, session, number):
250                 Screen.__init__(self, session)
251                 self.field = str(number)
252
253                 self["channel"] = Label(_("Channel:"))
254
255                 self["number"] = Label(self.field)
256
257                 self["actions"] = NumberActionMap( [ "SetupActions" ],
258                         {
259                                 "cancel": self.quit,
260                                 "ok": self.keyOK,
261                                 "1": self.keyNumberGlobal,
262                                 "2": self.keyNumberGlobal,
263                                 "3": self.keyNumberGlobal,
264                                 "4": self.keyNumberGlobal,
265                                 "5": self.keyNumberGlobal,
266                                 "6": self.keyNumberGlobal,
267                                 "7": self.keyNumberGlobal,
268                                 "8": self.keyNumberGlobal,
269                                 "9": self.keyNumberGlobal,
270                                 "0": self.keyNumberGlobal
271                         })
272
273                 self.Timer = eTimer()
274                 self.Timer_conn = self.Timer.timeout.connect(self.keyOK)
275                 self.Timer.start(3000, True)
276
277 class InfoBarNumberZap:
278         """ Handles an initial number for NumberZapping """
279         def __init__(self):
280                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
281                         {
282                                 "1": self.keyNumberGlobal,
283                                 "2": self.keyNumberGlobal,
284                                 "3": self.keyNumberGlobal,
285                                 "4": self.keyNumberGlobal,
286                                 "5": self.keyNumberGlobal,
287                                 "6": self.keyNumberGlobal,
288                                 "7": self.keyNumberGlobal,
289                                 "8": self.keyNumberGlobal,
290                                 "9": self.keyNumberGlobal,
291                                 "0": self.keyNumberGlobal,
292                         })
293
294         def keyNumberGlobal(self, number):
295 #               print "You pressed number " + str(number)
296                 if number == 0:
297                         if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
298                                 self.pipDoHandle0Action()
299                         else:
300                                 self.servicelist.recallPrevService()
301                 else:
302                         if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
303                                 self.session.openWithCallback(self.numberEntered, NumberZap, number)
304
305         def numberEntered(self, retval):
306 #               print self.servicelist
307                 if retval > 0:
308                         self.zapToNumber(retval)
309
310         def searchNumberHelper(self, serviceHandler, num, bouquet):
311                 servicelist = serviceHandler.list(bouquet)
312                 if not servicelist is None:
313                         while num:
314                                 serviceIterator = servicelist.getNext()
315                                 if not serviceIterator.valid(): #check end of list
316                                         break
317                                 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
318                                 if playable:
319                                         num -= 1;
320                         if not num: #found service with searched number ?
321                                 return serviceIterator, 0
322                 return None, num
323
324         def zapToNumber(self, number):
325                 bouquet = self.servicelist.bouquet_root
326                 service = None
327                 serviceHandler = eServiceCenter.getInstance()
328                 if not config.usage.multibouquet.value:
329                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
330                 else:
331                         bouquetlist = serviceHandler.list(bouquet)
332                         if not bouquetlist is None:
333                                 while number:
334                                         bouquet = bouquetlist.getNext()
335                                         if not bouquet.valid(): #check end of list
336                                                 break
337                                         if bouquet.flags & eServiceReference.isDirectory:
338                                                 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
339                 if not service is None:
340                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
341                                 self.servicelist.clearPath()
342                                 if self.servicelist.bouquet_root != bouquet:
343                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
344                                 self.servicelist.enterPath(bouquet)
345                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
346                         self.servicelist.zap()
347
348 config.misc.initialchannelselection = ConfigBoolean(default = True)
349
350 class InfoBarChannelSelection:
351         """ ChannelSelection - handles the channelSelection dialog and the initial
352         channelChange actions which open the channelSelection dialog """
353         def __init__(self):
354                 #instantiate forever
355                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
356                 self.servicelist.onRootChanged.append(self.__onServiceListRootChanged)
357
358                 if config.misc.initialchannelselection.value:
359                         self.onShown.append(self.firstRun)
360
361                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
362                         {
363                                 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
364                                 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
365                                 "zapUp": (self.zapUp, _("previous channel")),
366                                 "zapDown": (self.zapDown, _("next channel")),
367                                 "historyBack": (self.historyBack, _("previous channel in history")),
368                                 "historyNext": (self.historyNext, _("next channel in history")),
369                                 "openServiceList": (self.openServiceList, _("open servicelist")),
370                         })
371
372                 self.onServiceListRootChanged = []
373                 self.onClose.append(self.__delChannelSelectionScreen)
374
375         def __delChannelSelectionScreen(self):
376                 self.session.deleteDialog(self.servicelist)
377                 self.servicelist = None
378
379         def __onServiceListRootChanged(self, ref):
380                 for fnc in self.onServiceListRootChanged:
381                         fnc(ref)
382
383         def showTvChannelList(self, zap=False):
384                 self.servicelist.setModeTv()
385                 if zap:
386                         self.servicelist.zap()
387                 self.session.execDialog(self.servicelist)
388
389         def showRadioChannelList(self, zap=False):
390                 self.servicelist.setModeRadio()
391                 if zap:
392                         self.servicelist.zap()
393                 self.session.execDialog(self.servicelist)
394
395         def firstRun(self):
396                 self.onShown.remove(self.firstRun)
397                 config.misc.initialchannelselection.value = False
398                 config.misc.initialchannelselection.save()
399                 self.switchChannelDown()
400
401         def historyBack(self):
402                 self.servicelist.historyBack()
403
404         def historyNext(self):
405                 self.servicelist.historyNext()
406
407         def switchChannelUp(self):
408                 self.servicelist.moveUp()
409                 self.session.execDialog(self.servicelist)
410
411         def switchChannelDown(self):
412                 self.servicelist.moveDown()
413                 self.session.execDialog(self.servicelist)
414
415         def openServiceList(self):
416                 self.session.execDialog(self.servicelist)
417
418         def zapUp(self):
419                 if self.servicelist.inBouquet():
420                         prev = self.servicelist.getCurrentSelection()
421                         if prev:
422                                 prev = prev.toString()
423                                 while True:
424                                         if config.usage.quickzap_bouquet_change.value:
425                                                 if self.servicelist.atBegin():
426                                                         self.servicelist.prevBouquet()
427                                         self.servicelist.moveUp()
428                                         cur = self.servicelist.getCurrentSelection()
429                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
430                                                 break
431                 else:
432                         self.servicelist.moveUp()
433                 self.servicelist.zap()
434
435         def zapDown(self):
436                 if self.servicelist.inBouquet():
437                         prev = self.servicelist.getCurrentSelection()
438                         if prev:
439                                 prev = prev.toString()
440                                 while True:
441                                         if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
442                                                 self.servicelist.nextBouquet()
443                                         else:
444                                                 self.servicelist.moveDown()
445                                         cur = self.servicelist.getCurrentSelection()
446                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
447                                                 break
448                 else:
449                         self.servicelist.moveDown()
450                 self.servicelist.zap()
451
452         def getNextService(self, currentService):
453                 services = self.servicelist.servicelist.getRootServices()
454                 isRelevant = False
455                 ref = currentService.toString()
456                 for service in services:
457                         if isRelevant:
458                                 cur = eServiceReference(service)
459                                 print "getNextService {0} / {1}".format(service, ref)
460                                 if not cur or (not (cur.flags & 64)):
461                                         return cur
462                         if service == ref:
463                                 isRelevant = True
464                 return currentService
465
466         def getPrevService(self, currentService):
467                 services = self.servicelist.servicelist.getRootServices()
468                 previous = []
469                 ref = currentService.toString()
470                 for service in services:
471                         if service == ref:
472                                 for svc in previous:
473                                         cur = eServiceReference(svc)
474                                         print "getPrevService {0} / {1}".format(svc, ref)
475                                         if not cur or (not (cur.flags & 64)):
476                                                 return cur
477                         else:
478                                 previous.append(service)
479                 return currentService
480
481 class InfoBarMenu:
482         """ Handles a menu action, to open the (main) menu """
483         def __init__(self):
484                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
485                         {
486                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
487                         })
488                 self.session.infobar = None
489
490         def mainMenu(self):
491                 print "loading mainmenu XML..."
492                 menu = mdom.getroot()
493                 assert menu.tag == "menu", "root element in menu must be 'menu'!"
494
495                 self.session.infobar = self
496                 # so we can access the currently active infobar from screens opened from within the mainmenu
497                 # at the moment used from the SubserviceSelection
498
499                 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
500
501         def mainMenuClosed(self, *val):
502                 self.session.infobar = None
503
504 #yet used in MoviePlayer.. not normal Infobar ... look at InfoBarEPG!
505 class InfoBarSimpleEventView:
506         """ Opens the Eventview for now/next """
507         def __init__(self):
508                 if config.misc.rcused.value == 0:
509                         self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
510                                 {
511                                         "showEventInfo": (self.openEventView, _("show event details")),
512                                         "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
513                                         "showEventList": (self.audioSelection, _("Audio Options...")),
514                                 })
515                 else:
516                         self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
517                                 {
518                                         "showEventInfo": (self.openEventView, _("show event details")),
519                                         "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
520                                 })
521
522         def showEventInfoWhenNotVisible(self):
523                 if self.shown:
524                         self.openEventView()
525                 else:
526                         self.toggleShow()
527                         return 1
528
529         def openEventView(self):
530                 epglist = [ ]
531                 self.epglist = epglist
532                 service = self.session.nav.getCurrentService()
533                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
534                 info = service.info()
535                 ptr=info.getEvent(0)
536                 if ptr:
537                         epglist.append(ptr)
538                 ptr=info.getEvent(1)
539                 if ptr:
540                         epglist.append(ptr)
541                 if epglist:
542                         self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
543
544         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
545                 epglist = self.epglist
546                 if len(epglist) > 1:
547                         tmp = epglist[0]
548                         epglist[0] = epglist[1]
549                         epglist[1] = tmp
550                         setEvent(epglist[0])
551
552 class SimpleServicelist:
553         def __init__(self, services):
554                 self.services = services
555                 self.length = len(services)
556                 self.current = 0
557
558         def selectService(self, service):
559                 if not self.length:
560                         self.current = -1
561                         return False
562                 else:
563                         self.current = 0
564                         while self.services[self.current].ref != service:
565                                 self.current += 1
566                                 if self.current >= self.length:
567                                         return False
568                 return True
569
570         def nextService(self):
571                 if not self.length:
572                         return
573                 if self.current+1 < self.length:
574                         self.current += 1
575                 else:
576                         self.current = 0
577
578         def prevService(self):
579                 if not self.length:
580                         return
581                 if self.current-1 > -1:
582                         self.current -= 1
583                 else:
584                         self.current = self.length - 1
585
586         def currentService(self):
587                 if not self.length or self.current >= self.length:
588                         return None
589                 return self.services[self.current]
590
591 class InfoBarEPG:
592         """ EPG - Opens an EPG list when the showEPGList action fires """
593         def __init__(self):
594                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
595                         {
596                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
597                         })
598
599                 self.is_now_next = False
600                 self.dlg_stack = [ ]
601                 self.bouquetSel = None
602                 self.eventView = None
603
604                 yellow = (self.openSingleServiceEPG, _("show single service EPG..."))
605                 if config.misc.rcused.value == 0:
606                         yellow = (self.audioSelection, _("Audio Options..."))
607                 elif config.misc.rcused.value == 1:
608                         yellow = (self.startTimeshift, _("start timeshift"))
609
610                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
611                         {
612                                 "showEventInfo": (self.openEventView, _("show EPG...")),
613                                 "showEventInfoPlugin": (self.showEventInfoPlugins, _("list of EPG views...")),
614                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
615                                 "showEventList": yellow
616                         })
617
618         def showEventInfoWhenNotVisible(self):
619                 if self.shown:
620                         self.openEventView()
621                 else:
622                         self.toggleShow()
623                         return 1
624
625         def zapToService(self, service):
626                 if not service is None:
627                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
628                                 self.servicelist.clearPath()
629                                 if self.servicelist.bouquet_root != self.epg_bouquet:
630                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
631                                 self.servicelist.enterPath(self.epg_bouquet)
632                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
633                         self.servicelist.zap()
634
635         def getBouquetServices(self, bouquet):
636                 services = [ ]
637                 servicelist = eServiceCenter.getInstance().list(bouquet)
638                 if not servicelist is None:
639                         while True:
640                                 service = servicelist.getNext()
641                                 if not service.valid(): #check if end of list
642                                         break
643                                 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
644                                         continue
645                                 services.append(ServiceReference(service))
646                 return services
647
648         def openBouquetEPG(self, bouquet, withCallback=True):
649                 services = self.getBouquetServices(bouquet)
650                 if services:
651                         self.epg_bouquet = bouquet
652                         if withCallback:
653                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
654                         else:
655                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
656
657         def changeBouquetCB(self, direction, epg):
658                 if self.bouquetSel:
659                         if direction > 0:
660                                 self.bouquetSel.down()
661                         else:
662                                 self.bouquetSel.up()
663                         bouquet = self.bouquetSel.getCurrent()
664                         services = self.getBouquetServices(bouquet)
665                         if services:
666                                 self.epg_bouquet = bouquet
667                                 epg.setServices(services)
668
669         def closed(self, ret=False):
670                 closedScreen = self.dlg_stack.pop()
671                 if closedScreen is self.bouquetSel:
672                         self.bouquetSel = None
673                 elif closedScreen is self.eventView:
674                         self.eventView = None
675                         self.is_now_next = False
676                 if ret:
677                         dlgs=len(self.dlg_stack)
678                         if dlgs > 0:
679                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
680
681         def openMultiServiceEPG(self, withCallback=True):
682                 bouquets = self.servicelist.getBouquetList()
683                 if bouquets is None:
684                         cnt = 0
685                 else:
686                         cnt = len(bouquets)
687                 if config.usage.multiepg_ask_bouquet.value:
688                         self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
689                 else:
690                         self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
691
692         def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
693                 if cnt > 1: # show bouquet list
694                         if withCallback:
695                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
696                                 self.dlg_stack.append(self.bouquetSel)
697                         else:
698                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
699                 elif cnt == 1:
700                         self.openBouquetEPG(bouquets[0][1], withCallback)
701
702         def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
703                 root = self.servicelist.getRoot()
704                 rootstr = root.toCompareString()
705                 current = 0
706                 for bouquet in bouquets:
707                         if bouquet[1].toCompareString() == rootstr:
708                                 break
709                         current += 1
710                 if current >= cnt:
711                         current = 0
712                 if cnt > 1: # create bouquet list for bouq+/-
713                         self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
714                 if cnt >= 1:
715                         self.openBouquetEPG(root, withCallback)
716
717         def changeServiceCB(self, direction, epg):
718                 if self.serviceSel:
719                         if direction > 0:
720                                 self.serviceSel.nextService()
721                         else:
722                                 self.serviceSel.prevService()
723                         epg.setService(self.serviceSel.currentService())
724
725         def SingleServiceEPGClosed(self, ret=False):
726                 self.serviceSel = None
727
728         def openSingleServiceEPG(self):
729                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
730                 if ref:
731                         if self.servicelist.getMutableList() is not None: # bouquet in channellist
732                                 current_path = self.servicelist.getRoot()
733                                 services = self.getBouquetServices(current_path)
734                                 self.serviceSel = SimpleServicelist(services)
735                                 if self.serviceSel.selectService(ref):
736                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
737                                 else:
738                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
739                         else:
740                                 self.session.open(EPGSelection, ref)
741
742         def openOutdatedSingleServiceEPG(self):
743                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
744                 if ref:
745                         if self.servicelist.getMutableList() is not None: # bouquet in channellist
746                                 current_path = self.servicelist.getRoot()
747                                 services = self.getBouquetServices(current_path)
748                                 self.serviceSel = SimpleServicelist(services)
749                                 if self.serviceSel.selectService(ref):
750                                         self.session.openWithCallback(self.SingleServiceEPGClosed, OutdatedEPGSelection, ref, serviceChangeCB = self.changeServiceCB)
751                                 else:
752                                         self.session.openWithCallback(self.SingleServiceEPGClosed, OutdatedEPGSelection, ref)
753                         else:
754                                 self.session.open(OutdatedEPGSelection, ref)
755
756         def showEventInfoPlugins(self):
757                 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
758
759                 if list:
760                         list.append((_("show single service EPG..."), self.openSingleServiceEPG))
761                         if config.misc.epgcache_outdated_timespan.value:
762                                 list.append((_("show outdated service EPG..."), self.openOutdatedSingleServiceEPG))
763                         list.append((_("Multi EPG"), self.openMultiServiceEPG))
764                         self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
765                 else:
766                         self.openSingleServiceEPG()
767
768         def runPlugin(self, plugin):
769                 plugin(session = self.session, servicelist = self.servicelist)
770
771         def EventInfoPluginChosen(self, answer):
772                 if answer is not None:
773                         answer[1]()
774
775         def openSimilarList(self, eventid, refstr):
776                 self.session.open(EPGSelection, refstr, None, eventid)
777
778         def getNowNext(self):
779                 epglist = [ ]
780                 service = self.session.nav.getCurrentService()
781                 info = service and service.info()
782                 ptr = info and info.getEvent(0)
783                 if ptr:
784                         epglist.append(ptr)
785                 ptr = info and info.getEvent(1)
786                 if ptr:
787                         epglist.append(ptr)
788                 self.epglist = epglist
789
790         def __evEventInfoChanged(self):
791                 if self.is_now_next:
792                         self.getNowNext()
793                         if self.epglist:
794                                 self.eventView.setEvent(self.epglist[0])
795
796         def openEventView(self):
797                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
798                 self.getNowNext()
799                 epglist = self.epglist
800                 if not epglist:
801                         epg = eEPGCache.getInstance()
802                         ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
803                         if ptr:
804                                 epglist.append(ptr)
805                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
806                                 if ptr:
807                                         epglist.append(ptr)
808                 else:
809                         self.is_now_next = True
810                 if epglist:
811                         self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
812                         self.dlg_stack.append(self.eventView)
813                 else:
814                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
815                         self.openMultiServiceEPG(False)
816
817         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
818                 epglist = self.epglist
819                 if len(epglist) > 1:
820                         tmp = epglist[0]
821                         epglist[0]=epglist[1]
822                         epglist[1]=tmp
823                         setEvent(epglist[0])
824
825 class InfoBarRdsDecoder:
826         """provides RDS and Rass support/display"""
827         def __init__(self):
828                 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
829                 self.rds_display.neverAnimate()
830                 self.rass_interactive = None
831
832                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
833                         {
834                                 iPlayableService.evEnd: self.__serviceStopped,
835                                 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
836                         })
837
838                 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
839                 {
840                         "startRassInteractive": self.startRassInteractive
841                 },-1)
842
843                 self["RdsActions"].setEnabled(False)
844
845                 self.onLayoutFinish.append(self.rds_display.show)
846                 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
847
848                 self.onClose.append(self.__delRdsInfoDisplayScreen)
849
850         def __delRdsInfoDisplayScreen(self):
851                 self.session.deleteDialog(self.rds_display)
852                 self.rds_display = None
853
854         def RassInteractivePossibilityChanged(self, state):
855                 self["RdsActions"].setEnabled(state)
856
857         def RassSlidePicChanged(self):
858                 if not self.rass_interactive:
859                         service = self.session.nav.getCurrentService()
860                         decoder = service and service.rdsDecoder()
861                         if decoder:
862                                 decoder.showRassSlidePicture()
863
864         def __serviceStopped(self):
865                 if self.rass_interactive is not None:
866                         rass_interactive = self.rass_interactive
867                         self.rass_interactive = None
868                         rass_interactive.close()
869
870         def startRassInteractive(self):
871                 self.rds_display.hide()
872                 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
873
874         def RassInteractiveClosed(self, *val):
875                 if self.rass_interactive is not None:
876                         self.rass_interactive = None
877                         self.RassSlidePicChanged()
878                 self.rds_display.show()
879
880 class PlayerBase:
881         def __init__(self):
882                 self.lastservice = self.session.nav.getCurrentlyPlayingServiceReference()
883
884                 if not isinstance(self, InfoBarChannelSelection):
885                         self.onFirstExecBegin.append(self.__registerPlayer)
886
887         def __registerPlayer(self):
888                 self.prev_player = self.session.current_player
889                 self.session.current_player = self
890                 self.onClose.append(self.__unRegisterPlayer)
891
892         def __unRegisterPlayer(self):
893                 self.session.current_player = self.prev_player
894                 self.session.nav.playService(self.lastservice)
895
896 # Since we dont want to change any existing player class, here we assume that each player inherits from InfoBarSeek.
897 # If this is not the case the player explicitely have to inherit from PlayerBase and must call the init function!
898 class InfoBarSeek(PlayerBase):
899         """handles actions like seeking, pause"""
900
901         SEEK_STATE_PLAY = (0, 0, 0, ">")
902         SEEK_STATE_PAUSE = (1, 0, 0, "||")
903         SEEK_STATE_EOF = (1, 0, 0, "END")
904         SEEK_STATE_STOP = (0, 0, 0, "STOP")
905
906         def __init__(self, actionmap = "InfobarSeekActions"):
907                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
908                         {
909                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
910                                 iPlayableService.evStart: self.__serviceStarted,
911
912                                 iPlayableService.evEOF: self.__evEOF,
913                                 iPlayableService.evSOF: self.__evSOF
914                         })
915                 self.fast_winding_hint_message_showed = False
916
917                 class InfoBarSeekActionMap(HelpableActionMap):
918                         def __init__(self, screen, *args, **kwargs):
919                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
920                                 self.screen = screen
921
922                         def action(self, contexts, action):
923                                 print "action:", action
924                                 if action[:5] == "seek:":
925                                         time = int(action[5:])
926                                         self.screen.doSeekRelative(time * 90000)
927                                         return 1
928                                 elif action[:8] == "seekdef:":
929                                         key = int(action[8:])
930                                         time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
931                                                 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
932                                                 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
933                                         self.screen.doSeekRelative(time * 90000)
934                                         return 1
935                                 else:
936                                         return HelpableActionMap.action(self, contexts, action)
937
938                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
939                         {
940                                 "playpauseService": self.playpauseService,
941                                 "pauseService": (self.pauseService, _("pause")),
942                                 "unPauseService": (self.unPauseService, _("continue")),
943
944                                 "seekFwd": (self.seekFwd, _("skip forward")),
945                                 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
946                                 "seekBack": (self.seekBack, _("skip backward")),
947                                 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
948                         }, prio=-1)
949                         # give them a little more priority to win over color buttons
950
951                 self["SeekActions"].setEnabled(False)
952
953                 self.seekstate = self.SEEK_STATE_STOP
954                 self.lastseekstate = self.SEEK_STATE_STOP
955
956                 self.onPlayStateChanged = [ ]
957
958                 self.lockedBecauseOfSkipping = False
959
960                 self.__seekableStatusChanged()
961
962                 PlayerBase.__init__(self)
963
964         def makeStateForward(self, n):
965                 return (0, n, 0, ">> %dx" % n)
966
967         def makeStateBackward(self, n):
968                 return (0, -n, 0, "<< %dx" % n)
969
970         def makeStateSlowMotion(self, n):
971                 return (0, 0, n, "/%d" % n)
972
973         def isStateForward(self, state):
974                 return state[1] > 1
975
976         def isStateBackward(self, state):
977                 return state[1] < 0
978
979         def isStateSlowMotion(self, state):
980                 return state[1] == 0 and state[2] > 1
981
982         def getHigher(self, n, lst):
983                 for x in lst:
984                         if x > n:
985                                 return x
986                 return False
987
988         def getLower(self, n, lst):
989                 lst = lst[:]
990                 lst.reverse()
991                 for x in lst:
992                         if x < n:
993                                 return x
994                 return False
995
996         def showAfterSeek(self):
997                 if isinstance(self, InfoBarShowHide):
998                         self.doShow()
999
1000         def up(self):
1001                 pass
1002
1003         def down(self):
1004                 pass
1005
1006         def getSeek(self):
1007                 service = self.session.nav.getCurrentService()
1008                 if service is None:
1009                         return None
1010
1011                 seek = service.seek()
1012
1013                 if seek is None or not seek.isCurrentlySeekable():
1014                         return None
1015
1016                 return seek
1017
1018         def isSeekable(self):
1019                 import Screens.InfoBar
1020
1021                 if self.getSeek() is None or (isinstance(self, Screens.InfoBar.InfoBar) and not self.timeshift_enabled):
1022                         return False
1023                 return True
1024
1025         def __seekableStatusChanged(self):
1026 #               print "seekable status changed!"
1027                 if not self.isSeekable():
1028                         self["SeekActions"].setEnabled(False)
1029 #                       print "not seekable, return to play"
1030                         self.setSeekState(self.SEEK_STATE_PLAY)
1031                 else:
1032                         self["SeekActions"].setEnabled(True)
1033 #                       print "seekable"
1034
1035         def __serviceStarted(self):
1036                 self.fast_winding_hint_message_showed = False
1037                 self.setSeekState(self.SEEK_STATE_PLAY, True)
1038                 self.__seekableStatusChanged()
1039
1040         def setSeekState(self, state, onlyGUI = False):
1041                 service = self.session.nav.getCurrentService()
1042                 ret = 0
1043
1044                 if service is None:
1045                         return False
1046
1047                 if not onlyGUI and state != self.SEEK_STATE_STOP:
1048                         if not self.isSeekable():
1049                                 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
1050                                         state = self.SEEK_STATE_PLAY
1051
1052                         pauseable = service.pause()
1053
1054                         if pauseable is None:
1055                                 print "not pauseable."
1056                                 state = self.SEEK_STATE_PLAY
1057
1058                         if pauseable is not None:
1059                                 if state[0]:
1060                                         ret = pauseable.pause()
1061                                         print "resolved to PAUSE", ret
1062                                 elif state[1]:
1063                                         ret = pauseable.setFastForward(state[1])
1064                                         print "resolved to FAST FORWARD", ret
1065                                 elif state[2]:
1066                                         ret = pauseable.setSlowMotion(state[2])
1067                                         print "resolved to SLOW MOTION", ret
1068                                 else:
1069                                         ret = pauseable.unpause()
1070                                         print "resolved to PLAY", ret
1071
1072                 if ret == 0:
1073                         self.seekstate = state
1074                         for c in self.onPlayStateChanged:
1075                                 c(self.seekstate)
1076
1077                 self.checkSkipShowHideLock()
1078
1079                 return (ret == 0)
1080
1081         def playpauseService(self):
1082                 if self.seekstate != self.SEEK_STATE_PLAY:
1083                         self.unPauseService()
1084                 else:
1085                         self.pauseService()
1086
1087         def pauseService(self):
1088                 if self.seekstate == self.SEEK_STATE_PAUSE:
1089                         if config.seek.on_pause.value == "play":
1090                                 self.unPauseService()
1091                         elif config.seek.on_pause.value == "step":
1092                                 self.doSeekRelative(1)
1093                         elif config.seek.on_pause.value == "last":
1094                                 self.setSeekState(self.lastseekstate)
1095                                 self.lastseekstate = self.SEEK_STATE_PLAY
1096                 else:
1097                         if self.seekstate != self.SEEK_STATE_EOF:
1098                                 self.lastseekstate = self.seekstate
1099                         self.setSeekState(self.SEEK_STATE_PAUSE);
1100
1101         def unPauseService(self):
1102                 print "unpause"
1103                 if self.seekstate == self.SEEK_STATE_PLAY:
1104                         return 0
1105                 self.setSeekState(self.SEEK_STATE_PLAY)
1106
1107         def doSeek(self, pts):
1108                 seekable = self.getSeek()
1109                 if seekable is None:
1110                         return
1111                 seekable.seekTo(pts)
1112
1113         def doSeekRelative(self, pts):
1114                 seekable = self.getSeek()
1115                 if seekable is None:
1116                         return
1117                 prevstate = self.seekstate
1118
1119                 if self.seekstate == self.SEEK_STATE_EOF:
1120                         if prevstate == self.SEEK_STATE_PAUSE:
1121                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1122                         else:
1123                                 self.setSeekState(self.SEEK_STATE_PLAY)
1124                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
1125                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
1126                         self.showAfterSeek()
1127
1128         def seekFwd(self):
1129                 seek = self.getSeek()
1130                 if seek and not (seek.isCurrentlySeekable() & 2):
1131                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1132                                 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
1133                                 self.fast_winding_hint_message_showed = True
1134                                 return
1135                         return 0 # trade as unhandled action
1136                 if self.seekstate == self.SEEK_STATE_PLAY:
1137                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1138                 elif self.seekstate == self.SEEK_STATE_PAUSE:
1139                         if len(config.seek.speeds_slowmotion.value):
1140                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
1141                         else:
1142                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
1143                 elif self.seekstate == self.SEEK_STATE_EOF:
1144                         pass
1145                 elif self.isStateForward(self.seekstate):
1146                         speed = self.seekstate[1]
1147                         if self.seekstate[2]:
1148                                 speed /= self.seekstate[2]
1149                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
1150                         self.setSeekState(self.makeStateForward(speed))
1151                 elif self.isStateBackward(self.seekstate):
1152                         speed = -self.seekstate[1]
1153                         if self.seekstate[2]:
1154                                 speed /= self.seekstate[2]
1155                         speed = self.getLower(speed, config.seek.speeds_backward.value)
1156                         if speed:
1157                                 self.setSeekState(self.makeStateBackward(speed))
1158                         else:
1159                                 self.setSeekState(self.SEEK_STATE_PLAY)
1160                 elif self.isStateSlowMotion(self.seekstate):
1161                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
1162                         self.setSeekState(self.makeStateSlowMotion(speed))
1163
1164         def seekBack(self):
1165                 seek = self.getSeek()
1166                 if seek and not (seek.isCurrentlySeekable() & 2):
1167                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1168                                 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
1169                                 self.fast_winding_hint_message_showed = True
1170                                 return
1171                         return 0 # trade as unhandled action
1172                 seekstate = self.seekstate
1173                 if seekstate == self.SEEK_STATE_PLAY:
1174                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1175                 elif seekstate == self.SEEK_STATE_EOF:
1176                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1177                         self.doSeekRelative(-6)
1178                 elif seekstate == self.SEEK_STATE_PAUSE:
1179                         self.doSeekRelative(-1)
1180                 elif self.isStateForward(seekstate):
1181                         speed = seekstate[1]
1182                         if seekstate[2]:
1183                                 speed /= seekstate[2]
1184                         speed = self.getLower(speed, config.seek.speeds_forward.value)
1185                         if speed:
1186                                 self.setSeekState(self.makeStateForward(speed))
1187                         else:
1188                                 self.setSeekState(self.SEEK_STATE_PLAY)
1189                 elif self.isStateBackward(seekstate):
1190                         speed = -seekstate[1]
1191                         if seekstate[2]:
1192                                 speed /= seekstate[2]
1193                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1194                         self.setSeekState(self.makeStateBackward(speed))
1195                 elif self.isStateSlowMotion(seekstate):
1196                         speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1197                         if speed:
1198                                 self.setSeekState(self.makeStateSlowMotion(speed))
1199                         else:
1200                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1201
1202         def seekFwdManual(self):
1203                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1204
1205         def fwdSeekTo(self, minutes):
1206                 print "Seek", minutes, "minutes forward"
1207                 self.doSeekRelative(minutes * 60 * 90000)
1208
1209         def seekBackManual(self):
1210                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1211
1212         def rwdSeekTo(self, minutes):
1213                 print "rwdSeekTo"
1214                 self.doSeekRelative(-minutes * 60 * 90000)
1215
1216         def checkSkipShowHideLock(self):
1217                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1218
1219                 if config.usage.show_infobar_on_skip.value:
1220                         if self.lockedBecauseOfSkipping and not wantlock:
1221                                 self.unlockShow()
1222                                 self.lockedBecauseOfSkipping = False
1223
1224                         if wantlock and not self.lockedBecauseOfSkipping:
1225                                 self.lockShow()
1226                                 self.lockedBecauseOfSkipping = True
1227
1228         def calcRemainingTime(self):
1229                 seekable = self.getSeek()
1230                 if seekable is not None:
1231                         len = seekable.getLength()
1232                         try:
1233                                 tmp = self.cueGetEndCutPosition()
1234                                 if tmp:
1235                                         len = [False, tmp]
1236                         except:
1237                                 pass
1238                         pos = seekable.getPlayPosition()
1239                         speednom = self.seekstate[1] or 1
1240                         speedden = self.seekstate[2] or 1
1241                         if not len[0] and not pos[0]:
1242                                 if len[1] <= pos[1]:
1243                                         return 0
1244                                 time = (len[1] - pos[1])*speedden/(90*speednom)
1245                                 return time
1246                 return False
1247
1248         def __evEOF(self):
1249                 if self.seekstate == self.SEEK_STATE_EOF:
1250                         return
1251
1252                 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1253                 seekstate = self.seekstate
1254                 if self.seekstate != self.SEEK_STATE_PAUSE:
1255                         self.setSeekState(self.SEEK_STATE_EOF)
1256
1257                 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1258                         seekable = self.getSeek()
1259                         if seekable is not None:
1260                                 seekable.seekTo(-1)
1261                 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1262                         self.doEofInternal(True)
1263                 else:
1264                         self.doEofInternal(False)
1265
1266         def doEofInternal(self, playing):
1267                 pass            # Defined in subclasses
1268
1269         def __evSOF(self):
1270                 self.setSeekState(self.SEEK_STATE_PLAY)
1271                 self.doSeek(0)
1272
1273 from Screens.PVRState import PVRState, TimeshiftState
1274
1275 class InfoBarPVRState:
1276         def __init__(self, screen=PVRState, force_show = False):
1277                 self.onPlayStateChanged.append(self.__playStateChanged)
1278                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1279                 self.pvrStateDialog.neverAnimate()
1280                 self.onShow.append(self._mayShow)
1281                 self.onHide.append(self.pvrStateDialog.hide)
1282                 self.onClose.append(self.__delPvrState)
1283                 self.force_show = force_show
1284
1285         def __delPvrState(self):
1286                 self.session.deleteDialog(self.pvrStateDialog)
1287                 self.pvrStateDialog = None
1288
1289         def _mayShow(self):
1290                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1291                         self.pvrStateDialog.show()
1292
1293         def __playStateChanged(self, state):
1294                 playstateString = state[3]
1295                 self.pvrStateDialog["state"].setText(playstateString)
1296
1297                 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1298                 # also hide if service stopped and returning into MovieList
1299                 if not config.usage.show_infobar_on_skip.value and self.seekstate in (self.SEEK_STATE_PLAY, self.SEEK_STATE_STOP) and not self.force_show:
1300                         self.pvrStateDialog.hide()
1301                 else:
1302                         self._mayShow()
1303
1304 class InfoBarTimeshiftState(InfoBarPVRState):
1305         def __init__(self):
1306                 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1307                 self.__hideTimer = eTimer()
1308                 self.__hideTimer_conn = self.__hideTimer.timeout.connect(self.__hideTimeshiftState)
1309
1310         def _mayShow(self):
1311                 if self.execing and self.timeshift_enabled:
1312                         self.pvrStateDialog.show()
1313                         if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
1314                                 self.__hideTimer.start(5*1000, True)
1315
1316         def __hideTimeshiftState(self):
1317                 self.pvrStateDialog.hide()
1318
1319 class InfoBarShowMovies:
1320
1321         # i don't really like this class.
1322         # it calls a not further specified "movie list" on up/down/movieList,
1323         # so this is not more than an action map
1324         def __init__(self):
1325                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1326                         {
1327                                 "movieList": (self.showMovies, _("movie list")),
1328                                 "up": (self.showMovies, _("movie list")),
1329                                 "down": (self.showMovies, _("movie list"))
1330                         })
1331
1332 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1333
1334 # Hrmf.
1335 #
1336 # Timeshift works the following way:
1337 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1338 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1339 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1340 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1341 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1342 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1343 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1344 #
1345
1346 # in other words:
1347 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1348 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1349 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1350 # - the user can now PVR around
1351 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1352 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1353 # after!
1354 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1355 # - if the user rewinds, or press pause, timeshift will be activated again
1356
1357 # note that a timeshift can be enabled ("recording") and
1358 # activated (currently time-shifting).
1359
1360 class InfoBarTimeshift:
1361         def __init__(self):
1362                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1363                         {
1364                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
1365                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
1366                         }, prio=1)
1367                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1368                         {
1369                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1370                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1371                         }, prio=-1) # priority over record
1372
1373                 self.timeshift_enabled = 0
1374                 self.timeshift_state = 0
1375                 self.ts_rewind_timer = eTimer()
1376                 self.ts_rewind_timer_conn = self.ts_rewind_timer.timeout.connect(self.rewindService)
1377
1378                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1379                         {
1380                                 iPlayableService.evStart: self.__serviceStarted,
1381                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1382                         })
1383
1384         def getTimeshift(self):
1385                 service = self.session.nav.getCurrentService()
1386                 return service and service.timeshift()
1387
1388         def startTimeshift(self):
1389                 print "enable timeshift"
1390                 ts = self.getTimeshift()
1391                 if ts is None:
1392                         if harddiskmanager.HDDCount() and not harddiskmanager.HDDEnabledCount():
1393                                 self.session.open(MessageBox, _("Timeshift not possible!") + "\n" \
1394                                         + _("Please make sure to set up your storage devices with the storage management in menu -> setup -> system -> storage devices."), MessageBox.TYPE_ERROR)
1395                         elif harddiskmanager.HDDEnabledCount() and defaultStorageDevice() == "<undefined>":
1396                                 self.session.open(MessageBox, _("Timeshift not possible!") + "\n" \
1397                                         + _("Please make sure to set up your default storage device in menu -> setup -> system -> recording paths."), MessageBox.TYPE_ERROR)
1398                         elif harddiskmanager.HDDEnabledCount() and defaultStorageDevice() != "<undefined>":
1399                                 part = harddiskmanager.getDefaultStorageDevicebyUUID(defaultStorageDevice())
1400                                 if part is None:
1401                                         self.session.open(MessageBox, _("Timeshift not possible!") + "\n" \
1402                                                 + _("Please verify if your default storage device is attached or set up your default storage device in menu -> setup -> system -> recording paths."), MessageBox.TYPE_ERROR)
1403                         else:
1404                                 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1405                                 print "no ts interface"
1406                         return 0
1407
1408                 if self.timeshift_enabled:
1409                         print "hu, timeshift already enabled?"
1410                 else:
1411                         if not ts.startTimeshift():
1412                                 self.timeshift_enabled = 1
1413
1414                                 # we remove the "relative time" for now.
1415                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1416
1417                                 # PAUSE.
1418                                 #self.setSeekState(self.SEEK_STATE_PAUSE)
1419                                 self.activateTimeshiftEnd(False)
1420
1421                                 # enable the "TimeshiftEnableActions", which will override
1422                                 # the startTimeshift actions
1423                                 self.__seekableStatusChanged()
1424                         else:
1425                                 print "timeshift failed"
1426
1427         def stopTimeshift(self):
1428                 if not self.timeshift_enabled:
1429                         return 0
1430                 print "disable timeshift"
1431                 ts = self.getTimeshift()
1432                 if ts is None:
1433                         return 0
1434                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1435
1436         def stopTimeshiftConfirmed(self, confirmed):
1437                 if not confirmed:
1438                         return
1439
1440                 ts = self.getTimeshift()
1441                 if ts is None:
1442                         return
1443
1444                 ts.stopTimeshift()
1445                 self.timeshift_enabled = 0
1446
1447                 # disable actions
1448                 self.__seekableStatusChanged()
1449
1450         # activates timeshift, and seeks to (almost) the end
1451         def activateTimeshiftEnd(self, back = True):
1452                 ts = self.getTimeshift()
1453                 print "activateTimeshiftEnd"
1454
1455                 if ts is None:
1456                         return
1457
1458                 if ts.isTimeshiftActive():
1459                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1460                         self.pauseService()
1461                 else:
1462                         print "play, ..."
1463                         ts.activateTimeshift() # activate timeshift will automatically pause
1464                         self.setSeekState(self.SEEK_STATE_PAUSE)
1465
1466                 if back:
1467                         self.ts_rewind_timer.start(200, 1)
1468
1469         def rewindService(self):
1470                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1471
1472         # same as activateTimeshiftEnd, but pauses afterwards.
1473         def activateTimeshiftEndAndPause(self):
1474                 print "activateTimeshiftEndAndPause"
1475                 #state = self.seekstate
1476                 self.activateTimeshiftEnd(False)
1477
1478         def __seekableStatusChanged(self):
1479                 enabled = False
1480
1481 #               print "self.isSeekable", self.isSeekable()
1482 #               print "self.timeshift_enabled", self.timeshift_enabled
1483
1484                 # when this service is not seekable, but timeshift
1485                 # is enabled, this means we can activate
1486                 # the timeshift
1487                 if not self.isSeekable() and self.timeshift_enabled:
1488                         enabled = True
1489
1490 #               print "timeshift activate:", enabled
1491                 self["TimeshiftActivateActions"].setEnabled(enabled)
1492
1493         def __serviceStarted(self):
1494                 self.timeshift_enabled = False
1495                 self.__seekableStatusChanged()
1496
1497 from Screens.PiPSetup import PiPSetup
1498
1499 class InfoBarExtensions:
1500         EXTENSION_SINGLE = 0
1501         EXTENSION_LIST = 1
1502
1503         def __init__(self):
1504                 self.list = []
1505
1506                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1507                         {
1508                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1509                         }, 1) # lower priority
1510
1511         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1512                 self.list.append((type, extension, key))
1513
1514         def updateExtension(self, extension, key = None):
1515                 self.extensionsList.append(extension)
1516                 if key is not None:
1517                         if self.extensionKeys.has_key(key):
1518                                 key = None
1519
1520                 if key is None:
1521                         for x in self.availableKeys:
1522                                 if not self.extensionKeys.has_key(x):
1523                                         key = x
1524                                         break
1525
1526                 if key is not None:
1527                         self.extensionKeys[key] = len(self.extensionsList) - 1
1528
1529         def updateExtensions(self):
1530                 self.extensionsList = []
1531                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue", "text" ]
1532                 self.extensionKeys = {}
1533                 for x in self.list:
1534                         if x[0] == self.EXTENSION_SINGLE:
1535                                 self.updateExtension(x[1], x[2])
1536                         else:
1537                                 for y in x[1]():
1538                                         self.updateExtension(y[0], y[1])
1539
1540
1541         def showExtensionSelection(self):
1542                 self.updateExtensions()
1543                 extensionsList = self.extensionsList[:]
1544                 keys = []
1545                 list = []
1546                 for x in self.availableKeys:
1547                         if self.extensionKeys.has_key(x):
1548                                 entry = self.extensionKeys[x]
1549                                 extension = self.extensionsList[entry]
1550                                 if extension[2]():
1551                                         list.append((extension[0](), extension))
1552                                         keys.append(x)
1553                                         extensionsList.remove(extension)
1554                                 else:
1555                                         extensionsList.remove(extension)
1556                 list.extend([(x[0](), x) for x in extensionsList])
1557
1558                 keys += [""] * len(extensionsList)
1559                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), titlebartext=_("Extensions"), list = list, keys = keys, skin_name = "ExtensionsList", is_dialog=False)
1560
1561         def extensionCallback(self, answer):
1562                 if answer is not None:
1563                         answer[1][1]()
1564
1565 from Tools.BoundFunction import boundFunction
1566 import inspect
1567
1568 # depends on InfoBarExtensions
1569
1570 class InfoBarPlugins:
1571         def __init__(self):
1572                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1573
1574         def getPluginName(self, name):
1575                 return name
1576
1577         def getPluginList(self):
1578                 l = []
1579                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1580                         args = inspect.getargspec(p.__call__)[0]
1581                         if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
1582                                 l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
1583                 l.sort(key = lambda e: e[2]) # sort by name
1584                 return l
1585
1586         def runPlugin(self, plugin):
1587                 if isinstance(self, InfoBarChannelSelection):
1588                         plugin(session = self.session, servicelist = self.servicelist)
1589                 else:
1590                         plugin(session = self.session)
1591
1592 from Components.Task import job_manager
1593 class InfoBarJobman:
1594         def __init__(self):
1595                 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1596
1597         def getJobList(self):
1598                 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1599
1600         def getJobName(self, job):
1601                 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1602
1603         def showJobView(self, job):
1604                 from Screens.TaskView import JobView
1605                 job_manager.in_background = False
1606                 self.session.openWithCallback(self.JobViewCB, JobView, job)
1607
1608         def JobViewCB(self, in_background):
1609                 job_manager.in_background = in_background
1610
1611 # depends on InfoBarExtensions
1612 class InfoBarPiP:
1613         def __init__(self):
1614                 try:
1615                         self.session.pipshown
1616                 except:
1617                         self.session.pipshown = False
1618                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1619                         if (self.allowPiP):
1620                                 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1621                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1622                                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1623                         else:
1624                                 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1625                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1626
1627         def pipShown(self):
1628                 return self.session.pipshown
1629
1630         def pipHandles0Action(self):
1631                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1632
1633         def getShowHideName(self):
1634                 if self.session.pipshown:
1635                         return _("Disable Picture in Picture")
1636                 else:
1637                         return _("Activate Picture in Picture")
1638
1639         def getSwapName(self):
1640                 return _("Swap Services")
1641
1642         def getMoveName(self):
1643                 return _("Move Picture in Picture")
1644
1645         def showPiP(self):
1646                 if self.session.pipshown:
1647                         print "pip currently shown.... pointer:", self.session.pip
1648                         self.session.deleteDialog(self.session.pip)
1649                         del self.session.pip
1650                         print 'hasattr(self.session,"pip")', hasattr(self.session,"pip")
1651                         self.session.pipshown = False
1652                 else:
1653                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1654                         self.session.pip.neverAnimate()
1655                         self.session.pip.show()
1656                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1657                         if self.session.pip.playService(newservice):
1658                                 self.session.pipshown = True
1659                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1660                         else:
1661                                 self.session.pipshown = False
1662                                 self.session.deleteDialog(self.session.pip)
1663                                 del self.session.pip
1664                         self.session.nav.playService(newservice)
1665
1666         def swapPiP(self):
1667                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1668                 if self.session.pip.servicePath:
1669                         servicepath = self.servicelist.getCurrentServicePath()
1670                         ref=servicepath[len(servicepath)-1]
1671                         pipref=self.session.pip.getCurrentService()
1672                         self.session.pip.playService(swapservice)
1673                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1674                         if pipref.toString() != ref.toString(): # is a subservice ?
1675                                 self.session.nav.stopService() # stop portal
1676                                 self.session.nav.playService(pipref) # start subservice
1677                         self.session.pip.servicePath=servicepath
1678
1679         def movePiP(self):
1680                 self.session.open(PiPSetup, pip = self.session.pip)
1681
1682         def pipDoHandle0Action(self):
1683                 use = config.usage.pip_zero_button.value
1684                 if "swap" == use:
1685                         self.swapPiP()
1686                 elif "swapstop" == use:
1687                         self.swapPiP()
1688                         self.showPiP()
1689                 elif "stop" == use:
1690                         self.showPiP()
1691
1692 from RecordTimer import parseEvent
1693
1694 class InfoBarInstantRecord:
1695         """Instant Record - handles the instantRecord action in order to
1696         start/stop instant records"""
1697         def __init__(self):
1698                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1699                         {
1700                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1701                         })
1702
1703                 self.session.nav.RecordTimer.on_state_change.append(self.timerentryOnStateChange)
1704                 self.recording = []
1705
1706         def timerentryOnStateChange(self, timer):
1707                 # timer recording has been started, append to self.recording list
1708                 if hasattr(self, "recording") and timer.isRunning():
1709                         if not timer in self.recording: # only if timer is not in list already
1710                                 self.recording.append(timer)
1711
1712         def stopCurrentRecording(self, entry = -1):
1713                 if entry is not None and entry != -1:
1714                         t = self.recording[entry]
1715                         if t.repeated:  # do not delete repeated timer, ask user what to do
1716                                 choicelist = (
1717                                         (_("Stop current event but not coming events"), "stoponlycurrent"),
1718                                         (_("Stop current event and disable coming events"), "stopall")
1719                                 )
1720                                 self.session.openWithCallback(boundFunction(self.runningRepeatedTimerCallback, t), ChoiceBox, title=_("Repeating event currently recording... What do you want to do?"), list = choicelist)
1721                         else:
1722                                 self.session.nav.RecordTimer.removeEntry(t)
1723                                 self.recording.remove(t)
1724
1725         def runningRepeatedTimerCallback(self, t, result):
1726                 if result is not None:
1727                         if result[1] == "stoponlycurrent":
1728                                 t.enable()
1729                                 t.processRepeated(findRunningEvent = False)
1730                                 self.session.nav.RecordTimer.doActivate(t)
1731                         elif result[1] == "stopall":
1732                                 t.disable()
1733                         self.session.nav.RecordTimer.timeChanged(t)
1734                         self.recording.remove(t)
1735
1736
1737         def startInstantRecording(self, limitEvent = False):
1738                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1739
1740                 # try to get event info
1741                 event = None
1742                 try:
1743                         service = self.session.nav.getCurrentService()
1744                         epg = eEPGCache.getInstance()
1745                         event = epg.lookupEventTime(serviceref, -1, 0)
1746                         if event is None:
1747                                 info = service.info()
1748                                 ev = info.getEvent(0)
1749                                 event = ev
1750                 except:
1751                         pass
1752
1753                 begin = int(time())
1754                 end = begin + 3600      # dummy
1755                 name = "instant record"
1756                 description = ""
1757                 eventid = None
1758
1759                 if event is not None:
1760                         curEvent = parseEvent(event)
1761                         name = curEvent[2]
1762                         description = curEvent[3]
1763                         eventid = curEvent[4]
1764                         if limitEvent:
1765                                 end = curEvent[1]
1766                 else:
1767                         if limitEvent:
1768                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1769
1770                 if isinstance(serviceref, eServiceReference):
1771                         serviceref = ServiceReference(serviceref)
1772
1773                 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1774                 recording.dontSave = True
1775
1776                 if event is None or limitEvent == False:
1777                         recording.autoincrease = True
1778                         recording.setAutoincreaseEnd()
1779
1780                 self.recording.append(recording)
1781                 simulTimerList = self.session.nav.RecordTimer.record(recording)
1782
1783                 if simulTimerList is not None:
1784                         if len(simulTimerList) > 1: # with other recording
1785                                 name = simulTimerList[1].name
1786                                 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1787                                 print "[TIMER] conflicts with", name_date
1788                                 recording.autoincrease = True   # start with max available length, then increment
1789                                 if recording.setAutoincreaseEnd():
1790                                         self.session.nav.RecordTimer.record(recording)
1791                                         self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1792                                 else:
1793                                         self.recording.remove(recording)
1794                                         self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1795                         else:
1796                                 self.recording.remove(recording)
1797                                 self.session.open(MessageBox, _("Couldn't record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
1798                         recording.autoincrease = False
1799
1800         def isInstantRecordRunning(self):
1801                 print "self.recording:", self.recording
1802                 if self.recording:
1803                         for x in self.recording:
1804                                 if x.isRunning():
1805                                         return True
1806                 return False
1807
1808         def recordQuestionCallback(self, answer):
1809                 print "pre:\n", self.recording
1810
1811                 if answer is None or answer[1] == "no":
1812                         return
1813                 list = []
1814                 recording = self.recording[:]
1815                 for x in recording:
1816                         if not x in self.session.nav.RecordTimer.timer_list or not x.isRunning(): # check for isRunning because of repeated timer (there are still in the timerlist!)
1817                                 self.recording.remove(x)
1818                         elif x.isRunning():
1819                                 list.append((x, False))
1820
1821                 if answer[1] == "changeduration":
1822                         self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1823                 elif answer[1] == "changeendtime":
1824                         self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1825                 elif answer[1] == "stop":
1826                         self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1827                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1828                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1829                         if answer[1] == "manualduration":
1830                                 self.changeDuration(len(self.recording)-1)
1831                         elif answer[1] == "manualendtime":
1832                                 self.setEndtime(len(self.recording)-1)
1833                 print "after:\n", self.recording
1834
1835         def setEndtime(self, entry):
1836                 if entry is not None and entry >= 0:
1837                         self.selectedEntry = entry
1838                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1839                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1840                         dlg.setTitle(_("Please change recording endtime"))
1841
1842         def TimeDateInputClosed(self, ret):
1843                 if len(ret) > 1:
1844                         if ret[0]:
1845                                 localendtime = localtime(ret[1])
1846                                 print "stopping recording at", strftime("%c", localendtime)
1847                                 if self.recording[self.selectedEntry].end != ret[1]:
1848                                         self.recording[self.selectedEntry].autoincrease = False
1849                                 self.recording[self.selectedEntry].end = ret[1]
1850                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1851
1852         def changeDuration(self, entry):
1853                 if entry is not None and entry >= 0:
1854                         self.selectedEntry = entry
1855                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1856
1857         def inputCallback(self, value):
1858                 if value is not None:
1859                         print "stopping recording after", int(value), "minutes."
1860                         entry = self.recording[self.selectedEntry]
1861                         if int(value) != 0:
1862                                 entry.autoincrease = False
1863                         entry.end = int(time()) + 60 * int(value)
1864                         self.session.nav.RecordTimer.timeChanged(entry)
1865
1866         def instantRecord(self):
1867                 dir = preferredInstantRecordPath()
1868                 if not dir or not fileExists(dir, 'w'):
1869                         dir = defaultMoviePath()
1870                 if not harddiskmanager.inside_mountpoint(dir):
1871                         if harddiskmanager.HDDCount() and not harddiskmanager.HDDEnabledCount():
1872                                 self.session.open(MessageBox, _("Unconfigured storage devices found!") + "\n" \
1873                                         + _("Please make sure to set up your storage devices with the storage management in menu -> setup -> system -> storage devices."), MessageBox.TYPE_ERROR)
1874                                 return
1875                         elif harddiskmanager.HDDEnabledCount() and defaultStorageDevice() == "<undefined>":
1876                                 self.session.open(MessageBox, _("No default storage device found!") + "\n" \
1877                                         + _("Please make sure to set up your default storage device in menu -> setup -> system -> recording paths."), MessageBox.TYPE_ERROR)
1878                                 return
1879                         elif harddiskmanager.HDDEnabledCount() and defaultStorageDevice() != "<undefined>":
1880                                 part = harddiskmanager.getDefaultStorageDevicebyUUID(defaultStorageDevice())
1881                                 if part is None:
1882                                         self.session.open(MessageBox, _("Default storage device is not available!") + "\n" \
1883                                                 + _("Please verify if your default storage device is attached or set up your default storage device in menu -> setup -> system -> recording paths."), MessageBox.TYPE_ERROR)
1884                                         return
1885                         else:
1886                                 # XXX: this message is a little odd as we might be recording to a remote device
1887                                 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1888                                 return
1889
1890                 if self.isInstantRecordRunning():
1891                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1892                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1893                                 list=((_("stop recording"), "stop"), \
1894                                 (_("add recording (stop after current event)"), "event"), \
1895                                 (_("add recording (indefinitely)"), "indefinitely"), \
1896                                 (_("add recording (enter recording duration)"), "manualduration"), \
1897                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1898                                 (_("change recording (duration)"), "changeduration"), \
1899                                 (_("change recording (endtime)"), "changeendtime"), \
1900                                 (_("do nothing"), "no")))
1901                 else:
1902                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1903                                 title=_("Start recording?"), \
1904                                 list=((_("add recording (stop after current event)"), "event"), \
1905                                 (_("add recording (indefinitely)"), "indefinitely"), \
1906                                 (_("add recording (enter recording duration)"), "manualduration"), \
1907                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1908                                 (_("don't record"), "no")))
1909
1910 class InfoBarAudioSelection:
1911         def __init__(self):
1912                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1913                         {
1914                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1915                         })
1916
1917         def audioSelection(self):
1918                 from Screens.AudioSelection import AudioSelection
1919                 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self)
1920
1921         def audioSelected(self, ret=None):
1922                 print "[infobar::audioSelected]", ret
1923
1924 class InfoBarSubserviceSelection:
1925         def __init__(self):
1926                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1927                         {
1928                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1929                         })
1930
1931                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1932                         {
1933                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1934                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1935                         }, -1)
1936                 self["SubserviceQuickzapAction"].setEnabled(False)
1937
1938                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1939                         {
1940                                 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1941                         })
1942
1943                 self.bsel = None
1944
1945         def checkSubservicesAvail(self):
1946                 service = self.session.nav.getCurrentService()
1947                 subservices = service and service.subServices()
1948                 if not subservices or subservices.getNumberOfSubservices() == 0:
1949                         self["SubserviceQuickzapAction"].setEnabled(False)
1950
1951         def nextSubservice(self):
1952                 self.changeSubservice(+1)
1953
1954         def prevSubservice(self):
1955                 self.changeSubservice(-1)
1956
1957         def changeSubservice(self, direction):
1958                 service = self.session.nav.getCurrentService()
1959                 subservices = service and service.subServices()
1960                 n = subservices and subservices.getNumberOfSubservices()
1961                 if n and n > 0:
1962                         selection = -1
1963                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1964                         idx = 0
1965                         while idx < n:
1966                                 if subservices.getSubservice(idx).toString() == ref.toString():
1967                                         selection = idx
1968                                         break
1969                                 idx += 1
1970                         if selection != -1:
1971                                 selection += direction
1972                                 if selection >= n:
1973                                         selection=0
1974                                 elif selection < 0:
1975                                         selection=n-1
1976                                 newservice = subservices.getSubservice(selection)
1977                                 if newservice.valid():
1978                                         del subservices
1979                                         del service
1980                                         self.playSubservice(newservice)
1981
1982         def playSubservice(self, ref):
1983                 if ref.getUnsignedData(6) == 0:
1984                         ref.setName("")
1985                 self.session.nav.playService(ref, False)
1986
1987         def subserviceSelection(self):
1988                 service = self.session.nav.getCurrentService()
1989                 subservices = service and service.subServices()
1990                 self.bouquets = self.servicelist.getBouquetList()
1991                 n = subservices and subservices.getNumberOfSubservices()
1992                 selection = 0
1993                 if n and n > 0:
1994                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1995                         tlist = []
1996                         idx = 0
1997                         cnt_parent = 0
1998                         while idx < n:
1999                                 i = subservices.getSubservice(idx)
2000                                 if i == ref:
2001                                         selection = idx
2002                                 tlist.append((i.getName(), i))
2003                                 if i.getUnsignedData(6):
2004                                         cnt_parent += 1
2005                                 idx += 1
2006
2007                         if cnt_parent and self.bouquets and len(self.bouquets):
2008                                 keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
2009                                 if config.usage.multibouquet.value:
2010                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
2011                                 else:
2012                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
2013                                 selection += 3
2014                         else:
2015                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
2016                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
2017                                 selection += 2
2018
2019                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
2020
2021         def subserviceSelected(self, service):
2022                 del self.bouquets
2023                 if not service is None:
2024                         if isinstance(service[1], str):
2025                                 if service[1] == "quickzap":
2026                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
2027                                         self.session.open(SubservicesQuickzap, service[2])
2028                         else:
2029                                 self["SubserviceQuickzapAction"].setEnabled(True)
2030                                 self.playSubservice(service[1])
2031
2032         def addSubserviceToBouquetCallback(self, service):
2033                 if len(service) > 1 and isinstance(service[1], eServiceReference):
2034                         self.selectedSubservice = service
2035                         if self.bouquets is None:
2036                                 cnt = 0
2037                         else:
2038                                 cnt = len(self.bouquets)
2039                         if cnt > 1: # show bouquet list
2040                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
2041                         elif cnt == 1: # add to only one existing bouquet
2042                                 self.addSubserviceToBouquet(self.bouquets[0][1])
2043                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
2044
2045         def bouquetSelClosed(self, confirmed):
2046                 self.bsel = None
2047                 del self.selectedSubservice
2048                 if confirmed:
2049                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
2050
2051         def addSubserviceToBouquet(self, dest):
2052                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
2053                 if self.bsel:
2054                         self.bsel.close(True)
2055                 else:
2056                         del self.selectedSubservice
2057
2058 class InfoBarAdditionalInfo:
2059         def __init__(self):
2060                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
2061                 self["TimeshiftPossible"] = self["RecordingPossible"]
2062                 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
2063                 self["ShowTimeshiftOnYellow"] = Boolean(fixed=config.misc.rcused.value == 1)
2064                 self["ShowEventListOnYellow"] = Boolean(fixed=config.misc.rcused.value == 2)
2065                 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
2066                 self["ExtensionsAvailable"] = Boolean(fixed=1)
2067                 self["PendingNotification"] = Boolean(fixed=0)
2068
2069 class InfoBarNotifications:
2070         def __init__(self):
2071                 self.onExecBegin.append(self.checkNotifications)
2072                 Notifications.notificationQueue.addedCB.append(self.checkNotificationsIfExecing)
2073                 self.onClose.append(self.__removeNotification)
2074                 if isinstance(self, InfoBarExtensions):
2075                         self.addExtension((self.getEntryText, self.showNotificationQueueViewer, lambda: True), key = "text")
2076
2077         def __removeNotification(self):
2078                 Notifications.notificationQueue.addedCB.remove(self.checkNotificationsIfExecing)
2079
2080         def checkNotificationsIfExecing(self):
2081                 if self.execing:
2082                         self.checkNotifications()
2083
2084         def checkNotifications(self, immediate=False):
2085                 def doCheck(self):
2086                         if self.execing:
2087                                 Notifications.notificationQueue.popNotification(self)
2088                 if immediate:
2089                         doCheck(self)
2090                 else:
2091                         from twisted.internet import reactor
2092                         reactor.callLater(0, doCheck, self)
2093
2094         def getEntryText(self):
2095                 numPending = len(Notifications.notificationQueue.getPending())
2096                 text = _("Notification Queue")
2097                 if numPending == 1:
2098                         text += " (1 new event)"
2099                 elif numPending > 1:
2100                         text += " (%i new events)" % numPending
2101                 return text
2102
2103         def showNotificationQueueViewer(self):
2104                 from Screens.NotificationQueueViewer import NotificationQueueViewer
2105                 self.session.open(NotificationQueueViewer)
2106
2107 class InfoBarServiceNotifications:
2108         def __init__(self):
2109                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2110                         {
2111                                 iPlayableService.evEnd: self.serviceHasEnded
2112                         })
2113
2114         def serviceHasEnded(self):
2115                 print "service end!"
2116                 try:
2117                         self.setSeekState(self.SEEK_STATE_STOP, True)
2118                 except:
2119                         pass
2120
2121 class InfoBarCueSheetSupport:
2122         CUT_TYPE_IN = 0
2123         CUT_TYPE_OUT = 1
2124         CUT_TYPE_MARK = 2
2125         CUT_TYPE_LAST = 3
2126
2127         ENABLE_RESUME_SUPPORT = False
2128
2129         def __init__(self, actionmap = "InfobarCueSheetActions"):
2130                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
2131                         {
2132                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
2133                                 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
2134                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
2135                         }, prio=1)
2136
2137                 self.cut_list = [ ]
2138                 self.is_closing = False
2139                 self.length = [0,0]
2140                 self._tryResume = False
2141                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2142                         {
2143                                 iPlayableService.evStart: self.__serviceStarted,
2144                                 iPlayableService.evSeekableStatusChanged: self.__onSeekableStatusChanged,
2145                                 iPlayableService.evCuesheetChanged: self.__downloadChangedCuesheet
2146                         })
2147
2148         def __serviceStarted(self):
2149                 self._tryResume = False
2150                 if self.is_closing:
2151                         return
2152                 print "new service started! trying to download cuts!"
2153                 self.downloadCuesheet()
2154
2155                 if self.ENABLE_RESUME_SUPPORT:
2156                         last = None
2157
2158                         for (pts, what) in self.cut_list:
2159                                 if what == self.CUT_TYPE_LAST:
2160                                         last = pts
2161
2162                         if last is not None:
2163                                 self.resume_point = last
2164
2165                                 if (self.length[1] > 0) and abs(self.length[1] - last) < 4*90000: # if last playpos is within 4 seconds span to the length of this recording, assume it has been watched all the way to the end and don't resume
2166                                         return
2167                                 l = last / 90000
2168                                 if l < config.usage.resume_treshold.value:
2169                                         Log.w("Resume treshold not reached, starting from the beginning")
2170                                         return
2171                                 if config.usage.on_movie_start.value == "ask":
2172                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?") + "\n" + (_("Resume position at %s") % ("%d:%02d:%02d" % (l/3600, l%3600/60, l%60))), timeout=10, domain = "InfoBar")
2173                                 elif config.usage.on_movie_start.value == "resume":
2174 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2175 # TRANSLATORS: at the start of a movie, when the user has selected
2176 # TRANSLATORS: "Resume from last position" as start behavior.
2177 # TRANSLATORS: The purpose is to notify the user that the movie starts
2178 # TRANSLATORS: in the middle somewhere and not from the beginning.
2179 # TRANSLATORS: (Some translators seem to have interpreted it as a
2180 # TRANSLATORS: question or a choice, but it is a statement.)
2181                                         self.session.toastManager.showToast(_("Resuming playback"))
2182                                         if self.isSeekable():
2183                                                 self.playLastCB(True)
2184                                         else:
2185                                                 self._tryResume = True
2186
2187         def __onSeekableStatusChanged(self):
2188                 if self._tryResume and self.isSeekable():
2189                         self._tryResume = False
2190                         self.playLastCB(True)
2191
2192         def playLastCB(self, answer):
2193                 if answer == True:
2194                         self.doSeek(self.resume_point)
2195                 self.hideAfterResume()
2196
2197         def hideAfterResume(self):
2198                 if isinstance(self, InfoBarShowHide):
2199                         self.hide()
2200
2201         def __getSeekable(self):
2202                 service = self.session.nav.getCurrentService()
2203                 if service is None:
2204                         return None
2205                 return service.seek()
2206
2207         def cueGetCurrentPosition(self):
2208                 seek = self.__getSeekable()
2209                 if seek is None:
2210                         return None
2211                 r = seek.getPlayPosition()
2212                 if r[0]:
2213                         return None
2214                 return long(r[1])
2215
2216         def cueGetEndCutPosition(self):
2217                 ret = False
2218                 isin = True
2219                 for cp in self.cut_list:
2220                         if cp[1] == self.CUT_TYPE_OUT:
2221                                 if isin:
2222                                         isin = False
2223                                         ret = cp[0]
2224                         elif cp[1] == self.CUT_TYPE_IN:
2225                                 isin = True
2226                 return ret
2227
2228         def jumpPreviousNextMark(self, cmp, start=False):
2229                 current_pos = self.cueGetCurrentPosition()
2230                 if current_pos is None:
2231                         return False
2232                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2233                 if mark is not None:
2234                         pts = mark[0]
2235                 else:
2236                         return False
2237                 self.doSeek(pts)
2238                 return True
2239
2240         def jumpPreviousMark(self):
2241                 # we add 5 seconds, so if the play position is <5s after
2242                 # the mark, the mark before will be used
2243                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2244
2245         def jumpNextMark(self):
2246                 if not self.jumpPreviousNextMark(lambda x: x-90000):
2247                         self.doSeek(-1)
2248
2249         def getNearestCutPoint(self, pts, cmp=abs, start=False):
2250                 # can be optimized
2251                 beforecut = True
2252                 nearest = None
2253                 bestdiff = -1
2254                 instate = True
2255                 if start:
2256                         bestdiff = cmp(0 - pts)
2257                         if bestdiff >= 0:
2258                                 nearest = [0, False]
2259                 for cp in self.cut_list:
2260                         if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2261                                 beforecut = False
2262                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
2263                                         diff = cmp(cp[0] - pts)
2264                                         if start and diff >= 0:
2265                                                 nearest = cp
2266                                                 bestdiff = diff
2267                                         else:
2268                                                 nearest = None
2269                                                 bestdiff = -1
2270                         if cp[1] == self.CUT_TYPE_IN:
2271                                 instate = True
2272                         elif cp[1] == self.CUT_TYPE_OUT:
2273                                 instate = False
2274                         elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2275                                 diff = cmp(cp[0] - pts)
2276                                 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2277                                         nearest = cp
2278                                         bestdiff = diff
2279                 return nearest
2280
2281         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2282                 current_pos = self.cueGetCurrentPosition()
2283                 if current_pos is None:
2284                         print "not seekable"
2285                         return
2286
2287                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2288
2289                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2290                         if onlyreturn:
2291                                 return nearest_cutpoint
2292                         if not onlyadd:
2293                                 self.removeMark(nearest_cutpoint)
2294                 elif not onlyremove and not onlyreturn:
2295                         self.addMark((current_pos, self.CUT_TYPE_MARK))
2296
2297                 if onlyreturn:
2298                         return None
2299
2300         def addMark(self, point):
2301                 insort(self.cut_list, point)
2302                 self.uploadCuesheet()
2303                 self.showAfterCuesheetOperation()
2304
2305         def removeMark(self, point):
2306                 self.cut_list.remove(point)
2307                 self.uploadCuesheet()
2308                 self.showAfterCuesheetOperation()
2309
2310         def showAfterCuesheetOperation(self):
2311                 if isinstance(self, InfoBarShowHide):
2312                         self.doShow()
2313
2314         def __getCuesheet(self):
2315                 service = self.session.nav.getCurrentService()
2316                 if service is None:
2317                         return None
2318                 if self.__getSeekable():
2319                         self.length = self.__getSeekable().getLength()
2320                 return service.cueSheet()
2321
2322         def uploadCuesheet(self):
2323                 cue = self.__getCuesheet()
2324
2325                 if cue is None:
2326                         print "upload failed, no cuesheet interface"
2327                         return
2328                 cue.setCutList(self.cut_list)
2329
2330         def __downloadChangedCuesheet(self):
2331                 self.downloadCuesheet()
2332
2333         def downloadCuesheet(self):
2334                 cue = self.__getCuesheet()
2335
2336                 if cue is None:
2337                         print "download failed, no cuesheet interface"
2338                         self.cut_list = [ ]
2339                 else:
2340                         self.cut_list = cue.getCutList()
2341
2342 class InfoBarSummary(Screen):
2343         skin = """
2344         <screen position="0,0" size="132,64">
2345                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2346                         <convert type="ClockToText">WithSeconds</convert>
2347                 </widget>
2348                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2349                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2350                         <convert type="ConditionalShowHide">Blink</convert>
2351                 </widget>
2352                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2353                         <convert type="ServiceName">Name</convert>
2354                 </widget>
2355                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2356                         <convert type="EventTime">Progress</convert>
2357                 </widget>
2358         </screen>"""
2359
2360 # for picon:  (path="piconlcd" will use LCD picons)
2361 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2362 #                       <convert type="ServiceName">Reference</convert>
2363 #               </widget>
2364
2365 class InfoBarSummarySupport:
2366         def __init__(self):
2367                 pass
2368
2369         def createSummary(self):
2370                 return InfoBarSummary
2371
2372 class InfoBarMoviePlayerSummary(Screen):
2373         skin = """
2374         <screen position="0,0" size="132,64">
2375                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2376                         <convert type="ClockToText">WithSeconds</convert>
2377                 </widget>
2378                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2379                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2380                         <convert type="ConditionalShowHide">Blink</convert>
2381                 </widget>
2382                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2383                         <convert type="ServiceName">Name</convert>
2384                 </widget>
2385                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2386                         <convert type="ServicePosition">Position</convert>
2387                 </widget>
2388         </screen>"""
2389
2390 class InfoBarMoviePlayerSummarySupport:
2391         def __init__(self):
2392                 pass
2393
2394         def createSummary(self):
2395                 return InfoBarMoviePlayerSummary
2396
2397 class InfoBarTeletextPlugin:
2398         def __init__(self):
2399                 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2400                         {
2401                                 "startTeletext": (self.startTeletext, _("View teletext..."))
2402                         })
2403
2404         def startTeletext(self):
2405                 teletext_plugins = plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT)
2406                 l = len(teletext_plugins)
2407                 if l == 1:
2408                         teletext_plugins[0](session=self.session, service=self.session.nav.getCurrentService())
2409                 elif l > 1:
2410                         list = []
2411                         for p in teletext_plugins:
2412                                 list.append( (p.name, p) )
2413                         self.session.openWithCallback(self.onTextSelected, ChoiceBox, title=_("Please select a Text application"), list = list)
2414
2415         def onTextSelected(self, p):
2416                 p = p and p[1]
2417                 if p is not None:
2418                         p(session=self.session, service=self.session.nav.getCurrentService())
2419
2420 class InfobarHbbtvPlugin:
2421         def __init__(self):
2422                 if not self["ShowRecordOnRed"].boolean and haveHbbtvApplication:
2423                         self["HbbtvActions"] = HelpableActionMap(self, "InfobarHbbtvActions",
2424                                 {
2425                                         "hbbtvAutostart" : (self.startHbbtv, _("Start HbbTV..."))
2426                                 }
2427                         )
2428                         self["HbbtvApplication"] = HbbtvApplication()
2429                 else:
2430                         self["HbbtvApplication"] = Boolean(fixed=0)
2431                         self["HbbtvApplication"].name = "" #is this a hack?
2432
2433         def startHbbtv(self):
2434                 hbbtv_plugin = None
2435                 for p in plugins.getPlugins(PluginDescriptor.WHERE_HBBTV):
2436                         hbbtv_plugin = p
2437                 if hbbtv_plugin is not None:
2438                         hbbtv_plugin(session=self.session)
2439
2440 class InfoBarSubtitleSupport(object):
2441         def __init__(self):
2442                 object.__init__(self)
2443                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2444                 self.__subtitles_enabled = False
2445
2446                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2447                         {
2448                                 iPlayableService.evSubtitleListChanged: self.__subtitlesChanged,
2449                                 iPlayableService.evEnd: self.__serviceStopped
2450                         })
2451                 self.cached_subtitle_checked = False
2452                 self.__selected_subtitle = None
2453
2454         def __serviceStopped(self):
2455                 self.cached_subtitle_checked = False
2456                 self.setSubtitlesEnable(False)
2457
2458         def __subtitlesChanged(self):
2459                 subtitle = self.getCurrentServiceSubtitle()
2460                 sub_count = subtitle and subtitle.getNumberOfSubtitleTracks() or 0
2461                 if self.cached_subtitle_checked != sub_count:
2462                         self.cached_subtitle_checked = sub_count
2463                         if not config.plugins.dict().has_key('TrackAutoselect') or config.plugins.TrackAutoselect.subtitle_autoselect_enable.value:
2464                                 for idx in range(sub_count):
2465                                         info = subtitle.getSubtitleTrackInfo(idx)
2466                                         if info.isSaved():
2467                                                 self.selected_subtitle = idx
2468                                                 self.subtitles_enabled = True
2469                                                 return
2470
2471         def getCurrentServiceSubtitle(self):
2472                 service = self.session.nav.getCurrentService()
2473                 return service and service.subtitleTracks()
2474
2475         def setSubtitlesEnable(self, enable=True):
2476                 subtitle = self.getCurrentServiceSubtitle()
2477                 if enable:
2478                         if self.__selected_subtitle != None:
2479                                 if subtitle and (not self.__subtitles_enabled or self.__selected_subtitle != subtitle.getCurrentSubtitleTrack()):
2480                                         subtitle.enableSubtitles(self.subtitle_window.instance, self.__selected_subtitle)
2481                                         self.subtitle_window.show()
2482                                         self.__subtitles_enabled = True
2483                 else:
2484                         if subtitle:
2485                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2486                         self.__selected_subtitle = False
2487                         self.__subtitles_enabled = False
2488                         self.subtitle_window.hide()
2489
2490         def setSelectedSubtitle(self, idx):
2491                 if isinstance(idx, int):
2492                         self.__selected_subtitle = idx
2493
2494         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2495         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2496
2497 class InfoBarStateInfo(Screen):
2498         def __init__(self, session):
2499                 Screen.__init__(self, session)
2500                 self["state"] = Label()
2501                 self["message"] = Label()
2502                 self.onFirstExecBegin.append(self.__onFirstExecBegin)
2503                 self._stateSizeDefault = eSize(590,40)
2504                 self._stateSizeFull = eSize(590,130)
2505                 self._stateOnly = False
2506
2507         def __onFirstExecBegin(self):
2508                 self._stateSizeDefault = self["state"].getSize()
2509                 self._stateSizeFull = eSize( self._stateSizeDefault.width(), self.instance.size().height() - (2 * self["state"].position.x()) )
2510                 self._resizeBoxes()
2511
2512         def _resizeBoxes(self):
2513                 if self._stateOnly:
2514                         self["state"].resize(self._stateSizeFull)
2515                         self["message"].hide();
2516                 else:
2517                         self["state"].resize(self._stateSizeDefault)
2518                         self["message"].show();
2519
2520         def setPlaybackState(self, state, message=""):
2521                 self["state"].text = state
2522                 self["message"].text = message
2523                 self._stateOnly = False if message else True
2524                 #self._resizeBoxes()
2525
2526         def current(self):
2527                 return (self["state"].text, self["message"].text)
2528
2529 class InfoBarServiceErrorPopupSupport:
2530         STATE_TUNING = _("tuning...")
2531         STATE_CONNECTING = _("connecting...")
2532         MESSAGE_WAIT = _("Please wait!")
2533         STATE_RECONNECTING = _("reconnecting...")
2534
2535         _stateInfo = None
2536
2537         def __init__(self):
2538                 Notifications.notificationQueue.registerDomain("ZapError", _("ZapError"), Notifications.ICON_DEFAULT)
2539                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2540                         {
2541                                 iPlayableService.evServiceChanged: self.__serviceChanged,
2542                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2543                                 iPlayableService.evStart: self.__serviceStarted,
2544                                 iPlayableService.evPlay: self.__servicePlaying,
2545                                 iPlayableService.evNotFound: self.__notFound,
2546                                 iPlayableService.evUpdatedEventInfo: self.__servicePlaying #just to be sure we're not staying on screen forever
2547                         })
2548                 self._isStream = False
2549                 self._isLiveStream = False
2550                 self._isReconnect = False
2551                 self._currentRef = None
2552                 self.last_error = None
2553                 if not InfoBarServiceErrorPopupSupport._stateInfo:
2554                         InfoBarServiceErrorPopupSupport._stateInfo = self.session.instantiateDialog(InfoBarStateInfo,zPosition=-5)
2555                         InfoBarServiceErrorPopupSupport._stateInfo.neverAnimate()
2556                 self._reconnTimer = eTimer()
2557                 self._reconnTimer_conn = self._reconnTimer.timeout.connect(self._doReconnect)
2558                 self._restoreInfo = None
2559
2560                 self.onShown.append(self.__restoreState)
2561                 self.onExecEnd.append(self.__hideState)
2562                 self.onClose.append(self.__hideState)
2563
2564                 self.__servicePlaying()
2565
2566         def __restoreState(self):
2567                 Log.i()
2568                 if self.execing and self._restoreInfo:
2569                         self.setPlaybackState(*self._restoreInfo)
2570
2571         def __hideState(self):
2572                 if InfoBarServiceErrorPopupSupport._stateInfo.shown:
2573                         self._restoreInfo = InfoBarServiceErrorPopupSupport._stateInfo.current()
2574                 InfoBarServiceErrorPopupSupport._stateInfo.hide()
2575
2576         def setPlaybackState(self, state=None, message=None):
2577                 Log.i("%s %s %s" %(state, message, time()))
2578                 if state or message:
2579                         if self.execing:
2580                                 InfoBarServiceErrorPopupSupport._stateInfo.setPlaybackState(state, message)
2581                                 InfoBarServiceErrorPopupSupport._stateInfo.show()
2582                                 self._restoreInfo = None
2583                         else:
2584                                 self._restoreInfo = (state, message)
2585                 else:
2586                         self._restoreInfo = None
2587                         InfoBarServiceErrorPopupSupport._stateInfo.hide()
2588
2589         def __serviceStarted(self):
2590                 if not self._isStream:
2591                         self.__servicePlaying()
2592                 self.last_error = None
2593
2594         def __serviceChanged(self):
2595                 ref = self.session.nav.getCurrentServiceReference()
2596                 if not ref:
2597                         self.setPlaybackState()
2598                         return
2599                 path = ref and ref.getPath()
2600                 self._isReconnect = self._currentRef and ref.toCompareString() == self._currentRef.toCompareString()
2601                 if not self._isReconnect:
2602                         self._reconnTimer.stop()
2603                 self._isStream = path and not path.startswith("/")
2604                 self._isLiveStream = self._isStream and (ref and ref.flags & eServiceReference.isLive)
2605                 self._currentRef = ref
2606                 if self._isStream:
2607                         self._pendingState = self.STATE_RECONNECTING if self._isReconnect else self.STATE_CONNECTING
2608                         self.setPlaybackState(self._pendingState, self.MESSAGE_WAIT)
2609
2610         def __servicePlaying(self):
2611                 Log.w()
2612                 self.setPlaybackState()
2613
2614         def __notFound(self):
2615                 state = self.STATE_TUNING
2616                 if self._isStream:
2617                         state = self.STATE_RECONNECTING if self._isReconnect else self.STATE_CONNECTING
2618                 self.setPlaybackState(state, _("Service not found!"))
2619                 self._checkReconnect()
2620
2621         def _doReconnect(self):
2622                 if self._isReconnect:
2623                         self.setPlaybackState(self.STATE_RECONNECTING)
2624                         self.session.nav.playService(self._currentRef, forceRestart=True)
2625
2626         def _checkReconnect(self):
2627                 Log.w("%s / %s" %(str(self._isReconnect), str(self._isLiveStream)))
2628                 self._isReconnect = self._isLiveStream
2629                 if self._isReconnect:
2630                         self._reconnTimer.startLongTimer(3)
2631
2632         def __tuneFailed(self):
2633                 service = self.session.nav.getCurrentService()
2634                 info = service and service.info()
2635                 error = info and info.getInfo(iServiceInformation.sDVBState)
2636
2637                 if error == self.last_error:
2638                         error = None
2639                 else:
2640                         self.last_error = error
2641
2642                 error = {
2643                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2644                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2645                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2646                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2647                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2648                         eDVBServicePMTHandler.eventNewProgramInfo: None,
2649                         eDVBServicePMTHandler.eventTuned: None,
2650                         eDVBServicePMTHandler.eventSOF: None,
2651                         eDVBServicePMTHandler.eventEOF: None,
2652                         eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2653                 }.get(error) #this returns None when the key not exist in the dict
2654
2655                 if error:
2656                         self.setPlaybackState(self.STATE_TUNING, error)
2657                 else:
2658                         self.setPlaybackState()
2659
2660 class InfoBarGstreamerErrorPopupSupport(object):
2661         def __init__(self):
2662                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2663                 {
2664                         eServiceMP3.evAudioDecodeError  : self.__evAudioDecodeError,
2665                         eServiceMP3.evVideoDecodeError  : self.__evVideoDecodeError,
2666                         eServiceMP3.evPluginError               : self.__evPluginError,
2667                         eServiceMP3.evStreamingSrcError : self.__evStreamingSrcError,
2668                         eServiceMP3.evFileReadError             : self.__evFileReadError,
2669                         eServiceMP3.evTypeNotFoundError : self.__evTypeNotFoundError,
2670                         eServiceMP3.evGeneralGstError   : self.__evGeneralGstError
2671                 })
2672                 self.__messages = {
2673                                 eServiceMP3.evAudioDecodeError  : _("This Dreambox can't decode %s streams!"),
2674                                 eServiceMP3.evVideoDecodeError  : _("This Dreambox can't decode %s streams!"),
2675                                 eServiceMP3.evPluginError               : "%s",
2676                                 eServiceMP3.evStreamingSrcError : _("Streaming error: %s"),
2677                                 eServiceMP3.evFileReadError             : _("Couldn't read file: %s"),
2678                                 eServiceMP3.evTypeNotFoundError : _("Couldn't find media type"),
2679                                 eServiceMP3.evGeneralGstError   : _("Gstreamer error: %s")
2680                         }
2681
2682         def __notify(self, key, hasMessage=True):
2683                 error = self.__messages.get(key)
2684                 if hasMessage:
2685                         currPlay = self.session.nav.getCurrentService()
2686                         error = error %(currPlay.info().getInfoString(iServiceInformation.sErrorText),)
2687                 self.setPlaybackState(self.STATE_CONNECTING, error)
2688
2689         def __evAudioDecodeError(self):
2690                 self.__notify(eServiceMP3.evAudioDecodeError)
2691
2692         def __evVideoDecodeError(self):
2693                 self.__notify(eServiceMP3.evVideoDecodeError)
2694
2695         def __evPluginError(self):
2696                 self.__notify(eServiceMP3.evPluginError)
2697
2698         def __evStreamingSrcError(self):
2699                 self.__notify(eServiceMP3.evStreamingSrcError)
2700                 self._checkReconnect()
2701
2702         def __evFileReadError(self):
2703                 self.__notify(eServiceMP3.evFileReadError)
2704                 self._checkReconnect()
2705
2706         def __evTypeNotFoundError(self):
2707                 self.__notify(eServiceMP3.evTypeNotFoundError, hasMessage=False)
2708
2709         def __evGeneralGstError(self):
2710                 self.__notify(eServiceMP3.evGeneralGstError)
2711                 self._checkReconnect()