[videomode] support VusoloSE
[vuplus_dvbapp] / lib / python / Screens / InfoBarGenerics.py
1 from ChannelSelection import ChannelSelection, BouquetSelector, SilentBouquetSelector
2
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.Harddisk import harddiskmanager
6 from Components.Input import Input
7 from Components.Label import Label
8 from Components.PluginComponent import plugins
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Components.Sources.Boolean import Boolean
11 from Components.config import config, ConfigBoolean, ConfigClock
12 from Components.SystemInfo import SystemInfo
13 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
14 from EpgSelection import EPGSelection
15 from Plugins.Plugin import PluginDescriptor
16
17 from Screen import Screen
18 from Screens.ChoiceBox import ChoiceBox
19 from Screens.Dish import Dish
20 from Screens.EventView import EventViewEPGSelect, EventViewSimple
21 from Screens.InputBox import InputBox
22 from Screens.MessageBox import MessageBox
23 from Screens.MinuteInput import MinuteInput
24 from Screens.TimerSelection import TimerSelection
25 from Screens.PictureInPicture import PictureInPicture
26 from Screens.SubtitleDisplay import SubtitleDisplay
27 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
28 from Screens.TimeDateInput import TimeDateInput
29 from Screens.UnhandledKey import UnhandledKey
30 from ServiceReference import ServiceReference
31
32 from Tools import Notifications
33 from Tools.Directories import fileExists
34
35 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
36         iPlayableService, eServiceReference, eEPGCache, eActionMap
37
38 from time import time, localtime, strftime
39 from os import stat as os_stat
40 from bisect import insort
41
42 from RecordTimer import RecordTimerEntry, RecordTimer
43
44 # hack alert!
45 from Menu import MainMenu, mdom
46
47 class InfoBarDish:
48         def __init__(self):
49                 self.dishDialog = self.session.instantiateDialog(Dish)
50
51 class InfoBarUnhandledKey:
52         def __init__(self):
53                 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
54                 self.hideUnhandledKeySymbolTimer = eTimer()
55                 self.hideUnhandledKeySymbolTimer.callback.append(self.unhandledKeyDialog.hide)
56                 self.checkUnusedTimer = eTimer()
57                 self.checkUnusedTimer.callback.append(self.checkUnused)
58                 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
59                 eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.actionA) #highest prio
60                 eActionMap.getInstance().bindAction('', 0x7FFFFFFF, self.actionB) #lowest prio
61                 self.flags = (1<<1);
62                 self.uflags = 0;
63
64         #this function is called on every keypress!
65         def actionA(self, key, flag):
66                 if flag != 4:
67                         if self.flags & (1<<1):
68                                 self.flags = self.uflags = 0
69                         self.flags |= (1<<flag)
70                         if flag == 1: # break
71                                 self.checkUnusedTimer.start(0, True)
72                 return 0
73
74         #this function is only called when no other action has handled this key
75         def actionB(self, key, flag):
76                 if flag != 4:
77                         self.uflags |= (1<<flag)
78
79         def checkUnused(self):
80                 if self.flags == self.uflags:
81                         self.unhandledKeyDialog.show()
82                         self.hideUnhandledKeySymbolTimer.start(2000, True)
83
84 class InfoBarShowHide:
85         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
86         fancy animations. """
87         STATE_HIDDEN = 0
88         STATE_HIDING = 1
89         STATE_SHOWING = 2
90         STATE_SHOWN = 3
91
92         def __init__(self):
93                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
94                         {
95                                 "toggleShow": self.toggleShow,
96                                 "hide": self.hide,
97                         }, 1) # lower prio to make it possible to override ok and cancel..
98
99                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
100                         {
101                                 iPlayableService.evStart: self.serviceStarted,
102                         })
103
104                 self.__state = self.STATE_SHOWN
105                 self.__locked = 0
106
107                 self.hideTimer = eTimer()
108                 self.hideTimer.callback.append(self.doTimerHide)
109                 self.hideTimer.start(5000, True)
110
111                 self.onShow.append(self.__onShow)
112                 self.onHide.append(self.__onHide)
113
114         def serviceStarted(self):
115                 if self.execing:
116                         if config.usage.show_infobar_on_zap.value:
117                                 self.doShow()
118
119         def __onShow(self):
120                 self.__state = self.STATE_SHOWN
121                 self.startHideTimer()
122
123         def startHideTimer(self):
124                 if self.__state == self.STATE_SHOWN and not self.__locked:
125                         idx = config.usage.infobar_timeout.index
126                         if idx:
127                                 self.hideTimer.start(idx*1000, True)
128
129         def __onHide(self):
130                 self.__state = self.STATE_HIDDEN
131
132         def doShow(self):
133                 self.show()
134                 self.startHideTimer()
135
136         def doTimerHide(self):
137                 self.hideTimer.stop()
138                 if self.__state == self.STATE_SHOWN:
139                         self.hide()
140
141         def toggleShow(self):
142                 if self.__state == self.STATE_SHOWN:
143                         self.hide()
144                         self.hideTimer.stop()
145                 elif self.__state == self.STATE_HIDDEN:
146                         self.show()
147
148         def lockShow(self):
149                 self.__locked = self.__locked + 1
150                 if self.execing:
151                         self.show()
152                         self.hideTimer.stop()
153
154         def unlockShow(self):
155                 self.__locked = self.__locked - 1
156                 if self.execing:
157                         self.startHideTimer()
158
159 #       def startShow(self):
160 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
161 #               self.__state = self.STATE_SHOWN
162 #
163 #       def startHide(self):
164 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
165 #               self.__state = self.STATE_HIDDEN
166
167 class NumberZap(Screen):
168         def quit(self):
169                 self.Timer.stop()
170                 self.close(0)
171
172         def keyOK(self):
173                 self.Timer.stop()
174                 self.close(int(self["number"].getText()))
175
176         def keyNumberGlobal(self, number):
177                 self.Timer.start(3000, True)            #reset timer
178                 self.field = self.field + str(number)
179                 self["number"].setText(self.field)
180                 if len(self.field) >= 4:
181                         self.keyOK()
182
183         def __init__(self, session, number):
184                 Screen.__init__(self, session)
185                 self.field = str(number)
186
187                 self["channel"] = Label(_("Channel:"))
188
189                 self["number"] = Label(self.field)
190
191                 self["actions"] = NumberActionMap( [ "SetupActions" ],
192                         {
193                                 "cancel": self.quit,
194                                 "ok": self.keyOK,
195                                 "1": self.keyNumberGlobal,
196                                 "2": self.keyNumberGlobal,
197                                 "3": self.keyNumberGlobal,
198                                 "4": self.keyNumberGlobal,
199                                 "5": self.keyNumberGlobal,
200                                 "6": self.keyNumberGlobal,
201                                 "7": self.keyNumberGlobal,
202                                 "8": self.keyNumberGlobal,
203                                 "9": self.keyNumberGlobal,
204                                 "0": self.keyNumberGlobal
205                         })
206
207                 self.Timer = eTimer()
208                 self.Timer.callback.append(self.keyOK)
209                 self.Timer.start(3000, True)
210
211 class InfoBarNumberZap:
212         """ Handles an initial number for NumberZapping """
213         def __init__(self):
214                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
215                         {
216                                 "1": self.keyNumberGlobal,
217                                 "2": self.keyNumberGlobal,
218                                 "3": self.keyNumberGlobal,
219                                 "4": self.keyNumberGlobal,
220                                 "5": self.keyNumberGlobal,
221                                 "6": self.keyNumberGlobal,
222                                 "7": self.keyNumberGlobal,
223                                 "8": self.keyNumberGlobal,
224                                 "9": self.keyNumberGlobal,
225                                 "0": self.keyNumberGlobal,
226                         })
227
228         def keyNumberGlobal(self, number):
229 #               print "You pressed number " + str(number)
230                 if number == 0:
231                         if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
232                                 self.pipDoHandle0Action()
233                         else:
234                                 self.servicelist.recallPrevService()
235                 else:
236                         if self.has_key("TimeshiftActions") and not self.timeshift_enabled:
237                                 self.session.openWithCallback(self.numberEntered, NumberZap, number)
238
239         def numberEntered(self, retval):
240 #               print self.servicelist
241                 if retval > 0:
242                         self.zapToNumber(retval)
243
244         def searchNumberHelper(self, serviceHandler, num, bouquet):
245                 servicelist = serviceHandler.list(bouquet)
246                 if not servicelist is None:
247                         while num:
248                                 serviceIterator = servicelist.getNext()
249                                 if not serviceIterator.valid(): #check end of list
250                                         break
251                                 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
252                                 if playable:
253                                         num -= 1;
254                         if not num: #found service with searched number ?
255                                 return serviceIterator, 0
256                 return None, num
257
258         def zapToNumber(self, number):
259                 bouquet = self.servicelist.bouquet_root
260                 service = None
261                 serviceHandler = eServiceCenter.getInstance()
262                 if not config.usage.multibouquet.value:
263                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
264                 else:
265                         bouquetlist = serviceHandler.list(bouquet)
266                         if not bouquetlist is None:
267                                 while number:
268                                         bouquet = bouquetlist.getNext()
269                                         if not bouquet.valid(): #check end of list
270                                                 break
271                                         if bouquet.flags & eServiceReference.isDirectory:
272                                                 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
273                 if not service is None:
274                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
275                                 self.servicelist.clearPath()
276                                 if self.servicelist.bouquet_root != bouquet:
277                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
278                                 self.servicelist.enterPath(bouquet)
279                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
280                         self.servicelist.zap()
281
282 config.misc.initialchannelselection = ConfigBoolean(default = True)
283
284 class InfoBarChannelSelection:
285         """ ChannelSelection - handles the channelSelection dialog and the initial
286         channelChange actions which open the channelSelection dialog """
287         def __init__(self):
288                 #instantiate forever
289                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
290
291                 if config.misc.initialchannelselection.value:
292                         self.onShown.append(self.firstRun)
293
294                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
295                         {
296                                 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
297                                 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
298                                 "zapUp": (self.zapUp, _("previous channel")),
299                                 "zapDown": (self.zapDown, _("next channel")),
300                                 "historyBack": (self.historyBack, _("previous channel in history")),
301                                 "historyNext": (self.historyNext, _("next channel in history")),
302                                 "openServiceList": (self.openServiceList, _("open servicelist")),
303                         })
304
305         def showTvChannelList(self, zap=False):
306                 self.servicelist.setModeTv()
307                 if zap:
308                         self.servicelist.zap()
309                 self.session.execDialog(self.servicelist)
310
311         def showRadioChannelList(self, zap=False):
312                 self.servicelist.setModeRadio()
313                 if zap:
314                         self.servicelist.zap()
315                 self.session.execDialog(self.servicelist)
316
317         def firstRun(self):
318                 self.onShown.remove(self.firstRun)
319                 config.misc.initialchannelselection.value = False
320                 config.misc.initialchannelselection.save()
321                 self.switchChannelDown()
322
323         def historyBack(self):
324                 self.servicelist.historyBack()
325
326         def historyNext(self):
327                 self.servicelist.historyNext()
328
329         def switchChannelUp(self):
330                 self.servicelist.moveUp()
331                 self.session.execDialog(self.servicelist)
332
333         def switchChannelDown(self):
334                 self.servicelist.moveDown()
335                 self.session.execDialog(self.servicelist)
336
337         def openServiceList(self):
338                 self.session.execDialog(self.servicelist)
339
340         def zapUp(self):
341                 if self.servicelist.inBouquet():
342                         prev = self.servicelist.getCurrentSelection()
343                         if prev:
344                                 prev = prev.toString()
345                                 while True:
346                                         if config.usage.quickzap_bouquet_change.value:
347                                                 if self.servicelist.atBegin():
348                                                         self.servicelist.prevBouquet()
349                                         self.servicelist.moveUp()
350                                         cur = self.servicelist.getCurrentSelection()
351                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
352                                                 break
353                 else:
354                         self.servicelist.moveUp()
355                 self.servicelist.zap()
356
357         def zapDown(self):
358                 if self.servicelist.inBouquet():
359                         prev = self.servicelist.getCurrentSelection()
360                         if prev:
361                                 prev = prev.toString()
362                                 while True:
363                                         if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
364                                                 self.servicelist.nextBouquet()
365                                         else:
366                                                 self.servicelist.moveDown()
367                                         cur = self.servicelist.getCurrentSelection()
368                                         if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
369                                                 break
370                 else:
371                         self.servicelist.moveDown()
372                 self.servicelist.zap()
373
374 class InfoBarMenu:
375         """ Handles a menu action, to open the (main) menu """
376         def __init__(self):
377                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
378                         {
379                                 "mainMenu": (self.mainMenu, _("Enter main menu...")),
380                         })
381                 self.session.infobar = None
382
383         def mainMenu(self):
384                 print "loading mainmenu XML..."
385                 menu = mdom.getroot()
386                 assert menu.tag == "menu", "root element in menu must be 'menu'!"
387
388                 self.session.infobar = self
389                 # so we can access the currently active infobar from screens opened from within the mainmenu
390                 # at the moment used from the SubserviceSelection
391
392                 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
393
394         def mainMenuClosed(self, *val):
395                 self.session.infobar = None
396
397 class InfoBarSimpleEventView:
398         """ Opens the Eventview for now/next """
399         def __init__(self):
400                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
401                         {
402                                 "showEventInfo": (self.openEventView, _("show event details")),
403                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
404                         })
405
406         def showEventInfoWhenNotVisible(self):
407                 if self.shown:
408                         self.openEventView()
409                 else:
410                         self.toggleShow()
411                         return 1
412
413         def openEventView(self):
414                 epglist = [ ]
415                 self.epglist = epglist
416                 service = self.session.nav.getCurrentService()
417                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
418                 info = service.info()
419                 ptr=info.getEvent(0)
420                 if ptr:
421                         epglist.append(ptr)
422                 ptr=info.getEvent(1)
423                 if ptr:
424                         epglist.append(ptr)
425                 if epglist:
426                         self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
427
428         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
429                 epglist = self.epglist
430                 if len(epglist) > 1:
431                         tmp = epglist[0]
432                         epglist[0] = epglist[1]
433                         epglist[1] = tmp
434                         setEvent(epglist[0])
435
436 class SimpleServicelist:
437         def __init__(self, services):
438                 self.services = services
439                 self.length = len(services)
440                 self.current = 0
441
442         def selectService(self, service):
443                 if not self.length:
444                         self.current = -1
445                         return False
446                 else:
447                         self.current = 0
448                         while self.services[self.current].ref != service:
449                                 self.current += 1
450                                 if self.current >= self.length:
451                                         return False
452                 return True
453
454         def nextService(self):
455                 if not self.length:
456                         return
457                 if self.current+1 < self.length:
458                         self.current += 1
459                 else:
460                         self.current = 0
461
462         def prevService(self):
463                 if not self.length:
464                         return
465                 if self.current-1 > -1:
466                         self.current -= 1
467                 else:
468                         self.current = self.length - 1
469
470         def currentService(self):
471                 if not self.length or self.current >= self.length:
472                         return None
473                 return self.services[self.current]
474
475 class InfoBarEPG:
476         """ EPG - Opens an EPG list when the showEPGList action fires """
477         def __init__(self):
478                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
479                         {
480                                 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
481                         })
482
483                 self.is_now_next = False
484                 self.dlg_stack = [ ]
485                 self.bouquetSel = None
486                 self.eventView = None
487                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
488                         {
489                                 "showEventInfo": (self.openEventView, _("show EPG...")),
490                                 "showEventInfoPlugin": (self.showEventInfoPlugins, _("list of EPG views...")),
491                                 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
492                         })
493
494         def showEventInfoWhenNotVisible(self):
495                 if self.shown:
496                         self.openEventView()
497                 else:
498                         self.toggleShow()
499                         return 1
500
501         def zapToService(self, service):
502                 if not service is None:
503                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
504                                 self.servicelist.clearPath()
505                                 if self.servicelist.bouquet_root != self.epg_bouquet:
506                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
507                                 self.servicelist.enterPath(self.epg_bouquet)
508                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
509                         self.servicelist.zap()
510
511         def getBouquetServices(self, bouquet):
512                 services = [ ]
513                 servicelist = eServiceCenter.getInstance().list(bouquet)
514                 if not servicelist is None:
515                         while True:
516                                 service = servicelist.getNext()
517                                 if not service.valid(): #check if end of list
518                                         break
519                                 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
520                                         continue
521                                 services.append(ServiceReference(service))
522                 return services
523
524         def openBouquetEPG(self, bouquet, withCallback=True):
525                 services = self.getBouquetServices(bouquet)
526                 if services:
527                         self.epg_bouquet = bouquet
528                         if withCallback:
529                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
530                         else:
531                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
532
533         def changeBouquetCB(self, direction, epg):
534                 if self.bouquetSel:
535                         if direction > 0:
536                                 self.bouquetSel.down()
537                         else:
538                                 self.bouquetSel.up()
539                         bouquet = self.bouquetSel.getCurrent()
540                         services = self.getBouquetServices(bouquet)
541                         if services:
542                                 self.epg_bouquet = bouquet
543                                 epg.setServices(services)
544
545         def closed(self, ret=False):
546                 closedScreen = self.dlg_stack.pop()
547                 if self.bouquetSel and closedScreen == self.bouquetSel:
548                         self.bouquetSel = None
549                 elif self.eventView and closedScreen == self.eventView:
550                         self.eventView = None
551                 if ret:
552                         dlgs=len(self.dlg_stack)
553                         if dlgs > 0:
554                                 self.dlg_stack[dlgs-1].close(dlgs > 1)
555
556         def openMultiServiceEPG(self, withCallback=True):
557                 bouquets = self.servicelist.getBouquetList()
558                 if bouquets is None:
559                         cnt = 0
560                 else:
561                         cnt = len(bouquets)
562                 if config.usage.multiepg_ask_bouquet.value:
563                         self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
564                 else:
565                         self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
566
567         def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
568                 if cnt > 1: # show bouquet list
569                         if withCallback:
570                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
571                                 self.dlg_stack.append(self.bouquetSel)
572                         else:
573                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
574                 elif cnt == 1:
575                         self.openBouquetEPG(bouquets[0][1], withCallback)
576
577         def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
578                 root = self.servicelist.getRoot()
579                 rootstr = root.toCompareString()
580                 current = 0
581                 for bouquet in bouquets:
582                         if bouquet[1].toCompareString() == rootstr:
583                                 break
584                         current += 1
585                 if current >= cnt:
586                         current = 0
587                 if cnt > 1: # create bouquet list for bouq+/-
588                         self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
589                 if cnt >= 1:
590                         self.openBouquetEPG(root, withCallback)
591
592         def changeServiceCB(self, direction, epg):
593                 if self.serviceSel:
594                         if direction > 0:
595                                 self.serviceSel.nextService()
596                         else:
597                                 self.serviceSel.prevService()
598                         epg.setService(self.serviceSel.currentService())
599
600         def SingleServiceEPGClosed(self, ret=False):
601                 self.serviceSel = None
602
603         def openSingleServiceEPG(self):
604                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
605                 if ref:
606                         if self.servicelist.getMutableList() is not None: # bouquet in channellist
607                                 current_path = self.servicelist.getRoot()
608                                 services = self.getBouquetServices(current_path)
609                                 self.serviceSel = SimpleServicelist(services)
610                                 if self.serviceSel.selectService(ref):
611                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
612                                 else:
613                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
614                         else:
615                                 self.session.open(EPGSelection, ref)
616
617         def showEventInfoPlugins(self):
618                 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
619
620                 if list:
621                         list.append((_("show single service EPG..."), self.openSingleServiceEPG))
622                         list.append((_("Multi EPG"), self.openMultiServiceEPG))
623                         self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
624                 else:
625                         self.openSingleServiceEPG()
626
627         def runPlugin(self, plugin):
628                 plugin(session = self.session, servicelist = self.servicelist)
629                 
630         def EventInfoPluginChosen(self, answer):
631                 if answer is not None:
632                         answer[1]()
633
634         def openSimilarList(self, eventid, refstr):
635                 self.session.open(EPGSelection, refstr, None, eventid)
636
637         def getNowNext(self):
638                 epglist = [ ]
639                 service = self.session.nav.getCurrentService()
640                 info = service and service.info()
641                 ptr = info and info.getEvent(0)
642                 if ptr:
643                         epglist.append(ptr)
644                 ptr = info and info.getEvent(1)
645                 if ptr:
646                         epglist.append(ptr)
647                 self.epglist = epglist
648
649         def __evEventInfoChanged(self):
650                 if self.is_now_next and len(self.dlg_stack) == 1:
651                         self.getNowNext()
652                         assert self.eventView
653                         if self.epglist:
654                                 self.eventView.setEvent(self.epglist[0])
655
656         def openEventView(self):
657                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
658                 self.getNowNext()
659                 epglist = self.epglist
660                 if not epglist:
661                         self.is_now_next = False
662                         epg = eEPGCache.getInstance()
663                         ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
664                         if ptr:
665                                 epglist.append(ptr)
666                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
667                                 if ptr:
668                                         epglist.append(ptr)
669                 else:
670                         self.is_now_next = True
671                 if epglist:
672                         self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
673                         self.dlg_stack.append(self.eventView)
674                 else:
675                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
676                         self.openMultiServiceEPG(False)
677
678         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
679                 epglist = self.epglist
680                 if len(epglist) > 1:
681                         tmp = epglist[0]
682                         epglist[0]=epglist[1]
683                         epglist[1]=tmp
684                         setEvent(epglist[0])
685
686 class InfoBarRdsDecoder:
687         """provides RDS and Rass support/display"""
688         def __init__(self):
689                 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
690                 self.rass_interactive = None
691
692                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
693                         {
694                                 iPlayableService.evEnd: self.__serviceStopped,
695                                 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
696                         })
697
698                 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
699                 {
700                         "startRassInteractive": self.startRassInteractive
701                 },-1)
702
703                 self["RdsActions"].setEnabled(False)
704
705                 self.onLayoutFinish.append(self.rds_display.show)
706                 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
707
708         def RassInteractivePossibilityChanged(self, state):
709                 self["RdsActions"].setEnabled(state)
710
711         def RassSlidePicChanged(self):
712                 if not self.rass_interactive:
713                         service = self.session.nav.getCurrentService()
714                         decoder = service and service.rdsDecoder()
715                         if decoder:
716                                 decoder.showRassSlidePicture()
717
718         def __serviceStopped(self):
719                 if self.rass_interactive is not None:
720                         rass_interactive = self.rass_interactive
721                         self.rass_interactive = None
722                         rass_interactive.close()
723
724         def startRassInteractive(self):
725                 self.rds_display.hide()
726                 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
727
728         def RassInteractiveClosed(self, *val):
729                 if self.rass_interactive is not None:
730                         self.rass_interactive = None
731                         self.RassSlidePicChanged()
732                 self.rds_display.show()
733
734 class InfoBarSeek:
735         """handles actions like seeking, pause"""
736
737         SEEK_STATE_PLAY = (0, 0, 0, ">")
738         SEEK_STATE_PAUSE = (1, 0, 0, "||")
739         SEEK_STATE_EOF = (1, 0, 0, "END")
740
741         def __init__(self, actionmap = "InfobarSeekActions"):
742                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
743                         {
744                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
745                                 iPlayableService.evStart: self.__serviceStarted,
746
747                                 iPlayableService.evEOF: self.__evEOF,
748                                 iPlayableService.evSOF: self.__evSOF,
749                         })
750                 self.fast_winding_hint_message_showed = False
751
752                 class InfoBarSeekActionMap(HelpableActionMap):
753                         def __init__(self, screen, *args, **kwargs):
754                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
755                                 self.screen = screen
756
757                         def action(self, contexts, action):
758                                 print "action:", action
759                                 if action[:5] == "seek:":
760                                         time = int(action[5:])
761                                         self.screen.doSeekRelative(time * 90000)
762                                         return 1
763                                 elif action[:8] == "seekdef:":
764                                         key = int(action[8:])
765                                         time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
766                                                 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
767                                                 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
768                                         self.screen.doSeekRelative(time * 90000)
769                                         return 1                                        
770                                 else:
771                                         return HelpableActionMap.action(self, contexts, action)
772
773                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
774                         {
775                                 "playpauseService": self.playpauseService,
776                                 "pauseService": (self.pauseService, _("pause")),
777                                 "unPauseService": (self.unPauseService, _("continue")),
778
779                                 "seekFwd": (self.seekFwd, _("skip forward")),
780                                 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
781                                 "seekBack": (self.seekBack, _("skip backward")),
782                                 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
783                         }, prio=-1)
784                         # give them a little more priority to win over color buttons
785
786                 self["SeekActions"].setEnabled(False)
787
788                 self.seekstate = self.SEEK_STATE_PLAY
789                 self.lastseekstate = self.SEEK_STATE_PLAY
790
791                 self.onPlayStateChanged = [ ]
792
793                 self.lockedBecauseOfSkipping = False
794
795                 self.__seekableStatusChanged()
796
797         def makeStateForward(self, n):
798                 return (0, n, 0, ">> %dx" % n)
799
800         def makeStateBackward(self, n):
801                 return (0, -n, 0, "<< %dx" % n)
802
803         def makeStateSlowMotion(self, n):
804                 return (0, 0, n, "/%d" % n)
805
806         def isStateForward(self, state):
807                 return state[1] > 1
808
809         def isStateBackward(self, state):
810                 return state[1] < 0
811
812         def isStateSlowMotion(self, state):
813                 return state[1] == 0 and state[2] > 1
814
815         def getHigher(self, n, lst):
816                 for x in lst:
817                         if x > n:
818                                 return x
819                 return False
820
821         def getLower(self, n, lst):
822                 lst = lst[:]
823                 lst.reverse()
824                 for x in lst:
825                         if x < n:
826                                 return x
827                 return False
828
829         def showAfterSeek(self):
830                 if isinstance(self, InfoBarShowHide):
831                         self.doShow()
832
833         def up(self):
834                 pass
835
836         def down(self):
837                 pass
838
839         def getSeek(self):
840                 service = self.session.nav.getCurrentService()
841                 if service is None:
842                         return None
843
844                 seek = service.seek()
845
846                 if seek is None or not seek.isCurrentlySeekable():
847                         return None
848
849                 return seek
850
851         def isSeekable(self):
852                 if self.getSeek() is None:
853                         return False
854                 return True
855
856         def __seekableStatusChanged(self):
857 #               print "seekable status changed!"
858                 if not self.isSeekable():
859                         self["SeekActions"].setEnabled(False)
860 #                       print "not seekable, return to play"
861                         self.setSeekState(self.SEEK_STATE_PLAY)
862                 else:
863                         self["SeekActions"].setEnabled(True)
864 #                       print "seekable"
865
866         def __serviceStarted(self):
867                 self.fast_winding_hint_message_showed = False
868                 self.seekstate = self.SEEK_STATE_PLAY
869                 self.__seekableStatusChanged()
870
871         def setSeekState(self, state):
872                 service = self.session.nav.getCurrentService()
873
874                 if service is None:
875                         return False
876
877                 if not self.isSeekable():
878                         if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
879                                 state = self.SEEK_STATE_PLAY
880
881                 pauseable = service.pause()
882
883                 if pauseable is None:
884                         print "not pauseable."
885                         state = self.SEEK_STATE_PLAY
886
887                 self.seekstate = state
888
889                 if pauseable is not None:
890                         if self.seekstate[0]:
891                                 print "resolved to PAUSE"
892                                 pauseable.pause()
893                         elif self.seekstate[1]:
894                                 print "resolved to FAST FORWARD"
895                                 pauseable.setFastForward(self.seekstate[1])
896                         elif self.seekstate[2]:
897                                 print "resolved to SLOW MOTION"
898                                 pauseable.setSlowMotion(self.seekstate[2])
899                         else:
900                                 print "resolved to PLAY"
901                                 pauseable.unpause()
902
903                 for c in self.onPlayStateChanged:
904                         c(self.seekstate)
905
906                 self.checkSkipShowHideLock()
907
908                 return True
909
910         def playpauseService(self):
911                 if self.seekstate != self.SEEK_STATE_PLAY:
912                         self.unPauseService()
913                 else:
914                         self.pauseService()
915
916         def pauseService(self):
917                 if self.seekstate == self.SEEK_STATE_PAUSE:
918                         if config.seek.on_pause.value == "play":
919                                 self.unPauseService()
920                         elif config.seek.on_pause.value == "step":
921                                 self.doSeekRelative(1)
922                         elif config.seek.on_pause.value == "last":
923                                 self.setSeekState(self.lastseekstate)
924                                 self.lastseekstate = self.SEEK_STATE_PLAY
925                 else:
926                         if self.seekstate != self.SEEK_STATE_EOF:
927                                 self.lastseekstate = self.seekstate
928                         self.setSeekState(self.SEEK_STATE_PAUSE);
929
930         def unPauseService(self):
931                 print "unpause"
932                 if self.seekstate == self.SEEK_STATE_PLAY:
933                         return 0
934                 self.setSeekState(self.SEEK_STATE_PLAY)
935
936         def doSeek(self, pts):
937                 seekable = self.getSeek()
938                 if seekable is None:
939                         return
940                 seekable.seekTo(pts)
941
942         def doSeekRelative(self, pts):
943                 seekable = self.getSeek()
944                 if seekable is None:
945                         return
946                 prevstate = self.seekstate
947
948                 if self.seekstate == self.SEEK_STATE_EOF:
949                         if prevstate == self.SEEK_STATE_PAUSE:
950                                 self.setSeekState(self.SEEK_STATE_PAUSE)
951                         else:
952                                 self.setSeekState(self.SEEK_STATE_PLAY)
953                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
954                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
955                         self.showAfterSeek()
956
957         def seekFwd(self):
958                 seek = self.getSeek()
959                 if seek and not (seek.isCurrentlySeekable() & 2):
960                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
961                                 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
962                                 self.fast_winding_hint_message_showed = True
963                                 return
964                         return 0 # trade as unhandled action
965                 if self.seekstate == self.SEEK_STATE_PLAY:
966                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
967                 elif self.seekstate == self.SEEK_STATE_PAUSE:
968                         if len(config.seek.speeds_slowmotion.value):
969                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
970                         else:
971                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
972                 elif self.seekstate == self.SEEK_STATE_EOF:
973                         pass
974                 elif self.isStateForward(self.seekstate):
975                         speed = self.seekstate[1]
976                         if self.seekstate[2]:
977                                 speed /= self.seekstate[2]
978                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
979                         self.setSeekState(self.makeStateForward(speed))
980                 elif self.isStateBackward(self.seekstate):
981                         speed = -self.seekstate[1]
982                         if self.seekstate[2]:
983                                 speed /= self.seekstate[2]
984                         speed = self.getLower(speed, config.seek.speeds_backward.value)
985                         if speed:
986                                 self.setSeekState(self.makeStateBackward(speed))
987                         else:
988                                 self.setSeekState(self.SEEK_STATE_PLAY)
989                 elif self.isStateSlowMotion(self.seekstate):
990                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
991                         self.setSeekState(self.makeStateSlowMotion(speed))
992
993         def seekBack(self):
994                 seek = self.getSeek()
995                 if seek and not (seek.isCurrentlySeekable() & 2):
996                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
997                                 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
998                                 self.fast_winding_hint_message_showed = True
999                                 return
1000                         return 0 # trade as unhandled action
1001                 seekstate = self.seekstate
1002                 if seekstate == self.SEEK_STATE_PLAY:
1003                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1004                 elif seekstate == self.SEEK_STATE_EOF:
1005                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1006                         self.doSeekRelative(-6)
1007                 elif seekstate == self.SEEK_STATE_PAUSE:
1008                         self.doSeekRelative(-1)
1009                 elif self.isStateForward(seekstate):
1010                         speed = seekstate[1]
1011                         if seekstate[2]:
1012                                 speed /= seekstate[2]
1013                         speed = self.getLower(speed, config.seek.speeds_forward.value)
1014                         if speed:
1015                                 self.setSeekState(self.makeStateForward(speed))
1016                         else:
1017                                 self.setSeekState(self.SEEK_STATE_PLAY)
1018                 elif self.isStateBackward(seekstate):
1019                         speed = -seekstate[1]
1020                         if seekstate[2]:
1021                                 speed /= seekstate[2]
1022                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1023                         self.setSeekState(self.makeStateBackward(speed))
1024                 elif self.isStateSlowMotion(seekstate):
1025                         speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1026                         if speed:
1027                                 self.setSeekState(self.makeStateSlowMotion(speed))
1028                         else:
1029                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1030
1031         def seekFwdManual(self):
1032                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1033
1034         def fwdSeekTo(self, minutes):
1035                 print "Seek", minutes, "minutes forward"
1036                 self.doSeekRelative(minutes * 60 * 90000)
1037
1038         def seekBackManual(self):
1039                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1040
1041         def rwdSeekTo(self, minutes):
1042                 print "rwdSeekTo"
1043                 self.doSeekRelative(-minutes * 60 * 90000)
1044
1045         def checkSkipShowHideLock(self):
1046                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1047
1048                 if config.usage.show_infobar_on_skip.value:
1049                         if self.lockedBecauseOfSkipping and not wantlock:
1050                                 self.unlockShow()
1051                                 self.lockedBecauseOfSkipping = False
1052
1053                         if wantlock and not self.lockedBecauseOfSkipping:
1054                                 self.lockShow()
1055                                 self.lockedBecauseOfSkipping = True
1056
1057         def calcRemainingTime(self):
1058                 seekable = self.getSeek()
1059                 if seekable is not None:
1060                         len = seekable.getLength()
1061                         try:
1062                                 tmp = self.cueGetEndCutPosition()
1063                                 if tmp:
1064                                         len = [False, tmp]
1065                         except:
1066                                 pass
1067                         pos = seekable.getPlayPosition()
1068                         speednom = self.seekstate[1] or 1
1069                         speedden = self.seekstate[2] or 1
1070                         if not len[0] and not pos[0]:
1071                                 if len[1] <= pos[1]:
1072                                         return 0
1073                                 time = (len[1] - pos[1])*speedden/(90*speednom)
1074                                 return time
1075                 return False
1076                 
1077         def __evEOF(self):
1078                 if self.seekstate == self.SEEK_STATE_EOF:
1079                         return
1080
1081                 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1082                 seekstate = self.seekstate
1083                 if self.seekstate != self.SEEK_STATE_PAUSE:
1084                         self.setSeekState(self.SEEK_STATE_EOF)
1085
1086                 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1087                         seekable = self.getSeek()
1088                         if seekable is not None:
1089                                 seekable.seekTo(-1)
1090                 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1091                         self.doEofInternal(True)
1092                 else:
1093                         self.doEofInternal(False)
1094
1095         def doEofInternal(self, playing):
1096                 pass            # Defined in subclasses
1097
1098         def __evSOF(self):
1099                 self.setSeekState(self.SEEK_STATE_PLAY)
1100                 self.doSeek(0)
1101
1102 from Screens.PVRState import PVRState, TimeshiftState
1103
1104 class InfoBarPVRState:
1105         def __init__(self, screen=PVRState, force_show = False):
1106                 self.onPlayStateChanged.append(self.__playStateChanged)
1107                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1108                 self.onShow.append(self._mayShow)
1109                 self.onHide.append(self.pvrStateDialog.hide)
1110                 self.force_show = force_show
1111
1112         def _mayShow(self):
1113                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1114                         self.pvrStateDialog.show()
1115
1116         def __playStateChanged(self, state):
1117                 playstateString = state[3]
1118                 self.pvrStateDialog["state"].setText(playstateString)
1119                 
1120                 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1121                 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1122                         self.pvrStateDialog.hide()
1123                 else:
1124                         self._mayShow()
1125
1126 class InfoBarTimeshiftState(InfoBarPVRState):
1127         def __init__(self):
1128                 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1129                 self.__hideTimer = eTimer()
1130                 self.__hideTimer.callback.append(self.__hideTimeshiftState)
1131
1132         def _mayShow(self):
1133                 if self.execing and self.timeshift_enabled:
1134                         self.pvrStateDialog.show()
1135                         if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
1136                                 self.__hideTimer.start(5*1000, True)
1137
1138         def __hideTimeshiftState(self):
1139                 self.pvrStateDialog.hide()
1140
1141 class InfoBarShowMovies:
1142
1143         # i don't really like this class.
1144         # it calls a not further specified "movie list" on up/down/movieList,
1145         # so this is not more than an action map
1146         def __init__(self):
1147                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1148                         {
1149                                 "movieList": (self.showMovies, _("movie list")),
1150                                 "up": (self.showMovies, _("movie list")),
1151                                 "down": (self.showMovies, _("movie list"))
1152                         })
1153
1154 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1155
1156 # Hrmf.
1157 #
1158 # Timeshift works the following way:
1159 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1160 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1161 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1162 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1163 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1164 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1165 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1166 #
1167
1168 # in other words:
1169 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1170 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1171 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1172 # - the user can now PVR around
1173 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1174 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1175 # after!
1176 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1177 # - if the user rewinds, or press pause, timeshift will be activated again
1178
1179 # note that a timeshift can be enabled ("recording") and
1180 # activated (currently time-shifting).
1181
1182 class InfoBarTimeshift:
1183         def __init__(self):
1184                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1185                         {
1186                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
1187                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
1188                         }, prio=1)
1189                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1190                         {
1191                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1192                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1193                         }, prio=-1) # priority over record
1194
1195                 self.timeshift_enabled = 0
1196                 self.timeshift_state = 0
1197                 self.ts_rewind_timer = eTimer()
1198                 self.ts_rewind_timer.callback.append(self.rewindService)
1199
1200                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1201                         {
1202                                 iPlayableService.evStart: self.__serviceStarted,
1203                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1204                         })
1205
1206         def getTimeshift(self):
1207                 service = self.session.nav.getCurrentService()
1208                 return service and service.timeshift()
1209
1210         def startTimeshift(self):
1211                 print "enable timeshift"
1212                 ts = self.getTimeshift()
1213                 if ts is None:
1214                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1215                         print "no ts interface"
1216                         return 0
1217
1218                 if self.timeshift_enabled:
1219                         print "hu, timeshift already enabled?"
1220                 else:
1221                         if not ts.startTimeshift():
1222                                 self.timeshift_enabled = 1
1223
1224                                 # we remove the "relative time" for now.
1225                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1226
1227                                 # PAUSE.
1228                                 #self.setSeekState(self.SEEK_STATE_PAUSE)
1229                                 self.activateTimeshiftEnd(False)
1230
1231                                 # enable the "TimeshiftEnableActions", which will override
1232                                 # the startTimeshift actions
1233                                 self.__seekableStatusChanged()
1234                         else:
1235                                 print "timeshift failed"
1236
1237         def stopTimeshift(self):
1238                 if not self.timeshift_enabled:
1239                         return 0
1240                 print "disable timeshift"
1241                 ts = self.getTimeshift()
1242                 if ts is None:
1243                         return 0
1244                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1245
1246         def stopTimeshiftConfirmed(self, confirmed):
1247                 if not confirmed:
1248                         return
1249
1250                 ts = self.getTimeshift()
1251                 if ts is None:
1252                         return
1253
1254                 ts.stopTimeshift()
1255                 self.timeshift_enabled = 0
1256
1257                 # disable actions
1258                 self.__seekableStatusChanged()
1259
1260         # activates timeshift, and seeks to (almost) the end
1261         def activateTimeshiftEnd(self, back = True):
1262                 ts = self.getTimeshift()
1263                 print "activateTimeshiftEnd"
1264
1265                 if ts is None:
1266                         return
1267
1268                 if ts.isTimeshiftActive():
1269                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1270                         self.pauseService()
1271                 else:
1272                         print "play, ..."
1273                         ts.activateTimeshift() # activate timeshift will automatically pause
1274                         self.setSeekState(self.SEEK_STATE_PAUSE)
1275
1276                 if back:
1277                         self.ts_rewind_timer.start(200, 1)
1278
1279         def rewindService(self):
1280                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1281
1282         # same as activateTimeshiftEnd, but pauses afterwards.
1283         def activateTimeshiftEndAndPause(self):
1284                 print "activateTimeshiftEndAndPause"
1285                 #state = self.seekstate
1286                 self.activateTimeshiftEnd(False)
1287
1288         def __seekableStatusChanged(self):
1289                 enabled = False
1290
1291 #               print "self.isSeekable", self.isSeekable()
1292 #               print "self.timeshift_enabled", self.timeshift_enabled
1293
1294                 # when this service is not seekable, but timeshift
1295                 # is enabled, this means we can activate
1296                 # the timeshift
1297                 if not self.isSeekable() and self.timeshift_enabled:
1298                         enabled = True
1299
1300 #               print "timeshift activate:", enabled
1301                 self["TimeshiftActivateActions"].setEnabled(enabled)
1302
1303         def __serviceStarted(self):
1304                 self.timeshift_enabled = False
1305                 self.__seekableStatusChanged()
1306
1307 from Screens.PiPSetup import PiPSetup
1308
1309 class InfoBarExtensions:
1310         EXTENSION_SINGLE = 0
1311         EXTENSION_LIST = 1
1312
1313         def __init__(self):
1314                 self.list = []
1315
1316                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1317                         {
1318                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1319                         }, 1) # lower priority
1320
1321         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1322                 self.list.append((type, extension, key))
1323
1324         def updateExtension(self, extension, key = None):
1325                 self.extensionsList.append(extension)
1326                 if key is not None:
1327                         if self.extensionKeys.has_key(key):
1328                                 key = None
1329
1330                 if key is None:
1331                         for x in self.availableKeys:
1332                                 if not self.extensionKeys.has_key(x):
1333                                         key = x
1334                                         break
1335
1336                 if key is not None:
1337                         self.extensionKeys[key] = len(self.extensionsList) - 1
1338
1339         def updateExtensions(self):
1340                 self.extensionsList = []
1341                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1342                 self.extensionKeys = {}
1343                 for x in self.list:
1344                         if x[0] == self.EXTENSION_SINGLE:
1345                                 self.updateExtension(x[1], x[2])
1346                         else:
1347                                 for y in x[1]():
1348                                         self.updateExtension(y[0], y[1])
1349
1350
1351         def showExtensionSelection(self):
1352                 self.updateExtensions()
1353                 extensionsList = self.extensionsList[:]
1354                 keys = []
1355                 list = []
1356                 for x in self.availableKeys:
1357                         if self.extensionKeys.has_key(x):
1358                                 entry = self.extensionKeys[x]
1359                                 extension = self.extensionsList[entry]
1360                                 if extension[2]():
1361                                         name = str(extension[0]())
1362                                         list.append((extension[0](), extension))
1363                                         keys.append(x)
1364                                         extensionsList.remove(extension)
1365                                 else:
1366                                         extensionsList.remove(extension)
1367                 list.extend([(x[0](), x) for x in extensionsList])
1368
1369                 keys += [""] * len(extensionsList)
1370                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1371
1372         def extensionCallback(self, answer):
1373                 if answer is not None:
1374                         answer[1][1]()
1375
1376 from Tools.BoundFunction import boundFunction
1377 import inspect
1378
1379 # depends on InfoBarExtensions
1380
1381 class InfoBarPlugins:
1382         def __init__(self):
1383                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1384
1385         def getPluginName(self, name):
1386                 return name
1387
1388         def getPluginList(self):
1389                 l = []
1390                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1391                   args = inspect.getargspec(p.__call__)[0]
1392                   if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
1393                           l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
1394                 l.sort(key = lambda e: e[2]) # sort by name
1395                 return l
1396
1397         def runPlugin(self, plugin):
1398                 if isinstance(self, InfoBarChannelSelection):
1399                         plugin(session = self.session, servicelist = self.servicelist)
1400                 else:
1401                         plugin(session = self.session)
1402
1403 from Components.Task import job_manager
1404 class InfoBarJobman:
1405         def __init__(self):
1406                 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1407
1408         def getJobList(self):
1409                 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1410
1411         def getJobName(self, job):
1412                 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1413
1414         def showJobView(self, job):
1415                 from Screens.TaskView import JobView
1416                 job_manager.in_background = False
1417                 self.session.openWithCallback(self.JobViewCB, JobView, job)
1418         
1419         def JobViewCB(self, in_background):
1420                 job_manager.in_background = in_background
1421
1422 # depends on InfoBarExtensions
1423 class InfoBarPiP:
1424         def __init__(self):
1425                 try:
1426                         self.session.pipshown
1427                 except:
1428                         self.session.pipshown = False
1429                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1430                         if (self.allowPiP):
1431                                 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1432                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1433                                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1434                         else:
1435                                 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1436                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1437
1438         def pipShown(self):
1439                 return self.session.pipshown
1440
1441         def pipHandles0Action(self):
1442                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1443
1444         def getShowHideName(self):
1445                 if self.session.pipshown:
1446                         return _("Disable Picture in Picture")
1447                 else:
1448                         return _("Activate Picture in Picture")
1449
1450         def getSwapName(self):
1451                 return _("Swap Services")
1452
1453         def getMoveName(self):
1454                 return _("Move Picture in Picture")
1455
1456         def showPiP(self):
1457                 if self.session.pipshown:
1458                         del self.session.pip
1459                         self.session.pipshown = False
1460                 else:
1461                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1462                         self.session.pip.show()
1463                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1464                         if self.session.pip.playService(newservice):
1465                                 self.session.pipshown = True
1466                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1467                         else:
1468                                 self.session.pipshown = False
1469                                 del self.session.pip
1470                         self.session.nav.playService(newservice)
1471
1472         def swapPiP(self):
1473                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1474                 if self.session.pip.servicePath:
1475                         servicepath = self.servicelist.getCurrentServicePath()
1476                         ref=servicepath[len(servicepath)-1]
1477                         pipref=self.session.pip.getCurrentService()
1478                         self.session.pip.playService(swapservice)
1479                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1480                         if pipref.toString() != ref.toString(): # is a subservice ?
1481                                 self.session.nav.stopService() # stop portal
1482                                 self.session.nav.playService(pipref) # start subservice
1483                         self.session.pip.servicePath=servicepath
1484
1485         def movePiP(self):
1486                 self.session.open(PiPSetup, pip = self.session.pip)
1487
1488         def pipDoHandle0Action(self):
1489                 use = config.usage.pip_zero_button.value
1490                 if "swap" == use:
1491                         self.swapPiP()
1492                 elif "swapstop" == use:
1493                         self.swapPiP()
1494                         self.showPiP()
1495                 elif "stop" == use:
1496                         self.showPiP()
1497
1498 from RecordTimer import parseEvent, RecordTimerEntry
1499
1500 class InfoBarInstantRecord:
1501         """Instant Record - handles the instantRecord action in order to
1502         start/stop instant records"""
1503         def __init__(self):
1504                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1505                         {
1506                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1507                         })
1508                 self.recording = []
1509
1510         def stopCurrentRecording(self, entry = -1):
1511                 if entry is not None and entry != -1:
1512                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1513                         self.recording.remove(self.recording[entry])
1514
1515         def startInstantRecording(self, limitEvent = False):
1516                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1517
1518                 # try to get event info
1519                 event = None
1520                 try:
1521                         service = self.session.nav.getCurrentService()
1522                         epg = eEPGCache.getInstance()
1523                         event = epg.lookupEventTime(serviceref, -1, 0)
1524                         if event is None:
1525                                 info = service.info()
1526                                 ev = info.getEvent(0)
1527                                 event = ev
1528                 except:
1529                         pass
1530
1531                 begin = int(time())
1532                 end = begin + 3600      # dummy
1533                 name = "instant record"
1534                 description = ""
1535                 eventid = None
1536
1537                 if event is not None:
1538                         curEvent = parseEvent(event)
1539                         name = curEvent[2]
1540                         description = curEvent[3]
1541                         eventid = curEvent[4]
1542                         if limitEvent:
1543                                 end = curEvent[1]
1544                 else:
1545                         if limitEvent:
1546                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1547
1548                 if isinstance(serviceref, eServiceReference):
1549                         serviceref = ServiceReference(serviceref)
1550
1551                 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1552                 recording.dontSave = True
1553
1554                 if event is None or limitEvent == False:
1555                         recording.autoincrease = True
1556                         recording.setAutoincreaseEnd()
1557
1558                 simulTimerList = self.session.nav.RecordTimer.record(recording)
1559
1560                 if simulTimerList is None:      # no conflict
1561                         self.recording.append(recording)
1562                 else:
1563                         if len(simulTimerList) > 1: # with other recording
1564                                 name = simulTimerList[1].name
1565                                 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1566                                 print "[TIMER] conflicts with", name_date
1567                                 recording.autoincrease = True   # start with max available length, then increment
1568                                 if recording.setAutoincreaseEnd():
1569                                         self.session.nav.RecordTimer.record(recording)
1570                                         self.recording.append(recording)
1571                                         self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1572                                 else:
1573                                         self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1574                         else:
1575                                 self.session.open(MessageBox, _("Couldn't record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
1576                         recording.autoincrease = False
1577
1578         def isInstantRecordRunning(self):
1579                 print "self.recording:", self.recording
1580                 if self.recording:
1581                         for x in self.recording:
1582                                 if x.isRunning():
1583                                         return True
1584                 return False
1585
1586         def recordQuestionCallback(self, answer):
1587                 print "pre:\n", self.recording
1588
1589                 if answer is None or answer[1] == "no":
1590                         return
1591                 list = []
1592                 recording = self.recording[:]
1593                 for x in recording:
1594                         if not x in self.session.nav.RecordTimer.timer_list:
1595                                 self.recording.remove(x)
1596                         elif x.dontSave and x.isRunning():
1597                                 list.append((x, False))
1598
1599                 if answer[1] == "changeduration":
1600                         if len(self.recording) == 1:
1601                                 self.changeDuration(0)
1602                         else:
1603                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1604                 elif answer[1] == "changeendtime":
1605                         if len(self.recording) == 1:
1606                                 self.setEndtime(0)
1607                         else:
1608                                 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1609                 elif answer[1] == "stop":
1610                         if len(self.recording) == 1:
1611                                 self.stopCurrentRecording(0)
1612                         else:
1613                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1614                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1615                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1616                         if answer[1] == "manualduration":
1617                                 self.changeDuration(len(self.recording)-1)
1618                         elif answer[1] == "manualendtime":
1619                                 self.setEndtime(len(self.recording)-1)
1620                 print "after:\n", self.recording
1621
1622         def setEndtime(self, entry):
1623                 if entry is not None and entry >= 0:
1624                         self.selectedEntry = entry
1625                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1626                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1627                         dlg.setTitle(_("Please change recording endtime"))
1628
1629         def TimeDateInputClosed(self, ret):
1630                 if len(ret) > 1:
1631                         if ret[0]:
1632                                 localendtime = localtime(ret[1])
1633                                 print "stopping recording at", strftime("%c", localendtime)
1634                                 if self.recording[self.selectedEntry].end != ret[1]:
1635                                         self.recording[self.selectedEntry].autoincrease = False
1636                                 self.recording[self.selectedEntry].end = ret[1]
1637                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1638
1639         def changeDuration(self, entry):
1640                 if entry is not None and entry >= 0:
1641                         self.selectedEntry = entry
1642                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1643
1644         def inputCallback(self, value):
1645                 if value is not None:
1646                         print "stopping recording after", int(value), "minutes."
1647                         entry = self.recording[self.selectedEntry]
1648                         if int(value) != 0:
1649                                 entry.autoincrease = False
1650                         entry.end = int(time()) + 60 * int(value)
1651                         self.session.nav.RecordTimer.timeChanged(entry)
1652
1653         def instantRecord(self):
1654                 dir = preferredInstantRecordPath()
1655                 if not dir or not fileExists(dir, 'w'):
1656                         dir = defaultMoviePath()
1657 #       ikseong
1658                 if not fileExists("/hdd", 0):
1659                         print "not found /hdd"
1660                         system("ln -s /media/hdd /hdd")
1661 #
1662                 try:
1663                         stat = os_stat(dir)
1664                 except:
1665                         # XXX: this message is a little odd as we might be recording to a remote device
1666                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1667                         return
1668
1669                 if self.isInstantRecordRunning():
1670                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1671                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1672                                 list=((_("stop recording"), "stop"), \
1673                                 (_("add recording (stop after current event)"), "event"), \
1674                                 (_("add recording (indefinitely)"), "indefinitely"), \
1675                                 (_("add recording (enter recording duration)"), "manualduration"), \
1676                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1677                                 (_("change recording (duration)"), "changeduration"), \
1678                                 (_("change recording (endtime)"), "changeendtime"), \
1679                                 (_("do nothing"), "no")))
1680                 else:
1681                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1682                                 title=_("Start recording?"), \
1683                                 list=((_("add recording (stop after current event)"), "event"), \
1684                                 (_("add recording (indefinitely)"), "indefinitely"), \
1685                                 (_("add recording (enter recording duration)"), "manualduration"), \
1686                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1687                                 (_("don't record"), "no")))
1688
1689 from Tools.ISO639 import LanguageCodes
1690
1691 class InfoBarAudioSelection:
1692         def __init__(self):
1693                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1694                         {
1695                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1696                         })
1697
1698         def audioSelection(self):
1699                 from Screens.AudioSelection import AudioSelection
1700                 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self)
1701                 
1702         def audioSelected(self, ret=None):
1703                 print "[infobar::audioSelected]", ret
1704
1705 class InfoBarSubserviceSelection:
1706         def __init__(self):
1707                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1708                         {
1709                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1710                         })
1711
1712                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1713                         {
1714                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1715                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1716                         }, -1)
1717                 self["SubserviceQuickzapAction"].setEnabled(False)
1718
1719                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1720                         {
1721                                 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1722                         })
1723
1724                 self.bsel = None
1725
1726         def checkSubservicesAvail(self):
1727                 service = self.session.nav.getCurrentService()
1728                 subservices = service and service.subServices()
1729                 if not subservices or subservices.getNumberOfSubservices() == 0:
1730                         self["SubserviceQuickzapAction"].setEnabled(False)
1731
1732         def nextSubservice(self):
1733                 self.changeSubservice(+1)
1734
1735         def prevSubservice(self):
1736                 self.changeSubservice(-1)
1737
1738         def changeSubservice(self, direction):
1739                 service = self.session.nav.getCurrentService()
1740                 subservices = service and service.subServices()
1741                 n = subservices and subservices.getNumberOfSubservices()
1742                 if n and n > 0:
1743                         selection = -1
1744                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1745                         idx = 0
1746                         while idx < n:
1747                                 if subservices.getSubservice(idx).toString() == ref.toString():
1748                                         selection = idx
1749                                         break
1750                                 idx += 1
1751                         if selection != -1:
1752                                 selection += direction
1753                                 if selection >= n:
1754                                         selection=0
1755                                 elif selection < 0:
1756                                         selection=n-1
1757                                 newservice = subservices.getSubservice(selection)
1758                                 if newservice.valid():
1759                                         del subservices
1760                                         del service
1761                                         self.session.nav.playService(newservice, False)
1762
1763         def subserviceSelection(self):
1764                 service = self.session.nav.getCurrentService()
1765                 subservices = service and service.subServices()
1766                 self.bouquets = self.servicelist.getBouquetList()
1767                 n = subservices and subservices.getNumberOfSubservices()
1768                 selection = 0
1769                 if n and n > 0:
1770                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1771                         tlist = []
1772                         idx = 0
1773                         while idx < n:
1774                                 i = subservices.getSubservice(idx)
1775                                 if i.toString() == ref.toString():
1776                                         selection = idx
1777                                 tlist.append((i.getName(), i))
1778                                 idx += 1
1779
1780                         if self.bouquets and len(self.bouquets):
1781                                 keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1782                                 if config.usage.multibouquet.value:
1783                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1784                                 else:
1785                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1786                                 selection += 3
1787                         else:
1788                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1789                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1790                                 selection += 2
1791
1792                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1793
1794         def subserviceSelected(self, service):
1795                 del self.bouquets
1796                 if not service is None:
1797                         if isinstance(service[1], str):
1798                                 if service[1] == "quickzap":
1799                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1800                                         self.session.open(SubservicesQuickzap, service[2])
1801                         else:
1802                                 self["SubserviceQuickzapAction"].setEnabled(True)
1803                                 self.session.nav.playService(service[1], False)
1804
1805         def addSubserviceToBouquetCallback(self, service):
1806                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1807                         self.selectedSubservice = service
1808                         if self.bouquets is None:
1809                                 cnt = 0
1810                         else:
1811                                 cnt = len(self.bouquets)
1812                         if cnt > 1: # show bouquet list
1813                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1814                         elif cnt == 1: # add to only one existing bouquet
1815                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1816                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1817
1818         def bouquetSelClosed(self, confirmed):
1819                 self.bsel = None
1820                 del self.selectedSubservice
1821                 if confirmed:
1822                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1823
1824         def addSubserviceToBouquet(self, dest):
1825                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1826                 if self.bsel:
1827                         self.bsel.close(True)
1828                 else:
1829                         del self.selectedSubservice
1830
1831 from Components.Sources.HbbtvApplication import HbbtvApplication
1832 gHbbtvApplication = HbbtvApplication()
1833 class InfoBarRedButton:
1834         def __init__(self):
1835                 if not (config.misc.rcused.value == 1):
1836                         self["RedButtonActions"] = HelpableActionMap(self, "InfobarRedButtonActions",
1837                                 {
1838                                         "activateRedButton": (self.activateRedButton, _("Red button...")),
1839                                 })
1840                         self["HbbtvApplication"] = gHbbtvApplication
1841                 else:
1842                         self["HbbtvApplication"] = Boolean(fixed=0)
1843                         self["HbbtvApplication"].name = "" #is this a hack?
1844                         
1845                 self.onHBBTVActivation = [ ]
1846                 self.onRedButtonActivation = [ ]
1847                 self.onReadyForAIT = [ ]
1848                 self.__et = ServiceEventTracker(screen=self, eventmap=
1849                         {
1850                                 iPlayableService.evHBBTVInfo: self.detectedHbbtvApplication,
1851                                 iPlayableService.evUpdatedInfo: self.updateInfomation
1852                         })
1853
1854         def updateAIT(self, orgId=0):
1855                 for x in self.onReadyForAIT:
1856                         try:
1857                                 x(orgId)
1858                         except Exception, ErrMsg: 
1859                                 print ErrMsg
1860                                 #self.onReadyForAIT.remove(x)
1861
1862         def updateInfomation(self):
1863                 try:
1864                         self["HbbtvApplication"].setApplicationName("")
1865                         self.updateAIT()
1866                 except Exception, ErrMsg:
1867                         pass
1868                 
1869         def detectedHbbtvApplication(self):
1870                 service = self.session.nav.getCurrentService()
1871                 info = service and service.info()
1872                 try:
1873                         for x in info.getInfoObject(iServiceInformation.sHBBTVUrl):
1874                                 print x
1875                                 if x[0] in (-1, 1):
1876                                         self.updateAIT(x[3])
1877                                         self["HbbtvApplication"].setApplicationName(x[1])
1878                                         break
1879                 except Exception, ErrMsg:
1880                         pass
1881
1882         def activateRedButton(self):
1883                 service = self.session.nav.getCurrentService()
1884                 info = service and service.info()
1885                 if info and info.getInfoString(iServiceInformation.sHBBTVUrl) != "":
1886                         for x in self.onHBBTVActivation:
1887                                 x()
1888                 elif False: # TODO: other red button services
1889                         for x in self.onRedButtonActivation:
1890                                 x()
1891
1892 class InfoBarAdditionalInfo:
1893         def __init__(self):
1894
1895                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1896                 self["TimeshiftPossible"] = self["RecordingPossible"]
1897                 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1898                 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1899                 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1900                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1901
1902 class InfoBarNotifications:
1903         def __init__(self):
1904                 self.onExecBegin.append(self.checkNotifications)
1905                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1906                 self.onClose.append(self.__removeNotification)
1907
1908         def __removeNotification(self):
1909                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1910
1911         def checkNotificationsIfExecing(self):
1912                 if self.execing:
1913                         self.checkNotifications()
1914
1915         def checkNotifications(self):
1916                 notifications = Notifications.notifications
1917                 if notifications:
1918                         n = notifications[0]
1919
1920                         del notifications[0]
1921                         cb = n[0]
1922
1923                         if n[3].has_key("onSessionOpenCallback"):
1924                                 n[3]["onSessionOpenCallback"]()
1925                                 del n[3]["onSessionOpenCallback"]
1926
1927                         if cb is not None:
1928                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1929                         else:
1930                                 dlg = self.session.open(n[1], *n[2], **n[3])
1931
1932                         # remember that this notification is currently active
1933                         d = (n[4], dlg)
1934                         Notifications.current_notifications.append(d)
1935                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1936
1937         def __notificationClosed(self, d):
1938                 Notifications.current_notifications.remove(d)
1939
1940 class InfoBarServiceNotifications:
1941         def __init__(self):
1942                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1943                         {
1944                                 iPlayableService.evEnd: self.serviceHasEnded
1945                         })
1946
1947         def serviceHasEnded(self):
1948                 print "service end!"
1949
1950                 try:
1951                         self.setSeekState(self.SEEK_STATE_PLAY)
1952                 except:
1953                         pass
1954
1955 class InfoBarCueSheetSupport:
1956         CUT_TYPE_IN = 0
1957         CUT_TYPE_OUT = 1
1958         CUT_TYPE_MARK = 2
1959         CUT_TYPE_LAST = 3
1960
1961         ENABLE_RESUME_SUPPORT = False
1962
1963         def __init__(self, actionmap = "InfobarCueSheetActions"):
1964                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1965                         {
1966                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1967                                 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1968                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1969                         }, prio=1)
1970
1971                 self.cut_list = [ ]
1972                 self.is_closing = False
1973                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1974                         {
1975                                 iPlayableService.evStart: self.__serviceStarted,
1976                         })
1977
1978         def __serviceStarted(self):
1979                 if self.is_closing:
1980                         return
1981                 print "new service started! trying to download cuts!"
1982                 self.downloadCuesheet()
1983
1984                 if self.ENABLE_RESUME_SUPPORT:
1985                         last = None
1986
1987                         for (pts, what) in self.cut_list:
1988                                 if what == self.CUT_TYPE_LAST:
1989                                         last = pts
1990
1991                         if last is not None:
1992                                 self.resume_point = last
1993                                 
1994                                 l = last / 90000
1995                                 if config.usage.on_movie_start.value == "ask":
1996                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?") + "\n" + (_("Resume position at %s") % ("%d:%02d:%02d" % (l/3600, l%3600/60, l%60))), timeout=10)
1997                                 elif config.usage.on_movie_start.value == "resume":
1998 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1999 # TRANSLATORS: at the start of a movie, when the user has selected
2000 # TRANSLATORS: "Resume from last position" as start behavior.
2001 # TRANSLATORS: The purpose is to notify the user that the movie starts
2002 # TRANSLATORS: in the middle somewhere and not from the beginning.
2003 # TRANSLATORS: (Some translators seem to have interpreted it as a
2004 # TRANSLATORS: question or a choice, but it is a statement.)
2005                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2006
2007         def playLastCB(self, answer):
2008                 if answer == True:
2009                         self.doSeek(self.resume_point)
2010                 self.hideAfterResume()
2011
2012         def hideAfterResume(self):
2013                 if isinstance(self, InfoBarShowHide):
2014                         self.hide()
2015
2016         def __getSeekable(self):
2017                 service = self.session.nav.getCurrentService()
2018                 if service is None:
2019                         return None
2020                 return service.seek()
2021
2022         def cueGetCurrentPosition(self):
2023                 seek = self.__getSeekable()
2024                 if seek is None:
2025                         return None
2026                 r = seek.getPlayPosition()
2027                 if r[0]:
2028                         return None
2029                 return long(r[1])
2030
2031         def cueGetEndCutPosition(self):
2032                 ret = False
2033                 isin = True
2034                 for cp in self.cut_list:
2035                         if cp[1] == self.CUT_TYPE_OUT:
2036                                 if isin:
2037                                         isin = False
2038                                         ret = cp[0]
2039                         elif cp[1] == self.CUT_TYPE_IN:
2040                                 isin = True
2041                 return ret
2042                 
2043         def jumpPreviousNextMark(self, cmp, start=False):
2044                 current_pos = self.cueGetCurrentPosition()
2045                 if current_pos is None:
2046                         return False
2047                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2048                 if mark is not None:
2049                         pts = mark[0]
2050                 else:
2051                         return False
2052
2053                 self.doSeek(pts)
2054                 return True
2055
2056         def jumpPreviousMark(self):
2057                 # we add 5 seconds, so if the play position is <5s after
2058                 # the mark, the mark before will be used
2059                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2060
2061         def jumpNextMark(self):
2062                 if not self.jumpPreviousNextMark(lambda x: x-90000):
2063                         self.doSeek(-1)
2064
2065         def getNearestCutPoint(self, pts, cmp=abs, start=False):
2066                 # can be optimized
2067                 beforecut = True
2068                 nearest = None
2069                 bestdiff = -1
2070                 instate = True
2071                 if start:
2072                         bestdiff = cmp(0 - pts)
2073                         if bestdiff >= 0:
2074                                 nearest = [0, False]
2075                 for cp in self.cut_list:
2076                         if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2077                                 beforecut = False
2078                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
2079                                         diff = cmp(cp[0] - pts)
2080                                         if start and diff >= 0:
2081                                                 nearest = cp
2082                                                 bestdiff = diff
2083                                         else:
2084                                                 nearest = None
2085                                                 bestdiff = -1
2086                         if cp[1] == self.CUT_TYPE_IN:
2087                                 instate = True
2088                         elif cp[1] == self.CUT_TYPE_OUT:
2089                                 instate = False
2090                         elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2091                                 diff = cmp(cp[0] - pts)
2092                                 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2093                                         nearest = cp
2094                                         bestdiff = diff
2095                 return nearest
2096
2097         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2098                 current_pos = self.cueGetCurrentPosition()
2099                 if current_pos is None:
2100                         print "not seekable"
2101                         return
2102
2103                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2104
2105                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2106                         if onlyreturn:
2107                                 return nearest_cutpoint
2108                         if not onlyadd:
2109                                 self.removeMark(nearest_cutpoint)
2110                 elif not onlyremove and not onlyreturn:
2111                         self.addMark((current_pos, self.CUT_TYPE_MARK))
2112
2113                 if onlyreturn:
2114                         return None
2115
2116         def addMark(self, point):
2117                 insort(self.cut_list, point)
2118                 self.uploadCuesheet()
2119                 self.showAfterCuesheetOperation()
2120
2121         def removeMark(self, point):
2122                 self.cut_list.remove(point)
2123                 self.uploadCuesheet()
2124                 self.showAfterCuesheetOperation()
2125
2126         def showAfterCuesheetOperation(self):
2127                 if isinstance(self, InfoBarShowHide):
2128                         self.doShow()
2129
2130         def __getCuesheet(self):
2131                 service = self.session.nav.getCurrentService()
2132                 if service is None:
2133                         return None
2134                 return service.cueSheet()
2135
2136         def uploadCuesheet(self):
2137                 cue = self.__getCuesheet()
2138
2139                 if cue is None:
2140                         print "upload failed, no cuesheet interface"
2141                         return
2142                 cue.setCutList(self.cut_list)
2143
2144         def downloadCuesheet(self):
2145                 cue = self.__getCuesheet()
2146
2147                 if cue is None:
2148                         print "download failed, no cuesheet interface"
2149                         self.cut_list = [ ]
2150                 else:
2151                         self.cut_list = cue.getCutList()
2152
2153 class InfoBarSummary(Screen):
2154         skin = """
2155         <screen position="0,0" size="132,64">
2156                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2157                         <convert type="ClockToText">WithSeconds</convert>
2158                 </widget>
2159                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2160                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2161                         <convert type="ConditionalShowHide">Blink</convert>
2162                 </widget>
2163                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2164                         <convert type="ServiceName">Name</convert>
2165                 </widget>
2166                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2167                         <convert type="EventTime">Progress</convert>
2168                 </widget>
2169         </screen>"""
2170
2171 # for picon:  (path="piconlcd" will use LCD picons)
2172 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2173 #                       <convert type="ServiceName">Reference</convert>
2174 #               </widget>
2175
2176 class InfoBarSummarySupport:
2177         def __init__(self):
2178                 pass
2179
2180         def createSummary(self):
2181                 return InfoBarSummary
2182
2183 class InfoBarMoviePlayerSummary(Screen):
2184         skin = """
2185         <screen position="0,0" size="132,64">
2186                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2187                         <convert type="ClockToText">WithSeconds</convert>
2188                 </widget>
2189                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2190                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2191                         <convert type="ConditionalShowHide">Blink</convert>
2192                 </widget>
2193                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2194                         <convert type="ServiceName">Name</convert>
2195                 </widget>
2196                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2197                         <convert type="ServicePosition">Position</convert>
2198                 </widget>
2199         </screen>"""
2200
2201 class InfoBarMoviePlayerSummarySupport:
2202         def __init__(self):
2203                 pass
2204
2205         def createSummary(self):
2206                 return InfoBarMoviePlayerSummary
2207
2208 class InfoBarTeletextPlugin:
2209         def __init__(self):
2210                 self.teletext_plugin = None
2211
2212                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2213                         self.teletext_plugin = p
2214
2215                 if self.teletext_plugin is not None:
2216                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2217                                 {
2218                                         "startTeletext": (self.startTeletext, _("View teletext..."))
2219                                 })
2220                 else:
2221                         print "no teletext plugin found!"
2222
2223         def startTeletext(self):
2224                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2225
2226 class InfoBarSubtitleSupport(object):
2227         def __init__(self):
2228                 object.__init__(self)
2229                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2230                 self.__subtitles_enabled = False
2231
2232                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2233                         {
2234                                 iPlayableService.evEnd: self.__serviceStopped,
2235                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
2236                         })
2237                 self.cached_subtitle_checked = False
2238                 self.__selected_subtitle = None
2239
2240         def __serviceStopped(self):
2241                 self.cached_subtitle_checked = False
2242                 if self.__subtitles_enabled:
2243                         self.subtitle_window.hide()
2244                         self.__subtitles_enabled = False
2245                         self.__selected_subtitle = None
2246
2247         def __updatedInfo(self):
2248                 if not self.cached_subtitle_checked:
2249                         self.cached_subtitle_checked = True
2250                         subtitle = self.getCurrentServiceSubtitle()
2251                         self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2252                         if self.__selected_subtitle:
2253                                 self.setSubtitlesEnable(True)
2254
2255         def getCurrentServiceSubtitle(self):
2256                 service = self.session.nav.getCurrentService()
2257                 return service and service.subtitle()
2258
2259         def setSubtitlesEnable(self, enable=True):
2260                 subtitle = self.getCurrentServiceSubtitle()
2261                 if enable:
2262                         if self.__selected_subtitle:
2263                                 if subtitle and not self.__subtitles_enabled:
2264                                         subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2265                                         self.subtitle_window.show()
2266                                         self.__subtitles_enabled = True
2267                 else:
2268                         if subtitle:
2269                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2270                         self.__selected_subtitle = False
2271                         self.__subtitles_enabled = False
2272                         self.subtitle_window.hide()
2273
2274         def setSelectedSubtitle(self, subtitle):
2275                 self.__selected_subtitle = subtitle
2276
2277         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2278         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2279
2280 class InfoBarServiceErrorPopupSupport:
2281         def __init__(self):
2282                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2283                         {
2284                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2285                                 iPlayableService.evStart: self.__serviceStarted
2286                         })
2287                 self.__serviceStarted()
2288
2289         def __serviceStarted(self):
2290                 self.last_error = None
2291                 Notifications.RemovePopup(id = "ZapError")
2292
2293         def __tuneFailed(self):
2294                 service = self.session.nav.getCurrentService()
2295                 info = service and service.info()
2296                 error = info and info.getInfo(iServiceInformation.sDVBState)
2297
2298                 if error == self.last_error:
2299                         error = None
2300                 else:
2301                         self.last_error = error
2302
2303                 error = {
2304                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2305                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2306                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2307                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2308                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2309                         eDVBServicePMTHandler.eventNewProgramInfo: None,
2310                         eDVBServicePMTHandler.eventTuned: None,
2311                         eDVBServicePMTHandler.eventSOF: None,
2312                         eDVBServicePMTHandler.eventEOF: None,
2313                         eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2314                 }.get(error) #this returns None when the key not exist in the dict
2315
2316                 if error is not None:
2317                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2318                 else:
2319                         Notifications.RemovePopup(id = "ZapError")