2 from xml.dom import EMPTY_NAMESPACE
5 from enigma import eSize, ePoint, gFont, eWindow, eLabel, ePixmap, eWindowStyleManager, \
6 loadPNG, addFont, gRGB, eWindowStyleSkinned
8 from Components.config import ConfigSubsection, ConfigText, config
9 from Components.Element import Element
10 from Components.Converter.Converter import Converter
11 from Tools.Directories import resolveFilename, SCOPE_SKIN, SCOPE_SKIN_IMAGE, SCOPE_FONTS
12 from Tools.Import import my_import
14 from Tools.XMLTools import elementsWithTag, mergeText
19 print " " * i + str(x)
21 for n in x.childNodes:
26 class SkinError(Exception):
27 def __init__(self, message):
28 self.message = message
37 filename = resolveFilename(SCOPE_SKIN, name)
38 mpath = path.dirname(filename) + "/"
39 dom_skins.append((mpath, xml.dom.minidom.parse(filename)))
41 # we do our best to always select the "right" value
42 # skins are loaded in order of priority: skin with
43 # highest priority is loaded last, usually the user-provided
46 # currently, loadSingleSkinData (colors, bordersets etc.)
47 # are applied one-after-each, in order of ascending priority.
48 # the dom_skin will keep all screens in descending priority,
49 # so the first screen found will be used.
51 # example: loadSkin("nemesis_greenline/skin.xml")
52 config.skin = ConfigSubsection()
53 config.skin.primary_skin = ConfigText(default = "skin.xml")
56 loadSkin(config.skin.primary_skin.value)
57 except (SkinError, IOError, AssertionError), err:
58 print "SKIN ERROR:", err
59 print "defaulting to standard skin..."
61 loadSkin('skin_default.xml')
63 def parsePosition(str):
65 return ePoint(int(x), int(y))
69 return eSize(int(x), int(y))
72 name, size = str.split(';')
73 return gFont(name, int(size))
78 return colorNames[str]
80 raise ("color '%s' must be #aarrggbb or valid named color" % (str))
81 return gRGB(int(str[1:], 0x10))
83 def collectAttributes(skinAttributes, node, skin_path_prefix=None, ignore=[]):
85 for p in range(node.attributes.length):
86 a = node.attributes.item(p)
88 # convert to string (was: unicode)
90 # TODO: localization? as in e1?
91 value = a.value.encode("utf-8")
93 if attrib in ["pixmap", "pointer", "seek_pointer"]:
94 value = resolveFilename(SCOPE_SKIN_IMAGE, value, path_prefix=skin_path_prefix)
96 if attrib not in ignore:
97 skinAttributes.append((attrib, value))
102 raise "pixmap file %s not found!" % (path)
105 def applySingleAttribute(guiObject, desktop, attrib, value):
108 if attrib == 'position':
109 guiObject.move(parsePosition(value))
110 elif attrib == 'size':
111 guiObject.resize(parseSize(value))
112 elif attrib == 'title':
113 guiObject.setTitle(_(value))
114 elif attrib == 'text':
115 guiObject.setText(_(value))
116 elif attrib == 'font':
117 guiObject.setFont(parseFont(value))
118 elif attrib == 'zPosition':
119 guiObject.setZPosition(int(value))
120 elif attrib == "pixmap":
121 ptr = loadPixmap(value) # this should already have been filename-resolved.
122 desktop.makeCompatiblePixmap(ptr)
123 guiObject.setPixmap(ptr)
124 # guiObject.setPixmapFromFile(value)
125 elif attrib == "alphatest": # used by ePixmap
126 guiObject.setAlphatest(
130 elif attrib == "orientation": # used by eSlider
132 guiObject.setOrientation(
133 { "orVertical": guiObject.orVertical,
134 "orHorizontal": guiObject.orHorizontal
137 print "oprientation must be either orVertical or orHorizontal!"
138 elif attrib == "valign":
141 { "top": guiObject.alignTop,
142 "center": guiObject.alignCenter,
143 "bottom": guiObject.alignBottom
146 print "valign must be either top, center or bottom!"
147 elif attrib == "halign":
150 { "left": guiObject.alignLeft,
151 "center": guiObject.alignCenter,
152 "right": guiObject.alignRight,
153 "block": guiObject.alignBlock
156 print "halign must be either left, center, right or block!"
157 elif attrib == "flags":
158 flags = value.split(',')
161 fv = eWindow.__dict__[f]
162 guiObject.setFlag(fv)
164 print "illegal flag %s!" % f
165 elif attrib == "backgroundColor":
166 guiObject.setBackgroundColor(parseColor(value))
167 elif attrib == "foregroundColor":
168 guiObject.setForegroundColor(parseColor(value))
169 elif attrib == "shadowColor":
170 guiObject.setShadowColor(parseColor(value))
171 elif attrib == "selectionDisabled":
172 guiObject.setSelectionEnable(0)
173 elif attrib == "transparent":
174 guiObject.setTransparent(int(value))
175 elif attrib == "borderColor":
176 guiObject.setBorderColor(parseColor(value))
177 elif attrib == "borderWidth":
178 guiObject.setBorderWidth(int(value))
179 elif attrib == "scrollbarMode":
180 guiObject.setScrollbarMode(
181 { "showOnDemand": guiObject.showOnDemand,
182 "showAlways": guiObject.showAlways,
183 "showNever": guiObject.showNever
185 elif attrib == "enableWrapAround":
186 guiObject.setWrapAround(True)
187 elif attrib == "pointer" or attrib == "seek_pointer":
188 (name, pos) = value.split(':')
189 pos = parsePosition(pos)
190 ptr = loadPixmap(name)
191 desktop.makeCompatiblePixmap(ptr)
192 guiObject.setPointer({"pointer": 0, "seek_pointer": 1}[attrib], ptr, pos)
193 elif attrib == 'shadowOffset':
194 guiObject.setShadowOffset(parsePosition(value))
195 elif attrib == 'noWrap':
196 guiObject.setNoWrap(1)
198 raise "unsupported attribute " + attrib + "=" + value
201 print "widget %s (%s) doesn't support attribute %s!" % ("", guiObject.__class__.__name__, attrib)
203 def applyAllAttributes(guiObject, desktop, attributes):
204 for (attrib, value) in attributes:
205 applySingleAttribute(guiObject, desktop, attrib, value)
207 def loadSingleSkinData(desktop, dom_skin, path_prefix):
208 """loads skin data like colors, windowstyle etc."""
210 skin = dom_skin.childNodes[0]
211 assert skin.tagName == "skin", "root element in skin must be 'skin'!"
213 for c in elementsWithTag(skin.childNodes, "colors"):
214 for color in elementsWithTag(c.childNodes, "color"):
215 name = str(color.getAttribute("name"))
216 color = str(color.getAttribute("value"))
219 raise ("need color and name, got %s %s" % (name, color))
221 colorNames[name] = parseColor(color)
223 for c in elementsWithTag(skin.childNodes, "fonts"):
224 for font in elementsWithTag(c.childNodes, "font"):
225 filename = str(font.getAttribute("filename") or "<NONAME>")
226 name = str(font.getAttribute("name") or "Regular")
227 scale = int(font.getAttribute("scale") or "100")
228 is_replacement = font.getAttribute("replacement") != ""
229 addFont(resolveFilename(SCOPE_FONTS, filename, path_prefix=path_prefix), name, scale, is_replacement)
231 for windowstyle in elementsWithTag(skin.childNodes, "windowstyle"):
232 style = eWindowStyleSkinned()
233 id = int(windowstyle.getAttribute("id") or "0")
236 font = gFont("Regular", 20)
237 offset = eSize(20, 5)
239 for title in elementsWithTag(windowstyle.childNodes, "title"):
240 offset = parseSize(title.getAttribute("offset"))
241 font = parseFont(str(title.getAttribute("font")))
243 style.setTitleFont(font);
244 style.setTitleOffset(offset)
246 for borderset in elementsWithTag(windowstyle.childNodes, "borderset"):
247 bsName = str(borderset.getAttribute("name"))
248 for pixmap in elementsWithTag(borderset.childNodes, "pixmap"):
249 bpName = str(pixmap.getAttribute("pos"))
250 filename = str(pixmap.getAttribute("filename"))
252 png = loadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, filename, path_prefix=path_prefix))
255 desktop.makeCompatiblePixmap(png)
256 style.setPixmap(eWindowStyleSkinned.__dict__[bsName], eWindowStyleSkinned.__dict__[bpName], png)
258 for color in elementsWithTag(windowstyle.childNodes, "color"):
259 type = str(color.getAttribute("name"))
260 color = parseColor(color.getAttribute("color"))
263 style.setColor(eWindowStyleSkinned.__dict__["col" + type], color)
265 raise ("Unknown color %s" % (type))
267 x = eWindowStyleManager.getInstance()
268 x.setStyle(id, style)
270 def loadSkinData(desktop):
273 for (path, dom_skin) in skins:
274 loadSingleSkinData(desktop, dom_skin, path)
276 def lookupScreen(name):
277 for (path, dom_skin) in dom_skins:
278 # first, find the corresponding screen element
279 skin = dom_skin.childNodes[0]
280 for x in elementsWithTag(skin.childNodes, "screen"):
281 if x.getAttribute('name') == name:
285 def readSkin(screen, skin, name, desktop):
287 myscreen, path = lookupScreen(name)
289 # otherwise try embedded skin
290 myscreen = myscreen or getattr(screen, "parsedSkin", None)
292 # try uncompiled embedded skin
293 if myscreen is None and getattr(screen, "skin", None):
294 myscreen = screen.parsedSkin = xml.dom.minidom.parseString(screen.skin).childNodes[0]
296 assert myscreen is not None, "no skin for screen '" + name + "' found!"
298 screen.skinAttributes = [ ]
300 skin_path_prefix = getattr(screen, "skin_path", path)
302 collectAttributes(screen.skinAttributes, myscreen, skin_path_prefix, ignore=["name"])
304 screen.additionalWidgets = [ ]
305 screen.renderer = [ ]
307 visited_components = set()
309 # now walk all widgets
310 for widget in elementsWithTag(myscreen.childNodes, "widget"):
311 # ok, we either have 1:1-mapped widgets ('old style'), or 1:n-mapped
312 # widgets (source->renderer).
314 wname = widget.getAttribute('name')
315 wsource = widget.getAttribute('source')
318 if wname is None and wsource is None:
319 print "widget has no name and no source!"
323 visited_components.add(wname)
325 # get corresponding 'gui' object
327 attributes = screen[wname].skinAttributes = [ ]
329 raise SkinError("component with name '" + wname + "' was not found in skin of screen '" + name + "'!")
331 # assert screen[wname] is not Source
333 # and collect attributes for this
334 collectAttributes(attributes, widget, skin_path_prefix, ignore=['name'])
336 # get corresponding source
337 source = screen.get(wsource)
339 raise SkinError("source '" + wsource + "' was not found in screen '" + name + "'!")
341 wrender = widget.getAttribute('render')
344 raise SkinError("you must define a renderer with render= for source '%s'" % (wsource))
346 for converter in elementsWithTag(widget.childNodes, "convert"):
347 ctype = converter.getAttribute('type')
348 assert ctype, "'convert'-tag needs a 'type'-attribute"
349 parms = mergeText(converter.childNodes).strip()
350 converter_class = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype)
354 for i in source.downstream_elements:
355 if isinstance(i, converter_class) and i.converter_arguments == parms:
359 print "allocating new converter!"
360 c = converter_class(parms)
363 print "reused conveter!"
367 renderer_class = my_import('.'.join(["Components", "Renderer", wrender])).__dict__.get(wrender)
369 renderer = renderer_class() # instantiate renderer
371 renderer.connect(source) # connect to source
372 attributes = renderer.skinAttributes = [ ]
373 collectAttributes(attributes, widget, skin_path_prefix, ignore=['render', 'source'])
375 screen.renderer.append(renderer)
377 from Components.GUIComponent import GUIComponent
378 nonvisited_components = [x for x in set(screen.keys()) - visited_components if isinstance(x, GUIComponent)]
380 assert not nonvisited_components, "the following components in %s don't have a skin entry: %s" % (name, ', '.join(nonvisited_components))
382 # now walk additional objects
383 for widget in elementsWithTag(myscreen.childNodes, lambda x: x != "widget"):
384 if widget.tagName == "applet":
385 codeText = mergeText(widget.childNodes).strip()
386 type = widget.getAttribute('type')
388 code = compile(codeText, "skin applet", "exec")
390 if type == "onLayoutFinish":
391 screen.onLayoutFinish.append(code)
393 raise SkinError("applet type '%s' unknown!" % type)
397 class additionalWidget:
400 w = additionalWidget()
402 if widget.tagName == "eLabel":
404 elif widget.tagName == "ePixmap":
407 raise SkinError("unsupported stuff : %s" % widget.tagName)
409 w.skinAttributes = [ ]
410 collectAttributes(w.skinAttributes, widget, skin_path_prefix, ignore=['name'])
412 # applyAttributes(guiObject, widget, desktop)
413 # guiObject.thisown = 0
414 screen.additionalWidgets.append(w)