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