implement often wished feature :)
[enigma2.git] / lib / python / Screens / InfoBarGenerics.py
1 from Screen import Screen
2 from Components.ActionMap import ActionMap, HelpableActionMap
3 from Components.ActionMap import NumberActionMap
4 from Components.Label import *
5 from Components.ProgressBar import *
6 from Components.config import configfile, configsequencearg
7 from Components.config import config, configElement, ConfigSubsection, configSequence
8 from ChannelSelection import ChannelSelection
9
10 from Components.Pixmap import Pixmap, PixmapConditional
11 from Components.BlinkingPixmap import BlinkingPixmapConditional
12 from Components.ServiceName import ServiceName
13 from Components.EventInfo import EventInfo
14
15 from ServiceReference import ServiceReference
16 from EpgSelection import EPGSelection
17
18 from Screens.MessageBox import MessageBox
19 from Screens.Volume import Volume
20 from Screens.Mute import Mute
21 from Screens.Dish import Dish
22 from Screens.Standby import Standby
23 from Screens.EventView import EventView
24 from Components.Harddisk import harddiskmanager
25
26 from Tools import Notifications
27
28 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
29 from enigma import *
30
31 import time
32 import os
33
34 # hack alert!
35 from Menu import MainMenu, mdom
36
37 class InfoBarVolumeControl:
38         """Volume control, handles volUp, volDown, volMute actions and display 
39         a corresponding dialog"""
40         def __init__(self):
41                 config.audio = ConfigSubsection()
42                 config.audio.volume = configElement("config.audio.volume", configSequence, [5], configsequencearg.get("INTEGER", (0, 100)))
43
44                 self["VolumeActions"] = ActionMap( ["InfobarVolumeActions"] ,
45                         {
46                                 "volumeUp": self.volUp,
47                                 "volumeDown": self.volDown,
48                                 "volumeMute": self.volMute,
49                         })
50
51                 self.volumeDialog = self.session.instantiateDialog(Volume)
52                 self.muteDialog = self.session.instantiateDialog(Mute)
53
54                 self.hideVolTimer = eTimer()
55                 self.hideVolTimer.timeout.get().append(self.volHide)
56
57                 vol = config.audio.volume.value[0]
58                 self.volumeDialog.setValue(vol)
59                 eDVBVolumecontrol.getInstance().setVolume(vol, vol)
60         
61         def volSave(self):
62                 config.audio.volume.value = eDVBVolumecontrol.getInstance().getVolume()
63                 config.audio.volume.save()
64                 
65         def     volUp(self):
66                 if (eDVBVolumecontrol.getInstance().isMuted()):
67                         self.volMute()
68                 eDVBVolumecontrol.getInstance().volumeUp()
69                 self.volumeDialog.instance.show()
70                 self.volumeDialog.setValue(eDVBVolumecontrol.getInstance().getVolume())
71                 self.volSave()
72                 self.hideVolTimer.start(3000)
73
74         def     volDown(self):
75                 if (eDVBVolumecontrol.getInstance().isMuted()):
76                         self.volMute()
77                 eDVBVolumecontrol.getInstance().volumeDown()
78                 self.volumeDialog.instance.show()
79                 self.volumeDialog.setValue(eDVBVolumecontrol.getInstance().getVolume())
80                 self.volSave()
81                 self.hideVolTimer.start(3000)
82                 
83         def volHide(self):
84                 self.volumeDialog.instance.hide()
85
86         def     volMute(self):
87                 eDVBVolumecontrol.getInstance().volumeToggleMute()
88                 self.volumeDialog.setValue(eDVBVolumecontrol.getInstance().getVolume())
89                 
90                 if (eDVBVolumecontrol.getInstance().isMuted()):
91                         self.muteDialog.instance.show()
92                 else:
93                         self.muteDialog.instance.hide()
94
95 class InfoBarDish:
96         def __init__(self):
97                 self.dishDialog = self.session.instantiateDialog(Dish)
98                 self.onShown.append(self.dishDialog.instance.hide)
99
100 class InfoBarShowHide:
101         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
102         fancy animations. """
103         STATE_HIDDEN = 0
104         STATE_HIDING = 1
105         STATE_SHOWING = 2
106         STATE_SHOWN = 3
107         
108         def __init__(self):
109                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
110                         {
111                                 "toggleShow": self.toggleShow,
112                                 "hide": self.hide,
113                         })
114
115                 self.state = self.STATE_SHOWN
116                 
117                 self.onExecBegin.append(self.show)
118                 self.onClose.append(self.delHideTimer)
119                 
120                 self.hideTimer = eTimer()
121                 self.hideTimer.timeout.get().append(self.doTimerHide)
122                 self.hideTimer.start(5000)
123
124         def delHideTimer(self):
125                 del self.hideTimer
126
127         def hide(self): 
128                 self.instance.hide()
129                 
130         def show(self):
131                 self.state = self.STATE_SHOWN
132                 self.hideTimer.stop()
133                 self.hideTimer.start(5000)
134
135         def doTimerHide(self):
136                 self.hideTimer.stop()
137                 if self.state == self.STATE_SHOWN:
138                         self.instance.hide()
139                         self.state = self.STATE_HIDDEN
140
141         def toggleShow(self):
142                 if self.state == self.STATE_SHOWN:
143                         self.instance.hide()
144                         #pls check animation support, sorry
145 #                       self.startHide()
146                         self.hideTimer.stop()
147                         self.state = self.STATE_HIDDEN
148                 elif self.state == self.STATE_HIDDEN:
149                         self.instance.show()
150                         self.show()
151                         
152         def startShow(self):
153                 self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
154                 self.state = self.STATE_SHOWN
155         
156         def startHide(self):
157                 self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
158                 self.state = self.STATE_HIDDEN
159
160 class NumberZap(Screen):
161         def quit(self):
162                 self.Timer.stop()
163                 self.close(0)
164
165         def keyOK(self):
166                 self.Timer.stop()
167                 self.close(int(self["number"].getText()))
168
169         def keyNumberGlobal(self, number):
170                 self.Timer.start(3000)          #reset timer
171                 self.field = self.field + str(number)
172                 self["number"].setText(self.field)
173                 if len(self.field) >= 4:
174                         self.keyOK()
175
176         def __init__(self, session, number):
177                 Screen.__init__(self, session)
178                 self.field = str(number)
179
180                 self["channel"] = Label(_("Channel:"))
181
182                 self["number"] = Label(self.field)
183
184                 self["actions"] = NumberActionMap( [ "SetupActions" ], 
185                         {
186                                 "cancel": self.quit,
187                                 "ok": self.keyOK,
188                                 "1": self.keyNumberGlobal,
189                                 "2": self.keyNumberGlobal,
190                                 "3": self.keyNumberGlobal,
191                                 "4": self.keyNumberGlobal,
192                                 "5": self.keyNumberGlobal,
193                                 "6": self.keyNumberGlobal,
194                                 "7": self.keyNumberGlobal,
195                                 "8": self.keyNumberGlobal,
196                                 "9": self.keyNumberGlobal,
197                                 "0": self.keyNumberGlobal
198                         })
199
200                 self.Timer = eTimer()
201                 self.Timer.timeout.get().append(self.keyOK)
202                 self.Timer.start(3000)
203
204 class InfoBarPowerKey:
205         """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
206         
207         def __init__(self):
208                 self.powerKeyTimer = eTimer()
209                 self.powerKeyTimer.timeout.get().append(self.powertimer)
210                 self["PowerKeyActions"] = HelpableActionMap(self, "PowerKeyActions",
211                         {
212                                 "powerdown": self.powerdown,
213                                 "powerup": self.powerup,
214                                 "discreteStandby": (self.standby, "Go standby"),
215                                 "discretePowerOff": (self.quit, "Go to deep standby"),
216                         })
217
218         def powertimer(self):   
219                 print "PowerOff - Now!"
220                 self.quit()
221         
222         def powerdown(self):
223                 self.standbyblocked = 0
224                 self.powerKeyTimer.start(3000)
225
226         def powerup(self):
227                 self.powerKeyTimer.stop()
228                 if self.standbyblocked == 0:
229                         self.standbyblocked = 1
230                         self.standby()
231
232         def standby(self):
233                 self.session.open(Standby, self)
234
235         def quit(self):
236                 # halt
237                 quitMainloop(1)
238
239 class InfoBarNumberZap:
240         """ Handles an initial number for NumberZapping """
241         def __init__(self):
242                 self["NumberZapActions"] = NumberActionMap( [ "NumberZapActions"],
243                         {
244                                 "1": self.keyNumberGlobal,
245                                 "2": self.keyNumberGlobal,
246                                 "3": self.keyNumberGlobal,
247                                 "4": self.keyNumberGlobal,
248                                 "5": self.keyNumberGlobal,
249                                 "6": self.keyNumberGlobal,
250                                 "7": self.keyNumberGlobal,
251                                 "8": self.keyNumberGlobal,
252                                 "9": self.keyNumberGlobal,
253                                 "0": self.keyNumberGlobal,
254                         })
255
256         def keyNumberGlobal(self, number):
257 #               print "You pressed number " + str(number)
258                 self.session.openWithCallback(self.numberEntered, NumberZap, number)
259
260         def numberEntered(self, retval):
261 #               print self.servicelist
262                 if retval > 0:
263                         self.zapToNumber(retval)
264
265         def searchNumberHelper(self, serviceHandler, num, bouquet):
266                 servicelist = serviceHandler.list(bouquet)
267                 if not servicelist is None:
268                         while num:
269                                 serviceIterator = servicelist.getNext()
270                                 if not serviceIterator.valid(): #check end of list
271                                         break
272                                 if serviceIterator.flags: #assume normal dvb service have no flags set
273                                         continue
274                                 num -= 1;
275                         if not num: #found service with searched number ?
276                                 return serviceIterator, 0
277                 return None, num
278
279         def zapToNumber(self, number):
280                 bouquet = self.servicelist.bouquet_root
281                 service = None
282                 serviceHandler = eServiceCenter.getInstance()
283                 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
284                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
285                 else:
286                         bouquetlist = serviceHandler.list(bouquet)
287                         if not bouquetlist is None:
288                                 while number:
289                                         bouquet = bouquetlist.getNext()
290                                         if not bouquet.valid(): #check end of list
291                                                 break
292                                         if ((bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory):
293                                                 continue
294                                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
295                 if not service is None:
296                         self.session.nav.playService(service) #play service
297                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
298                                 self.servicelist.setRoot(bouquet)
299                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
300
301 class InfoBarChannelSelection:
302         """ ChannelSelection - handles the channelSelection dialog and the initial 
303         channelChange actions which open the channelSelection dialog """
304         def __init__(self):
305                 #instantiate forever
306                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
307
308                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
309                         {
310                                 "switchChannelUp": self.switchChannelUp,
311                                 "switchChannelDown": self.switchChannelDown,
312                                 "zapUp": (self.zapUp, _("next channel")),
313                                 "zapDown": (self.zapDown, _("previous channel")),
314                         })
315                         
316         def switchChannelUp(self):      
317                 self.servicelist.moveUp()
318                 self.session.execDialog(self.servicelist)
319
320         def switchChannelDown(self):    
321                 self.servicelist.moveDown()
322                 self.session.execDialog(self.servicelist)
323
324         def     zapUp(self):
325                 self.servicelist.moveUp()
326                 self.servicelist.zap()
327                 self.instance.show()
328                 self.show()
329
330         def     zapDown(self):
331                 self.servicelist.moveDown()
332                 self.servicelist.zap()
333                 self.instance.show()
334                 self.show()
335                 
336 class InfoBarMenu:
337         """ Handles a menu action, to open the (main) menu """
338         def __init__(self):
339                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions", 
340                         {
341                                 "mainMenu": (self.mainMenu, "Enter main menu..."),
342                         })
343
344         def mainMenu(self):
345                 print "loading mainmenu XML..."
346                 menu = mdom.childNodes[0]
347                 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
348                 self.session.open(MainMenu, menu, menu.childNodes)
349
350 class InfoBarEPG:
351         """ EPG - Opens an EPG list when the showEPGList action fires """
352         def __init__(self):
353                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
354                         {
355                                 "showEPGList": (self.showEPGList, _("show EPG...")),
356                         })
357
358         def showEPGList(self):
359                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
360                 ptr=eEPGCache.getInstance()
361                 if ptr.startTimeQuery(ref) != -1:
362                         self.session.open(EPGSelection, ref)
363                 else: # try to show now/next
364                         print 'no epg for service', ref.toString()
365                         try:
366                                 self.epglist = [ ]
367                                 service = self.session.nav.getCurrentService()
368                                 info = service.info()
369                                 ptr=info.getEvent(0)
370                                 if ptr:
371                                         self.epglist.append(ptr)
372                                 ptr=info.getEvent(1)
373                                 if ptr:
374                                         self.epglist.append(ptr)
375                                 if len(self.epglist) > 0:
376                                         self.session.open(EventView, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
377                         except:
378                                 pass
379
380         def eventViewCallback(self, setEvent, val): #used for now/next displaying
381                 if len(self.epglist) > 1:
382                         tmp = self.epglist[0]
383                         self.epglist[0]=self.epglist[1]
384                         self.epglist[1]=tmp
385                         setEvent(self.epglist[0])
386
387 from math import log
388
389 class InfoBarTuner:
390         """provides a snr/agc/ber display"""
391         def __init__(self):
392                 self["snr"] = Label()
393                 self["agc"] = Label()
394                 self["ber"] = Label()
395                 self["snr_percent"] = Label()
396                 self["agc_percent"] = Label()
397                 self["ber_count"] = Label()
398                 self["snr_progress"] = ProgressBar()
399                 self["agc_progress"] = ProgressBar()
400                 self["ber_progress"] = ProgressBar()
401                 self.timer = eTimer()
402                 self.timer.timeout.get().append(self.updateTunerInfo)
403                 self.timer.start(500)
404
405         def log2(self,val):
406                 if not val:
407                         return 0
408                 return (long)(log(val)/log(2))
409
410         def updateTunerInfo(self):
411                 if self.instance.isVisible():
412                         service = self.session.nav.getCurrentService()
413                         snr=0
414                         agc=0
415                         ber=0
416                         if service is not None:
417                                 feinfo = service.frontendStatusInfo()
418                                 if feinfo is not None:
419                                         ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
420                                         snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
421                                         agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
422                         self["snr_percent"].setText("%d%%"%(snr))
423                         self["agc_percent"].setText("%d%%"%(agc))
424                         self["ber_count"].setText("%d"%(ber))
425                         self["snr_progress"].setValue(snr)
426                         self["agc_progress"].setValue(agc)
427                         self["ber_progress"].setValue(self.log2(ber))
428
429 class InfoBarEvent:
430         """provides a current/next event info display"""
431         def __init__(self):
432                 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
433                 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
434                                 
435                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
436                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
437
438                 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Duration)
439                 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
440
441 class InfoBarServiceName:
442         def __init__(self):
443                 self["ServiceName"] = ServiceName(self.session.nav)
444
445 class InfoBarPVR:
446         """handles PVR specific actions like seeking, pause"""
447         def __init__(self):
448                 self["PVRActions"] = HelpableActionMap(self, "InfobarPVRActions", 
449                         {
450                                 "pauseService": (self.pauseService, "pause"),
451                                 "unPauseService": (self.unPauseService, "continue"),
452                                 
453                                 "seekFwd": (self.seekFwd, "skip forward"),
454                                 "seekBack": (self.seekBack, "skip backward"),
455                         })
456                 
457         def pauseService(self):
458                 self.session.nav.pause(1)
459                 
460         def unPauseService(self):
461                 self.session.nav.pause(0)
462         
463         def doSeek(self, dir, seektime):
464                 service = self.session.nav.getCurrentService()
465                 if service is None:
466                         return
467                 
468                 seekable = service.seek()
469                 if seekable is None:
470                         return
471                 seekable.seekRelative(dir, 90 * seektime)
472
473         def seekFwd(self):
474                 self.doSeek(+1, 60000)
475         
476         def seekBack(self):
477                 self.doSeek(-1, 60000)
478
479 class InfoBarInstantRecord:
480         """Instant Record - handles the instantRecord action in order to 
481         start/stop instant records"""
482         def __init__(self):
483                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
484                         {
485                                 "instantRecord": (self.instantRecord, "Instant Record..."),
486                         })
487                 self.recording = None
488                 
489                 self["BlinkingPoint"] = BlinkingPixmapConditional()
490                 self.onShown.append(self["BlinkingPoint"].hideWidget)
491                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
492                 
493         def stopCurrentRecording(self): 
494                 self.session.nav.RecordTimer.removeEntry(self.recording)
495                 self.recording = None
496                         
497         def startInstantRecording(self):
498                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
499                         
500                 # try to get event info
501                 epg = None
502                 try:
503                         service = self.session.nav.getCurrentService()
504                         info = service.info()
505                         ev = info.getEvent(0)
506                         epg = ev
507                 except:
508                         pass
509                 
510                 # fix me, description. 
511                 self.recording = self.session.nav.recordWithTimer(time.time(), time.time() + 3600, serviceref, epg, "instant record")
512                 self.recording.dontSave = True
513                 
514                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
515                 
516         def isInstantRecordRunning(self):
517                 if self.recording != None:
518                         if self.recording.isRunning():
519                                 return True
520                 return False
521
522         def recordQuestionCallback(self, answer):
523                 if answer == False:
524                         return
525                 
526                 if self.isInstantRecordRunning():
527                         self.stopCurrentRecording()
528                 else:
529                         self.startInstantRecording()
530
531         def instantRecord(self):
532                 try:
533                         stat = os.stat("/hdd/movies")
534                 except:
535                         self.session.open(MessageBox, "No HDD found!")
536                         return
537         
538                 if self.isInstantRecordRunning():
539                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
540                 else:
541                         self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
542
543 from Screens.AudioSelection import AudioSelection
544
545 class InfoBarAudioSelection:
546         def __init__(self):
547                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
548                         {
549                                 "audioSelection": (self.audioSelection, "Audio Options..."),
550                         })
551
552         def audioSelection(self):
553                 service = self.session.nav.getCurrentService()
554                 audio = service.audioTracks()
555                 n = audio.getNumberOfTracks()
556                 if n > 0:
557                         self.session.open(AudioSelection, audio)
558
559 from Screens.SubserviceSelection import SubserviceSelection
560
561 class InfoBarSubserviceSelection:
562         def __init__(self):
563                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
564                         {
565                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
566                         })
567
568         def subserviceSelection(self):
569                 service = self.session.nav.getCurrentService()
570                 subservices = service.subServices()
571                 n = subservices.getNumberOfSubservices()
572                 if n > 0:
573                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
574
575         def subserviceSelected(self, service):
576                 if not service is None:
577                         self.session.nav.playService(service)
578
579 class InfoBarAdditionalInfo:
580         def __init__(self):
581                 self["DolbyActive"] = PixmapConditional()
582                 # TODO: get the info from c++ somehow
583                 self["DolbyActive"].setConnect(lambda: False)
584                 
585                 self["CryptActive"] = PixmapConditional()
586                 # TODO: get the info from c++ somehow
587                 self["CryptActive"].setConnect(lambda: False)
588                 
589                 self["FormatActive"] = PixmapConditional()
590                 # TODO: get the info from c++ somehow
591                 self["FormatActive"].setConnect(lambda: False)
592                 
593                 self["ButtonRed"] = PixmapConditional(withTimer = False)
594                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
595                 self.onShown.append(self["ButtonRed"].update)
596                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
597                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
598                 self.onShown.append(self["ButtonRedText"].update)
599                                 
600                 self["ButtonGreen"] = PixmapConditional()
601                 self["ButtonGreen"].setConnect(lambda: self.session.nav.getCurrentService().subServices().getNumberOfSubservices() > 0)
602                 self["ButtonGreenText"] = LabelConditional(text = _("Subservices"))
603                 self["ButtonGreenText"].setConnect(lambda: self.session.nav.getCurrentService().subServices().getNumberOfSubservices() > 0)
604
605                 self["ButtonYellow"] = PixmapConditional()
606                 self["ButtonYellow"].setConnect(lambda: False)
607
608                 self["ButtonBlue"] = PixmapConditional()
609                 self["ButtonBlue"].setConnect(lambda: False)
610
611 class InfoBarNotifications:
612         def __init__(self):
613                 self.onExecBegin.append(self.checkNotifications)
614                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
615         
616         def checkNotificationsIfExecing(self):
617                 if self.execing:
618                         self.checkNotifications()
619
620         def checkNotifications(self):
621                 if len(Notifications.notifications):
622                         n = Notifications.notifications[0]
623                         Notifications.notifications = Notifications.notifications[1:]
624                         print "open",n
625                         cb = n[0]
626                         if cb is not None:
627                                 self.session.openWithCallback(cb, *n[1:])
628                         else:
629                                 self.session.open(*n[1:])