aboutsummaryrefslogtreecommitdiff
path: root/lib/python/Plugins/Extensions
diff options
context:
space:
mode:
authorAndreas Monzner <andreas.monzner@multimedia-labs.de>2007-07-31 11:00:01 +0000
committerAndreas Monzner <andreas.monzner@multimedia-labs.de>2007-07-31 11:00:01 +0000
commitb1d4a0b06608906144928198f66062705a8041fd (patch)
treecd68fda9eb954ae6e4226fb250265cd036069d1b /lib/python/Plugins/Extensions
parent6db060dce242cd1990ae82f518ffa0beaee74d06 (diff)
downloadenigma2-b1d4a0b06608906144928198f66062705a8041fd.tar.gz
enigma2-b1d4a0b06608906144928198f66062705a8041fd.zip
add graphical multiepg plugin (like e1)
yet it is just startable via blue button menu
Diffstat (limited to 'lib/python/Plugins/Extensions')
-rw-r--r--lib/python/Plugins/Extensions/GraphMultiEPG/GraphMultiEpg.py500
-rw-r--r--lib/python/Plugins/Extensions/GraphMultiEPG/Makefile.am7
-rw-r--r--lib/python/Plugins/Extensions/GraphMultiEPG/__init__.py0
-rw-r--r--lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py96
-rw-r--r--lib/python/Plugins/Extensions/Makefile.am2
5 files changed, 604 insertions, 1 deletions
diff --git a/lib/python/Plugins/Extensions/GraphMultiEPG/GraphMultiEpg.py b/lib/python/Plugins/Extensions/GraphMultiEPG/GraphMultiEpg.py
new file mode 100644
index 00000000..d8c654bc
--- /dev/null
+++ b/lib/python/Plugins/Extensions/GraphMultiEPG/GraphMultiEpg.py
@@ -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
index 00000000..eeaef4fb
--- /dev/null
+++ b/lib/python/Plugins/Extensions/GraphMultiEPG/Makefile.am
@@ -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
index 00000000..e69de29b
--- /dev/null
+++ b/lib/python/Plugins/Extensions/GraphMultiEPG/__init__.py
diff --git a/lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py b/lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py
new file mode 100644
index 00000000..a247a618
--- /dev/null
+++ b/lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py
@@ -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) ]
diff --git a/lib/python/Plugins/Extensions/Makefile.am b/lib/python/Plugins/Extensions/Makefile.am
index eca436bc..ce80cd60 100644
--- a/lib/python/Plugins/Extensions/Makefile.am
+++ b/lib/python/Plugins/Extensions/Makefile.am
@@ -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