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