X-Git-Url: http://code.vuplus.com/gitweb/?p=vuplus_dvbapp;a=blobdiff_plain;f=lib%2Fpython%2FComponents%2Fconfig.py;h=529b602078a42c443c9dd7d9cb4a3d54fa5ab0c5;hp=a28c40ca6500d9b51ea426af35598fbafe974985;hb=04f4636324ab06c5a3a4f618100048e159eaf6f9;hpb=554e450207f364dba6ff44cabed008427dc44440 diff --git a/lib/python/Components/config.py b/lib/python/Components/config.py index a28c40c..529b602 100644 --- a/lib/python/Components/config.py +++ b/lib/python/Components/config.py @@ -1,431 +1,1387 @@ -from time import * +import time +from enigma import getPrevAsciiCode +from Tools.NumericalTextInput import NumericalTextInput +from Tools.Directories import resolveFilename, SCOPE_CONFIG +from Components.Harddisk import harddiskmanager +import copy +import os -class configFile: +# ConfigElement, the base class of all ConfigElements. + +# it stores: +# value the current value, usefully encoded. +# usually a property which retrieves _value, +# and maybe does some reformatting +# _value the value as it's going to be saved in the configfile, +# though still in non-string form. +# this is the object which is actually worked on. +# default the initial value. If _value is equal to default, +# it will not be stored in the config file +# saved_value is a text representation of _value, stored in the config file +# +# and has (at least) the following methods: +# save() stores _value into saved_value, +# (or stores 'None' if it should not be stored) +# load() loads _value from saved_value, or loads +# the default if saved_value is 'None' (default) +# or invalid. +# +class ConfigElement(object): def __init__(self): - self.changed = 0 - self.configElements = { } - try: - self.file = open("config") - except IOError: - print "cannot open config file" - return - - while 1: - line = self.file.readline() - if line == "": - break - - if line.startswith("#"): #skip comments - continue - - self.addElement(line) - self.file.close() - def addElement(self, line): - x = line.find("=") - if x > -1: - self.configElements[line[:x]] = line[x + 1:] + object.__init__(self) + self.saved_value = None + self.save_disabled = False + self.notifiers = [] + self.enabled = True + + # you need to override this to do input validation + def setValue(self, value): + self._value = value + self.changed() + + def getValue(self): + return self._value - def getKey(self, key): - return self.configElements[key] + value = property(getValue, setValue) + + # you need to override this if self.value is not a string + def fromstring(self, value): + return value - def setKey(self, key, value): - self.changed = 1 - self.configElements[key] = value + # you can overide this for fancy default handling + def load(self): + if self.saved_value is None: + self.value = self.default + else: + self.value = self.fromstring(self.saved_value) + + def tostring(self, value): + return str(value) + # you need to override this if str(self.value) doesn't work def save(self): - if self.changed == 0: #no changes, so no write to disk needed - return - - fileHandle = open("config", "w") - - keys = self.configElements.keys() - keys.sort() - for x in keys: - wstr = x + "=" + self.configElements[x] + if self.save_disabled or self.value == self.default: + self.saved_value = None + else: + self.saved_value = self.tostring(self.value) + + def cancel(self): + self.load() + + def isChanged(self): + if self.saved_value is None and self.value == self.default: + return False + return self.tostring(self.value) != self.saved_value + + def changed(self): + for x in self.notifiers: + x(self) - if wstr[len(wstr) - 1] != '\n': - wstr = wstr + "\n" + def addNotifier(self, notifier, initial_call = True): + assert callable(notifier), "notifiers must be callable" + self.notifiers.append(notifier) - fileHandle.write(wstr) + # CHECKME: + # do we want to call the notifier + # - at all when adding it? (yes, though optional) + # - when the default is active? (yes) + # - when no value *yet* has been set, + # because no config has ever been read (currently yes) + # (though that's not so easy to detect. + # the entry could just be new.) + if initial_call: + notifier(self) - fileHandle.close() + def disableSave(self): + self.save_disabled = True -class configSelection: - def __init__(self, parent): - self.parent = parent - - def checkValues(self): - if self.parent.value < 0: - self.parent.value = 0 + def __call__(self, selected): + return self.getMulti(selected) - if(self.parent.value >= (len(self.parent.vals) - 1)): - self.parent.value = len(self.parent.vals) - 1 + def onSelect(self, session): + pass - def cancel(self): - self.parent.reload() + def onDeselect(self, session): + pass - def save(self): - self.parent.save() +KEY_LEFT = 0 +KEY_RIGHT = 1 +KEY_OK = 2 +KEY_DELETE = 3 +KEY_BACKSPACE = 4 +KEY_HOME = 5 +KEY_END = 6 +KEY_TOGGLEOW = 7 +KEY_ASCII = 8 +KEY_TIMEOUT = 9 +KEY_NUMBERS = range(12, 12+10) +KEY_0 = 12 +KEY_9 = 12+9 - def handleKey(self, key): - if key == config.key["prevElement"]: - self.parent.value = self.parent.value - 1 - if key == config.key["nextElement"]: - self.parent.value = self.parent.value + 1 +def getKeyNumber(key): + assert key in KEY_NUMBERS + return key - KEY_0 + +# +# ConfigSelection is a "one of.."-type. +# it has the "choices", usually a list, which contains +# (id, desc)-tuples (or just only the ids, in case the id +# will be used as description) +# +# all ids MUST be plain strings. +# +class ConfigSelection(ConfigElement): + def __init__(self, choices, default = None): + ConfigElement.__init__(self) + self._value = None + self.setChoices(choices, default) + + def setChoices(self, choices, default = None): + self.choices = [] + self.description = {} + + if isinstance(choices, list): + for x in choices: + if isinstance(x, tuple): + self.choices.append(x[0]) + self.description[x[0]] = x[1] + else: + self.choices.append(x) + self.description[x] = x + elif isinstance(choices, dict): + for (key, val) in choices.items(): + self.choices.append(key) + self.description[key] = val + else: + assert False, "ConfigSelection choices must be dict or list!" - self.checkValues() + #assert len(self.choices), "you can't have an empty configselection" + if len(self.choices) == 0: + self.choices = [""] + self.description[""] = "" - self.parent.change() + if default is None: + default = self.choices[0] - def __call__(self, selected): #needed by configlist - self.checkValues() - return ("text", self.parent.vals[self.parent.value]) + assert default in self.choices, "default must be in choice list, but " + repr(default) + " is not!" + for x in self.choices: + assert isinstance(x, str), "ConfigSelection choices must be strings" + + self.default = default -class configDateTime: - def __init__(self, parent): - self.parent = parent + if self.value == None or not self.value in self.choices: + self.value = default + + def setValue(self, value): + if value in self.choices: + self._value = value + else: + self._value = self.default - def checkValues(self): - pass -# if self.parent.value < 0: - #self.parent.value = 0 + self.changed() - #if(self.parent.value >= (len(self.parent.vals) - 1)): - #self.parent.value = len(self.parent.vals) - 1 + def tostring(self, val): + return val - def cancel(self): - self.parent.reload() + def getValue(self): + return self._value - def save(self): - self.parent.save() + def setCurrentText(self, text): + i = self.choices.index(self.value) + del self.description[self.choices[i]] + self.choices[i] = text + self.description[text] = text + self._value = text + value = property(getValue, setValue) + + def getIndex(self): + return self.choices.index(self.value) + + index = property(getIndex) + + # GUI def handleKey(self, key): - if key == config.key["prevElement"]: - self.parent.value = self.parent.value - self.parent.vals[1] - if key == config.key["nextElement"]: - self.parent.value = self.parent.value + self.parent.vals[1] - - self.checkValues() + nchoices = len(self.choices) + i = self.choices.index(self.value) + if key == KEY_LEFT: + self.value = self.choices[(i + nchoices - 1) % nchoices] + elif key == KEY_RIGHT: + self.value = self.choices[(i + 1) % nchoices] + elif key == KEY_HOME: + self.value = self.choices[0] + elif key == KEY_END: + self.value = self.choices[nchoices - 1] - self.parent.change() + def getText(self): + descr = self.description[self.value] + if len(descr): + return _(descr) + return descr - def __call__(self, selected): #needed by configlist - self.checkValues() - return ("text", strftime(self.parent.vals[0], localtime(self.parent.value))) - -class configSatlist: - def __init__(self, parent): - self.parent = parent + def getMulti(self, selected): + descr = self.description[self.value] + if len(descr): + return ("text", _(descr)) + return ("text", descr) - def checkValues(self): - if self.parent.value < 0: - self.parent.value = 0 + # HTML + def getHTML(self, id): + res = "" + for v in self.choices: + if self.value == v: + checked = 'checked="checked" ' + else: + checked = '' + res += '' + self.description[v] + "
\n" + return res; - if(self.parent.value >= (len(self.parent.vals) - 1)): - self.parent.value = len(self.parent.vals) - 1 - - def cancel(self): - self.parent.reload() + def unsafeAssign(self, value): + # setValue does check if value is in choices. This is safe enough. + self.value = value - def save(self): - self.parent.save() +# a binary decision. +# +# several customized versions exist for different +# descriptions. +# +class ConfigBoolean(ConfigElement): + def __init__(self, default = False, descriptions = {False: "false", True: "true"}): + ConfigElement.__init__(self) + self.descriptions = descriptions + self.value = self.default = default + def handleKey(self, key): + if key in [KEY_LEFT, KEY_RIGHT]: + self.value = not self.value + elif key == KEY_HOME: + self.value = False + elif key == KEY_END: + self.value = True + + def getText(self): + descr = self.descriptions[self.value] + if len(descr): + return _(descr) + return descr + + def getMulti(self, selected): + descr = self.descriptions[self.value] + if len(descr): + return ("text", _(descr)) + return ("text", descr) + + def tostring(self, value): + if not value: + return "false" + else: + return "true" + + def fromstring(self, val): + if val == "true": + return True + else: + return False + + def getHTML(self, id): + if self.value: + checked = ' checked="checked"' + else: + checked = '' + return '" + + # this is FLAWED. and must be fixed. + def unsafeAssign(self, value): + if value == "1": + self.value = True + else: + self.value = False + +class ConfigYesNo(ConfigBoolean): + def __init__(self, default = False): + ConfigBoolean.__init__(self, default = default, descriptions = {False: _("no"), True: _("yes")}) + +class ConfigOnOff(ConfigBoolean): + def __init__(self, default = False): + ConfigBoolean.__init__(self, default = default, descriptions = {False: _("off"), True: _("on")}) + +class ConfigEnableDisable(ConfigBoolean): + def __init__(self, default = False): + ConfigBoolean.__init__(self, default = default, descriptions = {False: _("disable"), True: _("enable")}) + +class ConfigDateTime(ConfigElement): + def __init__(self, default, formatstring, increment = 86400): + ConfigElement.__init__(self) + self.increment = increment + self.formatstring = formatstring + self.value = self.default = int(default) def handleKey(self, key): - if key == config.key["prevElement"]: - self.parent.value = self.parent.value - 1 - if key == config.key["nextElement"]: - self.parent.value = self.parent.value + 1 - - self.checkValues() + if key == KEY_LEFT: + self.value = self.value - self.increment + elif key == KEY_RIGHT: + self.value = self.value + self.increment + elif key == KEY_HOME or key == KEY_END: + self.value = self.default - self.parent.change() + def getText(self): + return time.strftime(self.formatstring, time.localtime(self.value)) - def __call__(self, selected): #needed by configlist - self.checkValues() - #fixme - return ("text", str(self.parent.vals[self.parent.value][0])) - -class configSequenceArg: - def get(self, type, args = ()): - # configsequencearg.get ("IP") - if (type == "IP"): - return (("."), [(1,255),(0,255),(0,255),(0,255)], "") - # configsequencearg.get ("MAC") - if (type == "MAC"): - return ((":"), [(1,255),(1,255),(1,255),(1,255),(1,255),(1,255)], "") - # configsequencearg.get("INTEGER", (min, max)) => x with min <= x <= max - if (type == "INTEGER"): - return ((":"), [args], "") - # configsequencearg.get("PINCODE", (number, "*")) => pin with number = length of pincode and "*" as numbers shown as stars - # configsequencearg.get("PINCODE", (number, "")) => pin with number = length of pincode and numbers shown - if (type == "PINCODE"): - return ((":"), [(0, (10**args[0])-1)], args[1]) - # configsequencearg.get("FLOAT", [(min,max),(min1,max1)]) => x.y with min <= x <= max and min1 <= y <= max1 - if (type == "FLOAT"): - return (("."), args, "") - -configsequencearg = configSequenceArg() + def getMulti(self, selected): + return ("text", time.strftime(self.formatstring, time.localtime(self.value))) + + def fromstring(self, val): + return int(val) + +# *THE* mighty config element class +# +# allows you to store/edit a sequence of values. +# can be used for IP-addresses, dates, plain integers, ... +# several helper exist to ease this up a bit. +# +class ConfigSequence(ConfigElement): + def __init__(self, seperator, limits, censor_char = "", default = None): + ConfigElement.__init__(self) + assert isinstance(limits, list) and len(limits[0]) == 2, "limits must be [(min, max),...]-tuple-list" + assert censor_char == "" or len(censor_char) == 1, "censor char must be a single char (or \"\")" + #assert isinstance(default, list), "default must be a list" + #assert isinstance(default[0], int), "list must contain numbers" + #assert len(default) == len(limits), "length must match" + + self.marked_pos = 0 + self.seperator = seperator + self.limits = limits + self.censor_char = censor_char + + self.default = default + self.value = copy.copy(default) -class configSequence: - def __init__(self, parent): - self.parent = parent - self.markedPos = 0 - self.seperator = self.parent.vals[0] - self.valueBounds = self.parent.vals[1] - self.censorChar = self.parent.vals[2] + self.endNotifier = [] - def checkValues(self): - maxPos = 0 + def validate(self): + max_pos = 0 num = 0 - for i in self.parent.value: - maxPos += len(str(self.valueBounds[num][1])) - while (self.valueBounds[num][0] > self.parent.value[num]): - self.parent.value[num] += 1 + for i in self._value: + max_pos += len(str(self.limits[num][1])) + + if self._value[num] < self.limits[num][0]: + self._value[num] = self.limits[num][0] + + if self._value[num] > self.limits[num][1]: + self._value[num] = self.limits[num][1] - while (self.valueBounds[num][1] < self.parent.value[num]): - self.parent.value[num] -= 1 - -# if (self.valueBounds[num][0] <= i <= self.valueBounds[num][1]): - #pass - #else: - #self.parent.value[num] = self.valueBounds[num][0] num += 1 - - if self.markedPos >= maxPos: - self.markedPos = maxPos - 1 - if self.markedPos < 0: - self.markedPos = 0 + + if self.marked_pos >= max_pos: + for x in self.endNotifier: + x(self) + self.marked_pos = max_pos - 1 + + if self.marked_pos < 0: + self.marked_pos = 0 + + def validatePos(self): + if self.marked_pos < 0: + self.marked_pos = 0 - def cancel(self): - self.parent.reload() + total_len = sum([len(str(x[1])) for x in self.limits]) - def save(self): - self.parent.save() + if self.marked_pos >= total_len: + self.marked_pos = total_len - 1 + + def addEndNotifier(self, notifier): + self.endNotifier.append(notifier) def handleKey(self, key): - #this will no change anything on the value itself - #so we can handle it here in gui element - if key == config.key["prevElement"]: - self.markedPos -= 1 - if key == config.key["nextElement"]: - self.markedPos += 1 + if key == KEY_LEFT: + self.marked_pos -= 1 + self.validatePos() + + if key == KEY_RIGHT: + self.marked_pos += 1 + self.validatePos() + + if key == KEY_HOME: + self.marked_pos = 0 + self.validatePos() + + if key == KEY_END: + max_pos = 0 + num = 0 + for i in self._value: + max_pos += len(str(self.limits[num][1])) + num += 1 + self.marked_pos = max_pos - 1 + self.validatePos() - if key >= config.key["0"] and key <= config.key["9"]: - number = 9 - config.key["9"] + key + if key in KEY_NUMBERS or key == KEY_ASCII: + if key == KEY_ASCII: + code = getPrevAsciiCode() + if code < 48 or code > 57: + return + number = code - 48 + else: + number = getKeyNumber(key) + + block_len = [] + for x in self.limits: + block_len.append(len(str(x[1]))) + + total_len = sum(block_len) + + pos = 0 + blocknumber = 0 + block_len_total = [0, ] + for x in block_len: + pos += block_len[blocknumber] + block_len_total.append(pos) + if pos - 1 >= self.marked_pos: + pass + else: + blocknumber += 1 + # length of numberblock - numberLen = len(str(self.valueBounds[0][1])) + number_len = len(str(self.limits[blocknumber][1])) + # position in the block - posinblock = self.markedPos % numberLen - # blocknumber - blocknumber = self.markedPos / numberLen + posinblock = self.marked_pos - block_len_total[blocknumber] - oldvalue = self.parent.value[blocknumber] - olddec = oldvalue % 10 ** (numberLen - posinblock) - (oldvalue % 10 ** (numberLen - posinblock - 1)) - newvalue = oldvalue - olddec + (10 ** (numberLen - posinblock - 1) * number) + oldvalue = self._value[blocknumber] + olddec = oldvalue % 10 ** (number_len - posinblock) - (oldvalue % 10 ** (number_len - posinblock - 1)) + newvalue = oldvalue - olddec + (10 ** (number_len - posinblock - 1) * number) - print "You actually pressed a number (" + str(number) + ") which will be added at block number " + str(blocknumber) + " on position " + str(posinblock) - print "Old value: " + str(oldvalue) + " olddec: " + str(olddec) + " newvalue: " + str(newvalue) - self.parent.value[blocknumber] = newvalue - self.markedPos += 1 + self._value[blocknumber] = newvalue + self.marked_pos += 1 - self.checkValues() - - print "markPos:", - print self.markedPos - - #FIXME: dont call when press left/right - self.parent.change() - - def __call__(self, selected): #needed by configlist + self.validate() + self.changed() + + def genText(self): value = "" - mPos = self.markedPos - print "Positon: " + str(mPos) + mPos = self.marked_pos num = 0; - for i in self.parent.value: + for i in self._value: if len(value): #fixme no heading separator possible value += self.seperator if mPos >= len(value) - 1: mPos += 1 - - #diff = self.valueBounds - len(str(i)) - #if diff > 0: - ## if this helps?! - #value += " " * diff - print (("%0" + str(len(str(self.valueBounds[num][1]))) + "d") % i) - if (self.censorChar == ""): - value += ("%0" + str(len(str(self.valueBounds[num][1]))) + "d") % i + + if self.censor_char == "": + value += ("%0" + str(len(str(self.limits[num][1]))) + "d") % i else: - value += (self.censorChar * len(str(self.valueBounds[num][1]))) + value += (self.censor_char * len(str(self.limits[num][1]))) num += 1 + return (value, mPos) + + def getText(self): + (value, mPos) = self.genText() + return value + + def getMulti(self, selected): + (value, mPos) = self.genText() # only mark cursor when we are selected # (this code is heavily ink optimized!) - return ("mtext"[1-selected:], value, [mPos]) + if self.enabled: + return ("mtext"[1-selected:], value, [mPos]) + else: + return ("text", value) + + def tostring(self, val): + return self.seperator.join([self.saveSingle(x) for x in val]) + + def saveSingle(self, v): + return str(v) + + def fromstring(self, value): + return [int(x) for x in value.split(self.seperator)] -class configValue: - def __init__(self, obj): - self.obj = obj +class ConfigIP(ConfigSequence): + def __init__(self, default): + ConfigSequence.__init__(self, seperator = ".", limits = [(0,255),(0,255),(0,255),(0,255)], default = default) + + def getHTML(self, id): + # we definitely don't want leading zeros + return '.'.join(["%d" % d for d in self.value]) + +class ConfigMAC(ConfigSequence): + def __init__(self, default): + ConfigSequence.__init__(self, seperator = ":", limits = [(1,255),(1,255),(1,255),(1,255),(1,255),(1,255)], default = default) + +class ConfigPosition(ConfigSequence): + def __init__(self, default, args): + ConfigSequence.__init__(self, seperator = ",", limits = [(0,args[0]),(0,args[1]),(0,args[2]),(0,args[3])], default = default) + +class ConfigClock(ConfigSequence): + def __init__(self, default): + import time + t = time.localtime(default) + ConfigSequence.__init__(self, seperator = ":", limits = [(0,23),(0,59)], default = [t.tm_hour, t.tm_min]) + +class ConfigInteger(ConfigSequence): + def __init__(self, default, limits = (0, 9999999999)): + ConfigSequence.__init__(self, seperator = ":", limits = [limits], default = default) + + # you need to override this to do input validation + def setValue(self, value): + self._value = [value] + self.changed() + + def getValue(self): + return self._value[0] + + value = property(getValue, setValue) + + def fromstring(self, value): + return int(value) + + def tostring(self, value): + return str(value) + +class ConfigPIN(ConfigInteger): + def __init__(self, default, len = 4, censor = ""): + assert isinstance(default, int), "ConfigPIN default must be an integer" + if default == -1: + default = "aaaa" + ConfigSequence.__init__(self, seperator = ":", limits = [(0, (10**len)-1)], censor_char = censor, default = default) + self.len = len + + def getLength(self): + return self.len + +class ConfigFloat(ConfigSequence): + def __init__(self, default, limits): + ConfigSequence.__init__(self, seperator = ".", limits = limits, default = default) + + def getFloat(self): + return float(self.value[1] / float(self.limits[1][1] + 1) + self.value[0]) + + float = property(getFloat) + +# an editable text... +class ConfigText(ConfigElement, NumericalTextInput): + def __init__(self, default = "", fixed_size = True, visible_width = False): + ConfigElement.__init__(self) + NumericalTextInput.__init__(self, nextFunc = self.nextFunc, handleTimeout = False) - def __str__(self): - return self.obj + self.marked_pos = 0 + self.allmarked = (default != "") + self.fixed_size = fixed_size + self.visible_width = visible_width + self.offset = 0 + self.overwrite = fixed_size + self.help_window = None + self.value = self.default = default -class Config: - def __init__(self): - self.key = { "choseElement": 0, - "prevElement": 1, - "nextElement": 2, - "0": 10, - "1": 11, - "2": 12, - "3": 13, - "4": 14, - "5": 15, - "6": 16, - "7": 17, - "8": 18, - "9": 19 } + def validateMarker(self): + if self.fixed_size: + if self.marked_pos > len(self.text)-1: + self.marked_pos = len(self.text)-1 + else: + if self.marked_pos > len(self.text): + self.marked_pos = len(self.text) + if self.marked_pos < 0: + self.marked_pos = 0 + if self.visible_width: + if self.marked_pos < self.offset: + self.offset = self.marked_pos + if self.marked_pos >= self.offset + self.visible_width: + if self.marked_pos == len(self.text): + self.offset = self.marked_pos - self.visible_width + else: + self.offset = self.marked_pos - self.visible_width + 1 + if self.offset > 0 and self.offset + self.visible_width > len(self.text): + self.offset = max(0, len(self.text) - self.visible_width) + + def insertChar(self, ch, pos, owr): + if owr or self.overwrite: + self.text = self.text[0:pos] + ch + self.text[pos + 1:] + elif self.fixed_size: + self.text = self.text[0:pos] + ch + self.text[pos:-1] + else: + self.text = self.text[0:pos] + ch + self.text[pos:] + + def deleteChar(self, pos): + if not self.fixed_size: + self.text = self.text[0:pos] + self.text[pos + 1:] + elif self.overwrite: + self.text = self.text[0:pos] + " " + self.text[pos + 1:] + else: + self.text = self.text[0:pos] + self.text[pos + 1:] + " " + + def deleteAllChars(self): + if self.fixed_size: + self.text = " " * len(self.text) + else: + self.text = "" + self.marked_pos = 0 + + def handleKey(self, key): + # this will no change anything on the value itself + # so we can handle it here in gui element + if key == KEY_DELETE: + self.timeout() + if self.allmarked: + self.deleteAllChars() + self.allmarked = False + else: + self.deleteChar(self.marked_pos) + if self.fixed_size and self.overwrite: + self.marked_pos += 1 + elif key == KEY_BACKSPACE: + self.timeout() + if self.allmarked: + self.deleteAllChars() + self.allmarked = False + elif self.marked_pos > 0: + self.deleteChar(self.marked_pos-1) + if not self.fixed_size and self.offset > 0: + self.offset -= 1 + self.marked_pos -= 1 + elif key == KEY_LEFT: + self.timeout() + if self.allmarked: + self.marked_pos = len(self.text) + self.allmarked = False + else: + self.marked_pos -= 1 + elif key == KEY_RIGHT: + self.timeout() + if self.allmarked: + self.marked_pos = 0 + self.allmarked = False + else: + self.marked_pos += 1 + elif key == KEY_HOME: + self.timeout() + self.allmarked = False + self.marked_pos = 0 + elif key == KEY_END: + self.timeout() + self.allmarked = False + self.marked_pos = len(self.text) + elif key == KEY_TOGGLEOW: + self.timeout() + self.overwrite = not self.overwrite + elif key == KEY_ASCII: + self.timeout() + newChar = unichr(getPrevAsciiCode()) + if self.allmarked: + self.deleteAllChars() + self.allmarked = False + self.insertChar(newChar, self.marked_pos, False) + self.marked_pos += 1 + elif key in KEY_NUMBERS: + owr = self.lastKey == getKeyNumber(key) + newChar = self.getKey(getKeyNumber(key)) + if self.allmarked: + self.deleteAllChars() + self.allmarked = False + self.insertChar(newChar, self.marked_pos, owr) + elif key == KEY_TIMEOUT: + self.timeout() + if self.help_window: + self.help_window.update(self) + return + + if self.help_window: + self.help_window.update(self) + self.validateMarker() + self.changed() + + def nextFunc(self): + self.marked_pos += 1 + self.validateMarker() + self.changed() + + def getValue(self): + return self.text.encode("utf-8") + + def setValue(self, val): + try: + self.text = val.decode("utf-8") + except UnicodeDecodeError: + self.text = val + print "Broken UTF8!" + + value = property(getValue, setValue) + _value = property(getValue, setValue) + + def getText(self): + return self.text.encode("utf-8") + + def getMulti(self, selected): + if self.visible_width: + if self.allmarked: + mark = range(0, min(self.visible_width, len(self.text))) + else: + mark = [self.marked_pos-self.offset] + return ("mtext"[1-selected:], self.text[self.offset:self.offset+self.visible_width].encode("utf-8")+" ", mark) + else: + if self.allmarked: + mark = range(0, len(self.text)) + else: + mark = [self.marked_pos] + return ("mtext"[1-selected:], self.text.encode("utf-8")+" ", mark) + + def onSelect(self, session): + self.allmarked = (self.value != "") + if session is not None: + from Screens.NumericalTextInputHelpDialog import NumericalTextInputHelpDialog + self.help_window = session.instantiateDialog(NumericalTextInputHelpDialog, self) + self.help_window.show() + + def onDeselect(self, session): + self.marked_pos = 0 + self.offset = 0 + if self.help_window: + session.deleteDialog(self.help_window) + self.help_window = None + + def getHTML(self, id): + return '
\n' + + def unsafeAssign(self, value): + self.value = str(value) + +class ConfigNumber(ConfigText): + def __init__(self, default = 0): + ConfigText.__init__(self, str(default), fixed_size = False) + + def getValue(self): + return int(self.text) -config = Config(); -configfile = configFile() + def setValue(self, val): + self.text = str(val) -class ConfigSlider: - def __init__(self, parent): - self.parent = parent + value = property(getValue, setValue) + _value = property(getValue, setValue) - def cancel(self): - self.parent.reload() + def conform(self): + pos = len(self.text) - self.marked_pos + self.text = self.text.lstrip("0") + if self.text == "": + self.text = "0" + if pos > len(self.text): + self.marked_pos = 0 + else: + self.marked_pos = len(self.text) - pos - def save(self): - self.parent.save() + def handleKey(self, key): + if key in KEY_NUMBERS or key == KEY_ASCII: + if key == KEY_ASCII: + ascii = getPrevAsciiCode() + if not (48 <= ascii <= 57): + return + else: + ascii = getKeyNumber(key) + 48 + newChar = unichr(ascii) + if self.allmarked: + self.deleteAllChars() + self.allmarked = False + self.insertChar(newChar, self.marked_pos, False) + self.marked_pos += 1 + else: + ConfigText.handleKey(self, key) + self.conform() + + def onSelect(self, session): + self.allmarked = (self.value != "") + + def onDeselect(self, session): + self.marked_pos = 0 + self.offset = 0 + +# a slider. +class ConfigSlider(ConfigElement): + def __init__(self, default = 0, increment = 1, limits = (0, 100)): + ConfigElement.__init__(self) + self.value = self.default = default + self.min = limits[0] + self.max = limits[1] + self.increment = increment def checkValues(self): - if self.parent.value < 0: - self.parent.value = 0 + if self.value < self.min: + self.value = self.min - if self.parent.value > 10: - self.parent.value = 10 + if self.value > self.max: + self.value = self.max def handleKey(self, key): - if key == config.key["prevElement"]: - self.parent.value = self.parent.value - 1 - if key == config.key["nextElement"]: - self.parent.value = self.parent.value + 1 - - self.checkValues() - self.parent.change() - - def __call__(self, selected): #needed by configlist + if key == KEY_LEFT: + self.value -= self.increment + elif key == KEY_RIGHT: + self.value += self.increment + elif key == KEY_HOME: + self.value = self.min + elif key == KEY_END: + self.value = self.max + else: + return + self.checkValues() - return ("slider", self.parent.value * 10) + self.changed() -class ConfigSubsection: - def __init__(self): - pass + def getText(self): + return "%d / %d" % (self.value, self.max) -class configElement: - - def getIndexbyEntry(self, data): - cnt = 0; - tcnt = -1; #for defaultval - for x in self.vals: - if int(x[1]) == int(data): - return cnt - if int(x[1]) == int(self.defaultValue): - tcnt = cnt - cnt += 1 - if tcnt != -1: - return tcnt - return 0 #prevent bigger then array - - def datafromFile(self, control, data): - if control == ConfigSlider: - return int(data); - elif control == configSelection: - return int(data); - elif control == configDateTime: - return int(data); - elif control == configSequence: - list = [ ] - part = data.split(self.vals[0]) - for x in part: - list.append(int(x)) - return list - elif control == configSatlist: - return self.getIndexbyEntry(data) - else: - return "" - - def datatoFile(self, control, data): - if control == ConfigSlider: - return str(data); - elif control == configSelection: - return str(data); - elif control == configDateTime: - return str(data); - elif control == configSequence: - value = ((len(data) * ("%d" + self.vals[0]))[0:-1]) % tuple(data) -# just in case you don't understand the above, here an equivalent: -# value = "" -# for i in data: -# if value !="": -# value += self.vals[0] -# value += str(i) - return value - elif control == configSatlist: - return str(self.vals[self.value][1]); - else: - return "" - - def loadData(self): - try: - value = self.datafromFile(self.controlType, configfile.getKey(self.configPath)) - except: - value = "" + def getMulti(self, selected): + self.checkValues() + return ("slider", self.value, self.max) - if value == "": - #print "value not found - using default" + def fromstring(self, value): + return int(value) - if self.controlType == configSatlist: - self.value = self.getIndexbyEntry(self.defaultValue) - else: - self.value = self.defaultValue +# a satlist. in fact, it's a ConfigSelection. +class ConfigSatlist(ConfigSelection): + def __init__(self, list, default = None): + if default is not None: + default = str(default) + ConfigSelection.__init__(self, choices = [(str(orbpos), desc) for (orbpos, desc, flags) in list], default = default) + + def getOrbitalPosition(self): + if self.value == "": + return None + return int(self.value) + + orbital_position = property(getOrbitalPosition) - self.save() #add missing value to dict +class ConfigSet(ConfigElement): + def __init__(self, choices, default = []): + ConfigElement.__init__(self) + self.choices = [] + self.description = {} + if isinstance(choices, list): + choices.sort() + for x in choices: + if isinstance(x, tuple): + self.choices.append(x[0]) + self.description[x[0]] = str(x[1]) + else: + self.choices.append(x) + self.description[x] = str(x) else: - self.value = value - - #is this right? activate settings after load/cancel and use default - self.change() - - def __init__(self, configPath, control, defaultValue, vals): - self.configPath = configPath - self.defaultValue = defaultValue - self.controlType = control - self.vals = vals - self.notifierList = [ ] - self.enabled = True - self.loadData() - def addNotifier(self, notifier): - self.notifierList.append(notifier); - notifier(self); - def change(self): - for notifier in self.notifierList: - notifier(self) - def reload(self): - self.loadData() + assert False, "ConfigSet choices must be a list!" + if len(self.choices) == 0: + self.choices = [""] + self.description[""] = "" + if default is None: + default = [] + self.pos = -1 + default.sort() + self.default = default + self.value = default+[] + + def toggleChoice(self, choice): + if choice in self.value: + self.value.remove(choice) + else: + self.value.append(choice) + self.value.sort() + + def handleKey(self, key): + if key in KEY_NUMBERS + [KEY_DELETE, KEY_BACKSPACE]: + if self.pos != -1: + self.toggleChoice(self.choices[self.pos]) + elif key == KEY_LEFT: + self.pos -= 1 + if self.pos < -1: + self.pos = len(self.choices)-1 + elif key == KEY_RIGHT: + self.pos += 1 + if self.pos >= len(self.choices): + self.pos = -1 + elif key in [KEY_HOME, KEY_END]: + self.pos = -1 + + def genString(self, lst): + res = "" + for x in lst: + res += self.description[x]+" " + return res + + def getText(self): + return self.genString(self.value) + + def getMulti(self, selected): + if not selected or self.pos == -1: + return ("text", self.genString(self.value)) + else: + tmp = self.value+[] + ch = self.choices[self.pos] + mem = ch in self.value + if not mem: + tmp.append(ch) + tmp.sort() + ind = tmp.index(ch) + val1 = self.genString(tmp[:ind]) + val2 = " "+self.genString(tmp[ind+1:]) + if mem: + chstr = " "+self.description[ch]+" " + else: + chstr = "("+self.description[ch]+")" + return ("mtext", val1+chstr+val2, range(len(val1),len(val1)+len(chstr))) + + def onDeselect(self, session): + self.pos = -1 + self.changed() + + def tostring(self, value): + return str(value) + + def fromstring(self, val): + return eval(val) + +class ConfigLocations(ConfigElement): + def __init__(self, default = [], visible_width = False): + ConfigElement.__init__(self) + self.pos = -1 + self.default = default + self.locations = [] + self.mountpoints = [] + harddiskmanager.on_partition_list_change.append(self.mountpointsChanged) + + def setValue(self, value): + loc = [x[0] for x in self.locations if x[3]] + add = [x for x in value if not x in loc] + diff = add + [x for x in loc if not x in value] + self.locations = [x for x in self.locations if not x[0] in diff] + [[x, self.getMountpoint(x), True, True] for x in add] + self.locations.sort(key = lambda x: x[0]) + self.changed() + + def getValue(self): + self.checkChangedMountpoints() + for x in self.locations: + x[3] = x[2] + return [x[0] for x in self.locations if x[3]] + + value = property(getValue, setValue) + + def tostring(self, value): + return str(value) + + def fromstring(self, val): + return eval(val) + + def load(self): + if self.saved_value is None: + tmp = self.default + else: + tmp = self.fromstring(self.saved_value) + self.locations = [[x, None, False, False] for x in tmp] + self.refreshMountpoints() + for x in self.locations: + if os.path.exists(x[0]): + x[1] = self.getMountpoint(x[0]) + x[2] = True + def save(self): - configfile.setKey(self.configPath, self.datatoFile(self.controlType,self.value)) + if self.save_disabled or self.locations == []: + self.saved_value = None + else: + self.saved_value = self.tostring([x[0] for x in self.locations]) + + def isChanged(self): + if self.saved_value is None and self.locations == []: + return False + return self.tostring([x[0] for x in self.locations]) != self.saved_value -class configElement_nonSave(configElement): - def __init__(self, configPath, control, defaultValue, vals): - configElement.__init__(self, configPath, control, defaultValue, vals) + def mountpointsChanged(self, action, dev): + print "Mounts changed: ", action, dev + mp = dev.mountpoint+"/" + if action == "add": + self.addedMount(mp) + elif action == "remove": + self.removedMount(mp) + self.refreshMountpoints() + + def addedMount(self, mp): + for x in self.locations: + if x[1] == mp: + x[2] = True + elif x[1] == None and os.path.exists(x[0]): + x[1] = self.getMountpoint(x[0]) + x[2] = True + + def removedMount(self, mp): + for x in self.locations: + if x[1] == mp: + x[2] = False + + def refreshMountpoints(self): + self.mountpoints = [p.mountpoint + "/" for p in harddiskmanager.getMountedPartitions() if p.mountpoint != "/"] + self.mountpoints.sort(key = lambda x: -len(x)) + + def checkChangedMountpoints(self): + oldmounts = self.mountpoints + self.refreshMountpoints() + if oldmounts == self.mountpoints: + return + for x in oldmounts: + if not x in self.mountpoints: + self.removedMount(x) + for x in self.mountpoints: + if not x in oldmounts: + self.addedMount(x) + + def getMountpoint(self, file): + file = os.path.realpath(file)+"/" + for m in self.mountpoints: + if file.startswith(m): + return m + return None + + def handleKey(self, key): + if key == KEY_LEFT: + self.pos -= 1 + if self.pos < -1: + self.pos = len(self.value)-1 + elif key == KEY_RIGHT: + self.pos += 1 + if self.pos >= len(self.value): + self.pos = -1 + elif key in [KEY_HOME, KEY_END]: + self.pos = -1 + + def getText(self): + return " ".join(self.value) + + def getMulti(self, selected): + if not selected: + valstr = " ".join(self.value) + if self.visible_width and len(valstr) > self.visible_width: + return ("text", valstr[0:self.visible_width]) + else: + return ("text", valstr) + else: + i = 0 + valstr = "" + ind1 = 0 + ind2 = 0 + for val in self.value: + if i == self.pos: + ind1 = len(valstr) + valstr += str(val)+" " + if i == self.pos: + ind2 = len(valstr) + i += 1 + if self.visible_width and len(valstr) > self.visible_width: + if ind1+1 < self.visible_width/2: + off = 0 + else: + off = min(ind1+1-self.visible_width/2, len(valstr)-self.visible_width) + return ("mtext", valstr[off:off+self.visible_width], range(ind1-off,ind2-off)) + else: + return ("mtext", valstr, range(ind1,ind2)) + + def onDeselect(self, session): + self.pos = -1 + +# nothing. +class ConfigNothing(ConfigSelection): + def __init__(self): + ConfigSelection.__init__(self, choices = [""]) + +# until here, 'saved_value' always had to be a *string*. +# now, in ConfigSubsection, and only there, saved_value +# is a dict, essentially forming a tree. +# +# config.foo.bar=True +# config.foobar=False +# +# turns into: +# config.saved_value == {"foo": {"bar": "True"}, "foobar": "False"} +# + + +class ConfigSubsectionContent(object): + pass + +# we store a backup of the loaded configuration +# data in self.stored_values, to be able to deploy +# them when a new config element will be added, +# so non-default values are instantly available + +# A list, for example: +# config.dipswitches = ConfigSubList() +# config.dipswitches.append(ConfigYesNo()) +# config.dipswitches.append(ConfigYesNo()) +# config.dipswitches.append(ConfigYesNo()) +class ConfigSubList(list, object): + def __init__(self): + object.__init__(self) + list.__init__(self) + self.stored_values = {} def save(self): - pass + for x in self: + x.save() + + def load(self): + for x in self: + x.load() + + def getSavedValue(self): + res = {} + for i in range(len(self)): + sv = self[i].saved_value + if sv is not None: + res[str(i)] = sv + return res + + def setSavedValue(self, values): + self.stored_values = dict(values) + for (key, val) in self.stored_values.items(): + if int(key) < len(self): + self[int(key)].saved_value = val + + saved_value = property(getSavedValue, setSavedValue) + + def append(self, item): + i = str(len(self)) + list.append(self, item) + if i in self.stored_values: + item.saved_value = self.stored_values[i] + item.load() + + def dict(self): + res = dict() + for index in range(len(self)): + res[str(index)] = self[index] + return res + +# same as ConfigSubList, just as a dictionary. +# care must be taken that the 'key' has a proper +# str() method, because it will be used in the config +# file. +class ConfigSubDict(dict, object): + def __init__(self): + object.__init__(self) + dict.__init__(self) + self.stored_values = {} + + def save(self): + for x in self.values(): + x.save() + + def load(self): + for x in self.values(): + x.load() + + def getSavedValue(self): + res = {} + for (key, val) in self.items(): + if val.saved_value is not None: + res[str(key)] = val.saved_value + return res + + def setSavedValue(self, values): + self.stored_values = dict(values) + for (key, val) in self.items(): + if str(key) in self.stored_values: + val = self.stored_values[str(key)] + + saved_value = property(getSavedValue, setSavedValue) + + def __setitem__(self, key, item): + dict.__setitem__(self, key, item) + if str(key) in self.stored_values: + item.saved_value = self.stored_values[str(key)] + item.load() + + def dict(self): + return self + +# Like the classes above, just with a more "native" +# syntax. +# +# some evil stuff must be done to allow instant +# loading of added elements. this is why this class +# is so complex. +# +# we need the 'content' because we overwrite +# __setattr__. +# If you don't understand this, try adding +# __setattr__ to a usual exisiting class and you will. +class ConfigSubsection(object): + def __init__(self): + object.__init__(self) + self.__dict__["content"] = ConfigSubsectionContent() + self.content.items = { } + self.content.stored_values = { } + + def __setattr__(self, name, value): + if name == "saved_value": + return self.setSavedValue(value) + assert isinstance(value, ConfigSubsection) or isinstance(value, ConfigElement) or isinstance(value, ConfigSubList) or isinstance(value, ConfigSubDict), "ConfigSubsections can only store ConfigSubsections, ConfigSubLists, ConfigSubDicts or ConfigElements" + self.content.items[name] = value + if name in self.content.stored_values: + #print "ok, now we have a new item,", name, "and have the following value for it:", self.content.stored_values[name] + value.saved_value = self.content.stored_values[name] + value.load() + + def __getattr__(self, name): + return self.content.items[name] + + def getSavedValue(self): + res = self.content.stored_values + for (key, val) in self.content.items.items(): + if val.saved_value is not None: + res[key] = val.saved_value + elif key in res: + del res[key] + + return res + + def setSavedValue(self, values): + values = dict(values) -def getConfigListEntry(description, element): - b = element - item = b.controlType(b) - return ((description, item)) + self.content.stored_values = values + + for (key, val) in self.content.items.items(): + if key in values: + val.saved_value = values[key] + + saved_value = property(getSavedValue, setSavedValue) + + def save(self): + for x in self.content.items.values(): + x.save() + + def load(self): + for x in self.content.items.values(): + x.load() + + def dict(self): + return self.content.items + +# the root config object, which also can "pickle" (=serialize) +# down the whole config tree. +# +# we try to keep non-existing config entries, to apply them whenever +# a new config entry is added to a subsection +# also, non-existing config entries will be saved, so they won't be +# lost when a config entry disappears. +class Config(ConfigSubsection): + def __init__(self): + ConfigSubsection.__init__(self) + + def pickle_this(self, prefix, topickle, result): + for (key, val) in topickle.items(): + name = prefix + "." + key + + if isinstance(val, dict): + self.pickle_this(name, val, result) + elif isinstance(val, tuple): + result.append(name + "=" + val[0]) # + " ; " + val[1]) + else: + result.append(name + "=" + val) + + def pickle(self): + result = [ ] + self.pickle_this("config", self.saved_value, result) + return '\n'.join(result) + "\n" + + def unpickle(self, lines): + tree = { } + for l in lines: + if not len(l) or l[0] == '#': + continue + + n = l.find('=') + val = l[n+1:].strip() + + names = l[:n].split('.') +# if val.find(' ') != -1: +# val = val[:val.find(' ')] + + base = tree + + for n in names[:-1]: + base = base.setdefault(n, {}) + + base[names[-1]] = val + + # we inherit from ConfigSubsection, so ... + #object.__setattr__(self, "saved_value", tree["config"]) + if "config" in tree: + self.setSavedValue(tree["config"]) + + def saveToFile(self, filename): + f = open(filename, "w") + f.write(self.pickle()) + f.close() + + def loadFromFile(self, filename): + f = open(filename, "r") + self.unpickle(f.readlines()) + f.close() + +config = Config() +config.misc = ConfigSubsection() + +class ConfigFile: + CONFIG_FILE = resolveFilename(SCOPE_CONFIG, "settings") + + def load(self): + try: + config.loadFromFile(self.CONFIG_FILE) + except IOError, e: + print "unable to load config (%s), assuming defaults..." % str(e) + + def save(self): +# config.save() + config.saveToFile(self.CONFIG_FILE) + + def __resolveValue(self, pickles, cmap): + if cmap.has_key(pickles[0]): + if len(pickles) > 1: + return self.__resolveValue(pickles[1:], cmap[pickles[0]].dict()) + else: + return str(cmap[pickles[0]].value) + return None + + def getResolvedKey(self, key): + names = key.split('.') + if len(names) > 1: + if names[0] == "config": + ret=self.__resolveValue(names[1:], config.content.items) + if ret and len(ret): + return ret + print "getResolvedKey", key, "failed !! (Typo??)" + return "" + +def NoSave(element): + element.disableSave() + return element + +configfile = ConfigFile() + +configfile.load() + +def getConfigListEntry(*args): + assert len(args) > 1, "getConfigListEntry needs a minimum of two arguments (descr, configElement)" + return args - \ No newline at end of file +#def _(x): +# return x +# +#config.bla = ConfigSubsection() +#config.bla.test = ConfigYesNo() +#config.nim = ConfigSubList() +#config.nim.append(ConfigSubsection()) +#config.nim[0].bla = ConfigYesNo() +#config.nim.append(ConfigSubsection()) +#config.nim[1].bla = ConfigYesNo() +#config.nim[1].blub = ConfigYesNo() +#config.arg = ConfigSubDict() +#config.arg["Hello"] = ConfigYesNo() +# +#config.arg["Hello"].handleKey(KEY_RIGHT) +#config.arg["Hello"].handleKey(KEY_RIGHT) +# +##config.saved_value +# +##configfile.save() +#config.save() +#print config.pickle()