#
#  MerlinEPGCenter E2 Plugin
#
#  $Id: EpgCenterList.py,v 1.0 2011-02-14 21:53:00 shaderman Exp $
#
#  Coded by Shaderman (c) 2011
#  Support: www.dreambox-tools.info
#
#  This plugin is licensed under the Creative Commons 
#  Attribution-NonCommercial-ShareAlike 3.0 Unported 
#  License. To view a copy of this license, visit
#  http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative
#  Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
#
#  Alternatively, this plugin may be distributed and executed on hardware which
#  is licensed by Dream Property GmbH.

#  This plugin is NOT free software. It is open source, you are allowed to
#  modify it (if you keep the license), but it may not be commercially 
#  distributed other than under the conditions noted above.
#
# PYTHON IMPORTS
from datetime import datetime
from time import localtime, strftime, time

# ENIGMA IMPORTS
from Components.config import config
from Components.GUIComponent import GUIComponent
from Components.TimerList import TimerList
from enigma import eEPGCache, eServiceReference, eServiceCenter, eListbox, eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT, RT_HALIGN_CENTER, RT_HALIGN_RIGHT, RT_VALIGN_CENTER, RT_VALIGN_TOP, RT_VALIGN_BOTTOM, getDesktop
from math import fabs
import NavigationInstance
from RecordTimer import RecordTimerEntry
from ServiceReference import ServiceReference
from skin import parseColor
from timer import TimerEntry
from Tools.Directories import resolveFilename, SCOPE_CURRENT_PLUGIN
from Tools.LoadPixmap import LoadPixmap

# OWN IMPORTS
from ConfigTabs import KEEP_OUTDATED_TIME, STYLE_SIMPLE_BAR, STYLE_PIXMAP_BAR, STYLE_MULTI_PIXMAP, STYLE_PERCENT_TEXT, STYLE_SIMPLE_BAR_LIST_OFF, STYLE_PIXMAP_BAR_LIST_OFF, STYLE_MULTI_PIXMAP_LIST_OFF, STYLE_PERCENT_TEXT_LIST_OFF
from HelperFunctions import getFuzzyDay, LIST_TYPE_EPG, LIST_TYPE_UPCOMING, TimerListObject
from MerlinEPGCenter import STYLE_SINGLE_LINE, STYLE_SHORT_DESCRIPTION
from Components.ServiceRecordingSettings import ServiceRecordingSettings # Dr. Best ServiceRecordingSettings

MODE_FHD = 0
MODE_HD = 1
MODE_XD = 2
MODE_SD = 3

MULTI_EPG_NOW = 0
MULTI_EPG_NEXT = 1
SINGLE_EPG = 2
MULTI_EPG_PRIMETIME = 3
TIMERLIST = 4
EPGSEARCH_HISTORY = 5
EPGSEARCH_RESULT = 6
EPGSEARCH_MANUAL = 7
UPCOMING = 8

TIMER_TYPE_EID_MATCH = 1
TIMER_TYPE_COVERS_FULL = 2
TIMER_TYPE_COVERS_END = 4
TIMER_TYPE_COVERS_BEGIN = 8
TIMER_TYPE_EID_REPEATED = 16
TIMER_TYPE_INSIDE_EVENT = 32
TIMER_TYPE_ADD = 64
TIMER_TYPE_ADD_INSIDE_EVENT = 128
TIMER_TYPE_ADD_COVERS_FULL = 256
TIMER_TYPE_ADD_COVERS_END = 512
TIMER_TYPE_ADD_COVERS_BEGIN = 1024


class EpgCenterList(GUIComponent):
	# some static stuff used by ["list"] and ["upcoming"] widgets
	infoBarInstance = None
	eServiceCenterInstance = None
	bouquetList = []
	bouquetServices = []
	alternativesDict = {}
	currentBouquetIndex = 0
	bouquetIndexRanges = []
	allServicesNameDict = {}
	recordTimer = None
	lenChannelDigits = 0
	
	def __init__(self, blinkTimer, listType, videoMode, piconLoader, bouquetList, currentIndex, piconSize, listStyle, epgList):
		self.blinkTimer = blinkTimer
		self.listType = listType
		self.videoMode = videoMode
		self.piconLoader = piconLoader
		self.piconSize = piconSize
		self.baseHeight = self.piconSize.height()
		self.listStyle = listStyle
		self.epgList = epgList
		
		GUIComponent.__init__(self)
		
		from Screens.InfoBar import InfoBar
		EpgCenterList.infoBarInstance = InfoBar.instance
		EpgCenterList.eServiceCenterInstance = eServiceCenter.getInstance()
		
		self.l = eListboxPythonMultiContent()
		self.l.setBuildFunc(self.buildEpgEntry)
		self.onSelectionChanged = [ ]
		
		if self.videoMode == MODE_SD or self.videoMode == MODE_XD:
			self.overallFontHeight = 36
		elif self.videoMode == MODE_HD:
			self.overallFontHeight = 44
		elif self.videoMode == MODE_FHD:
			self.overallFontHeight = 66
			
		#initialize
		self.list = []
		self.mode = None
		self.similarShown = False
		
		config.plugins.merlinEpgCenter.listItemHeight.addNotifier(self.changeHeight, initial_call = True)
		config.plugins.merlinEpgCenter.adjustFontSize.addNotifier(self.setFontSizes, initial_call = True)
		
		if listType == LIST_TYPE_EPG:
			EpgCenterList.bouquetList = bouquetList
			EpgCenterList.currentBouquetIndex = currentIndex
			EpgCenterList.updateBouquetServices()
			EpgCenterList.recordTimer = NavigationInstance.instance.RecordTimer
			
		# zap timer pixmaps
		self.zap_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/zap.png"))
		self.zap_pre_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/zap_pre.png"))
		self.zap_post_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/zap_post.png"))
		self.zap_event_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/zap_event.png"))
		self.zap_repeated_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/zap_repeated.png"))
		self.zap_add_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/zap_add.png"))
		
		# record timer pixmaps
		self.timer_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/timer.png"))
		self.timer_pre_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/timer_pre.png"))
		self.timer_post_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/timer_post.png"))
		self.timer_event_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/timer_event.png"))
		self.timer_repeated_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/timer_repeated.png"))
		self.timer_add_pixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/timer_add.png"))
		
		# progress pixmaps
		self.progressPixmap = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/Progress.png"))
		self.progressPixmap_1 = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/Progress_1.png"))
		self.progressPixmap_2 = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/Progress_2.png"))
		self.progressPixmap_3 = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/Progress_3.png"))
		self.progressPixmap_4 = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/Progress_4.png"))
		self.progressPixmapWidth = self.progressPixmap.size().width()
		
		self.epgcache = eEPGCache.getInstance()
		
		self.blinkTimer.callbacks.append(self.invalidateList)
		
	def onShow(self):
		self.maxWidth = self.l.getItemSize().width()
		
	def setFontSizes(self, configElement = None):
		diff = configElement.getValue()
		
		if self.videoMode == MODE_SD:
			self.l.setFont(0, gFont("Regular", 18 + diff))
			self.l.setFont(1, gFont("Regular", 16 + diff))
			self.l.setFont(2, gFont("Regular", 14 + diff))
			self.l.setFont(3, gFont("Regular", 12 + diff))
		elif self.videoMode == MODE_XD:
			self.l.setFont(0, gFont("Regular", 18 + diff))
			self.l.setFont(1, gFont("Regular", 16 + diff))
			self.l.setFont(2, gFont("Regular", 14 + diff))
			self.l.setFont(3, gFont("Regular", 12 + diff))
		elif self.videoMode == MODE_HD:
			self.l.setFont(0, gFont("Regular", 22 + diff))
			self.l.setFont(1, gFont("Regular", 20 + diff))
			self.l.setFont(2, gFont("Regular", 18 + diff))
			self.l.setFont(3, gFont("Regular", 16 + diff))
		elif self.videoMode == MODE_FHD:
			self.l.setFont(0, gFont("Regular", 33 + diff))
			self.l.setFont(1, gFont("Regular", 30 + diff))
			self.l.setFont(2, gFont("Regular", 27 + diff))
			self.l.setFont(3, gFont("Regular", 24 + diff))
			
	def setMaxWidth(self, newSize):
		self.maxWidth = newSize.width()
		
	def changeHeight(self, configElement = None):
		self.listStyle = config.plugins.merlinEpgCenter.listStyle.value
		if self.listStyle == STYLE_SINGLE_LINE:
			self.singleLineBorder = 4
		else:
			self.singleLineBorder = 0
			
		if self.listStyle == STYLE_SHORT_DESCRIPTION or (self.listStyle == STYLE_SINGLE_LINE and (self.mode == SINGLE_EPG or self.mode == EPGSEARCH_RESULT or self.similarShown)):
			if self.overallFontHeight > self.baseHeight:
				self.itemHeight = self.overallFontHeight + int(config.plugins.merlinEpgCenter.listItemHeight.value)
			else:
				self.itemHeight = self.baseHeight + int(config.plugins.merlinEpgCenter.listItemHeight.value)
		elif self.videoMode == MODE_HD and config.plugins.merlinEpgCenter.listProgressStyle.value == STYLE_PERCENT_TEXT: # HD skin adjustment for text size
			self.itemHeight = self.baseHeight + int(config.plugins.merlinEpgCenter.listItemHeight.value) + 4
		elif self.videoMode == MODE_FHD and config.plugins.merlinEpgCenter.listProgressStyle.value == STYLE_PERCENT_TEXT: # FullHD skin adjustment for text size
			self.itemHeight = self.baseHeight + int(config.plugins.merlinEpgCenter.listItemHeight.value) + 4
		else:
			self.itemHeight = self.baseHeight + int(config.plugins.merlinEpgCenter.listItemHeight.value)
		self.halfItemHeight = self.itemHeight / 2
		self.l.setItemHeight(self.itemHeight)
		
	def buildEpgEntry(self, ignoreMe, eventid, sRef, begin, duration, title, short, desc):
		columnSpace = config.plugins.merlinEpgCenter.columnSpace.getValue()
		progressPixmap = None
		offsetLeft = 5
		offsetRight = self.maxWidth - 5 - 8 # 8 = timer pixmap width, 5 = border
		secondLineColor = 0x00909090 # grey
		border = int(config.plugins.merlinEpgCenter.listItemHeight.value) / 2
		percent = 0
		
		if begin != None and duration != None:
			timeString = strftime("%H:%M", localtime(begin)) + "-" + strftime("%H:%M", localtime(begin + duration))
			now = int(time())
			if now > begin:
				percent = (now - begin) * 100 / duration
				
			if self.mode == MULTI_EPG_NOW:
				timeValue = (begin + duration - now) / 60 +1
			elif self.mode == MULTI_EPG_NEXT or self.mode == UPCOMING:
				timeValue = (now - begin) /  60
			elif self.mode == MULTI_EPG_PRIMETIME or self.mode == EPGSEARCH_RESULT:
				if now >= begin:
					timeValue = (begin + duration - now) /  60 + 1
				else:
					timeValue = (now - begin) /  60
			elif self.mode == SINGLE_EPG:
				if self.instance.getCurrentIndex() == 0:
					timeValue = (begin + duration - now) /  60 + 1
				else:
					timeValue = (now - begin) /  60
					
			if config.plugins.merlinEpgCenter.showBeginRemainTime.value:
				if (KEEP_OUTDATED_TIME == 0 and (begin + duration) > now) or (KEEP_OUTDATED_TIME != 0 and (begin + duration) > now):
					if config.plugins.merlinEpgCenter.showDuration.value:
						remainBeginString = " I "
					else:
						remainBeginString = ""
					
					if timeValue >= 0:
						remainBeginString += "+"
					if fabs(timeValue) >= 120 and fabs(timeValue) < 1440:
						timeValue /= 60
						remainBeginString += "%0dh" % timeValue
					elif fabs(timeValue) >= 1440:
						timeValue = (timeValue / 1440) +1
						remainBeginString += "%02dd" % timeValue
					else:
						if timeValue < 0:
							remainBeginString += "%03d" % timeValue
						else:
							remainBeginString += "%02d" % timeValue
				else:
					if config.plugins.merlinEpgCenter.showDuration.value:
						remainBeginString = " I <->"
					else:
						remainBeginString = "<->"
			else:
				remainBeginString = ""
				
			if config.plugins.merlinEpgCenter.showDuration.value:
				duraString = "%d" % (duration / 60)
				
			if self.mode == MULTI_EPG_NOW:
				if config.plugins.merlinEpgCenter.listProgressStyle.value == STYLE_MULTI_PIXMAP:
					part = int(round(percent / 25)) + 1
					progressPixmap = eval('self.progressPixmap_' + str(part))
				elif config.plugins.merlinEpgCenter.listProgressStyle.value == STYLE_PIXMAP_BAR:
					progressPixmap = self.progressPixmap
		else:
			timeString = ""
			duraString = ""
			remainBeginString = ""
			
		if remainBeginString.endswith('>'): # KEEP_OUTDATED_TIME
			outdated = True
			try:
				progColor = parseColor("eventNotAvailable").argb()
			except:
				progColor = 0x777777
		elif config.plugins.merlinEpgCenter.showBeginRemainTime.value and config.plugins.merlinEpgCenter.showColoredEpgTimes.value:
			outdated = False
			if remainBeginString.endswith('h'): # begins in... hours
				progColor = 0x00ef7f1a # brown
			elif remainBeginString.endswith('d'): # begins in... days
				progColor = 0x00e31e24 # red
			elif remainBeginString.startswith(' I +') or remainBeginString.startswith('+'): # already running
				progColor = 0x0074de0a # green
			elif remainBeginString.startswith(' I -') or remainBeginString.startswith('-'): # begins in... minutes
				progColor = 0x00ffed00 # yellow
			else: # undefined, shouldn't happen
				progColor = 0x00ffffff # white
		else:
			outdated = False
			progColor = 0x00ffed00 # yellow
			
		if outdated:
			textColor = progColor
		elif self.epgList != None:
			textColor = self.epgList.getColorEventAvailable(sRef, begin, duration)
		else:
			textColor = None
			
		res = [ None ]
		
		if config.plugins.merlinEpgCenter.showListNumbers.value:
			if ((self.mode == SINGLE_EPG and not self.similarShown) or self.mode == UPCOMING) and self.instance.getCurrentIndex() != 0:
				chNumber = ""
			else:
				# check if the service is found in our bouquets (or don't show the channel number if not found)
				if self.mode == EPGSEARCH_RESULT:
					if sRef in EpgCenterList.allServicesNameDict:
						i = 0
						while i < len(EpgCenterList.bouquetServices):
							if sRef in EpgCenterList.bouquetServices[i]:
								chOffset = EpgCenterList.bouquetIndexRanges[i]
								chNumber = str(EpgCenterList.bouquetServices[i].index(sRef) + chOffset)
								break
							i += 1
					else:
						chNumber = ""
				else:
					if sRef in EpgCenterList.bouquetServices[EpgCenterList.currentBouquetIndex]:
						chOffset = EpgCenterList.bouquetIndexRanges[EpgCenterList.currentBouquetIndex]
						chNumber = str(EpgCenterList.bouquetServices[EpgCenterList.currentBouquetIndex].index(sRef) + chOffset)
					else:
						chNumber = ""
						
			if EpgCenterList.lenChannelDigits < 3:
				width = self.maxWidth * 3 / 100
			else:
				width = self.maxWidth * 4 / 100
			res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, width, self.itemHeight, 1, RT_HALIGN_RIGHT|RT_VALIGN_CENTER, chNumber))
			offsetLeft = offsetLeft + width + columnSpace
			
		if config.plugins.merlinEpgCenter.showPicons.value:
			if ((self.mode == SINGLE_EPG and not self.similarShown) or self.mode == UPCOMING) and self.instance.getCurrentIndex() != 0:
				picon = None
			else:
				picon = self.piconLoader.getPicon(sRef)
				
			width = self.piconSize.width()
			if picon:
				res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetLeft, 0, width, self.itemHeight, picon))
			offsetLeft = offsetLeft + width + columnSpace
			
		if config.plugins.merlinEpgCenter.showServiceName.value:
			extraWidth = int(config.plugins.merlinEpgCenter.serviceNameWidth.value)
			if self.videoMode == MODE_SD:
				width = self.maxWidth * (12 + extraWidth) / 100
			elif self.videoMode == MODE_XD:
				width = self.maxWidth * (14 + extraWidth) / 100
			elif self.videoMode == MODE_HD:
				width = self.maxWidth * (16 + extraWidth) / 100
			elif self.videoMode == MODE_FHD:
				width = self.maxWidth * (24 + extraWidth) / 100
				
			if not (((self.mode == SINGLE_EPG and not self.similarShown) or self.mode == UPCOMING) and self.instance.getCurrentIndex() != 0):
				if sRef in EpgCenterList.allServicesNameDict:
					serviceName = EpgCenterList.allServicesNameDict[sRef]
				else:
					serviceName = ServiceReference(sRef).getServiceName()
					
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, width, self.itemHeight, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, serviceName))
			offsetLeft = offsetLeft + width + columnSpace
			
		if self.mode == MULTI_EPG_NOW and not self.similarShown:
			extraWidth = int(config.plugins.merlinEpgCenter.adjustFontSize.value)
			if extraWidth < 0:
				extraWidth = 0
			if self.videoMode == MODE_SD:
				width = self.maxWidth * (18 + extraWidth) / 100
			else:
				width = self.maxWidth * (14 + extraWidth) / 100
			progressHeight = 6
			
			if config.plugins.merlinEpgCenter.listProgressStyle.value < STYLE_SIMPLE_BAR_LIST_OFF: # show progress in lists
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, border, width, self.halfItemHeight - border + (self.singleLineBorder * 2), 1, RT_HALIGN_CENTER|RT_VALIGN_TOP, timeString))
			else: # don't show progress in lists
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, width, self.itemHeight, 1, RT_HALIGN_CENTER|RT_VALIGN_CENTER, timeString))
				
			if config.plugins.merlinEpgCenter.listProgressStyle.value == STYLE_MULTI_PIXMAP and progressPixmap is not None:
				if width > self.progressPixmapWidth:
					progressOffset = int((width - self.progressPixmapWidth) / 2)
				else:
					progressOffset = 0
				res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetLeft + progressOffset, self.halfItemHeight + (self.halfItemHeight - progressHeight) / 2 + self.singleLineBorder, width, progressHeight, progressPixmap))
			elif config.plugins.merlinEpgCenter.listProgressStyle.value == STYLE_SIMPLE_BAR:
				res.append((eListboxPythonMultiContent.TYPE_PROGRESS, offsetLeft, self.halfItemHeight + (self.halfItemHeight - progressHeight) / 2 + self.singleLineBorder, width, progressHeight, percent, 1, secondLineColor))
			elif config.plugins.merlinEpgCenter.listProgressStyle.value == STYLE_PIXMAP_BAR and progressPixmap is not None:
				if width > self.progressPixmapWidth:
					progressOffset = int((width - self.progressPixmapWidth) / 2)
				else:
					progressOffset = 0
				res.append((eListboxPythonMultiContent.TYPE_PROGRESS_PIXMAP, offsetLeft + progressOffset, self.halfItemHeight + (self.halfItemHeight - progressHeight) / 2 + self.singleLineBorder, width, progressHeight, percent, progressPixmap, 0))
			elif config.plugins.merlinEpgCenter.listProgressStyle.value == STYLE_PERCENT_TEXT:
				if self.videoMode == MODE_SD: # we need a bigger font for SD skins
					font = 2
				else:
					font = 3
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, self.halfItemHeight, width, self.halfItemHeight - border, font, RT_HALIGN_CENTER|RT_VALIGN_TOP, str(percent) + "%", secondLineColor))
				
			offsetLeft = offsetLeft + width + columnSpace
		else:
			extraWidth = int(config.plugins.merlinEpgCenter.adjustFontSize.value)
			if extraWidth < 0:
				extraWidth = 0
			if self.videoMode == MODE_SD:
				width = self.maxWidth * (18 + extraWidth) / 100
			else:
				width = self.maxWidth * (14 + extraWidth) / 100
			if self.mode == SINGLE_EPG or self.mode == EPGSEARCH_RESULT or self.similarShown:
				fd = getFuzzyDay(begin)
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, border, width, self.halfItemHeight - border, 1, RT_HALIGN_CENTER|RT_VALIGN_TOP, timeString, textColor))
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, self.halfItemHeight, width, self.halfItemHeight - border, 2, RT_HALIGN_CENTER|RT_VALIGN_TOP, fd, secondLineColor))
			else:
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, width, self.itemHeight, 1, RT_HALIGN_CENTER|RT_VALIGN_CENTER, timeString, textColor))
			offsetLeft = offsetLeft + width + columnSpace
			
		if begin != None and duration != None:
			(timerPixmaps, zapPixmaps, isRunning) = self.getTimerPixmapsForEntry(sRef, eventid, begin, duration)
		else:
			timerPixmaps = 0
			zapPixmaps = 0
			isRunning = 0
			
		idx = self.instance.getCurrentIndex()
		self.blinkTimer.updateEntry(self.listType, idx, isRunning)
		
		if zapPixmaps:
			if (zapPixmaps & TIMER_TYPE_EID_MATCH) or (zapPixmaps & TIMER_TYPE_COVERS_FULL) or (zapPixmaps & TIMER_TYPE_EID_REPEATED) or (zapPixmaps & TIMER_TYPE_ADD_COVERS_FULL):
				posY = 2
				height = self.itemHeight - 4
				if (zapPixmaps & TIMER_TYPE_EID_MATCH):
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_event_pixmap))
				elif (zapPixmaps & TIMER_TYPE_COVERS_FULL):
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_pixmap))
				elif (zapPixmaps & TIMER_TYPE_EID_REPEATED):
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_repeated_pixmap))
				elif (zapPixmaps & TIMER_TYPE_ADD_COVERS_FULL):
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_add_pixmap))
			elif (zapPixmaps & TIMER_TYPE_INSIDE_EVENT) or (zapPixmaps & TIMER_TYPE_ADD_INSIDE_EVENT):
				posY = self.itemHeight / 2 - 6
				height = 12
				if (zapPixmaps & TIMER_TYPE_INSIDE_EVENT):
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_pixmap))
				elif (zapPixmaps & TIMER_TYPE_ADD_INSIDE_EVENT):
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_add_pixmap))
			else:
				if zapPixmaps & TIMER_TYPE_COVERS_END:
					posY = self.itemHeight / 2 + 2
					height = self.itemHeight - posY - 2
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_pre_pixmap))
				elif zapPixmaps & TIMER_TYPE_ADD_COVERS_END:
					posY = self.itemHeight / 2 + 2
					height = self.itemHeight - posY - 2
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_add_pixmap))
				if zapPixmaps & TIMER_TYPE_COVERS_BEGIN:
					posY = 2
					height = self.itemHeight / 2 - 2
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_post_pixmap))
				elif zapPixmaps & TIMER_TYPE_ADD_COVERS_BEGIN:
					posY = 2
					height = self.itemHeight / 2 - 2
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_add_pixmap))
				if zapPixmaps & TIMER_TYPE_ADD:
					posY = 2
					height = self.itemHeight - 4
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.zap_add_pixmap))
					
		offsetRight -= 10
		
		if timerPixmaps:
			if (timerPixmaps & TIMER_TYPE_EID_MATCH) or (timerPixmaps & TIMER_TYPE_COVERS_FULL) or (timerPixmaps & TIMER_TYPE_EID_REPEATED) or (timerPixmaps & TIMER_TYPE_ADD_COVERS_FULL):
				posY = 2
				height = self.itemHeight - 4
				if (timerPixmaps & TIMER_TYPE_EID_MATCH):
					if (isRunning & TIMER_TYPE_EID_MATCH) and not self.blinkTimer.getBlinkState():
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, None))
					else:
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_event_pixmap))
				elif (timerPixmaps & TIMER_TYPE_COVERS_FULL):
					if (isRunning & TIMER_TYPE_COVERS_FULL) and not self.blinkTimer.getBlinkState():
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, None))
					else:
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_pixmap))
				elif (timerPixmaps & TIMER_TYPE_EID_REPEATED):
					if (isRunning & TIMER_TYPE_EID_REPEATED) and not self.blinkTimer.getBlinkState():
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, None))
					else:
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_repeated_pixmap))
				elif (timerPixmaps & TIMER_TYPE_ADD_COVERS_FULL):
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_add_pixmap))
			elif (timerPixmaps & TIMER_TYPE_INSIDE_EVENT) or (timerPixmaps & TIMER_TYPE_ADD_INSIDE_EVENT):
				posY = self.itemHeight / 2 - 6
				height = 12
				if (timerPixmaps & TIMER_TYPE_INSIDE_EVENT):
					if (isRunning & TIMER_TYPE_INSIDE_EVENT) and not self.blinkTimer.getBlinkState():
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, None))
					else:
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_pixmap))
				elif (timerPixmaps & TIMER_TYPE_ADD_INSIDE_EVENT):
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_add_pixmap))
			else:
				if timerPixmaps & TIMER_TYPE_COVERS_END:
					posY = self.itemHeight / 2 + 2
					height = self.itemHeight - posY - 2
					if (isRunning & TIMER_TYPE_COVERS_END) and not self.blinkTimer.getBlinkState():
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, None))
					else:
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_pre_pixmap))
				elif timerPixmaps & TIMER_TYPE_ADD_COVERS_END:
					posY = self.itemHeight / 2 + 2
					height = self.itemHeight - posY - 2
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_add_pixmap))
				if timerPixmaps & TIMER_TYPE_COVERS_BEGIN:
					posY = 2
					height = self.itemHeight / 2 - 2
					if (isRunning & TIMER_TYPE_COVERS_BEGIN) and not self.blinkTimer.getBlinkState():
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, None))
					else:
						res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_post_pixmap))
				elif timerPixmaps & TIMER_TYPE_ADD_COVERS_BEGIN:
					posY = 2
					height = self.itemHeight / 2 - 2
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_add_pixmap))
				if timerPixmaps & TIMER_TYPE_ADD:
					posY = 2
					height = self.itemHeight - 4
					res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetRight, posY, 8, height, self.timer_add_pixmap))
					
		if config.plugins.merlinEpgCenter.showBeginRemainTime.value and config.plugins.merlinEpgCenter.showDuration.value:
			width = self.maxWidth * 8 / 100
			offsetRight = offsetRight - width
			res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetRight, 0, width, self.itemHeight, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, remainBeginString, progColor))
		elif config.plugins.merlinEpgCenter.showBeginRemainTime.value:
			width = self.maxWidth * 6 / 100
			offsetRight = offsetRight - width
			res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetRight, 0, width, self.itemHeight, 1, RT_HALIGN_RIGHT|RT_VALIGN_CENTER, remainBeginString, progColor))
			
		if config.plugins.merlinEpgCenter.showDuration.value:
			width = self.maxWidth * 6 / 100
			offsetRight = offsetRight - width
		elif not config.plugins.merlinEpgCenter.showDuration.value and not config.plugins.merlinEpgCenter.showBeginRemainTime.value:
			width = self.maxWidth * 1 / 100
			offsetRight = offsetRight - width
			
		titleWidth = offsetRight - offsetLeft - columnSpace
		if self.listStyle == STYLE_SINGLE_LINE:
			res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, titleWidth, self.itemHeight, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, title, config.plugins.merlinEpgCenter.titleColor.value, config.plugins.merlinEpgCenter.titleColorSelected.value))
		elif self.listStyle == STYLE_SHORT_DESCRIPTION:
			if short and title != short:
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, border, titleWidth, self.halfItemHeight - border, 1, RT_HALIGN_LEFT|RT_VALIGN_TOP, title, textColor))
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, self.halfItemHeight, titleWidth, self.halfItemHeight - border, 2, RT_HALIGN_LEFT|RT_VALIGN_TOP, short, secondLineColor))
			else:
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, titleWidth, self.itemHeight, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, title, textColor))
			
		if config.plugins.merlinEpgCenter.showDuration.value:
			res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetRight, 0, width, self.itemHeight, 1, RT_HALIGN_RIGHT|RT_VALIGN_CENTER, duraString, textColor))
		
		return res
	
	GUI_WIDGET = eListbox
	
	def selectionChanged(self):
		for x in self.onSelectionChanged:
			x()
			
	def postWidgetCreate(self, instance):
		instance.setWrapAround(True)
		instance.setContent(self.l)
		self.selectionChanged_conn = instance.selectionChanged.connect(self.selectionChanged)

	def preWidgetRemove(self, instance):
		instance.setContent(None)
		self.selectionChanged_conn = None
		config.plugins.merlinEpgCenter.listItemHeight.removeNotifier(self.changeHeight)
		config.plugins.merlinEpgCenter.adjustFontSize.removeNotifier(self.setFontSizes)
		self.blinkTimer.callbacks.remove(self.invalidateList)
		
	def moveToIndex(self, index):
		self.instance.moveSelectionTo(index)
		
	def moveUp(self):
		self.instance.moveSelection(self.instance.moveUp)
		
	def moveDown(self):
		self.instance.moveSelection(self.instance.moveDown)
		
	def pageUp(self):
		if self.instance is not None:
			self.instance.moveSelection(self.instance.pageUp)
			
	def pageDown(self):
		if self.instance is not None:
			self.instance.moveSelection(self.instance.pageDown)
			
	def getCurrent(self):
		return self.l.getCurrentSelection()

	def invalidate(self, entry):
		# when the entry to invalidate does not exist, just ignore the request.
		# this eases up conditional setup screens a lot.
		if entry in self.list:
			self.l.invalidateEntry(self.list.index(entry))
			
	def invalidateList(self):
		self.l.invalidate()
		
	def setList(self, l):
		self.list = l
		self.l.setList(self.list)
	
	def queryEPG(self, servicelist):
		if self.epgcache is not None:
			return self.epgcache.lookupEvent(servicelist)
		return [ ]

	def fillMultiEPG(self, bouquet, bouquetIndex, mode, stime=-1):
		EpgCenterList.currentBouquetIndex = bouquetIndex
		
		# 1. if oldmode is MULTI_EPG_NOW and new mode is MULTI_EPG_NEXT --> use old list for querying EPG (speed up! :-) )
		# 2. otherwise build servicelist from bouquet, and
		#	a) if MULTI_EPG_NOW/MULTI_EPG_PRIMETIME --> query epg
		#	b) if MULTI_EPG_NEXT --> build query-list with servicelist for epg	
		oldmode = self.mode
		self.mode = mode
		self.similarShown = False
		
		if ((mode == MULTI_EPG_NOW or mode == MULTI_EPG_PRIMETIME) or (oldmode != MULTI_EPG_NOW and mode == MULTI_EPG_NEXT)):
			servicelist = EpgCenterList.getServiceList(bouquet, stime)
			
		returnTuples = '0IRBDTSEX'
		if mode == MULTI_EPG_NOW or mode == MULTI_EPG_PRIMETIME:
			servicelist.insert(0, returnTuples)
		else:
			if oldmode != MULTI_EPG_NOW:
				servicelist.insert(0, returnTuples)
				tmpList = self.queryEPG(servicelist)
			else:
				tmpList = self.list

			servicelist = [ x[3] and (x[2], 1, x[3]) or (x[2], 1, 0) for x in tmpList ] # build servicelist with "event after given start_time" and set the start time
			servicelist.insert(0, returnTuples)
			
		if self.listStyle == STYLE_SINGLE_LINE:
			self.changeHeight()
		self.list = self.queryEPG(servicelist)
		self.l.setList(self.list)
		
	def fillSingleEPG(self, bouquet, bouquetIndex, mode, sRef, showOutdated):
		self.mode = mode
		EpgCenterList.currentBouquetIndex = bouquetIndex
		EpgCenterList.getServiceList(bouquet)
		self.similarShown = False
		
		if sRef:
			if showOutdated:
				now = time()
				queryString = [ '0IRBDTSE', (sRef, 0, now - KEEP_OUTDATED_TIME * 60, KEEP_OUTDATED_TIME) ]
			else:
				queryString = [ '0IRBDTSE', (sRef, 0, -1, -1) ]
			self.list = self.queryEPG(queryString)
			
		if self.listStyle == STYLE_SINGLE_LINE:
			self.changeHeight()
		if showOutdated:
			self.list.sort(key = lambda x: x[3], reverse = True) # sort by time
		self.l.setList(self.list)
		
	def fillSimilar(self, sRef, eventId):
		if eventId is None:
			return
			
		self.similarShown = True
		self.list = self.epgcache.search(('0IRBDTSE', 1024, eEPGCache.SIMILAR_BROADCASTINGS_SEARCH, sRef, eventId))
		if self.list is not None:
			if config.plugins.merlinEpgCenter.limitSearchToBouquetServices.value:
				for item in self.list[:]:
					if not item[2] in EpgCenterList.allServicesNameDict:
						self.list.remove(item)
						
			if self.listStyle == STYLE_SINGLE_LINE:
				self.changeHeight()
				
			self.list.sort(key = lambda x: x[3]) # sort by time
			self.l.setList(self.list)
			
	def fillEpgSearch(self, searchString, mode):
		self.mode = mode
		self.similarShown = False
		
		if searchString == None:
			self.list = []
		else:
			searchString = searchString.decode('utf-8').encode("iso-8859-1","replace")
			self.list = self.epgcache.search(('0IRBDTSE', 1024, eEPGCache.PARTIAL_TITLE_SEARCH, searchString, eEPGCache.NO_CASE_CHECK)) or []
			if config.plugins.merlinEpgCenter.limitSearchToBouquetServices.value:
				for item in self.list[:]:
					if not item[2] in EpgCenterList.allServicesNameDict:
						self.list.remove(item)
			self.list.sort(key = lambda x: x[3]) # sort by time
			
		if self.listStyle == STYLE_SINGLE_LINE:
			self.changeHeight()
		self.l.setList(self.list)
		
	@staticmethod
	def getServiceList(bouquet, stime=-1, sRefOnly = False):
		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
				# alternative service?
				if service.flags & (eServiceReference.isGroup):
					altRoot = eServiceReference(service.toCompareString())
					altList = EpgCenterList.eServiceCenterInstance.list(altRoot)
					if altList:
						while True:
							nextService = altList.getNext()
							if not nextService.valid():
								break
							service = nextService
							break
							
				if sRefOnly:
					services.append(service.toCompareString())
				else:
					services.append((service.toCompareString(), 0, stime))
		return services
		
	# get a list of all services in all bouquets
	@staticmethod
	def getAllServices():
		allServices = {}
		index = 1
		EpgCenterList.lenChannelDigits = 0
		totalServices = 0 # the number of services in all bouquets
		for bouquetEntry in EpgCenterList.bouquetList:
			servicelist = eServiceCenter.getInstance().list(bouquetEntry[1])
			if not servicelist is None:
				numServices = 0
				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
					if service.flags & eServiceReference.isGroup:
						altRoot = eServiceReference(service.toCompareString())
						altList = EpgCenterList.eServiceCenterInstance.list(altRoot)
						if altList:
							while True:
								nextService = altList.getNext()
								if not nextService.valid():
									break
								service = nextService
								# this is ugly
								EpgCenterList.alternativesDict[service.toString()] = altRoot.toString()
								break	
					info = EpgCenterList.eServiceCenterInstance.info(service)
					serviceName = info.getName(service) or ServiceReference(service).getServiceName() or ""
					allServices[service.toCompareString()] = serviceName
					numServices += 1
				indexEntry = index
				index += numServices
				totalServices += numServices
				EpgCenterList.bouquetIndexRanges.append(indexEntry)
		EpgCenterList.lenChannelDigits = len(str(totalServices))
		return allServices
				
	@staticmethod
	def updateBouquetServices():
		EpgCenterList.bouquetIndexRanges = []
		EpgCenterList.allServicesNameDict = EpgCenterList.getAllServices()
		EpgCenterList.bouquetServices = []

		for bouquet in EpgCenterList.bouquetList:
			EpgCenterList.bouquetServices.append(EpgCenterList.getServiceList(bouquet[1], sRefOnly = True))
			
	def selectionEnabled(self, enabled):
		if self.instance is not None:
			self.instance.setSelectionEnable(enabled)
			
	def getTimerPixmapsForEntry(self, sRef, eventId, begin, duration):
		timerPixmaps = 0
		zapPixmaps = 0
		isRunning = 0
		
		for timer in self.recordTimer.timer_list:
			if timer.service_ref.ref.toString() == sRef:
				end = begin + duration
				if timer.begin > begin and timer.end < end: # the timer is inside the events bounds
					if timer.justplay:
						zapPixmaps |= TIMER_TYPE_INSIDE_EVENT
					else:
						timerPixmaps |= TIMER_TYPE_INSIDE_EVENT
						if timer.isRunning():
							isRunning |= TIMER_TYPE_INSIDE_EVENT
				elif end >= timer.begin and begin <= timer.end: # this event touches the timer
					if eventId == timer.eit: # exact event match
						if timer.repeated:
							if timer.justplay:
								zapPixmaps |= TIMER_TYPE_EID_REPEATED
							else:
								timerPixmaps |= TIMER_TYPE_EID_REPEATED
								if timer.isRunning():
									isRunning |= TIMER_TYPE_EID_REPEATED
						else:
							if timer.justplay:
								zapPixmaps |= TIMER_TYPE_EID_MATCH
							else:
								timerPixmaps |= TIMER_TYPE_EID_MATCH
								if timer.isRunning():
									isRunning |= TIMER_TYPE_EID_MATCH
					elif begin < timer.begin and end > timer.begin: # this event overlaps the end of the timer
						if timer.justplay:
							zapPixmaps |= TIMER_TYPE_COVERS_END
						else:
							timerPixmaps |= TIMER_TYPE_COVERS_END
							if timer.isRunning():
								isRunning |= TIMER_TYPE_COVERS_END
					elif end > timer.end and begin < timer.end: # this event overlaps the begin of the timer
						if timer.justplay:
							zapPixmaps |= TIMER_TYPE_COVERS_BEGIN
						else:
							timerPixmaps |= TIMER_TYPE_COVERS_BEGIN
							if timer.isRunning():
								isRunning |= TIMER_TYPE_COVERS_BEGIN
					elif end > timer.begin and begin < timer.end: # this event fully overlaps the timer but itsn't nor the timer event
						if timer.justplay:
							zapPixmaps |= TIMER_TYPE_COVERS_FULL
						else:
							timerPixmaps |= TIMER_TYPE_COVERS_FULL
							if timer.isRunning():
								isRunning |= TIMER_TYPE_COVERS_FULL
				elif timerPixmaps == 0 and zapPixmaps == 0 and self.recordTimer.isInTimer(eventId, begin, duration, sRef): # timer repetition
					# TODO do we need to care about local times?
					
					timerBegin = datetime.fromtimestamp(timer.begin).time()
					timerEnd = datetime.fromtimestamp(timer.end).time()
					netTimerBegin = datetime.fromtimestamp(int(timer.begin) + ServiceRecordingSettings.instance.getMarginBefore(timer.service_ref)).time()
					netTimerEnd = datetime.fromtimestamp(int(timer.end) - ServiceRecordingSettings.instance.getMarginAfter(timer.service_ref)).time()
					eventBegin = datetime.fromtimestamp(begin).time()
					eventEnd = datetime.fromtimestamp(end).time()
					
					if netTimerBegin == eventBegin and netTimerEnd == eventEnd: # the main timer entry
						if timer.justplay:
							zapPixmaps |= TIMER_TYPE_ADD
						else:
							timerPixmaps |= TIMER_TYPE_ADD
					elif netTimerBegin >= eventBegin and netTimerEnd <= eventEnd: # the timer is inside the events bounds
						if timer.justplay:
							zapPixmaps |= TIMER_TYPE_ADD_INSIDE_EVENT
						else:
							timerPixmaps |= TIMER_TYPE_ADD_INSIDE_EVENT
					elif eventBegin < timerBegin and eventEnd > timerBegin: # this event overlaps the end of the timer
						if timer.justplay:
							zapPixmaps |= TIMER_TYPE_ADD_COVERS_END
						else:
							timerPixmaps |= TIMER_TYPE_ADD_COVERS_END
					elif eventEnd > timerEnd and eventBegin < timerEnd: # this event overlaps the begin of the timer
						if timer.justplay:
							zapPixmaps |= TIMER_TYPE_ADD_COVERS_BEGIN
						else:
							timerPixmaps |= TIMER_TYPE_ADD_COVERS_BEGIN
					elif eventEnd > timerBegin and eventBegin < timerEnd: # this event fully overlaps the timer but itsn't nor the timer event
						if timer.justplay:
							zapPixmaps |= TIMER_TYPE_ADD_COVERS_FULL
						else:
							timerPixmaps |= TIMER_TYPE_ADD_COVERS_FULL
							
		return timerPixmaps, zapPixmaps, isRunning
		
class EpgCenterTimerlist(TimerList):
	def __init__(self, list, videoMode, piconLoader, piconSize, listStyle):
		self.videoMode = videoMode
		self.piconLoader = piconLoader
		self.piconSize = piconSize
		self.baseHeight = self.piconSize.height()
		self.listStyle = listStyle
		
		GUIComponent.__init__(self)
		
		self.l = eListboxPythonMultiContent()
		self.l.setBuildFunc(self.buildTimerEntry)
		self.onSelectionChanged = [ ]
		
		if self.videoMode == MODE_SD or self.videoMode == MODE_XD:
			self.overallFontHeight = 36
		elif self.videoMode == MODE_HD:
			self.overallFontHeight = 44
		elif self.videoMode == MODE_FHD:
			self.overallFontHeight = 66
			
		self.l.setList(list)
		config.plugins.merlinEpgCenter.listItemHeight.addNotifier(self.changeHeight, initial_call = True)
		config.plugins.merlinEpgCenter.adjustFontSize.addNotifier(self.setFontSizes, initial_call = True)
		
		self.autoTimerPixmap = LoadPixmap(cached=False, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/AutoTimerSmall.png"))
		
	def applySkin(self, desktop, parent):
		GUIComponent.applySkin(self, desktop, parent)

	def onShow(self):
		self.maxWidth = self.l.getItemSize().width()
		
	def setFontSizes(self, configElement = None):
		diff = configElement.getValue()
		
		if self.videoMode == MODE_SD:
			self.l.setFont(0, gFont("Regular", 18 + diff))
			self.l.setFont(1, gFont("Regular", 16 + diff))
			self.l.setFont(2, gFont("Regular", 14 + diff))
			self.l.setFont(3, gFont("Regular", 12 + diff))
		elif self.videoMode == MODE_XD:
			self.l.setFont(0, gFont("Regular", 18 + diff))
			self.l.setFont(1, gFont("Regular", 16 + diff))
			self.l.setFont(2, gFont("Regular", 14 + diff))
			self.l.setFont(3, gFont("Regular", 12 + diff))
		elif self.videoMode == MODE_HD:
			self.l.setFont(0, gFont("Regular", 22 + diff))
			self.l.setFont(1, gFont("Regular", 20 + diff))
			self.l.setFont(2, gFont("Regular", 18 + diff))
			self.l.setFont(3, gFont("Regular", 16 + diff))
		elif self.videoMode == MODE_FHD:
			self.l.setFont(0, gFont("Regular", 33 + diff))
			self.l.setFont(1, gFont("Regular", 30 + diff))
			self.l.setFont(2, gFont("Regular", 27 + diff))
			self.l.setFont(3, gFont("Regular", 24 + diff))
			
	def setMaxWidth(self, newSize):
		self.maxWidth = newSize.width()
		
	def changeHeight(self, configElement = None):
		if self.overallFontHeight > self.baseHeight:
			self.itemHeight = self.overallFontHeight + int(config.plugins.merlinEpgCenter.listItemHeight.value)
		else:
			self.itemHeight = self.baseHeight + int(config.plugins.merlinEpgCenter.listItemHeight.value)
		self.halfItemHeight = self.itemHeight / 2
		self.l.setItemHeight(self.itemHeight)
		
	def selectionChanged(self):
		for x in self.onSelectionChanged:
			x()
			
	def postWidgetCreate(self, instance):
		instance.setWrapAround(True)
		instance.setContent(self.l)
		self.selectionChanged_conn = instance.selectionChanged.connect(self.selectionChanged)

	def preWidgetRemove(self, instance):
		instance.setContent(None)
		self.selectionChanged_conn = None
		config.plugins.merlinEpgCenter.listItemHeight.removeNotifier(self.changeHeight)
		config.plugins.merlinEpgCenter.adjustFontSize.removeNotifier(self.setFontSizes)
		
	def buildTimerEntry(self, timer, processed):
		columnSpace = config.plugins.merlinEpgCenter.columnSpace.getValue()
		width = self.l.getItemSize().width()
		offsetLeft = 5 # 5 = left border
		offsetRight = self.maxWidth - 5 # 5 = right border
		secondLineColor = 0x00909090 # grey
		border = int(config.plugins.merlinEpgCenter.listItemHeight.value) / 2
		timeString = strftime("%H:%M", localtime(timer.begin)) + "-" + strftime("%H:%M", localtime(timer.end))

		if not processed:
			if timer.state == TimerEntry.StateWaiting:
				state = _("waiting")
				color = 0x00ffed00 # yellow
			elif timer.state == TimerEntry.StatePrepared:
				state = _("about to start")
				color = parseColor("red").argb()
			elif timer.state == TimerEntry.StateRunning:
				if timer.justplay:
					state = _("zapped")
				else:
					state = _("recording...")
				color = parseColor("red").argb()
			elif timer.state == TimerEntry.StateEnded:
				state = _("done!")
				color = parseColor("green").argb()
			else:
				state = _("<unknown>")
				color = parseColor("red").argb()
		else:
			state = _("done!")
			color = parseColor("green").argb()
		
		if timer.disabled:
			state = _("disabled")
			color = 0x009a9a9a
			
		if timer.justplay:
			state = "(ZAP) " + state
			
		res = [ None ]
		
		if config.plugins.merlinEpgCenter.showListNumbers.value:
			number = str(self.instance.getCurrentIndex() + 1)
			width = self.maxWidth * 3 / 100
			# 30 breite
			res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, width, self.itemHeight, 1, RT_HALIGN_RIGHT|RT_VALIGN_CENTER, number))
			offsetLeft = offsetLeft + width + columnSpace
			
		if config.plugins.merlinEpgCenter.showPicons.value:
			width = self.piconSize.width()
			height = self.piconSize.height()
			
			if isinstance(timer, TimerListObject) and self.autoTimerPixmap:
				picon = self.autoTimerPixmap
			else:
				picon = self.piconLoader.getPicon(str(timer.service_ref))
			if picon:
				res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, offsetLeft, (self.itemHeight - self.baseHeight) / 2, width, height, picon))
			offsetLeft = offsetLeft + width + columnSpace
			
		if config.plugins.merlinEpgCenter.showServiceName.value:
			extraWidth = int(config.plugins.merlinEpgCenter.serviceNameWidth.value)
			if self.videoMode == MODE_SD:
				width = self.maxWidth * (12 + extraWidth) / 100
			elif self.videoMode == MODE_XD:
				width = self.maxWidth * (14 + extraWidth) / 100
			elif self.videoMode == MODE_HD:
				width = self.maxWidth * (16 + extraWidth) / 100
			elif self.videoMode == MODE_FHD:
				width = self.maxWidth * (24 + extraWidth) / 100
				
			if isinstance(timer, RecordTimerEntry):
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, width, self.itemHeight, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, timer.service_ref.getServiceName()))
			else: # AutoTimer entry
				res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, width, self.itemHeight, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, "AutoTimer"))
			offsetLeft = offsetLeft + width + columnSpace
			
		extraWidth = int(config.plugins.merlinEpgCenter.adjustFontSize.value)
		if extraWidth < 0:
			extraWidth = 0
		if self.videoMode == MODE_SD:
			width = self.maxWidth * (18 + extraWidth) / 100
		else:
			width = self.maxWidth * (14 + extraWidth) / 100
			
		if isinstance(timer, RecordTimerEntry):
			fd = getFuzzyDay(timer.begin)
		
			res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, border, width, self.halfItemHeight - border, 1, RT_HALIGN_LEFT|RT_VALIGN_TOP, timeString))
			res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, self.halfItemHeight, width, self.halfItemHeight - border, 2, RT_HALIGN_CENTER|RT_VALIGN_TOP, fd, secondLineColor))
		else: # AutoTimer entry
			res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, width, self.itemHeight, 1, RT_HALIGN_CENTER|RT_VALIGN_CENTER, timeString))
			
		offsetLeft = offsetLeft + width + columnSpace
		
		width = self.maxWidth * 22 / 100
		offsetRight = offsetRight - width
		res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetLeft, 0, offsetRight - offsetLeft, self.itemHeight, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, timer.name, config.plugins.merlinEpgCenter.titleColor.value, config.plugins.merlinEpgCenter.titleColorSelected.value))
		res.append((eListboxPythonMultiContent.TYPE_TEXT, offsetRight, 0, width, self.itemHeight, 1, RT_HALIGN_RIGHT|RT_VALIGN_CENTER, state, color))
		
		return res
