add support for markers in bouquets
[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.BlinkingPixmap import BlinkingPixmapConditional
6 from Components.Harddisk import harddiskmanager
7 from Components.Input import Input
8 from Components.Label import *
9 from Components.Pixmap import Pixmap, PixmapConditional
10 from Components.PluginComponent import plugins
11 from Components.ProgressBar import *
12 from Components.ServiceEventTracker import ServiceEventTracker
13 from Components.Sources.CurrentService import CurrentService
14 from Components.Sources.EventInfo import EventInfo
15 from Components.Sources.FrontendStatus import FrontendStatus
16 from Components.Sources.Boolean import Boolean
17 from Components.Sources.Clock import Clock
18 from Components.TimerList import TimerEntryComponent
19 from Components.config import config, configElement, ConfigSubsection, configSequence, configElementBoolean, configSelection, configElement_nonSave, getConfigListEntry
20 from Components.config import configfile, configsequencearg
21
22 from EpgSelection import EPGSelection
23 from Plugins.Plugin import PluginDescriptor
24
25 from Screen import Screen
26 from Screens.ChoiceBox import ChoiceBox
27 from Screens.Dish import Dish
28 from Screens.EventView import EventViewEPGSelect, EventViewSimple
29 from Screens.InputBox import InputBox
30 from Screens.MessageBox import MessageBox
31 from Screens.MinuteInput import MinuteInput
32 from Screens.TimerSelection import TimerSelection
33 from Screens.PictureInPicture import PictureInPicture
34 from Screens.SubtitleDisplay import SubtitleDisplay
35 from ServiceReference import ServiceReference
36
37 from Tools import Notifications
38 from Tools.Directories import *
39
40 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
41 from enigma import *
42
43 import time
44 import os
45 import bisect
46
47 from Components.config import config, currentConfigSelectionElement
48
49 # hack alert!
50 from Menu import MainMenu, mdom
51
52 class InfoBarDish:
53         def __init__(self):
54                 self.dishDialog = self.session.instantiateDialog(Dish)
55                 self.onLayoutFinish.append(self.dishDialog.show)
56
57 class InfoBarShowHide:
58         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
59         fancy animations. """
60         STATE_HIDDEN = 0
61         STATE_HIDING = 1
62         STATE_SHOWING = 2
63         STATE_SHOWN = 3
64         
65         def __init__(self):
66                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
67                         {
68                                 "toggleShow": self.toggleShow,
69                                 "hide": self.hide,
70                         })
71
72                 self.__state = self.STATE_SHOWN
73                 self.__locked = 0
74                 
75                 self.onExecBegin.append(self.show)
76                 
77                 self.hideTimer = eTimer()
78                 self.hideTimer.timeout.get().append(self.doTimerHide)
79                 self.hideTimer.start(5000, True)
80                 
81                 self.onShow.append(self.__onShow)
82                 self.onHide.append(self.__onHide)
83
84         def __onShow(self):
85                 self.__state = self.STATE_SHOWN
86                 self.startHideTimer()
87         
88         def startHideTimer(self):
89                 if self.__state == self.STATE_SHOWN and not self.__locked:
90                         self.hideTimer.start(5000, True)
91
92         def __onHide(self):
93                 self.__state = self.STATE_HIDDEN
94
95         def doShow(self):
96                 self.show()
97                 self.startHideTimer()
98
99         def doTimerHide(self):
100                 self.hideTimer.stop()
101                 if self.__state == self.STATE_SHOWN:
102                         self.hide()
103
104         def toggleShow(self):
105                 if self.__state == self.STATE_SHOWN:
106                         self.hide()
107                         self.hideTimer.stop()
108                 elif self.__state == self.STATE_HIDDEN:
109                         self.show()
110
111         def lockShow(self):
112                 self.__locked = self.__locked + 1
113                 if self.execing:
114                         self.show()
115                         self.hideTimer.stop()
116         
117         def unlockShow(self):
118                 self.__locked = self.__locked - 1
119                 if self.execing:
120                         self.startHideTimer()
121
122 #       def startShow(self):
123 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
124 #               self.__state = self.STATE_SHOWN
125 #       
126 #       def startHide(self):
127 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
128 #               self.__state = self.STATE_HIDDEN
129
130 class NumberZap(Screen):
131         def quit(self):
132                 self.Timer.stop()
133                 self.close(0)
134
135         def keyOK(self):
136                 self.Timer.stop()
137                 self.close(int(self["number"].getText()))
138
139         def keyNumberGlobal(self, number):
140                 self.Timer.start(3000, True)            #reset timer
141                 self.field = self.field + str(number)
142                 self["number"].setText(self.field)
143                 if len(self.field) >= 4:
144                         self.keyOK()
145
146         def __init__(self, session, number):
147                 Screen.__init__(self, session)
148                 self.field = str(number)
149
150                 self["channel"] = Label(_("Channel:"))
151
152                 self["number"] = Label(self.field)
153
154                 self["actions"] = NumberActionMap( [ "SetupActions" ], 
155                         {
156                                 "cancel": self.quit,
157                                 "ok": self.keyOK,
158                                 "1": self.keyNumberGlobal,
159                                 "2": self.keyNumberGlobal,
160                                 "3": self.keyNumberGlobal,
161                                 "4": self.keyNumberGlobal,
162                                 "5": self.keyNumberGlobal,
163                                 "6": self.keyNumberGlobal,
164                                 "7": self.keyNumberGlobal,
165                                 "8": self.keyNumberGlobal,
166                                 "9": self.keyNumberGlobal,
167                                 "0": self.keyNumberGlobal
168                         })
169
170                 self.Timer = eTimer()
171                 self.Timer.timeout.get().append(self.keyOK)
172                 self.Timer.start(3000, True)
173
174 class InfoBarNumberZap:
175         """ Handles an initial number for NumberZapping """
176         def __init__(self):
177                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
178                         {
179                                 "1": self.keyNumberGlobal,
180                                 "2": self.keyNumberGlobal,
181                                 "3": self.keyNumberGlobal,
182                                 "4": self.keyNumberGlobal,
183                                 "5": self.keyNumberGlobal,
184                                 "6": self.keyNumberGlobal,
185                                 "7": self.keyNumberGlobal,
186                                 "8": self.keyNumberGlobal,
187                                 "9": self.keyNumberGlobal,
188                                 "0": self.keyNumberGlobal,
189                         })
190
191         def keyNumberGlobal(self, number):
192 #               print "You pressed number " + str(number)
193                 if number == 0:
194                         self.servicelist.recallPrevService()
195                         self.doShow()
196                 else:
197                         self.session.openWithCallback(self.numberEntered, NumberZap, number)
198
199         def numberEntered(self, retval):
200 #               print self.servicelist
201                 if retval > 0:
202                         self.zapToNumber(retval)
203
204         def searchNumberHelper(self, serviceHandler, num, bouquet):
205                 servicelist = serviceHandler.list(bouquet)
206                 if not servicelist is None:
207                         while num:
208                                 serviceIterator = servicelist.getNext()
209                                 if not serviceIterator.valid(): #check end of list
210                                         break
211                                 if serviceIterator.flags: #assume normal dvb service have no flags set
212                                         continue
213                                 num -= 1;
214                         if not num: #found service with searched number ?
215                                 return serviceIterator, 0
216                 return None, num
217
218         def zapToNumber(self, number):
219                 bouquet = self.servicelist.bouquet_root
220                 service = None
221                 serviceHandler = eServiceCenter.getInstance()
222                 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
223                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
224                 else:
225                         bouquetlist = serviceHandler.list(bouquet)
226                         if not bouquetlist is None:
227                                 while number:
228                                         bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
229                                         if not bouquet.valid(): #check end of list
230                                                 break
231                                         if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
232                                                 continue
233                                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
234                 if not service is None:
235                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
236                                 self.servicelist.clearPath()
237                                 if self.servicelist.bouquet_root != bouquet:
238                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
239                                 self.servicelist.enterPath(bouquet)
240                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
241                         self.servicelist.zap()
242
243 config.misc.initialchannelselection = configElementBoolean("config.misc.initialchannelselection", 1);
244
245 class InfoBarChannelSelection:
246         """ ChannelSelection - handles the channelSelection dialog and the initial 
247         channelChange actions which open the channelSelection dialog """
248         def __init__(self):
249                 #instantiate forever
250                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
251                 
252                 if config.misc.initialchannelselection.value == 1:
253                         self.onShown.append(self.firstRun)
254
255                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
256                         {
257                                 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
258                                 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
259                                 "zapUp": (self.zapUp, _("previous channel")),
260                                 "zapDown": (self.zapDown, _("next channel")),
261                                 "historyBack": (self.historyBack, _("previous channel in history")),
262                                 "historyNext": (self.historyNext, _("next channel in history")),
263                                 "openServiceList": (self.openServiceList, _("open servicelist")),
264                         })
265
266         def showTvChannelList(self, zap=False):
267                 self.servicelist.setModeTv()
268                 if zap:
269                         self.servicelist.zap()
270                 self.session.execDialog(self.servicelist)
271
272         def showRadioChannelList(self, zap=False):
273                 self.servicelist.setModeRadio()
274                 if zap:
275                         self.servicelist.zap()
276                 self.session.execDialog(self.servicelist)
277
278         def firstRun(self):
279                 self.onShown.remove(self.firstRun)
280                 config.misc.initialchannelselection.value = 0
281                 config.misc.initialchannelselection.save()
282                 self.switchChannelDown()
283
284         def historyBack(self):
285                 self.servicelist.historyBack()
286
287         def historyNext(self):
288                 self.servicelist.historyNext()
289
290         def switchChannelUp(self):
291                 self.servicelist.moveUp()
292                 self.session.execDialog(self.servicelist)
293
294         def switchChannelDown(self):
295                 self.servicelist.moveDown()
296                 self.session.execDialog(self.servicelist)
297         
298         def openServiceList(self):
299                 self.session.execDialog(self.servicelist)
300
301         def zapUp(self):
302                 if self.servicelist.inBouquet():
303                         prev = self.servicelist.getCurrentSelection()
304                         if prev:
305                                 prev = prev.toString()
306                                 while True:
307                                         if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
308                                                 if self.servicelist.atBegin():
309                                                         self.servicelist.prevBouquet()
310                                         self.servicelist.moveUp()
311                                         cur = self.servicelist.getCurrentSelection()
312                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
313                                                 break
314                 else:
315                         self.servicelist.moveUp()
316                 self.servicelist.zap()
317                 self.doShow()
318
319         def zapDown(self):
320                 if self.servicelist.inBouquet():
321                         prev = self.servicelist.getCurrentSelection()
322                         if prev:
323                                 prev = prev.toString()
324                                 while True:
325                                         if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
326                                                 if self.servicelist.atEnd():
327                                                         self.servicelist.nextBouquet()
328                                                 else:
329                                                         self.servicelist.moveDown()
330                                         else:
331                                                 self.servicelist.moveDown()
332                                         cur = self.servicelist.getCurrentSelection()
333                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
334                                                 break
335                 else:
336                         self.servicelist.moveDown()
337                 self.servicelist.zap()
338                 self.doShow()
339
340 class InfoBarMenu:
341         """ Handles a menu action, to open the (main) menu """
342         def __init__(self):
343                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions", 
344                         {
345                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
346                         })
347
348         def mainMenu(self):
349                 print "loading mainmenu XML..."
350                 menu = mdom.childNodes[0]
351                 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
352                 self.session.open(MainMenu, menu, menu.childNodes)
353
354 class InfoBarSimpleEventView:
355         """ Opens the Eventview for now/next """
356         def __init__(self):
357                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
358                         {
359                                 "showEventInfo": (self.openEventView, _("show event details")),
360                         })
361
362         def openEventView(self):
363                 self.epglist = [ ]
364                 service = self.session.nav.getCurrentService()
365                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
366                 info = service.info()
367                 ptr=info.getEvent(0)
368                 if ptr:
369                         self.epglist.append(ptr)
370                 ptr=info.getEvent(1)
371                 if ptr:
372                         self.epglist.append(ptr)
373                 if len(self.epglist) > 0:
374                         self.session.open(EventViewSimple, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
375
376         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
377                 if len(self.epglist) > 1:
378                         tmp = self.epglist[0]
379                         self.epglist[0]=self.epglist[1]
380                         self.epglist[1]=tmp
381                         setEvent(self.epglist[0])
382
383 class InfoBarEPG:
384         """ EPG - Opens an EPG list when the showEPGList action fires """
385         def __init__(self):
386                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
387                         {
388                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
389                         })
390
391                 self.is_now_next = False
392                 self.dlg_stack = [ ]
393                 self.bouquetSel = None
394                 self.eventView = None
395                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
396                         {
397                                 "showEventInfo": (self.openEventView, _("show EPG...")),
398                         })
399
400         def zapToService(self, service):
401                 if not service is None:
402                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
403                                 self.servicelist.clearPath()
404                                 if self.servicelist.bouquet_root != self.epg_bouquet:
405                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
406                                 self.servicelist.enterPath(self.epg_bouquet)
407                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
408                         self.servicelist.zap()
409
410         def getBouquetServices(self, bouquet):
411                 services = [ ]
412                 servicelist = eServiceCenter.getInstance().list(bouquet)
413                 if not servicelist is None:
414                         while True:
415                                 service = servicelist.getNext()
416                                 if not service.valid(): #check if end of list
417                                         break
418                                 if service.flags: #ignore non playable services
419                                         continue
420                                 services.append(ServiceReference(service))
421                 return services
422
423         def openBouquetEPG(self, bouquet, withCallback=True):
424                 services = self.getBouquetServices(bouquet)
425                 if len(services):
426                         self.epg_bouquet = bouquet
427                         if withCallback:
428                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
429                         else:
430                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
431
432         def changeBouquetCB(self, direction, epg):
433                 if self.bouquetSel:
434                         if direction > 0:
435                                 self.bouquetSel.down()
436                         else:
437                                 self.bouquetSel.up()
438                         bouquet = self.bouquetSel.getCurrent()
439                         services = self.getBouquetServices(bouquet)
440                         if len(services):
441                                 self.epg_bouquet = bouquet
442                                 epg.setServices(services)
443
444         def closed(self, ret=False):
445                 closedScreen = self.dlg_stack.pop()
446                 if self.bouquetSel and closedScreen == self.bouquetSel:
447                         self.bouquetSel = None
448                 elif self.eventView and closedScreen == self.eventView:
449                         self.eventView = None
450                 if ret:
451                         dlgs=len(self.dlg_stack)
452                         if dlgs > 0:
453                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
454
455         def openMultiServiceEPG(self, withCallback=True):
456                 bouquets = self.servicelist.getBouquetList()
457                 if bouquets is None:
458                         cnt = 0
459                 else:
460                         cnt = len(bouquets)
461                 if cnt > 1: # show bouquet list
462                         if withCallback:
463                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
464                                 self.dlg_stack.append(self.bouquetSel)
465                         else:
466                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
467                 elif cnt == 1: 
468                         self.openBouquetEPG(bouquets[0][1], withCallback)
469
470         def openSingleServiceEPG(self):
471                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
472                 self.session.open(EPGSelection, ref)
473
474         def openSimilarList(self, eventid, refstr):
475                 self.session.open(EPGSelection, refstr, None, eventid)
476
477         def getNowNext(self):
478                 self.epglist = [ ]
479                 service = self.session.nav.getCurrentService()
480                 info = service and service.info()
481                 ptr = info and info.getEvent(0)
482                 if ptr:
483                         self.epglist.append(ptr)
484                 ptr = info and info.getEvent(1)
485                 if ptr:
486                         self.epglist.append(ptr)
487
488         def __evEventInfoChanged(self):
489                 if self.is_now_next and len(self.dlg_stack) == 1:
490                         self.getNowNext()
491                         assert self.eventView
492                         if len(self.epglist):
493                                 self.eventView.setEvent(self.epglist[0])
494
495         def openEventView(self):
496                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
497                 self.getNowNext()
498                 if len(self.epglist) == 0:
499                         self.is_now_next = False
500                         epg = eEPGCache.getInstance()
501                         ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
502                         if ptr:
503                                 self.epglist.append(ptr)
504                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
505                                 if ptr:
506                                         self.epglist.append(ptr)
507                 else:
508                         self.is_now_next = True
509                 if len(self.epglist) > 0:
510                         self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
511                         self.dlg_stack.append(self.eventView)
512                 else:
513                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
514                         self.openMultiServiceEPG(False)
515
516         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
517                 if len(self.epglist) > 1:
518                         tmp = self.epglist[0]
519                         self.epglist[0]=self.epglist[1]
520                         self.epglist[1]=tmp
521                         setEvent(self.epglist[0])
522
523 class InfoBarTuner:
524         """provides a snr/agc/ber display"""
525         def __init__(self):
526                 self["FrontendStatus"] = FrontendStatus(service_source = self.session.nav.getCurrentService)
527
528 class InfoBarEvent:
529         """provides a current/next event info display"""
530         def __init__(self):
531                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.NOW)
532                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.NEXT)
533
534 class InfoBarServiceName:
535         def __init__(self):
536                 self["CurrentService"] = CurrentService(self.session.nav)
537
538 class InfoBarSeek:
539         """handles actions like seeking, pause"""
540         
541         # ispause, isff, issm
542         SEEK_STATE_PLAY = (0, 0, 0, ">")
543         SEEK_STATE_PAUSE = (1, 0, 0, "||")
544         SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
545         SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
546         SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
547         SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
548         SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
549         SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
550         
551         SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
552         SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
553         SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
554         SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
555         
556         SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
557         SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
558         SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
559         
560         def __init__(self):
561                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
562                         {
563                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
564                                 iPlayableService.evStart: self.__serviceStarted,
565                                 
566                                 iPlayableService.evEOF: self.__evEOF,
567                                 iPlayableService.evSOF: self.__evSOF,
568                         })
569
570                 class InfoBarSeekActionMap(HelpableActionMap):
571                         def __init__(self, screen, *args, **kwargs):
572                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
573                                 self.screen = screen
574                                 
575                         def action(self, contexts, action):
576                                 if action[:5] == "seek:":
577                                         time = int(action[5:])
578                                         self.screen.seekRelative(time * 90000)
579                                         return 1
580                                 else:
581                                         return HelpableActionMap.action(self, contexts, action)
582
583                 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
584                         {
585                                 "pauseService": (self.pauseService, _("pause")),
586                                 "unPauseService": (self.unPauseService, _("continue")),
587                                 
588                                 "seekFwd": (self.seekFwd, _("skip forward")),
589                                 "seekFwdDown": self.seekFwdDown,
590                                 "seekFwdUp": self.seekFwdUp,
591                                 "seekBack": (self.seekBack, _("skip backward")),
592                                 "seekBackDown": self.seekBackDown,
593                                 "seekBackUp": self.seekBackUp,
594                         }, prio=-1)
595                         # give them a little more priority to win over color buttons
596
597                 self.seekstate = self.SEEK_STATE_PLAY
598                 self.onClose.append(self.delTimer)
599                 
600                 self.fwdtimer = False
601                 self.fwdKeyTimer = eTimer()
602                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
603
604                 self.rwdtimer = False
605                 self.rwdKeyTimer = eTimer()
606                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
607                 
608                 self.onPlayStateChanged = [ ]
609                 
610                 self.lockedBecauseOfSkipping = False
611         
612         def up(self):
613                 pass
614         
615         def down(self):
616                 pass
617         
618         def delTimer(self):
619                 del self.fwdKeyTimer
620                 del self.rwdKeyTimer
621         
622         def getSeek(self):
623                 service = self.session.nav.getCurrentService()
624                 if service is None:
625                         return None
626
627                 seek = service.seek()
628
629                 if seek is None or not seek.isCurrentlySeekable():
630                         return None
631                 
632                 return seek
633         
634         def isSeekable(self):
635                 if self.getSeek() is None:
636                         return False
637                 return True
638
639         def __seekableStatusChanged(self):
640                 print "seekable status changed!"
641                 if not self.isSeekable():
642                         self["SeekActions"].setEnabled(False)
643                         print "not seekable, return to play"
644                         self.setSeekState(self.SEEK_STATE_PLAY)
645                 else:
646                         self["SeekActions"].setEnabled(True)
647                         print "seekable"
648
649         def __serviceStarted(self):
650                 self.seekstate = self.SEEK_STATE_PLAY
651
652         def setSeekState(self, state):
653                 service = self.session.nav.getCurrentService()
654                 
655                 if service is None:
656                         return False
657                 
658                 if not self.isSeekable():
659                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
660                                 state = self.SEEK_STATE_PLAY
661                 
662                 pauseable = service.pause()
663
664                 if pauseable is None:
665                         print "not pauseable."
666                         state = self.SEEK_STATE_PLAY
667                 
668                 oldstate = self.seekstate
669                 self.seekstate = state
670                 
671                 for i in range(3):
672                         if oldstate[i] != self.seekstate[i]:
673                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
674
675                 for c in self.onPlayStateChanged:
676                         c(self.seekstate)
677                 
678                 self.checkSkipShowHideLock()
679
680                 return True
681
682         def pauseService(self):
683                 if self.seekstate == self.SEEK_STATE_PAUSE:
684                         print "pause, but in fact unpause"
685                         self.unPauseService()
686                 else:
687                         if self.seekstate == self.SEEK_STATE_PLAY:
688                                 print "yes, playing."
689                         else:
690                                 print "no", self.seekstate
691                         print "pause"
692                         self.setSeekState(self.SEEK_STATE_PAUSE);
693                 
694         def unPauseService(self):
695                 print "unpause"
696                 if self.seekstate == self.SEEK_STATE_PLAY:
697                         return 0
698                 self.setSeekState(self.SEEK_STATE_PLAY);
699         
700         def doSeek(self, seektime):
701                 print "doseek", seektime
702                 service = self.session.nav.getCurrentService()
703                 if service is None:
704                         return
705                 
706                 seekable = self.getSeek()
707                 if seekable is None:
708                         return
709                 
710                 seekable.seekTo(90 * seektime)
711
712         def seekFwdDown(self):
713                 print "start fwd timer"
714                 self.fwdtimer = True
715                 self.fwdKeyTimer.start(1000)
716
717         def seekBackDown(self):
718                 print "start rewind timer"
719                 self.rwdtimer = True
720                 self.rwdKeyTimer.start(1000)
721
722         def seekFwdUp(self):
723                 print "seekFwdUp"
724                 if self.fwdtimer:
725                         self.fwdKeyTimer.stop()
726                         self.fwdtimer = False
727                         self.seekFwd()
728
729         def seekFwd(self):
730                 lookup = {
731                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
732                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
733                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
734                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
735                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
736                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
737                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
738                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
739                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
740                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
741                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
742                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
743                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
744                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
745                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
746                         }
747                 self.setSeekState(lookup[self.seekstate])
748         
749         def seekBackUp(self):
750                 print "seekBackUp"
751                 if self.rwdtimer:
752                         self.rwdKeyTimer.stop()
753                         self.rwdtimer = False
754                         self.seekBack()
755                 
756         def seekBack(self):
757                 lookup = {
758                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
759                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
760                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
761                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
762                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
763                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
764                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
765                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
766                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
767                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
768                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
769                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
770                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
771                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
772                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
773                         }
774                 self.setSeekState(lookup[self.seekstate])
775                 
776                 if self.seekstate == self.SEEK_STATE_PAUSE:
777                         seekable = self.getSeek()
778                         if seekable is not None:
779                                 seekable.seekRelative(-1, 3)
780
781         def fwdTimerFire(self):
782                 print "Display seek fwd"
783                 self.fwdKeyTimer.stop()
784                 self.fwdtimer = False
785                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
786                 
787         def fwdSeekTo(self, minutes):
788                 print "Seek", minutes, "minutes forward"
789                 if minutes != 0:
790                         seekable = self.getSeek()
791                         if seekable is not None:
792                                 seekable.seekRelative(1, minutes * 60 * 90000)
793         
794         def rwdTimerFire(self):
795                 print "rwdTimerFire"
796                 self.rwdKeyTimer.stop()
797                 self.rwdtimer = False
798                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
799         
800         def rwdSeekTo(self, minutes):
801                 print "rwdSeekTo"
802                 self.fwdSeekTo(0 - minutes)
803         
804         def checkSkipShowHideLock(self):
805                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
806                 
807                 if self.lockedBecauseOfSkipping and not wantlock:
808                         self.unlockShow()
809                         self.lockedBecauseOfSkipping = False
810                 
811                 if wantlock and not self.lockedBecauseOfSkipping:
812                         self.lockShow()
813                         self.lockedBecauseOfSkipping = True
814
815         def __evEOF(self):
816                 if self.seekstate != self.SEEK_STATE_PLAY:
817                         self.setSeekState(self.SEEK_STATE_PAUSE)
818                         # HACK
819                         #self.getSeek().seekRelative(1, -90000)
820                         self.setSeekState(self.SEEK_STATE_PLAY)
821                 else:
822                         self.setSeekState(self.SEEK_STATE_PAUSE)
823         
824         def __evSOF(self):
825                 self.setSeekState(self.SEEK_STATE_PLAY)
826                 self.doSeek(0)
827
828         def seekRelative(self, diff):
829                 seekable = self.getSeek()
830                 if seekable is not None:
831                         seekable.seekRelative(1, diff)
832
833 from Screens.PVRState import PVRState, TimeshiftState
834
835 class InfoBarPVRState:
836         def __init__(self, screen=PVRState):
837                 self.onPlayStateChanged.append(self.__playStateChanged)
838                 self.pvrStateDialog = self.session.instantiateDialog(screen)
839                 self.onShow.append(self.__mayShow)
840                 self.onHide.append(self.pvrStateDialog.hide)
841         
842         def __mayShow(self):
843                 if self.seekstate != self.SEEK_STATE_PLAY and self.execing:
844                         self.pvrStateDialog.show()
845
846         def __playStateChanged(self, state):
847                 playstateString = state[3]
848                 self.pvrStateDialog["state"].setText(playstateString)
849                 self.__mayShow()
850
851 class InfoBarTimeshiftState(InfoBarPVRState):
852         def __init__(self):
853                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
854
855 class InfoBarShowMovies:
856
857         # i don't really like this class. 
858         # it calls a not further specified "movie list" on up/down/movieList,
859         # so this is not more than an action map
860         def __init__(self):
861                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
862                         {
863                                 "movieList": (self.showMovies, "movie list"),
864                                 "up": (self.showMovies, "movie list"),
865                                 "down": (self.showMovies, "movie list")
866                         })
867
868 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
869
870 # Hrmf.
871 #
872 # Timeshift works the following way:
873 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
874 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
875 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
876 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
877 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
878 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
879 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
880 #
881
882 # in other words:
883 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
884 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
885 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
886 # - the user can now PVR around
887 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
888 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
889 # after!
890 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
891 # - if the user rewinds, or press pause, timeshift will be activated again
892
893 # note that a timeshift can be enabled ("recording") and
894 # activated (currently time-shifting).
895
896 class InfoBarTimeshift:
897         def __init__(self):
898                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
899                         {
900                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
901                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
902                         }, prio=1)
903                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
904                         {
905                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
906                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
907                         }, prio=-1) # priority over record
908
909                 self.timeshift_enabled = 0
910                 self.timeshift_state = 0
911                 self.ts_pause_timer = eTimer()
912                 self.ts_pause_timer.timeout.get().append(self.pauseService)
913
914                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
915                         {
916                                 iPlayableService.evStart: self.__serviceStarted,
917                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
918                         })
919         
920         def getTimeshift(self):
921                 service = self.session.nav.getCurrentService()
922                 return service and service.timeshift()
923
924         def startTimeshift(self):
925                 print "enable timeshift"
926                 ts = self.getTimeshift()
927                 if ts is None:
928 #                       self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
929 #                       print "no ts interface"
930                         return 0;
931                 
932                 if self.timeshift_enabled:
933                         print "hu, timeshift already enabled?"
934                 else:
935                         if not ts.startTimeshift():
936                                 import time
937                                 self.timeshift_enabled = 1
938
939                                 # we remove the "relative time" for now.
940                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
941                                         
942                                 # PAUSE.
943                                 self.setSeekState(self.SEEK_STATE_PAUSE)
944                                 
945                                 # enable the "TimeshiftEnableActions", which will override
946                                 # the startTimeshift actions
947                                 self.__seekableStatusChanged()
948                         else:
949                                 print "timeshift failed"
950
951         def stopTimeshift(self):
952                 if not self.timeshift_enabled:
953                         return 0
954                 print "disable timeshift"
955                 ts = self.getTimeshift()
956                 if ts is None:
957                         return 0
958                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
959
960         def stopTimeshiftConfirmed(self, confirmed):
961                 if not confirmed:
962                         return
963
964                 ts = self.getTimeshift()
965                 if ts is None:
966                         return
967
968                 ts.stopTimeshift()
969                 self.timeshift_enabled = 0
970
971                 # disable actions
972                 self.__seekableStatusChanged()
973         
974         # activates timeshift, and seeks to (almost) the end
975         def activateTimeshiftEnd(self):
976                 ts = self.getTimeshift()
977                 
978                 if ts is None:
979                         return
980                 
981                 if ts.isTimeshiftActive():
982                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
983                         self.pauseService()
984                 else:
985                         self.setSeekState(self.SEEK_STATE_PLAY)
986                         ts.activateTimeshift()
987                         self.seekRelative(0)
988         
989         # same as activateTimeshiftEnd, but pauses afterwards.
990         def activateTimeshiftEndAndPause(self):
991                 state = self.seekstate
992                 self.activateTimeshiftEnd()
993                 
994                 # well, this is "andPause", but it could be pressed from pause,
995                 # when pausing on the (fake-)"live" picture, so an un-pause
996                 # is perfectly ok.
997                 
998                 print "now, pauseService"
999                 if state == self.SEEK_STATE_PLAY:
1000                         print "is PLAYING, start pause timer"
1001                         self.ts_pause_timer.start(200, 1)
1002                 else:
1003                         print "unpause"
1004                         self.unPauseService()
1005         
1006         def __seekableStatusChanged(self):
1007                 enabled = False
1008                 
1009                 print "self.isSeekable", self.isSeekable()
1010                 print "self.timeshift_enabled", self.timeshift_enabled
1011                 
1012                 # when this service is not seekable, but timeshift
1013                 # is enabled, this means we can activate
1014                 # the timeshift
1015                 if not self.isSeekable() and self.timeshift_enabled:
1016                         enabled = True
1017
1018                 print "timeshift activate:", enabled
1019                 self["TimeshiftActivateActions"].setEnabled(enabled)
1020
1021         def __serviceStarted(self):
1022                 self.timeshift_enabled = False
1023                 self.__seekableStatusChanged()
1024
1025 from Screens.PiPSetup import PiPSetup
1026
1027 class InfoBarExtensions:
1028         def __init__(self):
1029                 self.session.pipshown = False
1030                 
1031                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1032                         {
1033                                 "extensions": (self.extensions, _("view extensions...")),
1034                         })
1035
1036         PIPON = 0
1037         PIPOFF = 1
1038         MOVEPIP = 2
1039         PIPSWAP = 3
1040         ENABLE_SUBTITLE = 4
1041
1042         def extensions(self):
1043                 list = []
1044                 if self.session.pipshown == False:
1045                         list.append((_("Activate Picture in Picture"), self.PIPON))
1046                 elif self.session.pipshown == True:
1047                         list.append((_("Disable Picture in Picture"), self.PIPOFF))
1048                         list.append((_("Move Picture in Picture"), self.MOVEPIP))
1049                         list.append((_("Swap services"), self.PIPSWAP))
1050                 
1051                 s = self.getCurrentServiceSubtitle()
1052                 l = s and s.getSubtitleList() or [ ]
1053                 
1054                 for x in l:
1055                         list.append(("Enable Subtitles: " + x[0], self.ENABLE_SUBTITLE, x[1]))
1056                 
1057                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list)
1058
1059         def extensionCallback(self, answer):
1060                 if answer is not None:
1061                         if answer[1] == self.PIPON:
1062                                 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1063                                 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1064                                 if self.session.pip.playService(newservice):
1065                                         self.session.pipshown = True
1066                                         self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1067                                 else:
1068                                         self.session.pipshown = False
1069                                         del self.session.pip
1070                                 self.session.nav.playService(newservice)
1071                         elif answer[1] == self.PIPOFF:
1072                                 del self.session.pip
1073                                 self.session.pipshown = False
1074                         elif answer[1] == self.PIPSWAP:
1075                                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1076                                 if self.session.pip.servicePath:
1077                                         servicepath = self.servicelist.getCurrentServicePath()
1078                                         ref=servicepath[len(servicepath)-1]
1079                                         pipref=self.session.pip.getCurrentService()
1080                                         self.session.pip.playService(swapservice)
1081                                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1082                                         if pipref.toString() != ref.toString(): # is a subservice ?
1083                                                 self.session.nav.stopService() # stop portal
1084                                                 self.session.nav.playService(pipref) # start subservice
1085                                         self.session.pip.servicePath=servicepath
1086                         elif answer[1] == self.MOVEPIP:
1087                                 self.session.open(PiPSetup, pip = self.session.pip)
1088                         elif answer[1] == self.ENABLE_SUBTITLE:
1089                                 self.selected_subtitle = answer[2]
1090                                 self.subtitles_enabled = True
1091
1092 from RecordTimer import parseEvent
1093
1094 class InfoBarInstantRecord:
1095         """Instant Record - handles the instantRecord action in order to 
1096         start/stop instant records"""
1097         def __init__(self):
1098                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1099                         {
1100                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1101                         })
1102                 self.recording = []
1103                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1104                 self["BlinkingPoint"].hide()
1105                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1106
1107         def stopCurrentRecording(self, entry = -1):     
1108                 if entry is not None and entry != -1:
1109                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1110                         self.recording.remove(self.recording[entry])
1111
1112         def startInstantRecording(self, limitEvent = False):
1113                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1114                 
1115                 # try to get event info
1116                 event = None
1117                 try:
1118                         service = self.session.nav.getCurrentService()
1119                         epg = eEPGCache.getInstance()
1120                         event = epg.lookupEventTime(serviceref, -1, 0)
1121                         if event is None:
1122                                 info = service.info()
1123                                 ev = info.getEvent(0)
1124                                 event = ev
1125                 except:
1126                         pass
1127
1128                 begin = time.time()
1129                 end = time.time() + 3600 * 10
1130                 name = "instant record"
1131                 description = ""
1132                 eventid = None
1133                 
1134                 if event is not None:
1135                         curEvent = parseEvent(event)
1136                         name = curEvent[2]
1137                         description = curEvent[3]
1138                         eventid = curEvent[4]
1139                         if limitEvent:
1140                                 end = curEvent[1]
1141                 else:
1142                         if limitEvent:
1143                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1144                                 
1145                 data = (begin, end, name, description, eventid)
1146                 
1147                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1148                 recording.dontSave = True
1149                 self.recording.append(recording)
1150                 
1151                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1152                 
1153         def isInstantRecordRunning(self):
1154                 print "self.recording:", self.recording
1155                 if len(self.recording) > 0:
1156                         for x in self.recording:
1157                                 if x.isRunning():
1158                                         return True
1159                 return False
1160
1161         def recordQuestionCallback(self, answer):
1162                 print "pre:\n", self.recording
1163                 
1164                 if answer is None or answer[1] == "no":
1165                         return
1166                 list = []
1167                 recording = self.recording[:]
1168                 for x in recording:
1169                         if not x in self.session.nav.RecordTimer.timer_list:
1170                                 self.recording.remove(x)
1171                         elif x.dontSave and x.isRunning():
1172                                 list.append(TimerEntryComponent(x, False))              
1173
1174                 if answer[1] == "changeduration":
1175                         if len(self.recording) == 1:
1176                                 self.changeDuration(0)
1177                         else:
1178                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1179                 elif answer[1] == "stop":
1180                         if len(self.recording) == 1:
1181                                 self.stopCurrentRecording(0)
1182                         else:
1183                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1184                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1185                         limitEvent = False
1186                         if answer[1] == "event":
1187                                 limitEvent = True
1188                         if answer[1] == "manualduration":
1189                                 self.selectedEntry = len(self.recording)
1190                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1191                         self.startInstantRecording(limitEvent = limitEvent)
1192                         
1193                 print "after:\n", self.recording
1194
1195         def changeDuration(self, entry):
1196                 if entry is not None:
1197                         self.selectedEntry = entry
1198                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1199
1200         def inputCallback(self, value):
1201                 if value is not None:
1202                         print "stopping recording after", int(value), "minutes."
1203                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1204                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1205
1206         def instantRecord(self):
1207                 try:
1208                         stat = os.stat(resolveFilename(SCOPE_HDD))
1209                 except:
1210                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1211                         return
1212
1213                 if self.isInstantRecordRunning():
1214                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("A recording is currently running.\nWhat do you want to do?"), list=[(_("stop recording"), "stop"), (_("change recording (duration)"), "changeduration"), (_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"), (_("do nothing"), "no")])
1215                 else:
1216                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Start recording?"), list=[(_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"),(_("don't record"), "no")])
1217
1218 from Tools.ISO639 import LanguageCodes
1219
1220 class InfoBarAudioSelection:
1221         def __init__(self):
1222                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1223                         {
1224                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1225                         })
1226
1227         def audioSelection(self):
1228                 service = self.session.nav.getCurrentService()
1229                 audio = service and service.audioTracks()
1230                 self.audioTracks = audio
1231                 n = audio and audio.getNumberOfTracks()
1232                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1233                 tlist = []
1234                 print "tlist:", tlist
1235                 if n and n > 0:
1236                         self.audioChannel = service.audioChannel()
1237
1238                         for x in range(n):
1239                                 i = audio.getTrackInfo(x)
1240                                 language = i.getLanguage()
1241                                 description = i.getDescription()
1242         
1243                                 if len(language) == 3:
1244                                         if language in LanguageCodes:
1245                                                 language = LanguageCodes[language][0]
1246         
1247                                 if len(description):
1248                                         description += " (" + language + ")"
1249                                 else:
1250                                         description = language
1251         
1252                                 tlist.append((description, x))
1253                         
1254                         selectedAudio = tlist[0][1]
1255                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1256
1257                         selection = 2
1258                         for x in tlist:
1259                                 if x[1] != selectedAudio:
1260                                         selection += 1
1261                                 else:
1262                                         break
1263
1264                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1265                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1266                 else:
1267                         del self.audioTracks
1268
1269         def audioSelected(self, audio):
1270                 if audio is not None:
1271                         if isinstance(audio[1], str):
1272                                 if audio[1] == "mode":
1273                                         keys = ["red", "green", "yellow"]
1274                                         selection = self.audioChannel.getCurrentChannel()
1275                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1276                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1277                         else:
1278                                 del self.audioChannel
1279                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1280                                         self.audioTracks.selectTrack(audio[1])
1281                 else:
1282                         del self.audioChannel
1283                 del self.audioTracks
1284
1285         def modeSelected(self, mode):
1286                 if mode is not None:
1287                         self.audioChannel.selectChannel(mode[1])
1288                 del self.audioChannel
1289
1290 class InfoBarSubserviceSelection:
1291         def __init__(self):
1292                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1293                         {
1294                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1295                         })
1296
1297                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1298                         {
1299                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1300                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1301                         }, -1)
1302                 self["SubserviceQuickzapAction"].setEnabled(False)
1303
1304                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1305
1306         def checkSubservicesAvail(self, ev):
1307                 if ev == iPlayableService.evUpdatedEventInfo:
1308                         service = self.session.nav.getCurrentService()
1309                         subservices = service and service.subServices()
1310                         if not subservices or subservices.getNumberOfSubservices() == 0:
1311                                 self["SubserviceQuickzapAction"].setEnabled(False)
1312
1313         def nextSubservice(self):
1314                 self.changeSubservice(+1)
1315
1316         def prevSubservice(self):
1317                 self.changeSubservice(-1)
1318
1319         def changeSubservice(self, direction):
1320                 service = self.session.nav.getCurrentService()
1321                 subservices = service and service.subServices()
1322                 n = subservices and subservices.getNumberOfSubservices()
1323                 if n and n > 0:
1324                         selection = -1
1325                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1326                         for x in range(n):
1327                                 if subservices.getSubservice(x).toString() == ref.toString():
1328                                         selection = x
1329                         if selection != -1:
1330                                 selection += direction
1331                                 if selection >= n:
1332                                         selection=0
1333                                 elif selection < 0:
1334                                         selection=n-1
1335                                 newservice = subservices.getSubservice(selection)
1336                                 if newservice.valid():
1337                                         del subservices
1338                                         del service
1339                                         self.session.nav.playService(newservice)
1340
1341         def subserviceSelection(self):
1342                 service = self.session.nav.getCurrentService()
1343                 subservices = service and service.subServices()
1344                 
1345                 n = subservices and subservices.getNumberOfSubservices()
1346                 selection = 0
1347                 if n and n > 0:
1348                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1349                         tlist = []
1350                         for x in range(n):
1351                                 i = subservices.getSubservice(x)
1352                                 if i.toString() == ref.toString():
1353                                         selection = x
1354                                 tlist.append((i.getName(), i))
1355
1356                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection)
1357
1358         def subserviceSelected(self, service):
1359                 if not service is None:
1360                         self["SubserviceQuickzapAction"].setEnabled(True)
1361                         self.session.nav.playService(service[1])
1362
1363 class InfoBarAdditionalInfo:
1364         def __init__(self):
1365                 self["NimA"] = Pixmap()
1366                 self["NimB"] = Pixmap()
1367                 self["NimA_Active"] = Pixmap()
1368                 self["NimB_Active"] = Pixmap()
1369
1370                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1371                 self["TimeshiftPossible"] = self["RecordingPossible"]
1372                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1373
1374                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1375                 res_mgr = eDVBResourceManagerPtr()
1376                 if eDVBResourceManager.getInstance(res_mgr) == 0:
1377                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1378
1379         def tunerUseMaskChanged(self, mask):
1380                 if mask&1:
1381                         self["NimA_Active"].show()
1382                 else:
1383                         self["NimA_Active"].hide()
1384                 if mask&2:
1385                         self["NimB_Active"].show()
1386                 else:
1387                         self["NimB_Active"].hide()
1388
1389         def checkTunerState(self, service):
1390                 info = service.frontendInfo()
1391                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1392                 if feNumber is None:
1393                         self["NimA"].hide()
1394                         self["NimB"].hide()
1395                 elif feNumber == 0:
1396                         self["NimB"].hide()
1397                         self["NimA"].show()
1398                 elif feNumber == 1:
1399                         self["NimA"].hide()
1400                         self["NimB"].show()
1401
1402         def gotServiceEvent(self, ev):
1403                 service = self.session.nav.getCurrentService()
1404                 if ev == iPlayableService.evStart:
1405                         self.checkTunerState(service)
1406
1407 class InfoBarNotifications:
1408         def __init__(self):
1409                 self.onExecBegin.append(self.checkNotifications)
1410                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1411                 self.onClose.append(self.__removeNotification)
1412         
1413         def __removeNotification(self):
1414                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1415         
1416         def checkNotificationsIfExecing(self):
1417                 if self.execing:
1418                         self.checkNotifications()
1419
1420         def checkNotifications(self):
1421                 if len(Notifications.notifications):
1422                         n = Notifications.notifications[0]
1423                         Notifications.notifications = Notifications.notifications[1:]
1424                         cb = n[0]
1425                         if cb is not None:
1426                                 self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1427                         else:
1428                                 self.session.open(n[1], *n[2], **n[3])
1429
1430 class InfoBarServiceNotifications:
1431         def __init__(self):
1432                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1433                         {
1434                                 iPlayableService.evEnd: self.serviceHasEnded
1435                         })
1436
1437         def serviceHasEnded(self):
1438                 print "service end!"
1439
1440                 try:
1441                         self.setSeekState(self.SEEK_STATE_PLAY)
1442                 except:
1443                         pass
1444
1445 class InfoBarCueSheetSupport:
1446         CUT_TYPE_IN = 0
1447         CUT_TYPE_OUT = 1
1448         CUT_TYPE_MARK = 2
1449         
1450         def __init__(self):
1451                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1452                         {
1453                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1454                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1455                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1456                         }, prio=1) 
1457                 
1458                 self.cut_list = [ ]
1459                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1460                         {
1461                                 iPlayableService.evStart: self.__serviceStarted,
1462                         })
1463
1464         def __serviceStarted(self):
1465                 print "new service started! trying to download cuts!"
1466                 self.downloadCuesheet()
1467
1468         def __getSeekable(self):
1469                 service = self.session.nav.getCurrentService()
1470                 if service is None:
1471                         return None
1472                 return service.seek()
1473
1474         def cueGetCurrentPosition(self):
1475                 seek = self.__getSeekable()
1476                 if seek is None:
1477                         return None
1478                 r = seek.getPlayPosition()
1479                 if r[0]:
1480                         return None
1481                 return long(r[1])
1482
1483         def jumpPreviousNextMark(self, cmp, alternative=None):
1484                 current_pos = self.cueGetCurrentPosition()
1485                 if current_pos is None:
1486                         return
1487                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1488                 if mark is not None:
1489                         pts = mark[0]
1490                 elif alternative is not None:
1491                         pts = alternative
1492                 else:
1493                         return
1494
1495                 seekable = self.__getSeekable()
1496                 if seekable is not None:
1497                         seekable.seekTo(pts)
1498
1499         def jumpPreviousMark(self):
1500                 # we add 2 seconds, so if the play position is <2s after
1501                 # the mark, the mark before will be used
1502                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1503
1504         def jumpNextMark(self):
1505                 self.jumpPreviousNextMark(lambda x: x)
1506
1507         def getNearestCutPoint(self, pts, cmp=abs):
1508                 # can be optimized
1509                 nearest = None
1510                 for cp in self.cut_list:
1511                         diff = cmp(cp[0] - pts)
1512                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1513                                 nearest = cp
1514                 return nearest
1515
1516         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1517                 current_pos = self.cueGetCurrentPosition()
1518                 if current_pos is None:
1519                         print "not seekable"
1520                         return
1521                 
1522                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1523                 
1524                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1525                         if onlyreturn:
1526                                 return nearest_cutpoint
1527                         if not onlyadd:
1528                                 self.removeMark(nearest_cutpoint)
1529                 elif not onlyremove and not onlyreturn:
1530                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1531                 
1532                 if onlyreturn:
1533                         return None
1534
1535         def addMark(self, point):
1536                 bisect.insort(self.cut_list, point)
1537                 self.uploadCuesheet()
1538
1539         def removeMark(self, point):
1540                 self.cut_list.remove(point)
1541                 self.uploadCuesheet()
1542
1543         def __getCuesheet(self):
1544                 service = self.session.nav.getCurrentService()
1545                 if service is None:
1546                         return None
1547                 return service.cueSheet()
1548
1549         def uploadCuesheet(self):
1550                 cue = self.__getCuesheet()
1551
1552                 if cue is None:
1553                         print "upload failed, no cuesheet interface"
1554                         return
1555                 cue.setCutList(self.cut_list)
1556
1557         def downloadCuesheet(self):
1558                 cue = self.__getCuesheet()
1559
1560                 if cue is None:
1561                         print "upload failed, no cuesheet interface"
1562                         return
1563                 self.cut_list = cue.getCutList()
1564
1565 class InfoBarSummary(Screen):
1566         skin = """
1567         <screen position="0,0" size="132,64">
1568                 <widget source="CurrentTime" render="Label" position="50,46" size="82,18" font="Regular;16" >
1569                         <convert type="ClockToText">WithSeconds</convert>
1570                 </widget>
1571                 <widget source="CurrentService" render="Label" position="0,4" size="132,42" font="Regular;18" >
1572                         <convert type="ServiceName">Name</convert>
1573                 </widget>
1574         </screen>"""
1575
1576         def __init__(self, session, parent):
1577                 Screen.__init__(self, session)
1578                 self["CurrentService"] = CurrentService(self.session.nav)
1579                 self["CurrentTime"] = Clock()
1580
1581 class InfoBarSummarySupport:
1582         def __init__(self):
1583                 pass
1584         
1585         def createSummary(self):
1586                 return InfoBarSummary
1587
1588 class InfoBarTeletextPlugin:
1589         def __init__(self):
1590                 self.teletext_plugin = None
1591                 
1592                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1593                         self.teletext_plugin = p
1594                 
1595                 if self.teletext_plugin is not None:
1596                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1597                                 {
1598                                         "startTeletext": (self.startTeletext, _("View teletext..."))
1599                                 })
1600                 else:
1601                         print "no teletext plugin found!"
1602
1603         def startTeletext(self):
1604                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1605
1606 class InfoBarSubtitleSupport(object):
1607         def __init__(self):
1608                 object.__init__(self)
1609                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1610                 self.__subtitles_enabled = False
1611
1612                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1613                         {
1614                                 iPlayableService.evStart: self.__serviceStarted,
1615                         })
1616
1617         def __serviceStarted(self):
1618                 # reenable if it was enabled
1619                 r = self.__subtitles_enabled
1620                 self.__subtitles_enabled = False
1621                 self.__selected_subtitle = None
1622                 self.setSubtitlesEnable(r)
1623
1624         def getCurrentServiceSubtitle(self):
1625                 service = self.session.nav.getCurrentService()
1626                 return service and service.subtitle()
1627         
1628         def setSubtitlesEnable(self, enable=True):
1629                 subtitle = self.getCurrentServiceSubtitle()
1630                 if enable and self.__selected_subtitle:
1631                         if subtitle and not self.__subtitles_enabled:
1632                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1633                                 self.subtitle_window.show()
1634                                 self.__subtitles_enabled = True
1635                 else:
1636                         if subtitle:
1637                                 subtitle.disableSubtitles(self.subtitle_window.instance)
1638
1639                         self.subtitle_window.hide()
1640                         self.__subtitles_enabled = False
1641
1642         def setSelectedSubtitle(self, subtitle):
1643                 if self.__selected_subtitle != subtitle and self.subtitles_enabled:
1644                         # kick
1645                         self.__selected_subtitle = subtitle
1646                         self.__serviceStarted()
1647                 else:
1648                         self.__selected_subtitle = subtitle
1649
1650         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
1651         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)