add subtitle window
[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
1026         def extensions(self):
1027                 list = []
1028                 if self.pipshown == False:
1029                         list.append((_("Activate Picture in Picture"), self.PIPON))
1030                 elif self.pipshown == True:
1031                         list.append((_("Disable Picture in Picture"), self.PIPOFF))
1032                         list.append((_("Move Picture in Picture"), self.MOVEPIP))
1033                         list.append((_("Swap services"), self.PIPSWAP))
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.pip = self.session.instantiateDialog(PictureInPicture)
1040                                 
1041                                 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1042                                 
1043                                 if self.pip.playService(newservice):
1044                                         self.pipshown = True
1045                                 else:
1046                                         self.pipshown = False
1047                                         del self.pip
1048                                 self.session.nav.playService(newservice)
1049                         elif answer[1] == self.PIPOFF:
1050                                 del self.pip
1051                                 self.pipshown = False
1052                         elif answer[1] == self.PIPSWAP:
1053                                 swapservice = self.pip.getCurrentService()
1054                                 self.pip.playService(self.session.nav.getCurrentlyPlayingServiceReference())
1055                                 self.session.nav.playService(swapservice)
1056                                 
1057                         elif answer[1] == self.MOVEPIP:
1058                                 self.session.open(PiPSetup, pip = self.pip)
1059
1060 from RecordTimer import parseEvent
1061
1062 class InfoBarInstantRecord:
1063         """Instant Record - handles the instantRecord action in order to 
1064         start/stop instant records"""
1065         def __init__(self):
1066                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1067                         {
1068                                 "instantRecord": (self.instantRecord, "Instant Record..."),
1069                         })
1070                 self.recording = []
1071                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1072                 self["BlinkingPoint"].hide()
1073                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1074
1075         def stopCurrentRecording(self, entry = -1):     
1076                 if entry is not None and entry != -1:
1077                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1078                         self.recording.remove(self.recording[entry])
1079                         
1080         def startInstantRecording(self, limitEvent = False):
1081                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1082                 
1083                 # try to get event info
1084                 event = None
1085                 try:
1086                         service = self.session.nav.getCurrentService()
1087                         epg = eEPGCache.getInstance()
1088                         event = epg.lookupEventTime(serviceref, -1, 0)
1089                         if event is None:
1090                                 info = service.info()
1091                                 ev = info.getEvent(0)
1092                                 event = ev
1093                 except:
1094                         pass
1095
1096                 begin = time.time()
1097                 end = time.time() + 3600 * 10
1098                 name = "instant record"
1099                 description = ""
1100                 eventid = None
1101                 
1102                 if event is not None:
1103                         curEvent = parseEvent(event)
1104                         name = curEvent[2]
1105                         description = curEvent[3]
1106                         eventid = curEvent[4]
1107                         if limitEvent:
1108                                 end = curEvent[1]
1109                 else:
1110                         if limitEvent:
1111                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1112                                 
1113                 data = (begin, end, name, description, eventid)
1114                 
1115                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1116                 recording.dontSave = True
1117                 self.recording.append(recording)
1118                 
1119                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1120                 
1121         def isInstantRecordRunning(self):
1122                 print "self.recording:", self.recording
1123                 if len(self.recording) > 0:
1124                         for x in self.recording:
1125                                 if x.isRunning():
1126                                         return True
1127                 return False
1128
1129         def recordQuestionCallback(self, answer):
1130                 print "pre:\n", self.recording
1131                 
1132                 if answer is None or answer[1] == "no":
1133                         return
1134                 list = []
1135                 recording = self.recording[:]
1136                 for x in recording:
1137                         if not x in self.session.nav.RecordTimer.timer_list:
1138                                 self.recording.remove(x)
1139                         elif x.dontSave and x.isRunning():
1140                                 list.append(TimerEntryComponent(x, False))              
1141
1142                 if answer[1] == "changeduration":
1143                         if len(self.recording) == 1:
1144                                 self.changeDuration(0)
1145                         else:
1146                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1147                 elif answer[1] == "stop":
1148                         if len(self.recording) == 1:
1149                                 self.stopCurrentRecording(0)
1150                         else:
1151                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1152                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1153                         limitEvent = False
1154                         if answer[1] == "event":
1155                                 limitEvent = True
1156                         if answer[1] == "manualduration":
1157                                 self.selectedEntry = len(self.recording)
1158                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1159                         self.startInstantRecording(limitEvent = limitEvent)
1160                         
1161                 print "after:\n", self.recording
1162
1163         def changeDuration(self, entry):
1164                 if entry is not None:
1165                         self.selectedEntry = entry
1166                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1167
1168         def inputCallback(self, value):
1169                 if value is not None:
1170                         print "stopping recording after", int(value), "minutes."
1171                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1172                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1173
1174         def instantRecord(self):
1175                 try:
1176                         stat = os.stat(resolveFilename(SCOPE_HDD))
1177                 except:
1178                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1179                         return
1180         
1181                 if self.isInstantRecordRunning():
1182                         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")])
1183                 else:
1184                         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")])
1185
1186 from Tools.ISO639 import LanguageCodes
1187
1188 class InfoBarAudioSelection:
1189         def __init__(self):
1190                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1191                         {
1192                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1193                         })
1194
1195         def audioSelection(self):
1196                 service = self.session.nav.getCurrentService()
1197                 audio = service.audioTracks()
1198                 self.audioTracks = audio
1199                 n = audio.getNumberOfTracks()
1200                 if n > 0:
1201 #                       self.audioChannel = service.audioChannel()
1202 #                       config.audio.audiochannel = configElement_nonSave("config.audio.audiochannel", configSelection, self.audioChannel.getCurrentChannel(), (("left", _("Left  >")), ("stereo", _("<  Stereo  >")), ("right", _("<  Right"))))
1203                         tlist = []
1204                         for x in range(n):
1205                                 i = audio.getTrackInfo(x)
1206                                 language = i.getLanguage()
1207                                 description = i.getDescription();
1208         
1209                                 if len(language) == 3:
1210                                         if language in LanguageCodes:
1211                                                 language = LanguageCodes[language][0]
1212         
1213                                 if len(description):
1214                                         description += " (" + language + ")"
1215                                 else:
1216                                         description = language
1217         
1218                                 tlist.append((description, x))
1219                         
1220                         selectedAudio = tlist[0][1]
1221                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1222
1223 #                       tlist.insert(0, getConfigListEntry(_("Audio Channel"), config.audio.audiochannel))
1224
1225                         selection = 0
1226                         for x in tlist:
1227                                 if x[1] != selectedAudio:
1228                                         selection += 1
1229                                 else:
1230                                         break
1231                         
1232                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection)
1233                 else:
1234                         del self.audioTracks
1235
1236         def audioSelected(self, audio):
1237                 if audio is not None:
1238                         self.audioTracks.selectTrack(audio[1])
1239                 del self.audioTracks
1240 #               del self.audioChannel
1241 #               del config.audio.audiochannel
1242
1243 class InfoBarSubserviceSelection:
1244         def __init__(self):
1245                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1246                         {
1247                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1248                         })
1249
1250                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1251                         {
1252                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1253                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1254                         }, -1)
1255                 self["SubserviceQuickzapAction"].setEnabled(False)
1256
1257                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1258
1259         def checkSubservicesAvail(self, ev):
1260                 if ev == iPlayableService.evUpdatedEventInfo:
1261                         service = self.session.nav.getCurrentService()
1262                         subservices = service.subServices()
1263                         if subservices.getNumberOfSubservices() == 0:
1264                                 self["SubserviceQuickzapAction"].setEnabled(False)
1265
1266         def nextSubservice(self):
1267                 self.changeSubservice(+1)
1268
1269         def prevSubservice(self):
1270                 self.changeSubservice(-1)
1271
1272         def changeSubservice(self, direction):
1273                 service = self.session.nav.getCurrentService()
1274                 subservices = service.subServices()
1275                 n = subservices.getNumberOfSubservices()
1276                 if n > 0:
1277                         selection = -1
1278                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1279                         for x in range(n):
1280                                 if subservices.getSubservice(x).toString() == ref.toString():
1281                                         selection = x
1282                         if selection != -1:
1283                                 selection += direction
1284                                 if selection >= n:
1285                                         selection=0
1286                                 elif selection < 0:
1287                                         selection=n-1
1288                                 newservice = subservices.getSubservice(selection)
1289                                 if newservice.valid():
1290                                         del subservices
1291                                         del service
1292                                         self.session.nav.playService(newservice)
1293
1294         def subserviceSelection(self):
1295                 service = self.session.nav.getCurrentService()
1296                 subservices = service.subServices()
1297                 
1298                 n = subservices.getNumberOfSubservices()
1299                 selection = 0
1300                 if n > 0:
1301                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1302                         tlist = []
1303                         for x in range(n):
1304                                 i = subservices.getSubservice(x)
1305                                 if i.toString() == ref.toString():
1306                                         selection = x
1307                                 tlist.append((i.getName(), i))
1308
1309                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection)
1310
1311         def subserviceSelected(self, service):
1312                 if not service is None:
1313                         self["SubserviceQuickzapAction"].setEnabled(True)
1314                         self.session.nav.playService(service[1])
1315
1316 class InfoBarAdditionalInfo:
1317         def __init__(self):
1318                 self["DolbyActive"] = Pixmap()
1319                 self["CryptActive"] = Pixmap()
1320                 self["FormatActive"] = Pixmap()
1321                 
1322                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1323                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1324                 self.onLayoutFinish.append(self["ButtonRed"].update)
1325                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1326                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1327                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1328
1329                 self["ButtonGreen"] = Pixmap()
1330                 self["ButtonGreenText"] = Label(_("Subservices"))
1331
1332                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1333                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1334                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1335                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1336                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1337                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1338
1339                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1340                 self["ButtonBlue"].setConnect(lambda: True)
1341                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1342                 self["ButtonBlueText"].setConnect(lambda: True)
1343                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1344                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1345
1346                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1347
1348         def hideSubServiceIndication(self):
1349                 self["ButtonGreen"].hide()
1350                 self["ButtonGreenText"].hide()
1351
1352         def showSubServiceIndication(self):
1353                 self["ButtonGreen"].show()
1354                 self["ButtonGreenText"].show()
1355
1356         def checkFormat(self, service):
1357                 info = service.info()
1358                 if info is not None:
1359                         aspect = info.getInfo(iServiceInformation.sAspect)
1360                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1361                                 self["FormatActive"].show()
1362                         else:
1363                                 self["FormatActive"].hide()
1364
1365         def checkSubservices(self, service):
1366                 if service.subServices().getNumberOfSubservices() > 0:
1367                         self.showSubServiceIndication()
1368                 else:
1369                         self.hideSubServiceIndication()
1370
1371         def checkDolby(self, service):
1372                 # FIXME
1373                 dolby = False
1374                 audio = service.audioTracks()
1375                 if audio is not None:
1376                         n = audio.getNumberOfTracks()
1377                         for x in range(n):
1378                                 i = audio.getTrackInfo(x)
1379                                 description = i.getDescription();
1380                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1381                                         dolby = True
1382                                         break
1383                 if dolby:
1384                         self["DolbyActive"].show()
1385                 else:
1386                         self["DolbyActive"].hide()
1387
1388         def checkCrypted(self, service):
1389                 info = service.info()
1390                 if info is not None:
1391                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1392                                 self["CryptActive"].show()
1393                         else:
1394                                 self["CryptActive"].hide()
1395
1396         def gotServiceEvent(self, ev):
1397                 service = self.session.nav.getCurrentService()
1398                 if ev == iPlayableService.evUpdatedEventInfo:
1399                         self.checkSubservices(service)
1400                         self.checkFormat(service)
1401                 elif ev == iPlayableService.evUpdatedInfo:
1402                         self.checkCrypted(service)
1403                         self.checkDolby(service)
1404                 elif ev == iPlayableService.evEnd:
1405                         self.hideSubServiceIndication()
1406                         self["CryptActive"].hide()
1407                         self["DolbyActive"].hide()
1408                         self["FormatActive"].hide()
1409
1410 class InfoBarNotifications:
1411         def __init__(self):
1412                 self.onExecBegin.append(self.checkNotifications)
1413                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1414                 self.onClose.append(self.__removeNotification)
1415         
1416         def __removeNotification(self):
1417                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1418         
1419         def checkNotificationsIfExecing(self):
1420                 if self.execing:
1421                         self.checkNotifications()
1422
1423         def checkNotifications(self):
1424                 if len(Notifications.notifications):
1425                         n = Notifications.notifications[0]
1426                         Notifications.notifications = Notifications.notifications[1:]
1427                         cb = n[0]
1428                         if cb is not None:
1429                                 self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1430                         else:
1431                                 self.session.open(n[1], *n[2], **n[3])
1432
1433 class InfoBarServiceNotifications:
1434         def __init__(self):
1435                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1436                         {
1437                                 iPlayableService.evEnd: self.serviceHasEnded
1438                         })
1439
1440         def serviceHasEnded(self):
1441                 print "service end!"
1442
1443                 try:
1444                         self.setSeekState(self.SEEK_STATE_PLAY)
1445                 except:
1446                         pass
1447
1448 class InfoBarCueSheetSupport:
1449         CUT_TYPE_IN = 0
1450         CUT_TYPE_OUT = 1
1451         CUT_TYPE_MARK = 2
1452         
1453         def __init__(self):
1454                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1455                         {
1456                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1457                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1458                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1459                         }, prio=1) 
1460                 
1461                 self.cut_list = [ ]
1462                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1463                         {
1464                                 iPlayableService.evStart: self.__serviceStarted,
1465                         })
1466
1467         def __serviceStarted(self):
1468                 print "new service started! trying to download cuts!"
1469                 self.downloadCuesheet()
1470
1471         def __getSeekable(self):
1472                 service = self.session.nav.getCurrentService()
1473                 if service is None:
1474                         return None
1475                 return service.seek()
1476
1477         def cueGetCurrentPosition(self):
1478                 seek = self.__getSeekable()
1479                 if seek is None:
1480                         return None
1481                 r = seek.getPlayPosition()
1482                 if r[0]:
1483                         return None
1484                 return long(r[1])
1485
1486         def jumpPreviousNextMark(self, cmp, alternative=None):
1487                 current_pos = self.cueGetCurrentPosition()
1488                 if current_pos is None:
1489                         return
1490                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1491                 if mark is not None:
1492                         pts = mark[0]
1493                 elif alternative is not None:
1494                         pts = alternative
1495                 else:
1496                         return
1497
1498                 seekable = self.__getSeekable()
1499                 if seekable is not None:
1500                         seekable.seekTo(pts)
1501
1502         def jumpPreviousMark(self):
1503                 # we add 2 seconds, so if the play position is <2s after
1504                 # the mark, the mark before will be used
1505                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1506
1507         def jumpNextMark(self):
1508                 self.jumpPreviousNextMark(lambda x: x)
1509
1510         def getNearestCutPoint(self, pts, cmp=abs):
1511                 # can be optimized
1512                 nearest = None
1513                 for cp in self.cut_list:
1514                         diff = cmp(cp[0] - pts)
1515                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1516                                 nearest = cp
1517                 return nearest
1518
1519         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1520                 current_pos = self.cueGetCurrentPosition()
1521                 if current_pos is None:
1522                         print "not seekable"
1523                         return
1524                 
1525                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1526                 
1527                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1528                         if onlyreturn:
1529                                 return nearest_cutpoint
1530                         if not onlyadd:
1531                                 self.removeMark(nearest_cutpoint)
1532                 elif not onlyremove and not onlyreturn:
1533                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1534                 
1535                 if onlyreturn:
1536                         return None
1537
1538         def addMark(self, point):
1539                 bisect.insort(self.cut_list, point)
1540                 self.uploadCuesheet()
1541
1542         def removeMark(self, point):
1543                 self.cut_list.remove(point)
1544                 self.uploadCuesheet()
1545
1546         def __getCuesheet(self):
1547                 service = self.session.nav.getCurrentService()
1548                 if service is None:
1549                         return None
1550                 return service.cueSheet()
1551
1552         def uploadCuesheet(self):
1553                 cue = self.__getCuesheet()
1554
1555                 if cue is None:
1556                         print "upload failed, no cuesheet interface"
1557                         return
1558                 cue.setCutList(self.cut_list)
1559
1560         def downloadCuesheet(self):
1561                 cue = self.__getCuesheet()
1562
1563                 if cue is None:
1564                         print "upload failed, no cuesheet interface"
1565                         return
1566                 self.cut_list = cue.getCutList()
1567
1568 class InfoBarSummary(Screen):
1569         skin = """
1570         <screen position="0,0" size="132,64">
1571                 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1572                 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1573         </screen>"""
1574
1575         def __init__(self, session, parent):
1576                 Screen.__init__(self, session)
1577                 self["CurrentService"] = ServiceName(self.session.nav)
1578                 self["Clock"] = Clock()
1579
1580 class InfoBarSummarySupport:
1581         def __init__(self):
1582                 pass
1583         
1584         def createSummary(self):
1585                 return InfoBarSummary
1586
1587 class InfoBarTeletextPlugin:
1588         def __init__(self):
1589                 self.teletext_plugin = None
1590                 
1591                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1592                         self.teletext_plugin = p
1593                 
1594                 if self.teletext_plugin is not None:
1595                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1596                                 {
1597                                         "startTeletext": (self.startTeletext, "View teletext...")
1598                                 })
1599                 else:
1600                         print "no teletext plugin found!"
1601
1602         def startTeletext(self):
1603                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1604
1605 class InfoBarSubtitleSupport(object):
1606         def __init__(self):
1607                 object.__init__(self)
1608                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1609                 self.__subtitles_enabled = False
1610
1611                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1612                         {
1613                                 iPlayableService.evStart: self.__serviceStarted,
1614                         })
1615
1616         def __serviceStarted(self):
1617                 # reenable if it was enabled
1618                 r = self.__subtitles_enabled
1619                 self.__subtitles_enabled = False
1620                 self.setSubtitlesEnable(r)
1621
1622         def getCurrentServiceSubtitle(self):
1623                 service = self.session.nav.getCurrentService()
1624                 return service and service.subtitle()
1625         
1626         def setSubtitlesEnable(self, enable=True):
1627                 subtitle = self.getCurrentServiceSubtitle()
1628                 if enable:
1629                         if subtitle and not self.__subtitles_enabled:
1630                                 subtitle.enableSubtitles(self.subtitle_window.instance, 0)
1631                                 self.subtitle_window.show()
1632                                 self.__subtitles_enabled = True
1633                 else:
1634                         if subtitle:
1635                                 subtitle.disableSubtitles(self.subtitle_window.instance)
1636
1637                         self.subtitle_window.hide()
1638                         self.__subtitles_enabled = False
1639                 
1640         subtitlesEnabled = property(lambda self: self.__subtitlesEnabled, setSubtitlesEnable)