Support turbo2.
[vuplus_dvbapp] / lib / python / Plugins / Extensions / GraphMultiEPG / GraphMultiEpg.py
1 from skin import parseColor, parseFont, parseSize
2 from Components.config import config, ConfigClock, ConfigInteger
3 from Components.Pixmap import Pixmap
4 from Components.Button import Button
5 from Components.ActionMap import ActionMap
6 from Components.HTMLComponent import HTMLComponent
7 from Components.GUIComponent import GUIComponent
8 from Components.EpgList import Rect
9 from Components.Sources.Event import Event
10 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
11 from Components.TimerList import TimerList
12 from Screens.Screen import Screen
13 from Screens.EventView import EventViewSimple
14 from Screens.TimeDateInput import TimeDateInput
15 from Screens.TimerEntry import TimerEntry
16 from Screens.EpgSelection import EPGSelection
17 from Screens.TimerEdit import TimerSanityConflict
18 from Screens.MessageBox import MessageBox
19 from Tools.Directories import resolveFilename, SCOPE_CURRENT_SKIN
20 from RecordTimer import RecordTimerEntry, parseEvent, AFTEREVENT
21 from ServiceReference import ServiceReference
22 from Tools.LoadPixmap import LoadPixmap
23 from enigma import eEPGCache, eListbox, gFont, eListboxPythonMultiContent, \
24         RT_HALIGN_LEFT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, RT_WRAP, eRect, eTimer
25
26 from time import localtime, time, strftime
27
28 class EPGList(HTMLComponent, GUIComponent):
29         def __init__(self, selChangedCB=None, timer = None, time_epoch = 120, overjump_empty=True):
30                 self.cur_event = None
31                 self.cur_service = None
32                 self.offs = 0
33                 self.timer = timer
34                 self.onSelChanged = [ ]
35                 if selChangedCB is not None:
36                         self.onSelChanged.append(selChangedCB)
37                 GUIComponent.__init__(self)
38                 self.l = eListboxPythonMultiContent()
39                 self.l.setBuildFunc(self.buildEntry)
40                 if overjump_empty:
41                         self.l.setSelectableFunc(self.isSelectable)
42                 self.epgcache = eEPGCache.getInstance()
43                 self.clock_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock.png'))
44                 self.clock_add_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock_add.png'))
45                 self.clock_pre_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock_pre.png'))
46                 self.clock_post_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock_post.png'))
47                 self.clock_prepost_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, 'skin_default/icons/epgclock_prepost.png'))
48                 self.time_base = None
49                 self.time_epoch = time_epoch
50                 self.list = None
51                 self.event_rect = None
52
53                 self.foreColor = None
54                 self.foreColorSelected = None
55                 self.borderColor = None
56                 self.backColor = 0x586d88
57                 self.backColorSelected = 0x808080
58                 self.foreColorService = None
59                 self.backColorService = None
60                 self.serviceFont = gFont("Regular", 20)
61                 self.entryFont = gFont("Regular", 14)
62                 self.itemHeight = 54
63
64         def applySkin(self, desktop, screen):
65                 def EntryForegroundColor(value):
66                         self.foreColor = parseColor(value).argb()
67                 def EntryForegroundColorSelected(value):
68                         self.foreColorSelected = parseColor(value).argb()
69                 def EntryBackgroundColor(value):
70                         self.backColor = parseColor(value).argb()
71                 def EntryBackgroundColorSelected(value):
72                         self.backColorSelected = parseColor(value).argb()
73                 def EntryBorderColor(value):
74                         self.borderColor = parseColor(value).argb()
75                 def EntryItemHeight(value):
76                         self.itemHeight = int(value)
77                 def ServiceNameForegroundColor(value):
78                         self.foreColorService = parseColor(value).argb()                
79                 def ServiceNameBackgroundColor(value):
80                         self.backColorService = parseColor(value).argb()
81                 def ServiceFont(value):
82                         self.serviceFont = parseFont(value, ((1,1),(1,1)) )
83                 def EntryFont(value):
84                         self.entryFont = parseFont(value, ((1,1),(1,1)) )
85
86                 for (attrib, value) in list(self.skinAttributes):
87                         try:
88                                 locals().get(attrib)(value)
89                                 self.skinAttributes.remove((attrib, value))
90                         except:
91                                 pass
92                 self.l.setFont(0, self.serviceFont)
93                 self.l.setFont(1, self.entryFont)
94                 self.l.setItemHeight(self.itemHeight)
95                 return GUIComponent.applySkin(self, desktop, screen)
96
97         def isSelectable(self, service, sname, event_list):
98                 return (event_list and len(event_list) and True) or False
99
100         def setEpoch(self, epoch):
101 #               if self.cur_event is not None and self.cur_service is not None:
102                 self.offs = 0
103                 self.time_epoch = epoch
104                 self.fillMultiEPG(None) # refill
105
106         def getEventFromId(self, service, eventid):
107                 event = None
108                 if self.epgcache is not None and eventid is not None:
109                         event = self.epgcache.lookupEventId(service.ref, eventid)
110                 return event
111
112         def moveToService(self,serviceref):
113                 if serviceref is not None:
114                         for x in range(len(self.list)):
115                                 if self.list[x][0] == serviceref.toString():
116                                         self.instance.moveSelectionTo(x)
117                                         break
118         
119         def getIndexFromService(self, serviceref):
120                 if serviceref is not None:
121                         for x in range(len(self.list)):
122                                 if self.list[x][0] == serviceref.toString():
123                                         return x
124                 
125         def setCurrentIndex(self, index):
126                 if self.instance is not None:
127                         self.instance.moveSelectionTo(index)
128         
129         def getCurrent(self):
130                 if self.cur_service is None:
131                         return ( None, None )
132                 old_service = self.cur_service  #(service, service_name, events)
133                 events = self.cur_service[2]
134                 refstr = self.cur_service[0]
135                 if self.cur_event is None or not events or not len(events):
136                         return ( None, ServiceReference(refstr) )
137                 event = events[self.cur_event] #(event_id, event_title, begin_time, duration)
138                 eventid = event[0]
139                 service = ServiceReference(refstr)
140                 event = self.getEventFromId(service, eventid)
141                 return ( event, service )
142
143         def connectSelectionChanged(func):
144                 if not self.onSelChanged.count(func):
145                         self.onSelChanged.append(func)
146
147         def disconnectSelectionChanged(func):
148                 self.onSelChanged.remove(func)
149
150         def serviceChanged(self):
151                 cur_sel = self.l.getCurrentSelection()
152                 if cur_sel:
153                         self.findBestEvent()
154
155         def findBestEvent(self):
156                 old_service = self.cur_service  #(service, service_name, events)
157                 cur_service = self.cur_service = self.l.getCurrentSelection()
158                 last_time = 0;
159                 time_base = self.getTimeBase()
160                 if old_service and self.cur_event is not None:
161                         events = old_service[2]
162                         cur_event = events[self.cur_event] #(event_id, event_title, begin_time, duration)
163                         last_time = cur_event[2]
164                         if last_time < time_base:
165                                 last_time = time_base
166                 if cur_service:
167                         self.cur_event = 0
168                         events = cur_service[2]
169                         if events and len(events):
170                                 if last_time:
171                                         best_diff = 0
172                                         best = len(events) #set invalid
173                                         idx = 0
174                                         for event in events: #iterate all events
175                                                 ev_time = event[2]
176                                                 if ev_time < time_base:
177                                                         ev_time = time_base
178                                                 diff = abs(ev_time-last_time)
179                                                 if (best == len(events)) or (diff < best_diff):
180                                                         best = idx
181                                                         best_diff = diff
182                                                 idx += 1
183                                         if best != len(events):
184                                                 self.cur_event = best
185                         else:
186                                 self.cur_event = None
187                 self.selEntry(0)
188
189         def selectionChanged(self):
190                 for x in self.onSelChanged:
191                         if x is not None:
192                                 x()
193 #                               try:
194 #                                       x()
195 #                               except: # FIXME!!!
196 #                                       print "FIXME in EPGList.selectionChanged"
197 #                                       pass
198
199         GUI_WIDGET = eListbox
200
201         def postWidgetCreate(self, instance):
202                 instance.setWrapAround(True)
203                 instance.selectionChanged.get().append(self.serviceChanged)
204                 instance.setContent(self.l)
205                 self.l.setSelectionClip(eRect(0,0,0,0), False)
206
207         def preWidgetRemove(self, instance):
208                 instance.selectionChanged.get().remove(self.serviceChanged)
209                 instance.setContent(None)
210
211         def recalcEntrySize(self):
212                 esize = self.l.getItemSize()
213                 width = esize.width()
214                 height = esize.height()
215                 xpos = 0;
216                 w = width/10*2;
217                 self.service_rect = Rect(xpos, 0, w-10, height)
218                 xpos += w;
219                 w = width/10*8;
220                 self.event_rect = Rect(xpos, 0, w, height)
221
222         def calcEntryPosAndWidthHelper(self, stime, duration, start, end, width):
223                 xpos = (stime - start) * width / (end - start)
224                 ewidth = (stime + duration - start) * width / (end - start)
225                 ewidth -= xpos;
226                 if xpos < 0:
227                         ewidth += xpos;
228                         xpos = 0;
229                 if (xpos+ewidth) > width:
230                         ewidth = width - xpos
231                 return xpos, ewidth
232
233         def calcEntryPosAndWidth(self, event_rect, time_base, time_epoch, ev_start, ev_duration):
234                 xpos, width = self.calcEntryPosAndWidthHelper(ev_start, ev_duration, time_base, time_base + time_epoch * 60, event_rect.width())
235                 return xpos+event_rect.left(), width
236
237         def buildEntry(self, service, service_name, events):
238                 r1=self.service_rect
239                 r2=self.event_rect
240                 res = [ None, MultiContentEntryText(
241                                                 pos = (r1.left(),r1.top()),
242                                                 size = (r1.width(), r1.height()),
243                                                 font = 0, flags = RT_HALIGN_LEFT | RT_VALIGN_CENTER,
244                                                 text = service_name,
245                                                 color = self.foreColorService,
246                                                 backcolor = self.backColorService) ]
247
248                 if events:
249                         start = self.time_base+self.offs*self.time_epoch*60
250                         end = start + self.time_epoch * 60
251                         left = r2.left()
252                         top = r2.top()
253                         width = r2.width()
254                         height = r2.height()
255                         foreColor = self.foreColor
256                         foreColorSelected = self.foreColorSelected
257                         backColor = self.backColor
258                         backColorSelected = self.backColorSelected
259                         borderColor = self.borderColor
260
261                         for ev in events:  #(event_id, event_title, begin_time, duration)
262                                 rec=ev[2] and self.timer.isInTimer(ev[0], ev[2], ev[3], service)
263                                 xpos, ewidth = self.calcEntryPosAndWidthHelper(ev[2], ev[3], start, end, width)
264                                 res.append(MultiContentEntryText(
265                                         pos = (left+xpos, top), size = (ewidth, height),
266                                         font = 1, flags = RT_HALIGN_CENTER | RT_VALIGN_CENTER | RT_WRAP,
267                                         text = ev[1], color = foreColor, color_sel = foreColorSelected,
268                                         backcolor = backColor, backcolor_sel = backColorSelected, border_width = 1, border_color = borderColor))
269                                 if rec and ewidth > 23:
270                                         res.append(MultiContentEntryPixmapAlphaTest(
271                                                 pos = (left+xpos+ewidth-22, top+height-22), size = (21, 21),
272                                                 png = self.getClockPixmap(service, ev[2], ev[3], ev[0]),
273                                                 backcolor = backColor,
274                                                 backcolor_sel = backColorSelected))
275                 return res
276
277         def selEntry(self, dir, visible=True):
278                 cur_service = self.cur_service #(service, service_name, events)
279                 self.recalcEntrySize()
280                 valid_event = self.cur_event is not None
281                 if cur_service:
282                         update = True
283                         entries = cur_service[2]
284                         if dir == 0: #current
285                                 update = False
286                         elif dir == +1: #next
287                                 if valid_event and self.cur_event+1 < len(entries):
288                                         self.cur_event+=1
289                                 else:
290                                         self.offs += 1
291                                         self.fillMultiEPG(None) # refill
292                                         return True
293                         elif dir == -1: #prev
294                                 if valid_event and self.cur_event-1 >= 0:
295                                         self.cur_event-=1
296                                 elif self.offs > 0:
297                                         self.offs -= 1
298                                         self.fillMultiEPG(None) # refill
299                                         return True
300                 if cur_service and valid_event:
301                         entry = entries[self.cur_event] #(event_id, event_title, begin_time, duration)
302                         time_base = self.time_base+self.offs*self.time_epoch*60
303                         xpos, width = self.calcEntryPosAndWidth(self.event_rect, time_base, self.time_epoch, entry[2], entry[3])
304                         self.l.setSelectionClip(eRect(xpos, 0, width, self.event_rect.height()), visible and update)
305                 else:
306                         self.l.setSelectionClip(eRect(self.event_rect.left(), self.event_rect.top(), self.event_rect.width(), self.event_rect.height()), False)
307                 self.selectionChanged()
308                 return False
309
310         def queryEPG(self, list, buildFunc=None):
311                 if self.epgcache is not None:
312                         if buildFunc is not None:
313                                 return self.epgcache.lookupEvent(list, buildFunc)
314                         else:
315                                 return self.epgcache.lookupEvent(list)
316                 return [ ]
317
318         def fillMultiEPG(self, services, stime=-1):
319                 if services is None:
320                         time_base = self.time_base+self.offs*self.time_epoch*60
321                         test = [ (service[0], 0, time_base, self.time_epoch) for service in self.list ]
322                 else:
323                         self.cur_event = None
324                         self.cur_service = None
325                         self.time_base = int(stime)
326                         test = [ (service.ref.toString(), 0, self.time_base, self.time_epoch) for service in services ]
327                 test.insert(0, 'XRnITBD')
328 #               print "BEFORE:"
329 #               for x in test:
330 #                       print x
331                 epg_data = self.queryEPG(test)
332 #               print "EPG:"
333 #               for x in epg_data:
334 #                       print x
335                 self.list = [ ]
336                 tmp_list = None
337                 service = ""
338                 sname = ""
339                 for x in epg_data:
340                         if service != x[0]:
341                                 if tmp_list is not None:
342                                         self.list.append((service, sname, tmp_list[0][0] is not None and tmp_list or None))
343                                 service = x[0]
344                                 sname = x[1]
345                                 tmp_list = [ ]
346                         tmp_list.append((x[2], x[3], x[4], x[5]))
347                 if tmp_list and len(tmp_list):
348                         self.list.append((service, sname, tmp_list[0][0] is not None and tmp_list or None))
349
350                 self.l.setList(self.list)
351                 self.findBestEvent()
352
353         def getEventRect(self):
354                 rc = self.event_rect
355                 return Rect( rc.left() + (self.instance and self.instance.position().x() or 0), rc.top(), rc.width(), rc.height() )
356
357         def getTimeEpoch(self):
358                 return self.time_epoch
359
360         def getTimeBase(self):
361                 return self.time_base + (self.offs * self.time_epoch * 60)
362
363         def resetOffset(self):
364                 self.offs = 0
365         
366         def getClockPixmap(self, refstr, beginTime, duration, eventId):
367                 pre_clock = 1
368                 post_clock = 2
369                 clock_type = 0
370                 endTime = beginTime + duration
371                 for x in self.timer.timer_list:
372                         if x.service_ref.ref.toString() == refstr:
373                                 if x.eit == eventId:
374                                         return self.clock_pixmap
375                                 beg = x.begin
376                                 end = x.end
377                                 if beginTime > beg and beginTime < end and endTime > end:
378                                         clock_type |= pre_clock
379                                 elif beginTime < beg and endTime > beg and endTime < end:
380                                         clock_type |= post_clock
381                 if clock_type == 0:
382                         return self.clock_add_pixmap
383                 elif clock_type == pre_clock:
384                         return self.clock_pre_pixmap
385                 elif clock_type == post_clock:
386                         return self.clock_post_pixmap
387                 else:
388                         return self.clock_prepost_pixmap
389
390 class TimelineText(HTMLComponent, GUIComponent):
391         def __init__(self):
392                 GUIComponent.__init__(self)
393                 self.l = eListboxPythonMultiContent()
394                 self.l.setSelectionClip(eRect(0,0,0,0))
395                 self.foreColor = 0xffc000
396                 self.backColor = 0x000000
397                 self.font = gFont("Regular", 20)
398                 self.itemHeight = 25
399
400         GUI_WIDGET = eListbox
401
402         def applySkin(self, desktop, screen):
403                 def foregroundColor(value):
404                         self.foreColor = parseColor(value).argb()
405                 def backgroundColor(value):
406                         self.backColor = parseColor(value).argb()
407                 def font(value):
408                         self.font = parseFont(value,  ((1, 1), (1, 1)) )
409                 def itemHeight(value):
410                         self.itemHeight = int(value)
411                 for (attrib, value) in list(self.skinAttributes):
412                         try:
413                                 locals().get(attrib)(value)
414                                 self.skinAttributes.remove((attrib, value))
415                         except:
416                                 pass
417                 self.l.setFont(0, self.font)
418                 self.l.setItemHeight(self.itemHeight);
419                 return GUIComponent.applySkin(self, desktop, screen)
420
421         def postWidgetCreate(self, instance):
422                 instance.setContent(self.l)
423
424         def setEntries(self, entries):
425                 ih = self.itemHeight
426                 width = ih * 60 / 25
427                 res = [ None ] # no private data needed
428                 for x in entries:
429                         tm = x[0]
430                         xpos = x[1]
431                         str = strftime("%H:%M", localtime(tm))
432                         res.append((eListboxPythonMultiContent.TYPE_TEXT, xpos-30, 0, width, ih, 0, RT_HALIGN_CENTER|RT_VALIGN_CENTER, str))
433                 self.l.setList([res])
434
435 config.misc.graph_mepg_prev_time=ConfigClock(default = time())
436 config.misc.graph_mepg_prev_time_period=ConfigInteger(default=120, limits=(60,300))
437
438 class GraphMultiEPG(Screen):
439         EMPTY = 0
440         ADD_TIMER = 1
441         REMOVE_TIMER = 2
442         
443         ZAP = 1
444
445         def __init__(self, session, services, zapFunc=None, bouquetChangeCB=None):
446                 Screen.__init__(self, session)
447                 self.bouquetChangeCB = bouquetChangeCB
448                 now = time()
449                 tmp = now % 900
450                 self.ask_time = now - tmp
451                 self.closeRecursive = False
452                 self["key_red"] = Button("")
453                 self["key_green"] = Button("")
454                 self.key_green_choice = self.EMPTY
455                 self.key_red_choice = self.EMPTY
456                 self["timeline_text"] = TimelineText()
457                 self["Event"] = Event()
458                 self.time_lines = [ ]
459                 for x in (0,1,2,3,4,5):
460                         pm = Pixmap()
461                         self.time_lines.append(pm)
462                         self["timeline%d"%(x)] = pm
463                 self["timeline_now"] = Pixmap()
464                 self.services = services
465                 self.zapFunc = zapFunc
466
467                 self["list"] = EPGList(selChangedCB = self.onSelectionChanged, timer = self.session.nav.RecordTimer, time_epoch = config.misc.graph_mepg_prev_time_period.value )
468
469                 self["actions"] = ActionMap(["EPGSelectActions", "OkCancelActions"],
470                         {
471                                 "cancel": self.closeScreen,
472                                 "ok": self.eventSelected,
473                                 "timerAdd": self.timerAdd,
474                                 "info": self.infoKeyPressed,
475                                 "red": self.zapTo,
476                                 "input_date_time": self.enterDateTime,
477                                 "nextBouquet": self.nextBouquet,
478                                 "prevBouquet": self.prevBouquet,
479                         })
480                 self["actions"].csel = self
481
482                 self["input_actions"] = ActionMap(["InputActions"],
483                         {
484                                 "left": self.leftPressed,
485                                 "right": self.rightPressed,
486                                 "1": self.key1,
487                                 "2": self.key2,
488                                 "3": self.key3,
489                                 "4": self.key4,
490                                 "5": self.key5,
491                         },-1)
492
493                 self.updateTimelineTimer = eTimer()
494                 self.updateTimelineTimer.callback.append(self.moveTimeLines)
495                 self.updateTimelineTimer.start(60*1000)
496                 self.onLayoutFinish.append(self.onCreate)
497
498         def leftPressed(self):
499                 self.prevEvent()
500
501         def rightPressed(self):
502                 self.nextEvent()
503
504         def nextEvent(self, visible=True):
505                 ret = self["list"].selEntry(+1, visible)
506                 if ret:
507                         self.moveTimeLines(True)
508
509         def prevEvent(self, visible=True):
510                 ret = self["list"].selEntry(-1, visible)
511                 if ret:
512                         self.moveTimeLines(True)
513
514         def key1(self):
515                 self["list"].setEpoch(60)
516                 config.misc.graph_mepg_prev_time_period.value = 60
517                 self.moveTimeLines()
518
519         def key2(self):
520                 self["list"].setEpoch(120)
521                 config.misc.graph_mepg_prev_time_period.value = 120
522                 self.moveTimeLines()
523
524         def key3(self):
525                 self["list"].setEpoch(180)
526                 config.misc.graph_mepg_prev_time_period.value = 180
527                 self.moveTimeLines()
528
529         def key4(self):
530                 self["list"].setEpoch(240)
531                 config.misc.graph_mepg_prev_time_period.value = 240
532                 self.moveTimeLines()
533
534         def key5(self):
535                 self["list"].setEpoch(300)
536                 config.misc.graph_mepg_prev_time_period.value = 300
537                 self.moveTimeLines()
538
539         def nextBouquet(self):
540                 if self.bouquetChangeCB:
541                         self.bouquetChangeCB(1, self)
542
543         def prevBouquet(self):
544                 if self.bouquetChangeCB:
545                         self.bouquetChangeCB(-1, self)
546
547         def enterDateTime(self):
548                 self.session.openWithCallback(self.onDateTimeInputClosed, TimeDateInput, config.misc.graph_mepg_prev_time )
549
550         def onDateTimeInputClosed(self, ret):
551                 if len(ret) > 1:
552                         if ret[0]:
553                                 self.ask_time=ret[1]
554                                 l = self["list"]
555                                 l.resetOffset()
556                                 l.fillMultiEPG(self.services, ret[1])
557                                 self.moveTimeLines(True)
558
559         def closeScreen(self):
560                 self.close(self.closeRecursive)
561
562         def infoKeyPressed(self):
563                 cur = self["list"].getCurrent()
564                 event = cur[0]
565                 service = cur[1]
566                 if event is not None:
567                         self.session.open(EventViewSimple, event, service, self.eventViewCallback, self.openSimilarList)
568
569         def openSimilarList(self, eventid, refstr):
570                 self.session.open(EPGSelection, refstr, None, eventid)
571
572         def setServices(self, services):
573                 self.services = services
574                 self.onCreate()
575
576         #just used in multipeg
577         def onCreate(self):
578                 self["list"].fillMultiEPG(self.services, self.ask_time)
579                 self["list"].moveToService(self.session.nav.getCurrentlyPlayingServiceReference())
580                 self.moveTimeLines()
581
582         def eventViewCallback(self, setEvent, setService, val):
583                 l = self["list"]
584                 old = l.getCurrent()
585                 if val == -1:
586                         self.prevEvent(False)
587                 elif val == +1:
588                         self.nextEvent(False)
589                 cur = l.getCurrent()
590                 if cur[0] is None and cur[1].ref != old[1].ref:
591                         self.eventViewCallback(setEvent, setService, val)
592                 else:
593                         setService(cur[1])
594                         setEvent(cur[0])
595
596         def zapTo(self):
597                 if self.zapFunc and self.key_red_choice == self.ZAP:
598                         self.closeRecursive = True
599                         ref = self["list"].getCurrent()[1]
600                         if ref:
601                                 self.zapFunc(ref.ref)
602
603         def eventSelected(self):
604                 self.infoKeyPressed()
605
606         def removeTimer(self, timer):
607                 timer.afterEvent = AFTEREVENT.NONE
608                 self.session.nav.RecordTimer.removeEntry(timer)
609                 self["key_green"].setText(_("Add timer"))
610                 self.key_green_choice = self.ADD_TIMER
611         
612         def timerAdd(self):
613                 cur = self["list"].getCurrent()
614                 event = cur[0]
615                 serviceref = cur[1]
616                 if event is None:
617                         return
618                 eventid = event.getEventId()
619                 refstr = serviceref.ref.toString()
620                 for timer in self.session.nav.RecordTimer.timer_list:
621                         if timer.eit == eventid and timer.service_ref.ref.toString() == refstr:
622                                 cb_func = lambda ret : not ret or self.removeTimer(timer)
623                                 self.session.openWithCallback(cb_func, MessageBox, _("Do you really want to delete %s?") % event.getEventName())
624                                 break
625                 else:
626                         newEntry = RecordTimerEntry(serviceref, checkOldTimers = True, *parseEvent(event))
627                         self.session.openWithCallback(self.finishedAdd, TimerEntry, newEntry)
628
629         def finishedAdd(self, answer):
630                 print "finished add"
631                 if answer[0]:
632                         entry = answer[1]
633                         simulTimerList = self.session.nav.RecordTimer.record(entry)
634                         if simulTimerList is not None:
635                                 for x in simulTimerList:
636                                         if x.setAutoincreaseEnd(entry):
637                                                 self.session.nav.RecordTimer.timeChanged(x)
638                                 simulTimerList = self.session.nav.RecordTimer.record(entry)
639                                 if simulTimerList is not None:
640                                         self.session.openWithCallback(self.finishSanityCorrection, TimerSanityConflict, simulTimerList)
641                         self["key_green"].setText(_("Remove timer"))
642                         self.key_green_choice = self.REMOVE_TIMER
643                 else:
644                         self["key_green"].setText(_("Add timer"))
645                         self.key_green_choice = self.ADD_TIMER
646                         print "Timeredit aborted"
647         
648         def finishSanityCorrection(self, answer):
649                 self.finishedAdd(answer)
650
651         def onSelectionChanged(self):
652                 cur = self["list"].getCurrent()
653                 if cur is None:
654                         if self.key_green_choice != self.EMPTY:
655                                 self["key_green"].setText("")
656                                 self.key_green_choice = self.EMPTY
657                         if self.key_red_choice != self.EMPTY:
658                                 self["key_red"].setText("")
659                                 self.key_red_choice = self.EMPTY
660                         return
661                 
662                 event = cur[0]
663                 self["Event"].newEvent(event)
664                 
665                 if cur[1] is None or cur[1].getServiceName() == "":
666                         if self.key_green_choice != self.EMPTY:
667                                 self["key_green"].setText("")
668                                 self.key_green_choice = self.EMPTY
669                         if self.key_red_choice != self.EMPTY:
670                                 self["key_red"].setText("")
671                                 self.key_red_choice = self.EMPTY
672                         return
673                 elif self.key_red_choice != self.ZAP:
674                                 self["key_red"].setText("Zap")
675                                 self.key_red_choice = self.ZAP
676                         
677                 if not event:
678                         if self.key_green_choice != self.EMPTY:
679                                 self["key_green"].setText("")
680                                 self.key_green_choice = self.EMPTY
681                         return
682                 
683                 serviceref = cur[1]
684                 eventid = event.getEventId()
685                 refstr = serviceref.ref.toString()
686                 isRecordEvent = False
687                 for timer in self.session.nav.RecordTimer.timer_list:
688                         if timer.eit == eventid and timer.service_ref.ref.toString() == refstr:
689                                 isRecordEvent = True
690                                 break
691                 if isRecordEvent and self.key_green_choice != self.REMOVE_TIMER:
692                         self["key_green"].setText(_("Remove timer"))
693                         self.key_green_choice = self.REMOVE_TIMER
694                 elif not isRecordEvent and self.key_green_choice != self.ADD_TIMER:
695                         self["key_green"].setText(_("Add timer"))
696                         self.key_green_choice = self.ADD_TIMER
697         
698         def moveTimeLines(self, force=False):
699                 self.updateTimelineTimer.start((60-(int(time())%60))*1000)      #keep syncronised
700                 l = self["list"]
701                 event_rect = l.getEventRect()
702                 time_epoch = l.getTimeEpoch()
703                 time_base = l.getTimeBase()
704                 if event_rect is None or time_epoch is None or time_base is None:
705                         return
706                 time_steps = time_epoch > 180 and 60 or 30
707                 
708                 num_lines = time_epoch/time_steps
709                 incWidth=event_rect.width()/num_lines
710                 pos=event_rect.left()
711                 timeline_entries = [ ]
712                 x = 0
713                 changecount = 0
714                 for line in self.time_lines:
715                         old_pos = line.position
716                         new_pos = (x == num_lines and event_rect.left()+event_rect.width() or pos, old_pos[1])
717                         if not x or x >= num_lines:
718                                 line.visible = False
719                         else:
720                                 if old_pos != new_pos:
721                                         line.setPosition(new_pos[0], new_pos[1])
722                                         changecount += 1
723                                 line.visible = True
724                         if not x or line.visible:
725                                 timeline_entries.append((time_base + x * time_steps * 60, new_pos[0]))
726                         x += 1
727                         pos += incWidth
728
729                 if changecount or force:
730                         self["timeline_text"].setEntries(timeline_entries)
731
732                 now=time()
733                 timeline_now = self["timeline_now"]
734                 if now >= time_base and now < (time_base + time_epoch * 60):
735                         xpos = int((((now - time_base) * event_rect.width()) / (time_epoch * 60))-(timeline_now.instance.size().width()/2))
736                         old_pos = timeline_now.position
737                         new_pos = (xpos+event_rect.left(), old_pos[1])
738                         if old_pos != new_pos:
739                                 timeline_now.setPosition(new_pos[0], new_pos[1])
740                         timeline_now.visible = True
741                 else:
742                         timeline_now.visible = False
743                 # here no l.l.invalidate() is needed when the zPosition in the skin is correct!
744
745