add graphical multiepg plugin (like e1)
authorAndreas Monzner <andreas.monzner@multimedia-labs.de>
Tue, 31 Jul 2007 11:00:01 +0000 (11:00 +0000)
committerAndreas Monzner <andreas.monzner@multimedia-labs.de>
Tue, 31 Jul 2007 11:00:01 +0000 (11:00 +0000)
yet it is just startable via blue button menu

configure.ac
data/skin_default.xml
lib/gui/elistboxcontent.cpp
lib/python/Plugins/Extensions/GraphMultiEPG/GraphMultiEpg.py [new file with mode: 0644]
lib/python/Plugins/Extensions/GraphMultiEPG/Makefile.am [new file with mode: 0644]
lib/python/Plugins/Extensions/GraphMultiEPG/__init__.py [new file with mode: 0644]
lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py [new file with mode: 0644]
lib/python/Plugins/Extensions/Makefile.am

index 0c303ef..bdf6f00 100644 (file)
@@ -91,6 +91,7 @@ lib/python/Plugins/Extensions/CutListEditor/Makefile
 lib/python/Plugins/Extensions/MediaScanner/Makefile
 lib/python/Plugins/Extensions/PicturePlayer/Makefile
 lib/python/Plugins/Extensions/PicturePlayer/data/Makefile
 lib/python/Plugins/Extensions/MediaScanner/Makefile
 lib/python/Plugins/Extensions/PicturePlayer/Makefile
 lib/python/Plugins/Extensions/PicturePlayer/data/Makefile
+lib/python/Plugins/Extensions/GraphMultiEPG/Makefile
 lib/python/Tools/Makefile
 lib/service/Makefile
 lib/components/Makefile
 lib/python/Tools/Makefile
 lib/service/Makefile
 lib/components/Makefile
index e52ab77..c04e3c0 100644 (file)
                <widget name="date" position="410,40" size="140,40" valign="center" halign="right" font="Regular;24" />
                <widget name="list" position="00,85" size="560,325" scrollbarMode="showOnDemand" />
        </screen>
                <widget name="date" position="410,40" size="140,40" valign="center" halign="right" font="Regular;24" />
                <widget name="list" position="00,85" size="560,325" scrollbarMode="showOnDemand" />
        </screen>
+       <screen name="GraphMultiEPG" position="90,95" size="560,430" title="Electronic Program Guide">
+               <widget name="key_red" position="0,0" size="140,30" backgroundColor="red" font="Regular;21" />
+               <widget name="key_green" position="140,0" size="140,30" backgroundColor="green" font="Regular;21" />
+               <widget source="Clock" render="Label" position="420,0" size="130,30" font="Regular;21" valign="center" halign="right" >
+                       <convert type="ClockToText">WithSeconds</convert>
+               </widget>
+               <widget name="timeline_text" position="0,35" size="560,25" />
+               <widget name="list" position="0,60" size="560,324" scrollbarMode="showOnDemand" />
+               <widget name="timeline0" position="0,55" size="1,334" pixmap="timeline-fs8.png" zPosition="1" />
+               <widget name="timeline1" position="0,55" size="1,334" pixmap="timeline-fs8.png" zPosition="1" />
+               <widget name="timeline2" position="0,55" size="1,334" pixmap="timeline-fs8.png" zPosition="1" />
+               <widget name="timeline3" position="0,55" size="1,334" pixmap="timeline-fs8.png" zPosition="1" />
+               <widget name="timeline4" position="0,55" size="1,334" pixmap="timeline-fs8.png" zPosition="1" />
+               <widget name="timeline5" position="0,55" size="1,334" pixmap="timeline-fs8.png" zPosition="1" />
+               <widget name="timeline_now" position="0,55" size="1,334" pixmap="timeline-now-fs8.png" zPosition="1" />
+               <widget source="Event" render="Label" position="0,395" size="55,30" font="Regular;19" halign="right" >
+                       <convert type="EventTime">StartTime</convert>
+                       <convert type="ClockToText"></convert>
+               </widget>
+               <widget source="Event" render="Label" position="62,395" size="70,30" font="Regular;19" >
+                       <convert type="EventTime">EndTime</convert>
+                       <convert type="ClockToText">Format:- %H:%M</convert>
+               </widget>
+               <widget source="Event" render="Label" position="135,395" size="425,30" font="Regular;19" >
+                       <convert type="EventName"></convert>
+               </widget>
+       </screen>
        <screen name="EventView" position="100,100" size="520,380" title="Eventview">
                <widget name="key_red" position="0,0" size="130,30" backgroundColor="red" font="Regular;21" />
                <widget name="key_green" position="130,0" size="130,30" backgroundColor="green" font="Regular;21" />
        <screen name="EventView" position="100,100" size="520,380" title="Eventview">
                <widget name="key_red" position="0,0" size="130,30" backgroundColor="red" font="Regular;21" />
                <widget name="key_green" position="130,0" size="130,30" backgroundColor="green" font="Regular;21" />
index 24a2951..b397ddc 100644 (file)
@@ -726,11 +726,11 @@ void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, c
                                                        pwidth = PyTuple_GET_ITEM(item, 3),
                                                        pheight = PyTuple_GET_ITEM(item, 4),
                                                        ppixmap = PyTuple_GET_ITEM(item, 5),
                                                        pwidth = PyTuple_GET_ITEM(item, 3),
                                                        pheight = PyTuple_GET_ITEM(item, 4),
                                                        ppixmap = PyTuple_GET_ITEM(item, 5),
-                                                       pforeColor, pbackColor, pbackColorSelected;
+                                                       pbackColor, pbackColorSelected;
 
                                if (!(px && py && pwidth && pheight && ppixmap))
                                {
 
                                if (!(px && py && pwidth && pheight && ppixmap))
                                {
-                                       eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, foreColor, backColor, backColorSelected] ))");
+                                       eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PIXMAP, x, y, width, height, pixmap [, backColor, backColorSelected] ))");
                                        goto error_out;
                                }
 
                                        goto error_out;
                                }
 
@@ -747,29 +747,23 @@ void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, c
 
                                if (size > 6)
                                {
 
                                if (size > 6)
                                {
-                                       pforeColor = PyTuple_GET_ITEM(item, 6);
-                                       if (pforeColor == Py_None)
-                                               pforeColor=ePyObject();
-                               }
-                               if (size > 7)
-                               {
-                                       pbackColor = PyTuple_GET_ITEM(item, 7);
+                                       pbackColor = PyTuple_GET_ITEM(item, 6);
                                        if (pbackColor == Py_None)
                                                pbackColor=ePyObject();
                                }
                                        if (pbackColor == Py_None)
                                                pbackColor=ePyObject();
                                }
-                               if (size > 8)
+                               if (size > 7)
                                {
                                {
-                                       pbackColorSelected = PyTuple_GET_ITEM(item, 8);
+                                       pbackColorSelected = PyTuple_GET_ITEM(item, 7);
                                        if (pbackColorSelected == Py_None)
                                                pbackColorSelected=ePyObject();
                                }
 
                                eRect rect(x, y, width, height);
                                painter.clip(rect);
                                        if (pbackColorSelected == Py_None)
                                                pbackColorSelected=ePyObject();
                                }
 
                                eRect rect(x, y, width, height);
                                painter.clip(rect);
-                               if (pbackColor || pbackColorSelected || pforeColor)
+                               if (pbackColor || pbackColorSelected)
                                {
                                        gRegion rc(rect);
                                {
                                        gRegion rc(rect);
-                                       clearRegion(painter, style, pforeColor, pbackColor, pbackColorSelected, selected, rc, sel_clip);
+                                       clearRegion(painter, style, ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip);
                                        reset_colors=true;
                                }
                                
                                        reset_colors=true;
                                }
                                
diff --git a/lib/python/Plugins/Extensions/GraphMultiEPG/GraphMultiEpg.py b/lib/python/Plugins/Extensions/GraphMultiEPG/GraphMultiEpg.py
new file mode 100644 (file)
index 0000000..d8c654b
--- /dev/null
@@ -0,0 +1,500 @@
+from Components.config import config, ConfigClock, ConfigInteger
+from Components.Button import Button
+from Components.Pixmap import Pixmap
+from Components.ActionMap import ActionMap
+from Components.HTMLComponent import HTMLComponent
+from Components.GUIComponent import GUIComponent
+from Components.EpgList import Rect
+from Components.Sources.Event import Event
+from Components.Sources.Clock import Clock
+from Screens.Screen import Screen
+from Screens.EventView import EventViewSimple
+from Screens.TimeDateInput import TimeDateInput
+from Screens.TimerEntry import TimerEntry
+from Tools.Directories import resolveFilename, SCOPE_SKIN_IMAGE
+from RecordTimer import RecordTimerEntry, parseEvent
+from ServiceReference import ServiceReference
+from enigma import eEPGCache, eListbox, eListboxPythonMultiContent, gFont, loadPNG, \
+       RT_HALIGN_LEFT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, RT_WRAP, eRect, eTimer
+
+from time import localtime, time, strftime
+
+class EPGList(HTMLComponent, GUIComponent):
+       def __init__(self, selChangedCB=None, timer = None, time_epoch = 120, overjump_empty=True):
+               self.cur_event = None
+               self.cur_service = None
+               self.offs = 0
+               self.timer = timer
+               self.onSelChanged = [ ]
+               if selChangedCB is not None:
+                       self.onSelChanged.append(selChangedCB)
+               GUIComponent.__init__(self)
+               self.l = eListboxPythonMultiContent()
+               self.l.setItemHeight(54);
+               self.l.setBuildFunc(self.buildEntry)
+               if overjump_empty:
+                       self.l.setSelectableFunc(self.isSelectable)
+               self.epgcache = eEPGCache.getInstance()
+               self.clock_pixmap = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, 'epgclock-fs8.png'))
+               self.time_base = None
+               self.time_epoch = time_epoch
+               self.list = None
+               self.entry_rect = None
+
+       def isSelectable(self, service, sname, event_list):
+               return (event_list and len(event_list) and True) or False
+
+       def setEpoch(self, epoch):
+               if self.cur_event is not None and self.cur_service is not None:
+                       self.offs = 0
+                       self.time_epoch = epoch
+                       self.fillMultiEPG(None) # refill
+
+       def getEventFromId(self, service, eventid):
+               event = None
+               if self.epgcache is not None and eventid is not None:
+                       event = self.epgcache.lookupEventId(service.ref, eventid)
+               return event
+
+       def getCurrent(self):
+               if self.cur_service is None or self.cur_event is None:
+                       return ( None, None )
+               old_service = self.cur_service  #(service, service_name, events)
+               events = self.cur_service[2]
+               refstr = self.cur_service[0]
+               if not events or not len(events):
+                       return ( None, None )
+               event = events[self.cur_event] #(event_id, event_title, begin_time, duration)
+               eventid = event[0]
+               service = ServiceReference(refstr)
+               event = self.getEventFromId(service, eventid)
+               return ( event, service )
+
+       def connectSelectionChanged(func):
+               if not self.onSelChanged.count(func):
+                       self.onSelChanged.append(func)
+
+       def disconnectSelectionChanged(func):
+               self.onSelChanged.remove(func)
+
+       def serviceChanged(self):
+               cur_sel = self.l.getCurrentSelection()
+               if cur_sel:
+                       self.findBestEvent()
+
+       def findBestEvent(self):
+               old_service = self.cur_service  #(service, service_name, events)
+               cur_service = self.cur_service = self.l.getCurrentSelection()
+               last_time = 0;
+               if old_service and self.cur_event is not None:
+                       events = old_service[2]
+                       cur_event = events[self.cur_event] #(event_id, event_title, begin_time, duration)
+                       last_time = cur_event[2]
+               if cur_service:
+                       self.cur_event = 0
+                       events = cur_service[2]
+                       if events and len(events):
+                               if last_time:
+                                       best_diff = 0
+                                       best = len(events) #set invalid
+                                       idx = 0
+                                       for event in events: #iterate all events
+                                               diff = abs(event[2]-last_time)
+                                               if (best == len(events)) or (diff < best_diff):
+                                                       best = idx
+                                                       best_diff = diff
+                                               idx += 1
+                                       if best != len(events):
+                                               self.cur_event = best
+                       else:
+                               self.cur_event = None
+               self.selEntry(0)
+
+       def selectionChanged(self):
+               for x in self.onSelChanged:
+                       if x is not None:
+                               try:
+                                       x()
+                               except: # FIXME!!!
+                                       print "FIXME in EPGList.selectionChanged"
+                                       pass
+
+       GUI_WIDGET = eListbox
+
+       def postWidgetCreate(self, instance):
+               instance.setWrapAround(True)
+               instance.selectionChanged.get().append(self.serviceChanged)
+               instance.setContent(self.l)
+
+       def recalcEntrySize(self):
+               esize = self.l.getItemSize()
+               self.l.setFont(0, gFont("Regular", 20))
+               self.l.setFont(1, gFont("Regular", 14))
+               width = esize.width()
+               height = esize.height()
+               xpos = 0;
+               w = width/10*2;
+               self.service_rect = Rect(xpos, 0, w-10, height)
+               xpos += w;
+               w = width/10*8;
+               self.event_rect = Rect(xpos, 0, w, height)
+               self.l.setSelectionClip(eRect(xpos, 0, w, height), False)
+
+       def calcEntryPosAndWidthHelper(self, stime, duration, start, end, width):
+               xpos = (stime - start) * width / (end - start)
+               ewidth = (stime + duration - start) * width / (end - start)
+               ewidth -= xpos;
+               if xpos < 0:
+                       ewidth += xpos;
+                       xpos = 0;
+               if (xpos+ewidth) > width:
+                       ewidth = width - xpos
+               return xpos, ewidth
+
+       def calcEntryPosAndWidth(self, event_rect, time_base, time_epoch, ev_start, ev_duration):
+               xpos, width = self.calcEntryPosAndWidthHelper(ev_start, ev_duration, time_base, time_base + time_epoch * 60, event_rect.width())
+               return xpos+event_rect.left(), width
+
+       def buildEntry(self, service, service_name, events):
+               r1=self.service_rect
+               r2=self.event_rect
+               res = [ None ] # no private data needed
+               res.append((eListboxPythonMultiContent.TYPE_TEXT, r1.left(), r1.top(), r1.width(), r1.height(), 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, service_name))
+               start = self.time_base+self.offs*self.time_epoch*60
+               end = start + self.time_epoch * 60
+               left = r2.left()
+               top = r2.top()
+               width = r2.width()
+               height = r2.height()
+               if events:
+                       for ev in events:  #(event_id, event_title, begin_time, duration)
+                               rec=self.timer.isInTimer(ev[0], ev[2], ev[3], service) > ((ev[3]/10)*8)
+                               xpos, ewidth = self.calcEntryPosAndWidthHelper(ev[2], ev[3], start, end, width)
+                               res.append((eListboxPythonMultiContent.TYPE_TEXT, left+xpos, top, ewidth, height, 1, RT_HALIGN_CENTER|RT_VALIGN_CENTER|RT_WRAP, ev[1], None, 0x586d88, 0x808080, 1))
+                               if rec and ewidth > 23:
+                                       res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, left+xpos+ewidth-22, top+height-22, 21, 21, self.clock_pixmap, 0x586d88, 0x808080))
+               return res
+
+       def selEntry(self, dir):
+               cur_service = self.cur_service #(service, service_name, events)
+               if not self.entry_rect:
+                       self.recalcEntrySize()
+               if cur_service and self.cur_event is not None:
+                       update = True
+                       entries = cur_service[2]
+                       if dir == 0: #current
+                               update = False
+                               pass
+                       elif dir == +1: #next
+                               if self.cur_event+1 < len(entries):
+                                       self.cur_event+=1
+                               else:
+                                       self.offs += 1
+                                       self.fillMultiEPG(None) # refill
+                                       return
+                       elif dir == -1: #prev
+                               if self.cur_event-1 >= 0:
+                                       self.cur_event-=1
+                               elif self.offs > 0:
+                                       self.offs -= 1
+                                       self.fillMultiEPG(None) # refill
+                                       return
+                       entry = entries[self.cur_event] #(event_id, event_title, begin_time, duration)
+                       time_base = self.time_base+self.offs*self.time_epoch*60
+                       xpos, width = self.calcEntryPosAndWidth(self.event_rect, time_base, self.time_epoch, entry[2], entry[3])
+                       self.l.setSelectionClip(eRect(xpos, 0, width, self.event_rect.height()), update)
+               else:
+                       self.l.setSelectionClip(eRect(self.event_rect.left(), self.event_rect.top(), self.event_rect.width(), self.event_rect.height()), False)
+               self.selectionChanged()
+
+       def queryEPG(self, list, buildFunc=None):
+               if self.epgcache is not None:
+                       if buildFunc is not None:
+                               return self.epgcache.lookupEvent(list, buildFunc)
+                       else:
+                               return self.epgcache.lookupEvent(list)
+               return [ ]
+
+       def fillMultiEPG(self, services, stime=-1):
+               if services is None:
+                       time_base = self.time_base+self.offs*self.time_epoch*60
+                       test = [ (service[0], 0, time_base, self.time_epoch) for service in self.list ]
+               else:
+                       self.cur_event = None
+                       self.cur_service = None
+                       self.time_base = int(stime)
+                       test = [ (service.ref.toString(), 0, self.time_base, self.time_epoch) for service in services ]
+               test.insert(0, 'RnITBD')
+               epg_data = self.queryEPG(test)
+
+               self.list = [ ]
+               tmp_list = None
+               service = ""
+               sname = ""
+               for x in epg_data:
+                       if service != x[0]:
+                               if tmp_list is not None:
+                                       self.list.append((service, sname, tmp_list[0][0] and tmp_list))
+                               service = x[0]
+                               sname = x[1]
+                               tmp_list = [ ]
+                       tmp_list.append((x[2], x[3], x[4], x[5]))
+               if tmp_list and len(tmp_list):
+                       self.list.append((service, sname, tmp_list[0][0] and tmp_list))
+
+               self.l.setList(self.list)
+               self.findBestEvent()
+
+       def getEventRect(self):
+               return self.event_rect
+
+       def getTimeEpoch(self):
+               return self.time_epoch
+
+       def getTimeBase(self):
+               return self.time_base
+
+class TimelineText(HTMLComponent, GUIComponent):
+       def __init__(self):
+               GUIComponent.__init__(self)
+               self.l = eListboxPythonMultiContent()
+               self.l.setSelectionClip(eRect(0,0,0,0))
+               self.l.setItemHeight(25);
+               self.l.setFont(0, gFont("Regular", 20))
+
+       GUI_WIDGET = eListbox
+
+       def postWidgetCreate(self, instance):
+               instance.setContent(self.l)
+
+       def setEntries(self, entries):
+               res = [ None ] # no private data needed
+               for x in entries:
+                       tm = x[0]
+                       xpos = x[1]
+                       str = strftime("%H:%M", localtime(tm))
+                       res.append((eListboxPythonMultiContent.TYPE_TEXT, xpos-30, 0, 60, 25, 0, RT_HALIGN_CENTER|RT_VALIGN_CENTER, str))
+               self.l.setList([res])
+
+config.misc.graph_mepg_prev_time=ConfigClock(default = time())
+config.misc.graph_mepg_prev_time_period=ConfigInteger(default=120, limits=(60,300))
+
+class GraphMultiEPG(Screen):
+       def __init__(self, session, services, zapFunc=None, bouquetChangeCB=None):
+               Screen.__init__(self, session)
+               self.bouquetChangeCB = bouquetChangeCB
+               now = time()
+               tmp = now % 900
+               self.ask_time = now - tmp
+               self.closeRecursive = False
+               self["key_red"] = Button("")
+               self["key_green"] = Button(_("Add timer"))
+               self["timeline_text"] = TimelineText()
+               self["Event"] = Event()
+               self["Clock"] = Clock()
+               self.time_lines = [ ]
+               for x in (0,1,2,3,4,5):
+                       pm = Pixmap()
+                       self.time_lines.append(pm)
+                       self["timeline%d"%(x)] = pm
+               self["timeline_now"] = Pixmap()
+               self.services = services
+               self.zapFunc = zapFunc
+
+               self["list"] = EPGList(selChangedCB = self.onSelectionChanged, timer = self.session.nav.RecordTimer, time_epoch = config.misc.graph_mepg_prev_time_period.value )
+
+               self["actions"] = ActionMap(["EPGSelectActions", "OkCancelActions"],
+                       {
+                               "cancel": self.closeScreen,
+                               "ok": self.eventSelected,
+                               "timerAdd": self.timerAdd,
+                               "info": self.infoKeyPressed,
+                               "red": self.zapTo,
+                               "input_date_time": self.enterDateTime,
+                               "nextBouquet": self.nextBouquet,
+                               "prevBouquet": self.prevBouquet,
+                       })
+               self["actions"].csel = self
+
+               self["input_actions"] = ActionMap(["InputActions"],
+                       {
+                               "left": self.prevEvent,
+                               "right": self.nextEvent,
+                               "1": self.key1,
+                               "2": self.key2,
+                               "3": self.key3,
+                               "4": self.key4,
+                               "5": self.key5,
+                       },-1)
+
+               self.updateTimelineTimer = eTimer()
+               self.updateTimelineTimer.timeout.get().append(self.moveTimeLines)
+               self.updateTimelineTimer.start(60*1000)
+               self.onLayoutFinish.append(self.onCreate)
+
+       def nextEvent(self):
+               self["list"].selEntry(+1)
+
+       def prevEvent(self):
+               self["list"].selEntry(-1)
+
+       def key1(self):
+               self["list"].setEpoch(60)
+               config.misc.graph_mepg_prev_time_period.value = 60
+               self.moveTimeLines()
+
+       def key2(self):
+               self["list"].setEpoch(120)
+               config.misc.graph_mepg_prev_time_period.value = 120
+               self.moveTimeLines()
+
+       def key3(self):
+               self["list"].setEpoch(180)
+               config.misc.graph_mepg_prev_time_period.value = 180
+               self.moveTimeLines()
+
+       def key4(self):
+               self["list"].setEpoch(240)
+               config.misc.graph_mepg_prev_time_period.value = 240
+               self.moveTimeLines()
+
+       def key5(self):
+               self["list"].setEpoch(300)
+               config.misc.graph_mepg_prev_time_period.value = 300
+               self.moveTimeLines()
+
+       def nextBouquet(self):
+               if self.bouquetChangeCB:
+                       self.bouquetChangeCB(1, self)
+
+       def prevBouquet(self):
+               if self.bouquetChangeCB:
+                       self.bouquetChangeCB(-1, self)
+
+       def enterDateTime(self):
+               self.session.openWithCallback(self.onDateTimeInputClosed, TimeDateInput, config.misc.graph_mepg_prev_time )
+
+       def onDateTimeInputClosed(self, ret):
+               if len(ret) > 1:
+                       if ret[0]:
+                               self.ask_time=ret[1]
+                               self["list"].fillMultiEPG(self.services, ret[1])
+                               self.moveTimeLines()
+
+       def closeScreen(self):
+               self.close(self.closeRecursive)
+
+       def infoKeyPressed(self):
+               cur = self["list"].getCurrent()
+               event = cur[0]
+               service = cur[1]
+               if event is not None:
+                       self.session.open(EventViewSimple, event, service, self.eventViewCallback, self.openSimilarList)
+
+       def openSimilarList(self, eventid, refstr):
+               self.session.open(EPGSelection, refstr, None, eventid)
+
+       def setServices(self, services):
+               self.services = services
+               self.onCreate()
+
+       #just used in multipeg
+       def onCreate(self):
+               self["list"].fillMultiEPG(self.services, self.ask_time)
+               self.moveTimeLines()
+
+       def eventViewCallback(self, setEvent, setService, val):
+               l = self["list"]
+               old = l.getCurrent()
+               if val == -1:
+                       l.selEntry(-1)
+               elif val == +1:
+                       self.selEntry(+1)
+               cur = l.getCurrent()
+               if cur[0] is None and cur[1].ref != old[1].ref:
+                       self.eventViewCallback(setEvent, setService, val)
+               else:
+                       setService(cur[1])
+                       setEvent(cur[0])
+
+       def zapTo(self):
+               if self.zapFunc and self["key_red"].getText() == "Zap":
+                       self.closeRecursive = True
+                       ref = self["list"].getCurrent()[1]
+                       self.zapFunc(ref.ref)
+
+       def eventSelected(self):
+               self.infoKeyPressed()
+
+       def timerAdd(self):
+               cur = self["list"].getCurrent()
+               event = cur[0]
+               serviceref = cur[1]
+               if event is None:
+                       return
+               newEntry = RecordTimerEntry(serviceref, checkOldTimers = True, *parseEvent(event))
+               self.session.openWithCallback(self.timerEditFinished, TimerEntry, newEntry)
+
+       def timerEditFinished(self, answer):
+               if answer[0]:
+                       self.session.nav.RecordTimer.record(answer[1])
+               else:
+                       print "Timeredit aborted"
+
+       def onSelectionChanged(self):
+               evt = self["list"].getCurrent()
+               self["Event"].newEvent(evt and evt[0])
+               if evt and evt[0]:
+                       evt = evt[0]
+                       now = time()
+                       start = evt.getBeginTime()
+                       end = start + evt.getDuration()
+                       if now >= start and now <= end:
+                               self["key_red"].setText("Zap")
+                       else:
+                               self["key_red"].setText("")
+
+       def moveTimeLines(self):
+               l = self["list"]
+               event_rect = l.getEventRect()
+               time_epoch = l.getTimeEpoch()
+               time_base = l.getTimeBase()
+               if event_rect is None or time_epoch is None or time_base is None:
+                       return
+               time_steps = time_epoch > 180 and 60 or 30
+               num_lines = time_epoch/time_steps
+               incWidth=event_rect.width()/num_lines
+               pos=event_rect.left()
+               timeline_entries = [ ]
+               x = 0
+               changecount = 0
+               for line in self.time_lines:
+                       old_pos = line.position
+                       new_pos = (x == num_lines and event_rect.left()+event_rect.width() or pos, old_pos[1])
+                       if not x or x >= num_lines:
+                               line.visible = False
+                       else:
+                               if old_pos != new_pos:
+                                       line.setPosition(new_pos[0], new_pos[1])
+                                       changecount += 1
+                               line.visible = True
+                       if not x or line.visible:
+                               timeline_entries.append((time_base + x * time_steps * 60, new_pos[0]))
+                       x += 1
+                       pos += incWidth
+
+               if changecount:
+                       self["timeline_text"].setEntries(timeline_entries)
+
+               now=time()
+               timeline_now = self["timeline_now"]
+               if now >= time_base and now < (time_base + time_epoch * 60):
+                       bla = (event_rect.width() * 1000) / time_epoch
+                       xpos = ((now/60) - (time_base/60)) * bla / 1000
+                       old_pos = timeline_now.position
+                       new_pos = (xpos+event_rect.left(), old_pos[1])
+                       if old_pos != new_pos:
+                               timeline_now.setPosition(new_pos[0], new_pos[1])
+                       timeline_now.visible = True
+               else:
+                       timeline_now.visible = False
diff --git a/lib/python/Plugins/Extensions/GraphMultiEPG/Makefile.am b/lib/python/Plugins/Extensions/GraphMultiEPG/Makefile.am
new file mode 100644 (file)
index 0000000..eeaef4f
--- /dev/null
@@ -0,0 +1,7 @@
+installdir = $(LIBDIR)/enigma2/python/Plugins/Extensions/GraphMultiEPG
+
+install_PYTHON =       \
+       __init__.py \
+       plugin.py \
+       GraphMultiEpg.py
diff --git a/lib/python/Plugins/Extensions/GraphMultiEPG/__init__.py b/lib/python/Plugins/Extensions/GraphMultiEPG/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py b/lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py
new file mode 100644 (file)
index 0000000..a247a61
--- /dev/null
@@ -0,0 +1,96 @@
+from Plugins.Plugin import PluginDescriptor
+from GraphMultiEpg import GraphMultiEPG
+from Screens.ChannelSelection import BouquetSelector
+from enigma import eServiceCenter, eServiceReference
+from ServiceReference import ServiceReference
+
+Session = None
+Servicelist = None
+
+bouquetSel = None
+epg_bouquet = None
+dlg_stack = [ ]
+
+def zapToService(service):
+       if not service is None:
+               if Servicelist.getRoot() != epg_bouquet: #already in correct bouquet?
+                       Servicelist.clearPath()
+                       if Servicelist.bouquet_root != epg_bouquet:
+                               Servicelist.enterPath(Servicelist.bouquet_root)
+                       Servicelist.enterPath(epg_bouquet)
+               Servicelist.setCurrentSelection(service) #select the service in Servicelist
+               Servicelist.zap()
+
+def getBouquetServices(bouquet):
+       services = [ ]
+       Servicelist = eServiceCenter.getInstance().list(bouquet)
+       if not Servicelist is None:
+               while True:
+                       service = Servicelist.getNext()
+                       if not service.valid(): #check if end of list
+                               break
+                       if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
+                               continue
+                       services.append(ServiceReference(service))
+       return services
+
+def cleanup():
+       global Session
+       Session = None
+       global Servicelist
+       Servicelist = None
+
+def closed(ret=False):
+       closedScreen = dlg_stack.pop()
+       if bouquetSel and closedScreen == bouquetSel:
+               global bouquetSel
+               bouquetSel = None
+       dlgs=len(dlg_stack)
+       if ret and dlgs > 0: # recursive close wished
+               dlg_stack[dlgs-1].close(dlgs > 1)
+       if dlgs <= 0:
+               cleanup()
+
+def openBouquetEPG(bouquet):
+       services = getBouquetServices(bouquet)
+       if len(services):
+               global epg_bouquet
+               epg_bouquet = bouquet
+               dlg_stack.append(Session.openWithCallback(closed, GraphMultiEPG, services, zapToService, changeBouquetCB))
+               return True
+       return False
+
+def changeBouquetCB(direction, epg):
+       if bouquetSel:
+               if direction > 0:
+                       bouquetSel.down()
+               else:
+                       bouquetSel.up()
+               bouquet = bouquetSel.getCurrent()
+               services = getBouquetServices(bouquet)
+               if len(services):
+                       epg_bouquet = bouquet
+                       epg.setServices(services)
+
+def main(session, servicelist, **kwargs):
+       global Session
+       Session = session
+       global Servicelist
+       Servicelist = servicelist
+       bouquets = Servicelist.getBouquetList()
+       if bouquets is None:
+               cnt = 0
+       else:
+               cnt = len(bouquets)
+       if cnt > 1: # show bouquet list
+               global bouquetSel
+               bouquetSel = Session.openWithCallback(closed, BouquetSelector, bouquets, openBouquetEPG, enableWrapAround=True)
+               dlg_stack.append(bouquetSel)
+       elif cnt == 1:
+               if not openBouquetEPG(bouquets[0][1]):
+                       cleanup()
+
+def Plugins(**kwargs):
+       name = _("Graphical Multi EPG")
+       descr = _("A graphical EPG for all services of an specific bouquet")
+       return [ PluginDescriptor(name=name, description=descr, where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main) ]
index eca436b..ce80cd6 100644 (file)
@@ -1,2 +1,2 @@
-SUBDIRS = TuxboxPlugins FileManager CutListEditor FritzCall PicturePlayer MediaScanner IpkgInstaller
+SUBDIRS = TuxboxPlugins FileManager CutListEditor FritzCall PicturePlayer MediaScanner IpkgInstaller GraphMultiEPG
 # SimpleRSS is still not finished
 # SimpleRSS is still not finished