Support duo4k.
[vuplus_dvbapp] / lib / python / Components / EpgList.py
1 from HTMLComponent import HTMLComponent
2 from GUIComponent import GUIComponent
3
4 from enigma import eEPGCache, eListbox, eListboxPythonMultiContent, gFont, \
5         RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_HALIGN_CENTER, RT_VALIGN_CENTER
6
7 from Tools.LoadPixmap import LoadPixmap
8
9 from time import localtime, time
10 from ServiceReference import ServiceReference
11 from Tools.Directories import resolveFilename, SCOPE_CURRENT_SKIN
12 import skin
13
14 EPG_TYPE_SINGLE = 0
15 EPG_TYPE_MULTI = 1
16 EPG_TYPE_SIMILAR = 2
17
18 class Rect:
19         def __init__(self, x, y, width, height):
20                 self.__left = x
21                 self.__top = y
22                 self.__width = width
23                 self.__height = height
24
25         def left(self):
26                 return self.__left
27
28         def top(self):
29                 return self.__top
30
31         def height(self):
32                 return self.__height
33
34         def width(self):
35                 return self.__width
36
37 class EPGList(HTMLComponent, GUIComponent):
38         def __init__(self, type=EPG_TYPE_SINGLE, selChangedCB=None, timer = None):
39                 self.days = (_("Mon"), _("Tue"), _("Wed"), _("Thu"), _("Fri"), _("Sat"), _("Sun"))
40                 self.timer = timer
41                 self.onSelChanged = [ ]
42                 if selChangedCB is not None:
43                         self.onSelChanged.append(selChangedCB)
44                 GUIComponent.__init__(self)
45                 self.type=type
46                 self.l = eListboxPythonMultiContent()
47                 font = skin.fonts.get("EPGList0", ("Regular", 22))
48                 self.l.setFont(0, gFont(font[0], font[1]))
49                 font = skin.fonts.get("EPGList1", ("Regular", 16))
50                 self.l.setFont(1, gFont(font[0], font[1]))
51                 if type == EPG_TYPE_SINGLE:
52                         self.l.setBuildFunc(self.buildSingleEntry)
53                 elif type == EPG_TYPE_MULTI:
54                         self.l.setBuildFunc(self.buildMultiEntry)
55                 else:
56                         assert(type == EPG_TYPE_SIMILAR)
57                         self.l.setBuildFunc(self.buildSimilarEntry)
58                 self.epgcache = eEPGCache.getInstance()
59                 self.clock_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock.png'))
60                 self.clock_add_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock_add.png'))
61                 self.clock_pre_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock_pre.png'))
62                 self.clock_post_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock_post.png'))
63                 self.clock_prepost_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock_prepost.png'))
64
65         def getEventFromId(self, service, eventid):
66                 event = None
67                 if self.epgcache is not None and eventid is not None:
68                         event = self.epgcache.lookupEventId(service.ref, eventid)
69                 return event
70
71         def getCurrentChangeCount(self):
72                 if self.type == EPG_TYPE_MULTI and self.l.getCurrentSelection() is not None:
73                         return self.l.getCurrentSelection()[0]
74                 return 0
75
76         def getCurrent(self):
77                 idx=0
78                 if self.type == EPG_TYPE_MULTI:
79                         idx += 1
80                 tmp = self.l.getCurrentSelection()
81                 if tmp is None:
82                         return ( None, None )
83                 eventid = tmp[idx+1]
84                 service = ServiceReference(tmp[idx])
85                 event = self.getEventFromId(service, eventid)
86                 return ( event, service )
87
88         def moveUp(self):
89                 self.instance.moveSelection(self.instance.moveUp)
90
91         def moveDown(self):
92                 self.instance.moveSelection(self.instance.moveDown)
93
94         def connectSelectionChanged(func):
95                 if not self.onSelChanged.count(func):
96                         self.onSelChanged.append(func)
97
98         def disconnectSelectionChanged(func):
99                 self.onSelChanged.remove(func)
100
101         def selectionChanged(self):
102                 for x in self.onSelChanged:
103                         if x is not None:
104                                 x()
105 #                               try:
106 #                                       x()
107 #                               except: # FIXME!!!
108 #                                       print "FIXME in EPGList.selectionChanged"
109 #                                       pass
110
111         GUI_WIDGET = eListbox
112
113         def postWidgetCreate(self, instance):
114                 instance.setWrapAround(True)
115                 instance.selectionChanged.get().append(self.selectionChanged)
116                 instance.setContent(self.l)
117
118         def preWidgetRemove(self, instance):
119                 instance.selectionChanged.get().remove(self.selectionChanged)
120                 instance.setContent(None)
121
122         def recalcEntrySize(self):
123                 esize = self.l.getItemSize()
124                 width = esize.width()
125                 height = esize.height()
126                 if self.type == EPG_TYPE_SINGLE:
127                         self.weekday_rect = Rect(0, 0, width/20*2-10, height)
128                         self.datetime_rect = Rect(width/20*2, 0, width/20*5-15, height)
129                         self.descr_rect = Rect(width/20*7, 0, width/20*13, height)
130                 elif self.type == EPG_TYPE_MULTI:
131                         xpos = 0;
132                         w = width/10*3;
133                         self.service_rect = Rect(xpos, 0, w-10, height)
134                         xpos += w;
135                         w = width/10*2;
136                         self.start_end_rect = Rect(xpos, 0, w-10, height)
137                         self.progress_rect = Rect(xpos, 4, w-10, height-8)
138                         xpos += w
139                         w = width/10*5;
140                         self.descr_rect = Rect(xpos, 0, width, height)
141                 else: # EPG_TYPE_SIMILAR
142                         self.weekday_rect = Rect(0, 0, width/20*2-10, height)
143                         self.datetime_rect = Rect(width/20*2, 0, width/20*5-15, height)
144                         self.service_rect = Rect(width/20*7, 0, width/20*13, height)
145
146         def getClockPixmap(self, refstr, beginTime, duration, eventId):
147                 pre_clock = 1
148                 post_clock = 2
149                 clock_type = 0
150                 endTime = beginTime + duration
151                 for x in self.timer.timer_list:
152                         if x.service_ref.ref.toString() == refstr:
153                                 if x.eit == eventId:
154                                         return self.clock_pixmap
155                                 beg = x.begin
156                                 end = x.end
157                                 if beginTime > beg and beginTime < end and endTime > end:
158                                         clock_type |= pre_clock
159                                 elif beginTime < beg and endTime > beg and endTime < end:
160                                         clock_type |= post_clock
161                 if clock_type == 0:
162                         return self.clock_add_pixmap
163                 elif clock_type == pre_clock:
164                         return self.clock_pre_pixmap
165                 elif clock_type == post_clock:
166                         return self.clock_post_pixmap
167                 else:
168                         return self.clock_prepost_pixmap
169                 
170         def getPixmapForEntry(self, service, eventId, beginTime, duration):
171                 rec=beginTime and (self.timer.isInTimer(eventId, beginTime, duration, service))
172                 if rec:
173                         clock_pic = self.getClockPixmap(service, beginTime, duration, eventId)
174                 else:
175                         clock_pic = None
176                 return (clock_pic, rec)
177
178         def buildSingleEntry(self, service, eventId, beginTime, duration, EventName):
179                 (clock_pic, rec) = self.getPixmapForEntry(service, eventId, beginTime, duration)
180                 r1=self.weekday_rect
181                 r2=self.datetime_rect
182                 r3=self.descr_rect
183                 t = localtime(beginTime)
184                 res = [
185                         None, # no private data needed
186                         (eListboxPythonMultiContent.TYPE_TEXT, r1.left(), r1.top(), r1.width(), r1.height(), 0, RT_HALIGN_RIGHT, self.days[t[6]]),
187                         (eListboxPythonMultiContent.TYPE_TEXT, r2.left(), r2.top(), r2.width(), r1.height(), 0, RT_HALIGN_RIGHT, "%02d.%02d, %02d:%02d"%(t[2],t[1],t[3],t[4]))
188                 ]
189                 if rec:
190                         res.extend((
191                                 (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, r3.left(), r3.top(), 21, 21, clock_pic),
192                                 (eListboxPythonMultiContent.TYPE_TEXT, r3.left() + 25, r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, EventName)
193                         ))
194                 else:
195                         res.append((eListboxPythonMultiContent.TYPE_TEXT, r3.left(), r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, EventName))
196                 return res
197
198         def buildSimilarEntry(self, service, eventId, beginTime, service_name, duration):
199                 (clock_pic, rec) = self.getPixmapForEntry(service, eventId, beginTime, duration)
200                 r1=self.weekday_rect
201                 r2=self.datetime_rect
202                 r3=self.service_rect
203                 t = localtime(beginTime)
204                 res = [
205                         None,  # no private data needed
206                         (eListboxPythonMultiContent.TYPE_TEXT, r1.left(), r1.top(), r1.width(), r1.height(), 0, RT_HALIGN_RIGHT, self.days[t[6]]),
207                         (eListboxPythonMultiContent.TYPE_TEXT, r2.left(), r2.top(), r2.width(), r1.height(), 0, RT_HALIGN_RIGHT, "%02d.%02d, %02d:%02d"%(t[2],t[1],t[3],t[4]))
208                 ]
209                 if rec:
210                         res.extend((
211                                 (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, r3.left(), r3.top(), 21, 21, clock_pic),
212                                 (eListboxPythonMultiContent.TYPE_TEXT, r3.left() + 25, r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, service_name)
213                         ))
214                 else:
215                         res.append((eListboxPythonMultiContent.TYPE_TEXT, r3.left(), r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, service_name))
216                 return res
217
218         def buildMultiEntry(self, changecount, service, eventId, beginTime, duration, EventName, nowTime, service_name):
219                 (clock_pic, rec) = self.getPixmapForEntry(service, eventId, beginTime, duration)
220                 r1=self.service_rect
221                 r2=self.progress_rect
222                 r3=self.descr_rect
223                 r4=self.start_end_rect
224                 res = [ None ] # no private data needed
225                 if rec:
226                         res.extend((
227                                 (eListboxPythonMultiContent.TYPE_TEXT, r1.left(), r1.top(), r1.width()-21, r1.height(), 0, RT_HALIGN_LEFT, service_name),
228                                 (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, r1.left()+r1.width()-16, r1.top(), 21, 21, clock_pic)
229                         ))
230                 else:
231                         res.append((eListboxPythonMultiContent.TYPE_TEXT, r1.left(), r1.top(), r1.width(), r1.height(), 0, RT_HALIGN_LEFT, service_name))
232                 if beginTime is not None:
233                         if nowTime < beginTime:
234                                 begin = localtime(beginTime)
235                                 end = localtime(beginTime+duration)
236 #                               print "begin", begin
237 #                               print "end", end
238                                 res.extend((
239                                         (eListboxPythonMultiContent.TYPE_TEXT, r4.left(), r4.top(), r4.width(), r4.height(), 1, RT_HALIGN_CENTER|RT_VALIGN_CENTER, "%02d.%02d - %02d.%02d"%(begin[3],begin[4],end[3],end[4])),
240                                         (eListboxPythonMultiContent.TYPE_TEXT, r3.left(), r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, EventName)
241                                 ))
242                         else:
243                                 percent = (nowTime - beginTime) * 100 / duration
244                                 res.extend((
245                                         (eListboxPythonMultiContent.TYPE_PROGRESS, r2.left(), r2.top(), r2.width(), r2.height(), percent),
246                                         (eListboxPythonMultiContent.TYPE_TEXT, r3.left(), r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, EventName)
247                                 ))
248                 return res
249
250         def queryEPG(self, list, buildFunc=None):
251                 if self.epgcache is not None:
252                         if buildFunc is not None:
253                                 return self.epgcache.lookupEvent(list, buildFunc)
254                         else:
255                                 return self.epgcache.lookupEvent(list)
256                 return [ ]
257
258         def fillMultiEPG(self, services, stime=-1):
259                 #t = time()
260                 test = [ (service.ref.toString(), 0, stime) for service in services ]
261                 test.insert(0, 'X0RIBDTCn')
262                 self.list = self.queryEPG(test)
263                 self.l.setList(self.list)
264                 #print time() - t
265                 self.selectionChanged()
266
267         def updateMultiEPG(self, direction):
268                 #t = time()
269                 test = [ x[3] and (x[1], direction, x[3]) or (x[1], direction, 0) for x in self.list ]
270                 test.insert(0, 'XRIBDTCn')
271                 tmp = self.queryEPG(test)
272                 cnt=0
273                 for x in tmp:
274                         changecount = self.list[cnt][0] + direction
275                         if changecount >= 0:
276                                 if x[2] is not None:
277                                         self.list[cnt]=(changecount, x[0], x[1], x[2], x[3], x[4], x[5], x[6])
278                         cnt+=1
279                 self.l.setList(self.list)
280                 #print time() - t
281                 self.selectionChanged()
282
283         def fillSingleEPG(self, service):
284                 #t = time()
285                 test = [ 'RIBDT', (service.ref.toString(), 0, -1, -1) ]
286                 self.list = self.queryEPG(test)
287                 self.l.setList(self.list)
288                 #print time() - t
289                 self.selectionChanged()
290
291         def sortSingleEPG(self, type):
292                 list = self.list
293                 if list:
294                         event_id = self.getSelectedEventId()
295                         if type == 1:
296                                 list.sort(key=lambda x: (x[4] and x[4].lower(), x[2]))
297                         else:
298                                 assert(type == 0)
299                                 list.sort(key=lambda x: x[2])
300                         self.l.invalidate()
301                         self.moveToEventId(event_id)
302
303         def getSelectedEventId(self):
304                 x = self.l.getCurrentSelection()
305                 return x and x[1]
306
307         def moveToService(self,serviceref):
308                 if not serviceref:
309                         return
310                 index = 0
311                 refstr = serviceref.toString()
312                 for x in self.list:
313                         if x[1] == refstr:
314                                 self.instance.moveSelectionTo(index)
315                                 break
316                         index += 1
317                         
318         def moveToEventId(self, eventId):
319                 if not eventId:
320                         return
321                 index = 0
322                 for x in self.list:
323                         if x[1] == eventId:
324                                 self.instance.moveSelectionTo(index)
325                                 break
326                         index += 1
327
328         def fillSimilarList(self, refstr, event_id):
329                 t = time()
330          # search similar broadcastings
331                 if event_id is None:
332                         return
333                 l = self.epgcache.search(('RIBND', 1024, eEPGCache.SIMILAR_BROADCASTINGS_SEARCH, refstr, event_id))
334                 if l and len(l):
335                         l.sort(key=lambda x: x[2])
336                 self.l.setList(l)
337                 self.selectionChanged()
338                 print time() - t