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