Merge commit 'dm/experimental' into test branch
[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                 root = self.servicelist.getRoot()
559                 if bouquets is None:
560                         cnt = 0
561                 else:
562                         cnt = len(bouquets)
563                 if cnt > 1: # create bouquet list for bouq+/-
564                         self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
565                 if cnt >= 1:
566                         self.openBouquetEPG(root, withCallback)
567
568         def changeServiceCB(self, direction, epg):
569                 if self.serviceSel:
570                         if direction > 0:
571                                 self.serviceSel.nextService()
572                         else:
573                                 self.serviceSel.prevService()
574                         epg.setService(self.serviceSel.currentService())
575
576         def SingleServiceEPGClosed(self, ret=False):
577                 self.serviceSel = None
578
579         def openSingleServiceEPG(self):
580                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
581                 if ref:
582                         if self.servicelist.getMutableList() is not None: # bouquet in channellist
583                                 current_path = self.servicelist.getRoot()
584                                 services = self.getBouquetServices(current_path)
585                                 self.serviceSel = SimpleServicelist(services)
586                                 if self.serviceSel.selectService(ref):
587                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
588                                 else:
589                                         self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
590                         else:
591                                 self.session.open(EPGSelection, ref)
592
593         def showEventInfoPlugins(self):
594                 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
595
596                 if list:
597                         list.append((_("show single service EPG..."), self.openSingleServiceEPG))
598                         list.append((_("Multi EPG"), self.openMultiServiceEPG))
599                         self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
600                 else:
601                         self.openSingleServiceEPG()
602
603         def runPlugin(self, plugin):
604                 plugin(session = self.session, servicelist = self.servicelist)
605                 
606         def EventInfoPluginChosen(self, answer):
607                 if answer is not None:
608                         answer[1]()
609
610         def openSimilarList(self, eventid, refstr):
611                 self.session.open(EPGSelection, refstr, None, eventid)
612
613         def getNowNext(self):
614                 epglist = [ ]
615                 service = self.session.nav.getCurrentService()
616                 info = service and service.info()
617                 ptr = info and info.getEvent(0)
618                 if ptr:
619                         epglist.append(ptr)
620                 ptr = info and info.getEvent(1)
621                 if ptr:
622                         epglist.append(ptr)
623                 self.epglist = epglist
624
625         def __evEventInfoChanged(self):
626                 if self.is_now_next and len(self.dlg_stack) == 1:
627                         self.getNowNext()
628                         assert self.eventView
629                         if self.epglist:
630                                 self.eventView.setEvent(self.epglist[0])
631
632         def openEventView(self):
633                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
634                 self.getNowNext()
635                 epglist = self.epglist
636                 if not epglist:
637                         self.is_now_next = False
638                         epg = eEPGCache.getInstance()
639                         ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
640                         if ptr:
641                                 epglist.append(ptr)
642                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
643                                 if ptr:
644                                         epglist.append(ptr)
645                 else:
646                         self.is_now_next = True
647                 if epglist:
648                         self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
649                         self.dlg_stack.append(self.eventView)
650                 else:
651                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
652                         self.openMultiServiceEPG(False)
653
654         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
655                 epglist = self.epglist
656                 if len(epglist) > 1:
657                         tmp = epglist[0]
658                         epglist[0]=epglist[1]
659                         epglist[1]=tmp
660                         setEvent(epglist[0])
661
662 class InfoBarRdsDecoder:
663         """provides RDS and Rass support/display"""
664         def __init__(self):
665                 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
666                 self.rass_interactive = None
667
668                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
669                         {
670                                 iPlayableService.evEnd: self.__serviceStopped,
671                                 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
672                         })
673
674                 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
675                 {
676                         "startRassInteractive": self.startRassInteractive
677                 },-1)
678
679                 self["RdsActions"].setEnabled(False)
680
681                 self.onLayoutFinish.append(self.rds_display.show)
682                 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
683
684         def RassInteractivePossibilityChanged(self, state):
685                 self["RdsActions"].setEnabled(state)
686
687         def RassSlidePicChanged(self):
688                 if not self.rass_interactive:
689                         service = self.session.nav.getCurrentService()
690                         decoder = service and service.rdsDecoder()
691                         if decoder:
692                                 decoder.showRassSlidePicture()
693
694         def __serviceStopped(self):
695                 if self.rass_interactive is not None:
696                         rass_interactive = self.rass_interactive
697                         self.rass_interactive = None
698                         rass_interactive.close()
699
700         def startRassInteractive(self):
701                 self.rds_display.hide()
702                 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
703
704         def RassInteractiveClosed(self, *val):
705                 if self.rass_interactive is not None:
706                         self.rass_interactive = None
707                         self.RassSlidePicChanged()
708                 self.rds_display.show()
709
710 class InfoBarSeek:
711         """handles actions like seeking, pause"""
712
713         SEEK_STATE_PLAY = (0, 0, 0, ">")
714         SEEK_STATE_PAUSE = (1, 0, 0, "||")
715         SEEK_STATE_EOF = (1, 0, 0, "END")
716
717         def __init__(self, actionmap = "InfobarSeekActions"):
718                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
719                         {
720                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
721                                 iPlayableService.evStart: self.__serviceStarted,
722
723                                 iPlayableService.evEOF: self.__evEOF,
724                                 iPlayableService.evSOF: self.__evSOF,
725                         })
726                 self.fast_winding_hint_message_showed = False
727
728                 class InfoBarSeekActionMap(HelpableActionMap):
729                         def __init__(self, screen, *args, **kwargs):
730                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
731                                 self.screen = screen
732
733                         def action(self, contexts, action):
734                                 print "action:", action
735                                 if action[:5] == "seek:":
736                                         time = int(action[5:])
737                                         self.screen.doSeekRelative(time * 90000)
738                                         return 1
739                                 elif action[:8] == "seekdef:":
740                                         key = int(action[8:])
741                                         time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
742                                                 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
743                                                 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
744                                         self.screen.doSeekRelative(time * 90000)
745                                         return 1                                        
746                                 else:
747                                         return HelpableActionMap.action(self, contexts, action)
748
749                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
750                         {
751                                 "playpauseService": self.playpauseService,
752                                 "pauseService": (self.pauseService, _("pause")),
753                                 "unPauseService": (self.unPauseService, _("continue")),
754
755                                 "seekFwd": (self.seekFwd, _("skip forward")),
756                                 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
757                                 "seekBack": (self.seekBack, _("skip backward")),
758                                 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
759                         }, prio=-1)
760                         # give them a little more priority to win over color buttons
761
762                 self["SeekActions"].setEnabled(False)
763
764                 self.seekstate = self.SEEK_STATE_PLAY
765                 self.lastseekstate = self.SEEK_STATE_PLAY
766
767                 self.onPlayStateChanged = [ ]
768
769                 self.lockedBecauseOfSkipping = False
770
771                 self.__seekableStatusChanged()
772
773         def makeStateForward(self, n):
774                 return (0, n, 0, ">> %dx" % n)
775
776         def makeStateBackward(self, n):
777                 return (0, -n, 0, "<< %dx" % n)
778
779         def makeStateSlowMotion(self, n):
780                 return (0, 0, n, "/%d" % n)
781
782         def isStateForward(self, state):
783                 return state[1] > 1
784
785         def isStateBackward(self, state):
786                 return state[1] < 0
787
788         def isStateSlowMotion(self, state):
789                 return state[1] == 0 and state[2] > 1
790
791         def getHigher(self, n, lst):
792                 for x in lst:
793                         if x > n:
794                                 return x
795                 return False
796
797         def getLower(self, n, lst):
798                 lst = lst[:]
799                 lst.reverse()
800                 for x in lst:
801                         if x < n:
802                                 return x
803                 return False
804
805         def showAfterSeek(self):
806                 if isinstance(self, InfoBarShowHide):
807                         self.doShow()
808
809         def up(self):
810                 pass
811
812         def down(self):
813                 pass
814
815         def getSeek(self):
816                 service = self.session.nav.getCurrentService()
817                 if service is None:
818                         return None
819
820                 seek = service.seek()
821
822                 if seek is None or not seek.isCurrentlySeekable():
823                         return None
824
825                 return seek
826
827         def isSeekable(self):
828                 if self.getSeek() is None:
829                         return False
830                 return True
831
832         def __seekableStatusChanged(self):
833 #               print "seekable status changed!"
834                 if not self.isSeekable():
835                         self["SeekActions"].setEnabled(False)
836 #                       print "not seekable, return to play"
837                         self.setSeekState(self.SEEK_STATE_PLAY)
838                 else:
839                         self["SeekActions"].setEnabled(True)
840 #                       print "seekable"
841
842         def __serviceStarted(self):
843                 self.fast_winding_hint_message_showed = False
844                 self.seekstate = self.SEEK_STATE_PLAY
845                 self.__seekableStatusChanged()
846
847         def setSeekState(self, state):
848                 service = self.session.nav.getCurrentService()
849
850                 if service is None:
851                         return False
852
853                 if not self.isSeekable():
854                         if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
855                                 state = self.SEEK_STATE_PLAY
856
857                 pauseable = service.pause()
858
859                 if pauseable is None:
860                         print "not pauseable."
861                         state = self.SEEK_STATE_PLAY
862
863                 self.seekstate = state
864
865                 if pauseable is not None:
866                         if self.seekstate[0]:
867                                 print "resolved to PAUSE"
868                                 pauseable.pause()
869                         elif self.seekstate[1]:
870                                 print "resolved to FAST FORWARD"
871                                 pauseable.setFastForward(self.seekstate[1])
872                         elif self.seekstate[2]:
873                                 print "resolved to SLOW MOTION"
874                                 pauseable.setSlowMotion(self.seekstate[2])
875                         else:
876                                 print "resolved to PLAY"
877                                 pauseable.unpause()
878
879                 for c in self.onPlayStateChanged:
880                         c(self.seekstate)
881
882                 self.checkSkipShowHideLock()
883
884                 return True
885
886         def playpauseService(self):
887                 if self.seekstate != self.SEEK_STATE_PLAY:
888                         self.unPauseService()
889                 else:
890                         self.pauseService()
891
892         def pauseService(self):
893                 if self.seekstate == self.SEEK_STATE_PAUSE:
894                         if config.seek.on_pause.value == "play":
895                                 self.unPauseService()
896                         elif config.seek.on_pause.value == "step":
897                                 self.doSeekRelative(1)
898                         elif config.seek.on_pause.value == "last":
899                                 self.setSeekState(self.lastseekstate)
900                                 self.lastseekstate = self.SEEK_STATE_PLAY
901                 else:
902                         if self.seekstate != self.SEEK_STATE_EOF:
903                                 self.lastseekstate = self.seekstate
904                         self.setSeekState(self.SEEK_STATE_PAUSE);
905
906         def unPauseService(self):
907                 print "unpause"
908                 if self.seekstate == self.SEEK_STATE_PLAY:
909                         return 0
910                 self.setSeekState(self.SEEK_STATE_PLAY)
911
912         def doSeek(self, pts):
913                 seekable = self.getSeek()
914                 if seekable is None:
915                         return
916                 seekable.seekTo(pts)
917
918         def doSeekRelative(self, pts):
919                 seekable = self.getSeek()
920                 if seekable is None:
921                         return
922                 prevstate = self.seekstate
923
924                 if self.seekstate == self.SEEK_STATE_EOF:
925                         if prevstate == self.SEEK_STATE_PAUSE:
926                                 self.setSeekState(self.SEEK_STATE_PAUSE)
927                         else:
928                                 self.setSeekState(self.SEEK_STATE_PLAY)
929                 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
930                 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
931                         self.showAfterSeek()
932
933         def seekFwd(self):
934                 seek = self.getSeek()
935                 if seek and not (seek.isCurrentlySeekable() & 2):
936                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
937                                 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)
938                                 self.fast_winding_hint_message_showed = True
939                                 return
940                         return 0 # trade as unhandled action
941                 if self.seekstate == self.SEEK_STATE_PLAY:
942                         self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
943                 elif self.seekstate == self.SEEK_STATE_PAUSE:
944                         if len(config.seek.speeds_slowmotion.value):
945                                 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
946                         else:
947                                 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
948                 elif self.seekstate == self.SEEK_STATE_EOF:
949                         pass
950                 elif self.isStateForward(self.seekstate):
951                         speed = self.seekstate[1]
952                         if self.seekstate[2]:
953                                 speed /= self.seekstate[2]
954                         speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
955                         self.setSeekState(self.makeStateForward(speed))
956                 elif self.isStateBackward(self.seekstate):
957                         speed = -self.seekstate[1]
958                         if self.seekstate[2]:
959                                 speed /= self.seekstate[2]
960                         speed = self.getLower(speed, config.seek.speeds_backward.value)
961                         if speed:
962                                 self.setSeekState(self.makeStateBackward(speed))
963                         else:
964                                 self.setSeekState(self.SEEK_STATE_PLAY)
965                 elif self.isStateSlowMotion(self.seekstate):
966                         speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
967                         self.setSeekState(self.makeStateSlowMotion(speed))
968
969         def seekBack(self):
970                 seek = self.getSeek()
971                 if seek and not (seek.isCurrentlySeekable() & 2):
972                         if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
973                                 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)
974                                 self.fast_winding_hint_message_showed = True
975                                 return
976                         return 0 # trade as unhandled action
977                 seekstate = self.seekstate
978                 if seekstate == self.SEEK_STATE_PLAY:
979                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
980                 elif seekstate == self.SEEK_STATE_EOF:
981                         self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
982                         self.doSeekRelative(-6)
983                 elif seekstate == self.SEEK_STATE_PAUSE:
984                         self.doSeekRelative(-1)
985                 elif self.isStateForward(seekstate):
986                         speed = seekstate[1]
987                         if seekstate[2]:
988                                 speed /= seekstate[2]
989                         speed = self.getLower(speed, config.seek.speeds_forward.value)
990                         if speed:
991                                 self.setSeekState(self.makeStateForward(speed))
992                         else:
993                                 self.setSeekState(self.SEEK_STATE_PLAY)
994                 elif self.isStateBackward(seekstate):
995                         speed = -seekstate[1]
996                         if seekstate[2]:
997                                 speed /= seekstate[2]
998                         speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
999                         self.setSeekState(self.makeStateBackward(speed))
1000                 elif self.isStateSlowMotion(seekstate):
1001                         speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1002                         if speed:
1003                                 self.setSeekState(self.makeStateSlowMotion(speed))
1004                         else:
1005                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1006
1007         def seekFwdManual(self):
1008                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1009
1010         def fwdSeekTo(self, minutes):
1011                 print "Seek", minutes, "minutes forward"
1012                 self.doSeekRelative(minutes * 60 * 90000)
1013
1014         def seekBackManual(self):
1015                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1016
1017         def rwdSeekTo(self, minutes):
1018                 print "rwdSeekTo"
1019                 self.doSeekRelative(-minutes * 60 * 90000)
1020
1021         def checkSkipShowHideLock(self):
1022                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1023
1024                 if config.usage.show_infobar_on_skip.value:
1025                         if self.lockedBecauseOfSkipping and not wantlock:
1026                                 self.unlockShow()
1027                                 self.lockedBecauseOfSkipping = False
1028
1029                         if wantlock and not self.lockedBecauseOfSkipping:
1030                                 self.lockShow()
1031                                 self.lockedBecauseOfSkipping = True
1032
1033         def calcRemainingTime(self):
1034                 seekable = self.getSeek()
1035                 if seekable is not None:
1036                         len = seekable.getLength()
1037                         try:
1038                                 tmp = self.cueGetEndCutPosition()
1039                                 if tmp:
1040                                         len = [False, tmp]
1041                         except:
1042                                 pass
1043                         pos = seekable.getPlayPosition()
1044                         speednom = self.seekstate[1] or 1
1045                         speedden = self.seekstate[2] or 1
1046                         if not len[0] and not pos[0]:
1047                                 if len[1] <= pos[1]:
1048                                         return 0
1049                                 time = (len[1] - pos[1])*speedden/(90*speednom)
1050                                 return time
1051                 return False
1052                 
1053         def __evEOF(self):
1054                 if self.seekstate == self.SEEK_STATE_EOF:
1055                         return
1056
1057                 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1058                 seekstate = self.seekstate
1059                 if self.seekstate != self.SEEK_STATE_PAUSE:
1060                         self.setSeekState(self.SEEK_STATE_EOF)
1061
1062                 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1063                         seekable = self.getSeek()
1064                         if seekable is not None:
1065                                 seekable.seekTo(-1)
1066                 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1067                         self.doEofInternal(True)
1068                 else:
1069                         self.doEofInternal(False)
1070
1071         def doEofInternal(self, playing):
1072                 pass            # Defined in subclasses
1073
1074         def __evSOF(self):
1075                 self.setSeekState(self.SEEK_STATE_PLAY)
1076                 self.doSeek(0)
1077
1078 from Screens.PVRState import PVRState, TimeshiftState
1079
1080 class InfoBarPVRState:
1081         def __init__(self, screen=PVRState, force_show = False):
1082                 self.onPlayStateChanged.append(self.__playStateChanged)
1083                 self.pvrStateDialog = self.session.instantiateDialog(screen)
1084                 self.onShow.append(self._mayShow)
1085                 self.onHide.append(self.pvrStateDialog.hide)
1086                 self.force_show = force_show
1087
1088         def _mayShow(self):
1089                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1090                         self.pvrStateDialog.show()
1091
1092         def __playStateChanged(self, state):
1093                 playstateString = state[3]
1094                 self.pvrStateDialog["state"].setText(playstateString)
1095                 
1096                 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1097                 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1098                         self.pvrStateDialog.hide()
1099                 else:
1100                         self._mayShow()
1101
1102 class InfoBarTimeshiftState(InfoBarPVRState):
1103         def __init__(self):
1104                 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1105                 self.__hideTimer = eTimer()
1106                 self.__hideTimer.callback.append(self.__hideTimeshiftState)
1107
1108         def _mayShow(self):
1109                 if self.execing and self.timeshift_enabled:
1110                         self.pvrStateDialog.show()
1111                         if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
1112                                 self.__hideTimer.start(5*1000, True)
1113
1114         def __hideTimeshiftState(self):
1115                 self.pvrStateDialog.hide()
1116
1117 class InfoBarShowMovies:
1118
1119         # i don't really like this class.
1120         # it calls a not further specified "movie list" on up/down/movieList,
1121         # so this is not more than an action map
1122         def __init__(self):
1123                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1124                         {
1125                                 "movieList": (self.showMovies, _("movie list")),
1126                                 "up": (self.showMovies, _("movie list")),
1127                                 "down": (self.showMovies, _("movie list"))
1128                         })
1129
1130 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1131
1132 # Hrmf.
1133 #
1134 # Timeshift works the following way:
1135 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1136 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
1137 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
1138 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
1139 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
1140 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
1141 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
1142 #
1143
1144 # in other words:
1145 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1146 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1147 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1148 # - the user can now PVR around
1149 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1150 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1151 # after!
1152 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1153 # - if the user rewinds, or press pause, timeshift will be activated again
1154
1155 # note that a timeshift can be enabled ("recording") and
1156 # activated (currently time-shifting).
1157
1158 class InfoBarTimeshift:
1159         def __init__(self):
1160                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1161                         {
1162                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
1163                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
1164                         }, prio=1)
1165                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1166                         {
1167                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1168                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1169                         }, prio=-1) # priority over record
1170
1171                 self.timeshift_enabled = 0
1172                 self.timeshift_state = 0
1173                 self.ts_rewind_timer = eTimer()
1174                 self.ts_rewind_timer.callback.append(self.rewindService)
1175
1176                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1177                         {
1178                                 iPlayableService.evStart: self.__serviceStarted,
1179                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1180                         })
1181
1182         def getTimeshift(self):
1183                 service = self.session.nav.getCurrentService()
1184                 return service and service.timeshift()
1185
1186         def startTimeshift(self):
1187                 print "enable timeshift"
1188                 ts = self.getTimeshift()
1189                 if ts is None:
1190                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1191                         print "no ts interface"
1192                         return 0
1193
1194                 if self.timeshift_enabled:
1195                         print "hu, timeshift already enabled?"
1196                 else:
1197                         if not ts.startTimeshift():
1198                                 self.timeshift_enabled = 1
1199
1200                                 # we remove the "relative time" for now.
1201                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1202
1203                                 # PAUSE.
1204                                 #self.setSeekState(self.SEEK_STATE_PAUSE)
1205                                 self.activateTimeshiftEnd(False)
1206
1207                                 # enable the "TimeshiftEnableActions", which will override
1208                                 # the startTimeshift actions
1209                                 self.__seekableStatusChanged()
1210                         else:
1211                                 print "timeshift failed"
1212
1213         def stopTimeshift(self):
1214                 if not self.timeshift_enabled:
1215                         return 0
1216                 print "disable timeshift"
1217                 ts = self.getTimeshift()
1218                 if ts is None:
1219                         return 0
1220                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1221
1222         def stopTimeshiftConfirmed(self, confirmed):
1223                 if not confirmed:
1224                         return
1225
1226                 ts = self.getTimeshift()
1227                 if ts is None:
1228                         return
1229
1230                 ts.stopTimeshift()
1231                 self.timeshift_enabled = 0
1232
1233                 # disable actions
1234                 self.__seekableStatusChanged()
1235
1236         # activates timeshift, and seeks to (almost) the end
1237         def activateTimeshiftEnd(self, back = True):
1238                 ts = self.getTimeshift()
1239                 print "activateTimeshiftEnd"
1240
1241                 if ts is None:
1242                         return
1243
1244                 if ts.isTimeshiftActive():
1245                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1246                         self.pauseService()
1247                 else:
1248                         print "play, ..."
1249                         ts.activateTimeshift() # activate timeshift will automatically pause
1250                         self.setSeekState(self.SEEK_STATE_PAUSE)
1251
1252                 if back:
1253                         self.ts_rewind_timer.start(200, 1)
1254
1255         def rewindService(self):
1256                 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1257
1258         # same as activateTimeshiftEnd, but pauses afterwards.
1259         def activateTimeshiftEndAndPause(self):
1260                 print "activateTimeshiftEndAndPause"
1261                 #state = self.seekstate
1262                 self.activateTimeshiftEnd(False)
1263
1264         def __seekableStatusChanged(self):
1265                 enabled = False
1266
1267 #               print "self.isSeekable", self.isSeekable()
1268 #               print "self.timeshift_enabled", self.timeshift_enabled
1269
1270                 # when this service is not seekable, but timeshift
1271                 # is enabled, this means we can activate
1272                 # the timeshift
1273                 if not self.isSeekable() and self.timeshift_enabled:
1274                         enabled = True
1275
1276 #               print "timeshift activate:", enabled
1277                 self["TimeshiftActivateActions"].setEnabled(enabled)
1278
1279         def __serviceStarted(self):
1280                 self.timeshift_enabled = False
1281                 self.__seekableStatusChanged()
1282
1283 from Screens.PiPSetup import PiPSetup
1284
1285 class InfoBarExtensions:
1286         EXTENSION_SINGLE = 0
1287         EXTENSION_LIST = 1
1288
1289         def __init__(self):
1290                 self.list = []
1291
1292                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1293                         {
1294                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1295                         }, 1) # lower priority
1296
1297         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1298                 self.list.append((type, extension, key))
1299
1300         def updateExtension(self, extension, key = None):
1301                 self.extensionsList.append(extension)
1302                 if key is not None:
1303                         if self.extensionKeys.has_key(key):
1304                                 key = None
1305
1306                 if key is None:
1307                         for x in self.availableKeys:
1308                                 if not self.extensionKeys.has_key(x):
1309                                         key = x
1310                                         break
1311
1312                 if key is not None:
1313                         self.extensionKeys[key] = len(self.extensionsList) - 1
1314
1315         def updateExtensions(self):
1316                 self.extensionsList = []
1317                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1318                 self.extensionKeys = {}
1319                 for x in self.list:
1320                         if x[0] == self.EXTENSION_SINGLE:
1321                                 self.updateExtension(x[1], x[2])
1322                         else:
1323                                 for y in x[1]():
1324                                         self.updateExtension(y[0], y[1])
1325
1326
1327         def showExtensionSelection(self):
1328                 self.updateExtensions()
1329                 extensionsList = self.extensionsList[:]
1330                 keys = []
1331                 list = []
1332                 for x in self.availableKeys:
1333                         if self.extensionKeys.has_key(x):
1334                                 entry = self.extensionKeys[x]
1335                                 extension = self.extensionsList[entry]
1336                                 if extension[2]():
1337                                         name = str(extension[0]())
1338                                         list.append((extension[0](), extension))
1339                                         keys.append(x)
1340                                         extensionsList.remove(extension)
1341                                 else:
1342                                         extensionsList.remove(extension)
1343                 list.extend([(x[0](), x) for x in extensionsList])
1344
1345                 keys += [""] * len(extensionsList)
1346                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1347
1348         def extensionCallback(self, answer):
1349                 if answer is not None:
1350                         answer[1][1]()
1351
1352 from Tools.BoundFunction import boundFunction
1353 import inspect
1354
1355 # depends on InfoBarExtensions
1356
1357 class InfoBarPlugins:
1358         def __init__(self):
1359                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1360
1361         def getPluginName(self, name):
1362                 return name
1363
1364         def getPluginList(self):
1365                 l = []
1366                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1367                   args = inspect.getargspec(p.__call__)[0]
1368                   if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
1369                           l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
1370                 l.sort(key = lambda e: e[2]) # sort by name
1371                 return l
1372
1373         def runPlugin(self, plugin):
1374                 if isinstance(self, InfoBarChannelSelection):
1375                         plugin(session = self.session, servicelist = self.servicelist)
1376                 else:
1377                         plugin(session = self.session)
1378
1379 from Components.Task import job_manager
1380 class InfoBarJobman:
1381         def __init__(self):
1382                 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1383
1384         def getJobList(self):
1385                 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1386
1387         def getJobName(self, job):
1388                 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1389
1390         def showJobView(self, job):
1391                 from Screens.TaskView import JobView
1392                 job_manager.in_background = False
1393                 self.session.openWithCallback(self.JobViewCB, JobView, job)
1394         
1395         def JobViewCB(self, in_background):
1396                 job_manager.in_background = in_background
1397
1398 # depends on InfoBarExtensions
1399 class InfoBarPiP:
1400         def __init__(self):
1401                 try:
1402                         self.session.pipshown
1403                 except:
1404                         self.session.pipshown = False
1405                 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1406                         if (self.allowPiP):
1407                                 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1408                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1409                                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1410                         else:
1411                                 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1412                                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1413
1414         def pipShown(self):
1415                 return self.session.pipshown
1416
1417         def pipHandles0Action(self):
1418                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1419
1420         def getShowHideName(self):
1421                 if self.session.pipshown:
1422                         return _("Disable Picture in Picture")
1423                 else:
1424                         return _("Activate Picture in Picture")
1425
1426         def getSwapName(self):
1427                 return _("Swap Services")
1428
1429         def getMoveName(self):
1430                 return _("Move Picture in Picture")
1431
1432         def showPiP(self):
1433                 if self.session.pipshown:
1434                         del self.session.pip
1435                         self.session.pipshown = False
1436                 else:
1437                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1438                         self.session.pip.show()
1439                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1440                         if self.session.pip.playService(newservice):
1441                                 self.session.pipshown = True
1442                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1443                         else:
1444                                 self.session.pipshown = False
1445                                 del self.session.pip
1446                         self.session.nav.playService(newservice)
1447
1448         def swapPiP(self):
1449                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1450                 if self.session.pip.servicePath:
1451                         servicepath = self.servicelist.getCurrentServicePath()
1452                         ref=servicepath[len(servicepath)-1]
1453                         pipref=self.session.pip.getCurrentService()
1454                         self.session.pip.playService(swapservice)
1455                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1456                         if pipref.toString() != ref.toString(): # is a subservice ?
1457                                 self.session.nav.stopService() # stop portal
1458                                 self.session.nav.playService(pipref) # start subservice
1459                         self.session.pip.servicePath=servicepath
1460
1461         def movePiP(self):
1462                 self.session.open(PiPSetup, pip = self.session.pip)
1463
1464         def pipDoHandle0Action(self):
1465                 use = config.usage.pip_zero_button.value
1466                 if "swap" == use:
1467                         self.swapPiP()
1468                 elif "swapstop" == use:
1469                         self.swapPiP()
1470                         self.showPiP()
1471                 elif "stop" == use:
1472                         self.showPiP()
1473
1474 from RecordTimer import parseEvent, RecordTimerEntry
1475
1476 class InfoBarInstantRecord:
1477         """Instant Record - handles the instantRecord action in order to
1478         start/stop instant records"""
1479         def __init__(self):
1480                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1481                         {
1482                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1483                         })
1484                 self.recording = []
1485
1486         def stopCurrentRecording(self, entry = -1):
1487                 if entry is not None and entry != -1:
1488                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1489                         self.recording.remove(self.recording[entry])
1490
1491         def startInstantRecording(self, limitEvent = False):
1492                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1493
1494                 # try to get event info
1495                 event = None
1496                 try:
1497                         service = self.session.nav.getCurrentService()
1498                         epg = eEPGCache.getInstance()
1499                         event = epg.lookupEventTime(serviceref, -1, 0)
1500                         if event is None:
1501                                 info = service.info()
1502                                 ev = info.getEvent(0)
1503                                 event = ev
1504                 except:
1505                         pass
1506
1507                 begin = int(time())
1508                 end = begin + 3600      # dummy
1509                 name = "instant record"
1510                 description = ""
1511                 eventid = None
1512
1513                 if event is not None:
1514                         curEvent = parseEvent(event)
1515                         name = curEvent[2]
1516                         description = curEvent[3]
1517                         eventid = curEvent[4]
1518                         if limitEvent:
1519                                 end = curEvent[1]
1520                 else:
1521                         if limitEvent:
1522                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1523
1524                 if isinstance(serviceref, eServiceReference):
1525                         serviceref = ServiceReference(serviceref)
1526
1527                 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1528                 recording.dontSave = True
1529
1530                 if event is None or limitEvent == False:
1531                         recording.autoincrease = True
1532                         recording.setAutoincreaseEnd()
1533
1534                 simulTimerList = self.session.nav.RecordTimer.record(recording)
1535
1536                 if simulTimerList is None:      # no conflict
1537                         self.recording.append(recording)
1538                 else:
1539                         if len(simulTimerList) > 1: # with other recording
1540                                 name = simulTimerList[1].name
1541                                 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1542                                 print "[TIMER] conflicts with", name_date
1543                                 recording.autoincrease = True   # start with max available length, then increment
1544                                 if recording.setAutoincreaseEnd():
1545                                         self.session.nav.RecordTimer.record(recording)
1546                                         self.recording.append(recording)
1547                                         self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1548                                 else:
1549                                         self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1550                         else:
1551                                 self.session.open(MessageBox, _("Couldn't record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
1552                         recording.autoincrease = False
1553
1554         def isInstantRecordRunning(self):
1555                 print "self.recording:", self.recording
1556                 if self.recording:
1557                         for x in self.recording:
1558                                 if x.isRunning():
1559                                         return True
1560                 return False
1561
1562         def recordQuestionCallback(self, answer):
1563                 print "pre:\n", self.recording
1564
1565                 if answer is None or answer[1] == "no":
1566                         return
1567                 list = []
1568                 recording = self.recording[:]
1569                 for x in recording:
1570                         if not x in self.session.nav.RecordTimer.timer_list:
1571                                 self.recording.remove(x)
1572                         elif x.dontSave and x.isRunning():
1573                                 list.append((x, False))
1574
1575                 if answer[1] == "changeduration":
1576                         if len(self.recording) == 1:
1577                                 self.changeDuration(0)
1578                         else:
1579                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1580                 elif answer[1] == "changeendtime":
1581                         if len(self.recording) == 1:
1582                                 self.setEndtime(0)
1583                         else:
1584                                 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1585                 elif answer[1] == "stop":
1586                         if len(self.recording) == 1:
1587                                 self.stopCurrentRecording(0)
1588                         else:
1589                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1590                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1591                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1592                         if answer[1] == "manualduration":
1593                                 self.changeDuration(len(self.recording)-1)
1594                         elif answer[1] == "manualendtime":
1595                                 self.setEndtime(len(self.recording)-1)
1596                 print "after:\n", self.recording
1597
1598         def setEndtime(self, entry):
1599                 if entry is not None and entry >= 0:
1600                         self.selectedEntry = entry
1601                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1602                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1603                         dlg.setTitle(_("Please change recording endtime"))
1604
1605         def TimeDateInputClosed(self, ret):
1606                 if len(ret) > 1:
1607                         if ret[0]:
1608                                 localendtime = localtime(ret[1])
1609                                 print "stopping recording at", strftime("%c", localendtime)
1610                                 if self.recording[self.selectedEntry].end != ret[1]:
1611                                         self.recording[self.selectedEntry].autoincrease = False
1612                                 self.recording[self.selectedEntry].end = ret[1]
1613                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1614
1615         def changeDuration(self, entry):
1616                 if entry is not None and entry >= 0:
1617                         self.selectedEntry = entry
1618                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1619
1620         def inputCallback(self, value):
1621                 if value is not None:
1622                         print "stopping recording after", int(value), "minutes."
1623                         entry = self.recording[self.selectedEntry]
1624                         if int(value) != 0:
1625                                 entry.autoincrease = False
1626                         entry.end = int(time()) + 60 * int(value)
1627                         self.session.nav.RecordTimer.timeChanged(entry)
1628
1629         def instantRecord(self):
1630                 dir = preferredInstantRecordPath()
1631                 if not dir or not fileExists(dir, 'w'):
1632                         dir = defaultMoviePath()
1633 #       ikseong
1634                 if not fileExists("/hdd", 0):
1635                         print "not found /hdd"
1636                         system("ln -s /media/hdd /hdd")
1637 #
1638                 try:
1639                         stat = os_stat(dir)
1640                 except:
1641                         # XXX: this message is a little odd as we might be recording to a remote device
1642                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1643                         return
1644
1645                 if self.isInstantRecordRunning():
1646                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1647                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1648                                 list=((_("stop recording"), "stop"), \
1649                                 (_("add recording (stop after current event)"), "event"), \
1650                                 (_("add recording (indefinitely)"), "indefinitely"), \
1651                                 (_("add recording (enter recording duration)"), "manualduration"), \
1652                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1653                                 (_("change recording (duration)"), "changeduration"), \
1654                                 (_("change recording (endtime)"), "changeendtime"), \
1655                                 (_("do nothing"), "no")))
1656                 else:
1657                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1658                                 title=_("Start recording?"), \
1659                                 list=((_("add recording (stop after current event)"), "event"), \
1660                                 (_("add recording (indefinitely)"), "indefinitely"), \
1661                                 (_("add recording (enter recording duration)"), "manualduration"), \
1662                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1663                                 (_("don't record"), "no")))
1664
1665 from Tools.ISO639 import LanguageCodes
1666
1667 class InfoBarAudioSelection:
1668         def __init__(self):
1669                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1670                         {
1671                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1672                         })
1673
1674         def audioSelection(self):
1675                 from Screens.AudioSelection import AudioSelection
1676                 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self)
1677                 
1678         def audioSelected(self, ret=None):
1679                 print "[infobar::audioSelected]", ret
1680
1681 class InfoBarSubserviceSelection:
1682         def __init__(self):
1683                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1684                         {
1685                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1686                         })
1687
1688                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1689                         {
1690                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1691                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1692                         }, -1)
1693                 self["SubserviceQuickzapAction"].setEnabled(False)
1694
1695                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1696                         {
1697                                 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1698                         })
1699
1700                 self.bsel = None
1701
1702         def checkSubservicesAvail(self):
1703                 service = self.session.nav.getCurrentService()
1704                 subservices = service and service.subServices()
1705                 if not subservices or subservices.getNumberOfSubservices() == 0:
1706                         self["SubserviceQuickzapAction"].setEnabled(False)
1707
1708         def nextSubservice(self):
1709                 self.changeSubservice(+1)
1710
1711         def prevSubservice(self):
1712                 self.changeSubservice(-1)
1713
1714         def changeSubservice(self, direction):
1715                 service = self.session.nav.getCurrentService()
1716                 subservices = service and service.subServices()
1717                 n = subservices and subservices.getNumberOfSubservices()
1718                 if n and n > 0:
1719                         selection = -1
1720                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1721                         idx = 0
1722                         while idx < n:
1723                                 if subservices.getSubservice(idx).toString() == ref.toString():
1724                                         selection = idx
1725                                         break
1726                                 idx += 1
1727                         if selection != -1:
1728                                 selection += direction
1729                                 if selection >= n:
1730                                         selection=0
1731                                 elif selection < 0:
1732                                         selection=n-1
1733                                 newservice = subservices.getSubservice(selection)
1734                                 if newservice.valid():
1735                                         del subservices
1736                                         del service
1737                                         self.session.nav.playService(newservice, False)
1738
1739         def subserviceSelection(self):
1740                 service = self.session.nav.getCurrentService()
1741                 subservices = service and service.subServices()
1742                 self.bouquets = self.servicelist.getBouquetList()
1743                 n = subservices and subservices.getNumberOfSubservices()
1744                 selection = 0
1745                 if n and n > 0:
1746                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1747                         tlist = []
1748                         idx = 0
1749                         while idx < n:
1750                                 i = subservices.getSubservice(idx)
1751                                 if i.toString() == ref.toString():
1752                                         selection = idx
1753                                 tlist.append((i.getName(), i))
1754                                 idx += 1
1755
1756                         if self.bouquets and len(self.bouquets):
1757                                 keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1758                                 if config.usage.multibouquet.value:
1759                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1760                                 else:
1761                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1762                                 selection += 3
1763                         else:
1764                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1765                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1766                                 selection += 2
1767
1768                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1769
1770         def subserviceSelected(self, service):
1771                 del self.bouquets
1772                 if not service is None:
1773                         if isinstance(service[1], str):
1774                                 if service[1] == "quickzap":
1775                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1776                                         self.session.open(SubservicesQuickzap, service[2])
1777                         else:
1778                                 self["SubserviceQuickzapAction"].setEnabled(True)
1779                                 self.session.nav.playService(service[1], False)
1780
1781         def addSubserviceToBouquetCallback(self, service):
1782                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1783                         self.selectedSubservice = service
1784                         if self.bouquets is None:
1785                                 cnt = 0
1786                         else:
1787                                 cnt = len(self.bouquets)
1788                         if cnt > 1: # show bouquet list
1789                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1790                         elif cnt == 1: # add to only one existing bouquet
1791                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1792                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1793
1794         def bouquetSelClosed(self, confirmed):
1795                 self.bsel = None
1796                 del self.selectedSubservice
1797                 if confirmed:
1798                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1799
1800         def addSubserviceToBouquet(self, dest):
1801                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1802                 if self.bsel:
1803                         self.bsel.close(True)
1804                 else:
1805                         del self.selectedSubservice
1806
1807 class InfoBarAdditionalInfo:
1808         def __init__(self):
1809
1810                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1811                 self["TimeshiftPossible"] = self["RecordingPossible"]
1812                 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
1813                 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
1814                 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1815                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1816
1817 class InfoBarNotifications:
1818         def __init__(self):
1819                 self.onExecBegin.append(self.checkNotifications)
1820                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1821                 self.onClose.append(self.__removeNotification)
1822
1823         def __removeNotification(self):
1824                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1825
1826         def checkNotificationsIfExecing(self):
1827                 if self.execing:
1828                         self.checkNotifications()
1829
1830         def checkNotifications(self):
1831                 notifications = Notifications.notifications
1832                 if notifications:
1833                         n = notifications[0]
1834
1835                         del notifications[0]
1836                         cb = n[0]
1837
1838                         if n[3].has_key("onSessionOpenCallback"):
1839                                 n[3]["onSessionOpenCallback"]()
1840                                 del n[3]["onSessionOpenCallback"]
1841
1842                         if cb is not None:
1843                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1844                         else:
1845                                 dlg = self.session.open(n[1], *n[2], **n[3])
1846
1847                         # remember that this notification is currently active
1848                         d = (n[4], dlg)
1849                         Notifications.current_notifications.append(d)
1850                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1851
1852         def __notificationClosed(self, d):
1853                 Notifications.current_notifications.remove(d)
1854
1855 class InfoBarServiceNotifications:
1856         def __init__(self):
1857                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1858                         {
1859                                 iPlayableService.evEnd: self.serviceHasEnded
1860                         })
1861
1862         def serviceHasEnded(self):
1863                 print "service end!"
1864
1865                 try:
1866                         self.setSeekState(self.SEEK_STATE_PLAY)
1867                 except:
1868                         pass
1869
1870 class InfoBarCueSheetSupport:
1871         CUT_TYPE_IN = 0
1872         CUT_TYPE_OUT = 1
1873         CUT_TYPE_MARK = 2
1874         CUT_TYPE_LAST = 3
1875
1876         ENABLE_RESUME_SUPPORT = False
1877
1878         def __init__(self, actionmap = "InfobarCueSheetActions"):
1879                 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1880                         {
1881                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1882                                 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1883                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1884                         }, prio=1)
1885
1886                 self.cut_list = [ ]
1887                 self.is_closing = False
1888                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1889                         {
1890                                 iPlayableService.evStart: self.__serviceStarted,
1891                         })
1892
1893         def __serviceStarted(self):
1894                 if self.is_closing:
1895                         return
1896                 print "new service started! trying to download cuts!"
1897                 self.downloadCuesheet()
1898
1899                 if self.ENABLE_RESUME_SUPPORT:
1900                         last = None
1901
1902                         for (pts, what) in self.cut_list:
1903                                 if what == self.CUT_TYPE_LAST:
1904                                         last = pts
1905
1906                         if last is not None:
1907                                 self.resume_point = last
1908                                 
1909                                 l = last / 90000
1910                                 if config.usage.on_movie_start.value == "ask":
1911                                         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)
1912                                 elif config.usage.on_movie_start.value == "resume":
1913 # TRANSLATORS: The string "Resuming playback" flashes for a moment
1914 # TRANSLATORS: at the start of a movie, when the user has selected
1915 # TRANSLATORS: "Resume from last position" as start behavior.
1916 # TRANSLATORS: The purpose is to notify the user that the movie starts
1917 # TRANSLATORS: in the middle somewhere and not from the beginning.
1918 # TRANSLATORS: (Some translators seem to have interpreted it as a
1919 # TRANSLATORS: question or a choice, but it is a statement.)
1920                                         Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
1921
1922         def playLastCB(self, answer):
1923                 if answer == True:
1924                         self.doSeek(self.resume_point)
1925                 self.hideAfterResume()
1926
1927         def hideAfterResume(self):
1928                 if isinstance(self, InfoBarShowHide):
1929                         self.hide()
1930
1931         def __getSeekable(self):
1932                 service = self.session.nav.getCurrentService()
1933                 if service is None:
1934                         return None
1935                 return service.seek()
1936
1937         def cueGetCurrentPosition(self):
1938                 seek = self.__getSeekable()
1939                 if seek is None:
1940                         return None
1941                 r = seek.getPlayPosition()
1942                 if r[0]:
1943                         return None
1944                 return long(r[1])
1945
1946         def cueGetEndCutPosition(self):
1947                 ret = False
1948                 isin = True
1949                 for cp in self.cut_list:
1950                         if cp[1] == self.CUT_TYPE_OUT:
1951                                 if isin:
1952                                         isin = False
1953                                         ret = cp[0]
1954                         elif cp[1] == self.CUT_TYPE_IN:
1955                                 isin = True
1956                 return ret
1957                 
1958         def jumpPreviousNextMark(self, cmp, start=False):
1959                 current_pos = self.cueGetCurrentPosition()
1960                 if current_pos is None:
1961                         return False
1962                 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
1963                 if mark is not None:
1964                         pts = mark[0]
1965                 else:
1966                         return False
1967
1968                 self.doSeek(pts)
1969                 return True
1970
1971         def jumpPreviousMark(self):
1972                 # we add 5 seconds, so if the play position is <5s after
1973                 # the mark, the mark before will be used
1974                 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
1975
1976         def jumpNextMark(self):
1977                 if not self.jumpPreviousNextMark(lambda x: x-90000):
1978                         self.doSeek(-1)
1979
1980         def getNearestCutPoint(self, pts, cmp=abs, start=False):
1981                 # can be optimized
1982                 beforecut = True
1983                 nearest = None
1984                 bestdiff = -1
1985                 instate = True
1986                 if start:
1987                         bestdiff = cmp(0 - pts)
1988                         if bestdiff >= 0:
1989                                 nearest = [0, False]
1990                 for cp in self.cut_list:
1991                         if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
1992                                 beforecut = False
1993                                 if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
1994                                         diff = cmp(cp[0] - pts)
1995                                         if start and diff >= 0:
1996                                                 nearest = cp
1997                                                 bestdiff = diff
1998                                         else:
1999                                                 nearest = None
2000                                                 bestdiff = -1
2001                         if cp[1] == self.CUT_TYPE_IN:
2002                                 instate = True
2003                         elif cp[1] == self.CUT_TYPE_OUT:
2004                                 instate = False
2005                         elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2006                                 diff = cmp(cp[0] - pts)
2007                                 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2008                                         nearest = cp
2009                                         bestdiff = diff
2010                 return nearest
2011
2012         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2013                 current_pos = self.cueGetCurrentPosition()
2014                 if current_pos is None:
2015                         print "not seekable"
2016                         return
2017
2018                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2019
2020                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2021                         if onlyreturn:
2022                                 return nearest_cutpoint
2023                         if not onlyadd:
2024                                 self.removeMark(nearest_cutpoint)
2025                 elif not onlyremove and not onlyreturn:
2026                         self.addMark((current_pos, self.CUT_TYPE_MARK))
2027
2028                 if onlyreturn:
2029                         return None
2030
2031         def addMark(self, point):
2032                 insort(self.cut_list, point)
2033                 self.uploadCuesheet()
2034                 self.showAfterCuesheetOperation()
2035
2036         def removeMark(self, point):
2037                 self.cut_list.remove(point)
2038                 self.uploadCuesheet()
2039                 self.showAfterCuesheetOperation()
2040
2041         def showAfterCuesheetOperation(self):
2042                 if isinstance(self, InfoBarShowHide):
2043                         self.doShow()
2044
2045         def __getCuesheet(self):
2046                 service = self.session.nav.getCurrentService()
2047                 if service is None:
2048                         return None
2049                 return service.cueSheet()
2050
2051         def uploadCuesheet(self):
2052                 cue = self.__getCuesheet()
2053
2054                 if cue is None:
2055                         print "upload failed, no cuesheet interface"
2056                         return
2057                 cue.setCutList(self.cut_list)
2058
2059         def downloadCuesheet(self):
2060                 cue = self.__getCuesheet()
2061
2062                 if cue is None:
2063                         print "download failed, no cuesheet interface"
2064                         self.cut_list = [ ]
2065                 else:
2066                         self.cut_list = cue.getCutList()
2067
2068 class InfoBarSummary(Screen):
2069         skin = """
2070         <screen position="0,0" size="132,64">
2071                 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2072                         <convert type="ClockToText">WithSeconds</convert>
2073                 </widget>
2074                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2075                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2076                         <convert type="ConditionalShowHide">Blink</convert>
2077                 </widget>
2078                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2079                         <convert type="ServiceName">Name</convert>
2080                 </widget>
2081                 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2082                         <convert type="EventTime">Progress</convert>
2083                 </widget>
2084         </screen>"""
2085
2086 # for picon:  (path="piconlcd" will use LCD picons)
2087 #               <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2088 #                       <convert type="ServiceName">Reference</convert>
2089 #               </widget>
2090
2091 class InfoBarSummarySupport:
2092         def __init__(self):
2093                 pass
2094
2095         def createSummary(self):
2096                 return InfoBarSummary
2097
2098 class InfoBarMoviePlayerSummary(Screen):
2099         skin = """
2100         <screen position="0,0" size="132,64">
2101                 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2102                         <convert type="ClockToText">WithSeconds</convert>
2103                 </widget>
2104                 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2105                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2106                         <convert type="ConditionalShowHide">Blink</convert>
2107                 </widget>
2108                 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2109                         <convert type="ServiceName">Name</convert>
2110                 </widget>
2111                 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2112                         <convert type="ServicePosition">Position</convert>
2113                 </widget>
2114         </screen>"""
2115
2116 class InfoBarMoviePlayerSummarySupport:
2117         def __init__(self):
2118                 pass
2119
2120         def createSummary(self):
2121                 return InfoBarMoviePlayerSummary
2122
2123 class InfoBarTeletextPlugin:
2124         def __init__(self):
2125                 self.teletext_plugin = None
2126
2127                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2128                         self.teletext_plugin = p
2129
2130                 if self.teletext_plugin is not None:
2131                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2132                                 {
2133                                         "startTeletext": (self.startTeletext, _("View teletext..."))
2134                                 })
2135                 else:
2136                         print "no teletext plugin found!"
2137
2138         def startTeletext(self):
2139                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2140
2141 class InfoBarSubtitleSupport(object):
2142         def __init__(self):
2143                 object.__init__(self)
2144                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2145                 self.__subtitles_enabled = False
2146
2147                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2148                         {
2149                                 iPlayableService.evEnd: self.__serviceStopped,
2150                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
2151                         })
2152                 self.cached_subtitle_checked = False
2153                 self.__selected_subtitle = None
2154
2155         def __serviceStopped(self):
2156                 self.cached_subtitle_checked = False
2157                 if self.__subtitles_enabled:
2158                         self.subtitle_window.hide()
2159                         self.__subtitles_enabled = False
2160                         self.__selected_subtitle = None
2161
2162         def __updatedInfo(self):
2163                 if not self.cached_subtitle_checked:
2164                         self.cached_subtitle_checked = True
2165                         subtitle = self.getCurrentServiceSubtitle()
2166                         self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2167                         if self.__selected_subtitle:
2168                                 self.setSubtitlesEnable(True)
2169
2170         def getCurrentServiceSubtitle(self):
2171                 service = self.session.nav.getCurrentService()
2172                 return service and service.subtitle()
2173
2174         def setSubtitlesEnable(self, enable=True):
2175                 subtitle = self.getCurrentServiceSubtitle()
2176                 if enable:
2177                         if self.__selected_subtitle:
2178                                 if subtitle and not self.__subtitles_enabled:
2179                                         subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2180                                         self.subtitle_window.show()
2181                                         self.__subtitles_enabled = True
2182                 else:
2183                         if subtitle:
2184                                 subtitle.disableSubtitles(self.subtitle_window.instance)
2185                         self.__selected_subtitle = False
2186                         self.__subtitles_enabled = False
2187                         self.subtitle_window.hide()
2188
2189         def setSelectedSubtitle(self, subtitle):
2190                 self.__selected_subtitle = subtitle
2191
2192         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2193         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2194
2195 class InfoBarServiceErrorPopupSupport:
2196         def __init__(self):
2197                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2198                         {
2199                                 iPlayableService.evTuneFailed: self.__tuneFailed,
2200                                 iPlayableService.evStart: self.__serviceStarted
2201                         })
2202                 self.__serviceStarted()
2203
2204         def __serviceStarted(self):
2205                 self.last_error = None
2206                 Notifications.RemovePopup(id = "ZapError")
2207
2208         def __tuneFailed(self):
2209                 service = self.session.nav.getCurrentService()
2210                 info = service and service.info()
2211                 error = info and info.getInfo(iServiceInformation.sDVBState)
2212
2213                 if error == self.last_error:
2214                         error = None
2215                 else:
2216                         self.last_error = error
2217
2218                 error = {
2219                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2220                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2221                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2222                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2223                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2224                         eDVBServicePMTHandler.eventNewProgramInfo: None,
2225                         eDVBServicePMTHandler.eventTuned: None,
2226                         eDVBServicePMTHandler.eventSOF: None,
2227                         eDVBServicePMTHandler.eventEOF: None,
2228                         eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2229                 }.get(error) #this returns None when the key not exist in the dict
2230
2231                 if error is not None:
2232                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2233                 else:
2234                         Notifications.RemovePopup(id = "ZapError")