Add QuadPiP plugin.
[vuplus_dvbapp] / lib / python / Plugins / Extensions / QuadPiP / qpip.py
1 from Screens.Screen import Screen
2 from Components.ConfigList import ConfigListScreen
3 from Components.config import config, ConfigSubsection, ConfigSelection, getConfigListEntry
4 from Components.ActionMap import ActionMap
5 from Components.Pixmap import Pixmap, MovingPixmap
6 from Screens.MessageBox import MessageBox
7 from Components.Sources.StaticText import StaticText
8 from Plugins.Plugin import PluginDescriptor
9 from Tools.Directories import fileExists
10 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
11
12 from Components.Sources.List import List
13 from Components.Label import Label
14 from Components.ActionMap import HelpableActionMap
15 from Components.MenuList import MenuList
16
17 from Screens.ChannelSelection import ChannelSelectionBase
18 from enigma import eServiceReference
19 from enigma import eListboxPythonMultiContent
20 from enigma import eTimer
21 from ServiceReference import ServiceReference
22 from Components.FileList import FileList
23 from Components.Button import Button
24 from Screens.ChoiceBox import ChoiceBox
25 from Screens.QuadPiP import QuadPiP
26
27 from Screens.VirtualKeyBoard import VirtualKeyBoard
28 from Screens.HelpMenu import HelpableScreen
29
30 import pickle
31 import os
32
33 from Components.config import config, ConfigSubsection, ConfigNumber
34 from Components.Slider import Slider
35
36 from Components.SystemInfo import SystemInfo
37
38 config.plugins.quadpip = ConfigSubsection()
39 config.plugins.quadpip.lastchannel = ConfigNumber(default = 1)
40
41 ENABLE_QPIP_PROCPATH = "/proc/stb/video/decodermode"
42
43 def setDecoderMode(value):
44         if os.access(ENABLE_QPIP_PROCPATH, os.F_OK):
45                 fd = open(ENABLE_QPIP_PROCPATH,"w")
46                 fd.write(value)
47                 fd.close()
48
49                 # read to check
50                 fd = open(ENABLE_QPIP_PROCPATH,"r")
51                 data = fd.read()
52                 fd.close()
53
54                 return data.strip() == value
55
56 class QuadPipChannelEntry:
57         def __init__(self, name, idx, ch1, ch2, ch3, ch4):
58                 self.name = name
59                 self.idx = idx
60                 self.channel = {"1" : ch1, "2" : ch2, "3" : ch1, "4" : ch1,}
61
62         def __str__(self):
63                 return "idx : %d, name : %s, ch0 : %s, ch1 : %s, ch2 : %s, ch3 : %s"\
64                                         % (self.idx, self.name, self.channel.get("1"), self.channel.get("2"), self.channel.get("3"), self.channel.get("4"))
65
66         def __cmp__(self, other):
67                 return self.idx - other.idx
68
69         def getName(self):
70                 return self.name
71
72         def getIndex(self):
73                 return self.idx
74
75         def setChannel(self, idx, chName, sref):
76                 if self.channel.has_key(idx):
77                         self.channel[idx] = (chName, sref)
78                         return True
79
80                 return False
81
82         def deleteChannel(self, idx):
83                 if self.channel.has_key(idx):
84                         self.channel[idx] = None
85                         return True
86
87                 return False
88
89         def getChannel(self, idx):
90                 return self.channel.get(idx, None)
91
92         def getChannelName(self, idx):
93                 chName = None
94                 ch = self.getChannel(idx)
95                 if ch:
96                         chName = ch[0]
97
98                 if chName is None:
99                         chName = " <No Channel>"
100
101                 return chName
102
103         def getChannelSref(self, idx):
104                 chSref = None
105                 ch = self.getChannel(idx)
106                 if ch:
107                         chSref = ch[1]
108                 return chSref
109
110         def setIndex(self, idx):
111                 self.idx = idx
112
113         def setName(self, name):
114                 self.name = name
115
116 class QuadPipChannelData:
117         def __init__(self):
118                 self.PipChannelList = []
119                 self.pipChannelDataPath = "/etc/enigma2/quadPipChannels.dat"
120                 self.dataLoad()
121
122         def dataSave(self):
123                 fd = open(self.pipChannelDataPath, "w")
124                 pickle.dump(self.PipChannelList, fd)
125                 fd.close()
126                 #print "[*] dataSave"
127
128         def dataLoad(self):
129                 if not os.access(self.pipChannelDataPath, os.R_OK):
130                         return
131
132                 fd = open(self.pipChannelDataPath, "r")
133                 self.PipChannelList = pickle.load(fd)
134                 fd.close()
135                 #print "[*] dataLoad"
136
137         def getPipChannels(self):
138                 return self.PipChannelList
139
140         def length(self):
141                 return len(self.PipChannelList)
142
143 class QuadPipChannelList(QuadPipChannelData):
144         def __init__(self):
145                 QuadPipChannelData.__init__(self)
146                 self._curIdx = config.plugins.quadpip.lastchannel.value # starting from 1
147                 self.defaultEntryPreName = "Quad PiP channel "
148
149         def saveAll(self):
150                 self.dataSave()
151                 config.plugins.quadpip.lastchannel.value = self._curIdx
152                 config.plugins.quadpip.lastchannel.save()
153
154         def setIdx(self, value):
155                 if self._curIdx != value:
156                         self._curIdx = value
157                         config.plugins.quadpip.lastchannel.value = self._curIdx
158                         config.plugins.quadpip.lastchannel.save()
159
160         def getIdx(self):
161                 return self._curIdx
162
163         def getCurrentChannel(self):
164                 return self.getChannel(self._curIdx)
165
166         def getChannel(self, idx):
167                 for ch in self.PipChannelList:
168                         if idx == ch.getIndex():
169                                 return ch
170
171                 return None
172
173         def addNewChannel(self, newChannel):
174                 self.PipChannelList.append(newChannel)
175
176         def removeChannel(self, _channel):
177                 if self.getIdx() == _channel.getIndex():
178                         self.setIdx(0) # set invalid index
179
180                 self.PipChannelList.remove(_channel)
181
182         def sortPipChannelList(self):
183                 self.PipChannelList.sort()
184                 newIdx = 1
185                 for ch in self.PipChannelList:
186                         ch.setIndex(newIdx)
187                         chName = ch.getName()
188                         if chName.startswith(self.defaultEntryPreName):
189                                 ch.setName("%s%d" % (self.defaultEntryPreName, ch.getIndex()))
190                         newIdx += 1
191
192         def getDefaultPreName(self):
193                 return self.defaultEntryPreName
194
195 quad_pip_channel_list_instance = QuadPipChannelList()
196
197 class CreateQuadPipChannelEntry(ChannelSelectionBase):
198         skin_default_1080p = """
199                 <screen name="CreateQuadPipChannelEntry" position="center,center" size="1500,850" flags="wfNoBorder">
200                         <widget source="Title" render="Label" position="100,60" size="1300,60" zPosition="3" font="Semiboldit;52" halign="left" valign="center" backgroundColor="#25062748" transparent="1" />
201                         <ePixmap pixmap="skin_default/buttons/red.png" position="137,140" size="140,40" alphatest="blend" />
202                         <ePixmap pixmap="skin_default/buttons/green.png" position="492,140" size="140,40" alphatest="blend" />
203                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="837,140" size="140,40" alphatest="blend" />
204                         <ePixmap pixmap="skin_default/buttons/blue.png" position="1192,140" size="140,40" alphatest="blend" />
205                         <widget name="key_red" position="137,140" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" foregroundColor="#ffffff" transparent="1" />
206                         <widget name="key_green" position="492,140" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" foregroundColor="#ffffff" transparent="1" />
207                         <widget name="key_yellow" position="837,140" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" foregroundColor="#ffffff" transparent="1" />
208                         <widget name="key_blue" position="1192,140" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" foregroundColor="#ffffff" transparent="1" />
209                         <widget name="list" position="100,200" size="1250,365" serviceItemHeight="40" serviceNumberFont="Regular;28" serviceNameFont="Regular;28" serviceInfoFont="Semibold;24" foregroundColorServiceNotAvail="#58595b" transparent="1" scrollbarMode="showOnDemand" />
210                         <widget name="textChannels" position="100,580" size="1250,30" font="Regular;33" transparent="1" />
211                         <widget name="selectedList" position="110,620" size="700,160" font="Regular;28" itemHeight="40" transparent="1" />
212                         <widget name="description" position="860,630" size="650,160" font="Regular;28" halign="left" transparent="1" />
213                 </screen>
214                 """
215         skin_default_720p = """
216                 <screen name="CreateQuadPipChannelEntry" position="center,center" size="1000,610" flags="wfNoBorder">
217                         <widget source="Title" render="Label" position="40,40" size="910,40" zPosition="3" font="Semiboldit;32" backgroundColor="#25062748" transparent="1" />
218                         <ePixmap pixmap="skin_default/buttons/red.png" position="75,80" size="140,40" alphatest="blend" />
219                         <ePixmap pixmap="skin_default/buttons/green.png" position="325,80" size="140,40" alphatest="blend" />
220                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="575,80" size="140,40" alphatest="blend" />
221                         <ePixmap pixmap="skin_default/buttons/blue.png" position="825,80" size="140,40" alphatest="blend" />
222                         <widget name="key_red" position="75,80" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" foregroundColor="#ffffff" transparent="1" />
223                         <widget name="key_green" position="325,80" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" foregroundColor="#ffffff" transparent="1" />
224                         <widget name="key_yellow" position="575,80" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" foregroundColor="#ffffff" transparent="1" />
225                         <widget name="key_blue" position="825,80" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" foregroundColor="#ffffff" transparent="1" />
226                         <widget name="list" position="60,130" size="700,255" transparent="1" scrollbarMode="showOnDemand" foregroundColorServiceNotAvail="#58595b" />
227                         <widget name="textChannels" position="60,400" size="850,20" font="Regular;24" transparent="1" />
228                         <widget name="selectedList" position="70,430" size="480,150" font="Regular;22" itemHeight="25" transparent="1" />
229                         <widget name="description" position="590,460" size="350,150" font="Regular;22" halign="left" transparent="1" />
230                 </screen>
231                 """
232         skin_default_576p = """
233                 <screen name="CreateQuadPipChannelEntry" position="center,center" size="680,520" flags="wfNoBorder">
234                         <widget source="Title" render="Label" position="30,20" size="600,30" zPosition="3" font="Regular;22" backgroundColor="#25062748" transparent="1" />
235                         <ePixmap pixmap="skin_default/buttons/red.png" position="15,60" size="140,40" alphatest="blend" />
236                         <ePixmap pixmap="skin_default/buttons/green.png" position="185,60" size="140,40" alphatest="blend" />
237                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="355,60" size="140,40" alphatest="blend" />
238                         <ePixmap pixmap="skin_default/buttons/blue.png" position="525,60" size="140,40" alphatest="blend" />
239                         <widget name="key_red" position="15,60" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" foregroundColor="#ffffff" transparent="1" />
240                         <widget name="key_green" position="185,60" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" foregroundColor="#ffffff" transparent="1" />
241                         <widget name="key_yellow" position="355,60" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" foregroundColor="#ffffff" transparent="1" />
242                         <widget name="key_blue" position="525,60" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" foregroundColor="#ffffff" transparent="1" />
243                         <widget name="list" position="50,115" size="580,230" transparent="1" scrollbarMode="showOnDemand" foregroundColorServiceNotAvail="#58595b" />
244                         <widget name="textChannels" position="45,360" size="295,20" font="Regular;22" transparent="1" />
245                         <widget name="selectedList" position="50,385" size="290,100" font="Regular;20" itemHeight="22" transparent="1" />
246                         <widget name="description" position="360,390" size="310,140" font="Regular;20" halign="left" transparent="1" />
247                 </screen>
248                 """
249         def __init__(self, session, defaultEntryName, channel = None):
250                 ChannelSelectionBase.__init__(self, session)
251
252                 self["actions"] = ActionMap(["OkCancelActions", "DirectionActions", "QuadPipChannelEditActions"],
253                 {
254                         "cancel": self.Exit,
255                         "ok": self.channelSelected,
256                         "toggleList" : self.toggleCurrList,
257                         "editName" : self.editEntryName,
258                         "up": self.goUp,
259                         "down": self.goDown,
260                 }, -1)
261
262                 self.session = session
263                 dh = self.session.desktop.size().height()
264                 self.skin = {1080:CreateQuadPipChannelEntry.skin_default_1080p, \
265                                                 720:CreateQuadPipChannelEntry.skin_default_720p, \
266                                                 576:CreateQuadPipChannelEntry.skin_default_576p}.get(dh, CreateQuadPipChannelEntry.skin_default_1080p)
267
268                 self.defaultEntryName = defaultEntryName
269                 self["textChannels"] = Label(_(" "))
270                 self["description"] = Label(_(" "))
271
272                 self.currList = None
273
274                 self.newChannel = channel
275                 self.descChannels = []
276                 self.prepareChannels()
277                 self["selectedList"] = MenuList(self.descChannels, True)
278                 self.selectedList = self["selectedList"]
279
280                 self.onLayoutFinish.append(self.layoutFinished)
281
282         def layoutFinished(self):
283                 self.setTvMode()
284                 self.showFavourites()
285                 self.switchToServices()
286                 self.updateEntryName()
287
288         def updateEntryName(self):
289                 self["textChannels"].setText("%s :" % self.newChannel.getName())
290
291         def editEntryName(self):
292                 self.session.openWithCallback(self.editEntryNameCB, VirtualKeyBoard, title = (_("Input channel name.")), text = self.newChannel.getName())
293
294         def editEntryNameCB(self, newName):
295                 if newName:
296                         self.newChannel.setName(newName)
297                         self.updateEntryName()          
298
299         def updateDescription(self):
300                 if self.currList == "channelList":
301                         desc = _("EPG key : Switch to quad PiP entry\nOk key : Add to new entry\nPVR key : Input channel name\nExit key : Finish channel edit")
302                 else:
303                         desc = _("EPG key : Switch to channel list\nOk key : Remove selected channel\nPVR key : Input channel name\nExit key : Finish channel edit")
304
305                 self["description"].setText(desc)
306
307         def prepareChannels(self):
308                 if self.newChannel is None:
309                         self.newChannel = QuadPipChannelEntry(self.defaultEntryName, 99999, None, None, None, None)
310
311                 self.updateDescChannels()
312
313         def updateDescChannels(self):
314                 self.descChannels = []
315                 for idx in range(1,5):
316                         sIdx = str(idx)
317                         _isEmpty = False
318                         chName = self.newChannel.getChannelName(sIdx)
319                         if chName is None:
320                                 chName = " <empty>"
321                                 _isEmpty = True
322                         self.descChannels.append(("%d)  %s" % (idx, chName), sIdx, _isEmpty))   
323
324         def updateDescChannelList(self):
325                 self["selectedList"].setList(self.descChannels)
326
327         def goUp(self):
328                 if self.currList == "channelList":
329                         self.servicelist.moveUp()
330                 else:
331                         self.selectedList.up()
332
333         def goDown(self):
334                 if self.currList == "channelList":
335                         self.servicelist.moveDown()
336                 else:
337                         self.selectedList.down()
338
339         def toggleCurrList(self):
340                 if self.currList == "channelList":
341                         self.switchToSelected()
342                 else:
343                         self.switchToServices()
344
345         def switchToServices(self):
346                 self.servicelist.selectionEnabled(1)
347                 self.selectedList.selectionEnabled(0)
348                 self.currList = "channelList"
349                 self.updateDescription()
350
351         def switchToSelected(self):
352                 self.servicelist.selectionEnabled(0)
353                 self.selectedList.selectionEnabled(1)
354                 self.currList = "selectedList"
355                 self.updateDescription()
356
357         def channelSelected(self): # just return selected service
358                 if self.currList == "channelList":
359                         ref = self.getCurrentSelection()
360                         if (ref.flags & 7) == 7:
361                                 self.enterPath(ref)
362                         elif not (ref.flags & eServiceReference.isMarker):
363                                 ref = self.getCurrentSelection()
364                                 serviceName = ServiceReference(ref).getServiceName()
365                                 sref = ref.toString()
366                                 #self.addChannel(serviceName, sref)
367                                 _title = _('Choice where to put "%s"') % serviceName
368                                 _list = []
369                                 for idx in range(1,5):
370                                         sIdx = str(idx)
371                                         _isEmpty = False
372                                         chName = self.newChannel.getChannelName(sIdx)
373                                         _list.append((chName, sIdx, serviceName, sref, _isEmpty))
374
375                                 self.session.openWithCallback(self.choiceIdxCallback, ChoiceBox, title=_title, list=tuple(_list))
376                 else:
377                         self.removeChannel()
378
379         def choiceIdxCallback(self, answer):
380                 if answer is not None:
381                         (desc, sIdx, serviceName, sref, _isEmpty) = answer
382                         self.addChannel(sIdx, serviceName, sref)
383
384         def addChannel(self, sIdx, serviceName, sref):
385                 if self.newChannel.setChannel(sIdx, serviceName, sref):
386                         self.updateDescChannels()
387                         self.updateDescChannelList()
388
389         def removeChannel(self):
390                 cur = self.selectedList.getCurrent()
391                 if cur:
392                         sIdx = cur[1]
393                         if self.newChannel.deleteChannel(sIdx):
394                                 self.updateDescChannels()
395                                 self.updateDescChannelList()
396
397         def getNewChannel(self):
398                 for idx in range(1,5):
399                         sIdx = str(idx)
400                         ch = self.newChannel.getChannel(sIdx)
401                         if ch is not None:
402                                 return self.newChannel
403
404                 return None
405
406         def Exit(self):
407                 self.close(self.getNewChannel())
408
409 class QuadPiPChannelSelection(Screen, HelpableScreen):
410         skin = """
411                 <screen position="%s,%s" size="%d,%d">
412                         <ePixmap pixmap="skin_default/buttons/red.png" position="%d,%d" size="140,40" alphatest="on" />
413                         <ePixmap pixmap="skin_default/buttons/green.png" position="%d,%d" size="140,40" alphatest="on" />
414                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="%d,%d" size="140,40" alphatest="on" />
415                         <ePixmap pixmap="skin_default/buttons/blue.png" position="%d,%d" size="140,40" alphatest="on" />
416                         <widget name="key_red" position="%d,%d" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" foregroundColor="#ffffff" backgroundColor="#9f1313" transparent="1" />
417                         <widget name="key_green" position="%d,%d" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" foregroundColor="#ffffff" backgroundColor="#1f771f" transparent="1" />
418                         <widget name="key_yellow" position="%d,%d" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" foregroundColor="#ffffff" backgroundColor="#a08500" transparent="1" />
419                         <widget name="key_blue" position="%d,%d" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" foregroundColor="#ffffff" backgroundColor="#18188b" transparent="1" />
420                         <widget source="ChannelList" render="Listbox" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand">
421                                 <convert type="TemplatedMultiContent">
422                                 {"template":
423                                         [
424                                                 MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 0),
425                                                 MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 1),
426                                                 MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 2),
427                                                 MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 3),
428                                                 MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 4),
429                                         ],
430                                         "fonts": [gFont("Regular", %d), gFont("Regular", %d)],
431                                         "itemHeight": %d
432                                 }
433                                 </convert>
434                         </widget>
435                 </screen>
436                 """
437         def __init__(self, session):
438                 self.session = session
439                 Screen.__init__(self, session)
440                 HelpableScreen.__init__(self)
441                 self.setTitle(_("Quad PiP Channel Selection"))
442
443                 dw = self.session.desktop.size().width()
444                 dh = self.session.desktop.size().height()
445                 pw, ph = {1080:("center", "center"), 720:("center", "center"), 576:("center", "20%")}.get(dh, ("center", "center"))
446                 (sw, sh) = {1080:(dw/3, dh/2), 720:(int(dw/2), int(dh/1.5)), 576:(int(dw/1.3), int(dh/1.5))}.get(dh, (28, 24))
447                 button_margin = 5
448                 button_h = 40
449                 list_y = 40+button_margin*3
450                 self.fontSize = {1080:(28, 24), 720:(24,20), 576:(20,18)}.get(dh, (28, 24))
451                 self.skin = QuadPiPChannelSelection.skin % (pw, ph, \
452                                                                                                                 sw, sh+list_y, \
453                                                                                                                 sw/8-70, button_margin, \
454                                                                                                                 sw/8-70+sw/4, button_margin, \
455                                                                                                                 sw/8-70+sw/4*2, button_margin, \
456                                                                                                                 sw/8-70+sw/4*3, button_margin, \
457                                                                                                                 sw/8-70, button_margin, \
458                                                                                                                 sw/8-70+sw/4, button_margin, \
459                                                                                                                 sw/8-70+sw/4*2, button_margin, \
460                                                                                                                 sw/8-70+sw/4*3, button_margin, \
461                                                                                                                 0, list_y, sw, sh, \
462                                                                                                                 sw/16, 1, sw-sw/16*2, sh/13, \
463                                                                                                                 sw/11, 1+sh/13,                         sw-sw/16*2-sw/8, sh/18, \
464                                                                                                                 sw/11, 1+sh/13+sh/18,   sw-sw/16*2-sw/8, sh/18, \
465                                                                                                                 sw/11, 1+sh/13+sh/18*2,         sw-sw/16*2-sw/8, sh/18, \
466                                                                                                                 sw/11, 1+sh/13+sh/18*3,         sw-sw/16*2-sw/8, sh/18, \
467                                                                                                                 self.fontSize[0], self.fontSize[1], \
468                                                                                                                 sh/3)
469                 self["key_red"] = Label(_("Select"))
470                 self["key_green"] = Label(_("Add"))
471                 self["key_yellow"] = Label(_("Remove"))
472                 self["key_blue"] = Label(_("Edit"))
473
474                 self.PipChannelListApply = []
475                 self["ChannelList"] = List(self.PipChannelListApply)
476
477                 self["qpipActions"] = HelpableActionMap(self, "QuadPipSetupActions",
478                 {
479                         "red": (self.keyRed, _("Select Quad Channels")),
480                         "green": (self.keyGreen, _("Add New Quad Channel Entry")),
481                         "yellow": (self.keyYellow, _("Remove Quad Channel Entry")),
482                         "blue": (self.keyBlue, _("Edit Quad Channel Entry")),
483                 }, -2)
484
485                 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
486                 {
487                         "ok": (self.keyOk, _("Select Quad Channels")),
488                         "cancel": (self.keyCancel, _("Exit Quad Channel Selection")),
489                 }, -2)
490
491                 self.oldPosition = None
492
493                 global quad_pip_channel_list_instance
494                 self.qpipChannelList = quad_pip_channel_list_instance
495
496                 self.oldPosition = self.qpipChannelList.getIdx()-1
497
498                 self.onLayoutFinish.append(self.layoutFinishedCB)
499
500         def layoutFinishedCB(self):
501                 self.updateDisplay()
502                 self.updatePosition()
503
504         def keyOk(self):
505                 idx = self.getCurrentIndex()
506                 if idx != -1:
507                         self.qpipChannelList.setIdx(idx)
508
509                 self.close()
510
511         def keyCancel(self):
512                 self.close()
513
514         def keyRed(self):
515                 self.keyOk()
516
517         def keyGreen(self):
518                 self.session.openWithCallback(self.CreateQuadPipChannelEntryCB, CreateQuadPipChannelEntry, self.getDefaultEntryName(), None)
519
520         def keyYellow(self):
521                 curChannel = self.getSelectedChannel()
522                 if curChannel:
523                         self.oldPosition = self["ChannelList"].getIndex()
524                         self.qpipChannelList.removeChannel(curChannel)
525                         self.updateChannelList()
526
527         def keyBlue(self):
528                 curChannel = self.getSelectedChannel()
529                 if curChannel:
530                         self.oldPosition = self["ChannelList"].getIndex()
531                         self.qpipChannelList.removeChannel(curChannel)
532                         self.session.openWithCallback(self.CreateQuadPipChannelEntryCB, CreateQuadPipChannelEntry, None, curChannel)
533
534         def getCurrentIndex(self):
535                 idx = -1
536                 cur = self["ChannelList"].getCurrent()
537                 if cur:
538                         idx = cur[5]
539
540                 return idx
541
542         def getSelectedChannel(self):
543                 selectedChannel = None
544                 idx = self.getCurrentIndex()
545                 if idx != -1:
546                         selectedChannel = self.qpipChannelList.getChannel(idx)
547
548                 return selectedChannel
549
550         def getDefaultEntryName(self):
551                 return "%s%d" % (self.qpipChannelList.getDefaultPreName(), self.qpipChannelList.length() + 1)
552
553         def CreateQuadPipChannelEntryCB(self, newChannel):
554                 if newChannel:
555                         self.qpipChannelList.addNewChannel(newChannel)
556                         self.qpipChannelList.sortPipChannelList()
557                         self.oldPosition = newChannel.getIndex()-1
558                         self.updateDisplay()
559                         self.updatePosition()
560
561         def updateDisplay(self):
562                 self.PipChannelListApply = []
563                 for ch in self.getChannelList():
564                         entry = []
565
566                         entryName = ch.getName()
567                         if not entryName:
568                                 entryName = "%s%d" % (self.qpipChannelList.getDefaultPreName(), ch.getIndex())
569
570                         if self.qpipChannelList.getIdx() == ch.getIndex():
571                                 entryName += " (current channel)"
572
573                         entry.append(entryName)
574                         entry.append("1) " + ch.getChannelName("1"))
575                         entry.append("2) " + ch.getChannelName("2"))
576                         entry.append("3) " + ch.getChannelName("3"))
577                         entry.append("4) " + ch.getChannelName("4"))
578                         entry.append(ch.getIndex())
579                         self.PipChannelListApply.append(tuple(entry))
580
581                 self["ChannelList"].setList(self.PipChannelListApply)
582
583         def updatePosition(self):
584                 if self.oldPosition:
585                         if self["ChannelList"].count() > self.oldPosition:
586                                 self["ChannelList"].setIndex(self.oldPosition)
587                         self.oldPosition = None
588
589         def updateChannelList(self):
590                 self.qpipChannelList.sortPipChannelList()
591                 self.updateDisplay()
592                 self.updatePosition()
593
594         def getChannelList(self):
595                 return self.qpipChannelList.getPipChannels()
596
597 class FocusShowHide:
598         STATE_HIDDEN = 0
599         STATE_SHOWN = 1
600
601         def __init__(self):
602                 self.__state = self.STATE_SHOWN
603                 self.hideTimer = eTimer()
604                 self.hideTimer.callback.append(self.hideTimerCB)
605                 self.onLayoutFinish.append(self.startHideTimer)
606
607         def startHideTimer(self):
608                 self.hideTimer.stop()
609                 self.hideTimer.start(5000, True)
610
611         def hideTimerCB(self):
612                 self.hideFocus()
613
614         def isShown(self):
615                 return self.__state == self.STATE_SHOWN
616
617         def showFocus(self):
618                 self.show()
619                 self.__state = self.STATE_SHOWN
620                 self.startHideTimer()
621
622         def hideFocus(self):
623                 self.hideTimer.stop()
624                 self.hide()
625                 self.__state = self.STATE_HIDDEN
626
627         def toggleShow(self):
628                 if self.__state == self.STATE_SHOWN:
629                         self.hideFocus()
630                 elif self.__state == self.STATE_HIDDEN:
631                         self.showFocus()
632
633 class QuadPipScreen(Screen, FocusShowHide, HelpableScreen):
634         skin = """
635                 <screen position="0,0" size="%d,%d" backgroundColor="transparent" flags="wfNoBorder">
636                     <widget name="ch1" position="240,240" zPosition="1" size="480,60" font="Regular; %d" halign="center" valign="center" foregroundColor="white" backgroundColor="#ffffffff" alphatest="on" />
637                     <widget name="ch2" position="1200,240" zPosition="1" size="480,60" font="Regular; %d" halign="center" valign="center" foregroundColor="white" backgroundColor="#ffffffff" alphatest="on" />
638                     <widget name="ch3" position="240,780" zPosition="1" size="480,60" font="Regular; %d" halign="center" valign="center" foregroundColor="white" backgroundColor="#ffffffff" alphatest="on" />
639                     <widget name="ch4" position="1200,780" zPosition="1" size="480,60" font="Regular; %d" halign="center" valign="center" foregroundColor="white" backgroundColor="#ffffffff" alphatest="on" />
640                         <widget name="text1" position="%d,%d" zPosition="2" size="%d,%d" font="Regular; %d" halign="left" valign="center" alphatest="on" />
641                         <widget name="text2" position="%d,%d" zPosition="2" size="%d,%d" font="Regular; %d" halign="left" valign="center" alphatest="on" />
642                         <widget name="focus" position="0,0" zPosition="-1" size="960,540" backgroundColor="#ffffffff" borderWidth="5" borderColor="#e61616" alphatest="on" />
643                   </screen>
644                 """
645         def __init__(self, session):
646                 self.session = session
647                 self.session.qPips = None
648                 Screen.__init__(self, session)
649                 FocusShowHide.__init__(self)
650                 HelpableScreen.__init__(self)
651                 self.setTitle(_("Quad PiP Screen"))
652
653                 self["actions"] = HelpableActionMap(self, "QuadPipSetupActions",
654                 {
655                         "cancel": (self.keyExit, _("Exit quad PiP")),
656                         "ok": (self.keyOk, _("Zap focused channel on full screen")),
657                         "left": (self.keyLeft, _("Select channel audio")),
658                         "right": (self.keyRight, _("Select channel audio")),
659                         "up": (self.keyUp, _("Select channel audio")),
660                         "down": (self.keyDown, _("Select channel audio")),
661                         "channelup" : (self.KeyChannel, _("Show channel selection")),
662                         "channeldown" : (self.KeyChannel, _("Show channel selection")),
663                         "menu" : (self.KeyChannel, _("Show channel selection")),
664                         "channelPrev" : (self.KeyPrev, _("Prev quad PiP channel")),
665                         "channelNext" : (self.KeyNext, _("Next quad PiP channel")),
666                         "red" : (self.KeyRed, _("Show/Hide focus bar")),
667                 }, -1)
668
669                 self["ch1"] = Label(_(" "))
670                 self["ch2"] = Label(_(" "))
671                 self["ch3"] = Label(_(" "))
672                 self["ch4"] = Label(_(" "))
673                 self["text1"] = Label(_("  Red key : Show/Hide channel name"))
674                 self["text2"] = Label(_("  Menu key : Select quad channel"))
675                 self["focus"] = Slider(-1, -1)
676
677                 self.currentPosition = 1 # 1~4
678                 self.updatePositionList()
679
680                 self.skin = QuadPipScreen.skin % (self.session.desktop.size().width(), self.session.desktop.size().height(), \
681                                                                                                 self.fontSize, self.fontSize, self.fontSize, self.fontSize, \
682                                                                                                 self.text1Pos[0], self.text1Pos[1], self.text1Pos[2], self.text1Pos[3], self.fontSize, \
683                                                                                                 self.text2Pos[0], self.text2Pos[1], self.text2Pos[2], self.text2Pos[3], self.fontSize)
684                 self.oldService = None
685                 self.curChannel = None
686                 self.curPlayAudio = -1
687
688                 global quad_pip_channel_list_instance
689                 self.qpipChannelList = quad_pip_channel_list_instance
690
691                 self.oldFccEnable = False
692                 self.oldMinitvEanble = False
693
694                 self.onLayoutFinish.append(self.layoutFinishedCB)
695
696                 self.notSupportTimer = eTimer()
697                 self.notSupportTimer.callback.append(self.showNotSupport)
698
699                 self.noChannelTimer = eTimer()
700                 self.noChannelTimer.callback.append(self.noChannelTimerCB)
701
702                 self.forceToExitTimer = eTimer()
703                 self.forceToExitTimer.callback.append(self.forceToExitTimerCB)
704
705         def forceToExitTimerCB(self):
706                 self.session.openWithCallback(self.close, MessageBox, _("Quad PiP is not available."), MessageBox.TYPE_ERROR)
707
708         def showNotSupport(self):
709                 self.session.openWithCallback(self.close, MessageBox, _("Box or driver is not support Quad PiP."), MessageBox.TYPE_ERROR)
710
711         def noChannelTimerCB(self):
712                 self.session.openWithCallback(self.ChannelSelectCB, QuadPiPChannelSelection)
713
714         def layoutFinishedCB(self):
715                 if not os.access(ENABLE_QPIP_PROCPATH, os.F_OK):
716                         self.notSupportTimer.start(100, True)
717                         return
718
719                 self.onClose.append(self.__onClose)
720
721                 if self.session.pipshown: # try to disable pip
722                         self.session.pipshown = False
723                         del self.session.pip
724
725                 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
726                 self.session.nav.stopService()
727
728                 if SystemInfo.get("FastChannelChange", False):
729                         self.disableFCC()
730
731                 if SystemInfo.get("MiniTV", False):
732                         self.disableMiniTV()
733
734                 ret = setDecoderMode("mosaic")
735                 if ret is not True:
736                         self.forceToExitTimer.start(0, True)
737                         return
738
739                 self.moveLabel()
740
741                 if self.qpipChannelList.length() == 0:
742                         self.noChannelTimer.start(10, True)
743                 else:
744                         self.playLastChannel()
745
746         def __onClose(self):
747                 self.disableQuadPip()
748                 setDecoderMode("normal")
749
750                 if SystemInfo.get("FastChannelChange", False):
751                         self.enableFCC()
752
753                 if SystemInfo.get("MiniTV", False):
754                         self.enableMiniTV()
755
756                 self.qpipChannelList.saveAll()
757                 self.session.nav.playService(self.oldService)
758
759         def getChannelPosMap(self, w, h):
760                 rectMap = {}
761                 ch1 = (0, 0, int(w*0.5), int(h*0.5))
762                 ch2 = (int(w*0.5), 0, int(w*0.5), int(h*0.5))
763                 ch3 = (0, int(h*0.5), int(w*0.5), int(h*0.5))
764                 ch4 = (int(w*0.5), int(h*0.5), int(w*0.5), int(h*0.5))
765                 rectMap = (None, ch1, ch2, ch3, ch4)
766
767                 return rectMap
768
769         def updatePositionList(self):
770                 w = self.session.desktop.size().width()
771                 h = self.session.desktop.size().height()
772                 self.framePosMap = self.getChannelPosMap(w, h)
773                 self.eVideoPosMap = self.getChannelPosMap(720, 576)
774
775                 self.movePositionMap = {}
776                 self.movePositionMap["left"] = [-1, 2, 1, 4, 3]
777                 self.movePositionMap["right"] = [-1, 2, 1, 4, 3]
778                 self.movePositionMap["up"] = [-1, 3, 4, 1, 2]
779                 self.movePositionMap["down"] = [-1, 3, 4, 1, 2]
780
781                 self.labelPositionMap = {}
782                 self.labelPositionMap["ch1"] = (w/8,            h/4-h/36,               w/4,    h/18)
783                 self.labelPositionMap["ch2"] = (w/8+w/2,        h/4-h/36,               w/4,    h/18)
784                 self.labelPositionMap["ch3"] = (w/8,            h/4-h/36+h/2,   w/4,    h/18)
785                 self.labelPositionMap["ch4"] = (w/8+w/2,        h/4-h/36+h/2,   w/4,    h/18)
786
787                 self.decoderIdxMap = [None, 0, 1, 2, 3]
788
789                 self.fontSize = {1080:40, 720:28, 576:18}.get(h, 40)
790                 self.text1Pos = (w-w/3, h-h/18-h/18, w/3, h/18)
791                 self.text2Pos = (w-w/3, h-h/18, w/3, h/18)
792
793         def moveFrame(self):
794                 self.showFocus()
795                 pos = self.framePosMap[self.currentPosition]
796                 self["focus"].resize(int(pos[2]), int(pos[3]))
797                 self["focus"].move(int(pos[0]), int(pos[1]))
798
799         def moveLabel(self):
800                 posMap = self.labelPositionMap
801
802                 ch1_posMap = posMap["ch1"]
803                 self["ch1"].move(int(ch1_posMap[0]), int(ch1_posMap[1]))
804                 self["ch1"].resize(int(ch1_posMap[2]), int(ch1_posMap[3]))
805
806                 ch2_posMap = posMap["ch2"]
807                 self["ch2"].move(int(ch2_posMap[0]), int(ch2_posMap[1]))
808                 self["ch2"].resize(int(ch2_posMap[2]), int(ch2_posMap[3]))
809
810                 ch3_posMap = posMap["ch3"]
811                 self["ch3"].move(int(ch3_posMap[0]), int(ch3_posMap[1]))
812                 self["ch3"].resize(int(ch3_posMap[2]), int(ch3_posMap[3]))
813
814                 ch4_posMap = posMap["ch4"]
815                 self["ch4"].move(int(ch4_posMap[0]), int(ch4_posMap[1]))
816                 self["ch4"].resize(int(ch4_posMap[2]), int(ch4_posMap[3]))
817
818         def keyExit(self):
819                 self.close()
820
821         def keyOk(self):
822                 if self.isShown():
823                         channel = self.qpipChannelList.getCurrentChannel()
824                         if channel:
825                                 chInfo = channel.getChannel(str(self.currentPosition))
826                                 if chInfo:
827                                         (sname, sref) = chInfo
828                                         self.oldService = eServiceReference(sref)
829                                         self.close()
830                 else:
831                         self.showFocus()
832
833         def keyLeft(self):
834                 newPosition = self.movePositionMap["left"][self.currentPosition]
835                 self.selectPosition(newPosition)
836
837         def keyRight(self):
838                 newPosition = self.movePositionMap["right"][self.currentPosition]
839                 self.selectPosition(newPosition)
840
841         def keyUp(self):
842                 newPosition = self.movePositionMap["up"][self.currentPosition]
843                 self.selectPosition(newPosition)
844
845         def keyDown(self):
846                 newPosition = self.movePositionMap["down"][self.currentPosition]
847                 self.selectPosition(newPosition)
848
849         def KeyChannel(self):
850                 self.session.openWithCallback(self.ChannelSelectCB, QuadPiPChannelSelection)
851
852         def KeyPrev(self):
853                 curIdx = self.qpipChannelList.getIdx()
854                 curIdx -= 1
855                 if curIdx == 0:
856                         curIdx = self.qpipChannelList.length()
857                 self.qpipChannelList.setIdx(curIdx)
858                 self.playLastChannel()
859
860         def KeyNext(self):
861                 curIdx = self.qpipChannelList.getIdx()
862                 curIdx += 1
863                 if curIdx > self.qpipChannelList.length():
864                         curIdx = 1
865                 self.qpipChannelList.setIdx(curIdx)
866                 self.playLastChannel()
867
868         def KeyRed(self):
869                 self.toggleShow()
870
871         def selectPosition(self, pos):
872                 self.currentPosition = pos
873                 self.moveFrame()
874                 self.selectAudio()
875
876         def selectAudio(self):
877                 if self.curPlayAudio == -1:
878                         return
879
880                 if self.curPlayAudio != self.currentPosition:
881                         if self.session.qPips and len(self.session.qPips) >= self.currentPosition:
882                                 self.playAudio(self.curPlayAudio, False)
883                                 self.playAudio(self.currentPosition, True)
884
885         def disableQuadPip(self):
886                 if self.session.qPips is not None:
887                         for qPip in self.session.qPips:
888                                 del qPip
889
890                         self.session.qPips = None
891                         self.curPlayAudio = -1
892
893                 self.updateChannelName(None)
894
895         def ChannelSelectCB(self):
896                 if self.qpipChannelList.length() == 0:
897                         self.disableQuadPip()
898                 else:
899                         self.playLastChannel()
900
901         def playLastChannel(self, first=True):
902                 if self.qpipChannelList.length() == 0:
903                         return
904
905                 channel = self.qpipChannelList.getCurrentChannel()
906                 if channel:
907                         self.playChannel(channel)
908                 elif first:
909                         self.qpipChannelList.setIdx(1)
910                         self.playLastChannel(False)
911
912                 return channel
913
914         def playChannel(self, channel):
915                 print "[playChannel] channel : ", channel
916
917                 if self.curChannel and self.curChannel == channel.channel:
918                         return
919
920                 self.disableQuadPip()
921                 self.selectPosition(1)
922
923                 self.curChannel = channel.channel.copy()
924
925                 self.session.qPips = []
926                 for idx in range(1,5):
927                         chInfo = channel.getChannel(str(idx))
928                         if chInfo is None:
929                                 continue
930
931                         (sname, sref) = chInfo
932
933                         qPipShown = False
934
935                         decoderIdx = self.decoderIdxMap[idx]
936                         pos = self.eVideoPosMap[idx]
937                         #print "===================================================================="
938                         #print "sname : ", sname
939                         #print "sref : ", sref
940                         #print "decoderIdx : " , decoderIdx
941                         #print "pos : ", pos
942                         #print "===================================================================="
943
944                         qPipInstance =  self.session.instantiateDialog(QuadPiP, decoderIdx, pos)
945                         qPipInstance.setAnimationMode(0)
946                         qPipInstance.show()
947
948                         isPlayAudio = False
949                         if self.currentPosition == idx:
950                                 isPlayAudio = True
951                                 self.curPlayAudio = idx
952
953                         if qPipInstance.playService(eServiceReference(sref), isPlayAudio):
954                                 self.session.qPips.append(qPipInstance)
955                         else:
956                                 print "play failed, ", sref
957                                 del qPipInstance
958
959                 self.updateChannelName(channel)
960                 self.showFocus()
961
962         def playAudio(self, idx, value):
963                 qPipInstance = self.session.qPips[idx-1]
964                 qPipInstance.setQpipMode(True, value)
965
966                 if value:
967                         self.curPlayAudio = idx
968                 else:
969                         self.curPlayAudio = -1
970
971         def updateChannelName(self, channel):
972                 for idx in range(1,5):
973                         self["ch%d" % idx].setText((channel and channel.getChannelName(str(idx))) or "No channel")
974
975         def disableFCC(self):
976                 try:
977                         self.oldFccEnable = config.plugins.fccsetup.activate.value
978                         if self.oldFccEnable:
979                                 config.plugins.fccsetup.activate.value = False
980                                 from Plugins.SystemPlugins.FastChannelChange.plugin import FCCChanged
981                                 FCCChanged()
982                 except:
983                         self.oldFccEnable = False
984
985         def enableFCC(self):
986                 if self.oldFccEnable:
987                         try:
988                                 config.plugins.fccsetup.activate.value = self.oldFccEnable
989                                 from Plugins.SystemPlugins.FastChannelChange.plugin import FCCChanged
990                                 FCCChanged()
991                         except:
992                                 pass
993
994         def disableMiniTV(self):
995                 try:
996                         self.oldMinitvEanble = config.plugins.minitv.enable.value
997                         if self.oldMinitvEanble:
998                                 config.plugins.minitv.enable.value = False
999                 except:
1000                         self.oldFccEnable = False
1001
1002         def enableMiniTV(self):
1003                 if self.oldMinitvEanble:
1004                         try:
1005                                 config.plugins.minitv.enable.value = self.oldMinitvEanble
1006                         except:
1007                                 pass
1008