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