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