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