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