+ def onDeselect(self, session):
+ if not self.last_value == self.value:
+ self.changedFinal()
+ self.last_value = self.value
+
+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 getKeyNumber(key):
+ assert key in KEY_NUMBERS
+ return key - KEY_0
+
+class choicesList(object): # XXX: we might want a better name for this
+ LIST_TYPE_LIST = 1
+ LIST_TYPE_DICT = 2
+
+ def __init__(self, choices, type = None):
+ self.choices = choices
+ if type is None:
+ if isinstance(choices, list):
+ self.type = choicesList.LIST_TYPE_LIST
+ elif isinstance(choices, dict):
+ self.type = choicesList.LIST_TYPE_DICT
+ else:
+ assert False, "choices must be dict or list!"
+ else:
+ self.type = type
+
+ def __list__(self):
+ if self.type == choicesList.LIST_TYPE_LIST:
+ ret = [not isinstance(x, tuple) and x or x[0] for x in self.choices]
+ else:
+ ret = self.choices.keys()
+ return ret or [""]
+
+ def __iter__(self):
+ if self.type == choicesList.LIST_TYPE_LIST:
+ ret = [not isinstance(x, tuple) and x or x[0] for x in self.choices]
+ else:
+ ret = self.choices
+ return iter(ret or [""])
+
+ def __len__(self):
+ return len(self.choices) or 1
+
+ def __getitem__(self, index):
+ if self.type == choicesList.LIST_TYPE_LIST:
+ ret = self.choices[index]
+ if isinstance(ret, tuple):
+ ret = ret[0]
+ return ret
+ return self.choices.keys()[index]
+
+ def index(self, value):
+ return self.__list__().index(value)
+
+ def __setitem__(self, index, value):
+ if self.type == choicesList.LIST_TYPE_LIST:
+ orig = self.choices[index]
+ if isinstance(orig, tuple):
+ self.choices[index] = (value, orig[1])
+ else:
+ self.choices[index] = value
+ else:
+ key = self.choices.keys()[index]
+ orig = self.choices[key]
+ del self.choices[key]
+ self.choices[value] = orig
+
+ def default(self):
+ choices = self.choices
+ if not choices:
+ return ""
+ if self.type is choicesList.LIST_TYPE_LIST:
+ default = choices[0]
+ if isinstance(default, tuple):
+ default = default[0]
+ else:
+ default = choices.keys()[0]
+ return default
+
+class descriptionList(choicesList): # XXX: we might want a better name for this
+ def __list__(self):
+ if self.type == choicesList.LIST_TYPE_LIST:
+ ret = [not isinstance(x, tuple) and x or x[1] for x in self.choices]
+ else:
+ ret = self.choices.values()
+ return ret or [""]
+
+ def __iter__(self):
+ return iter(self.__list__())
+
+ def __getitem__(self, index):
+ if self.type == choicesList.LIST_TYPE_LIST:
+ for x in self.choices:
+ if isinstance(x, tuple):
+ if x[0] == index:
+ return str(x[1])
+ elif x == index:
+ return str(x)
+ return str(index) # Fallback!
+ else:
+ return str(self.choices.get(index, ""))
+
+ def __setitem__(self, index, value):
+ if self.type == choicesList.LIST_TYPE_LIST:
+ i = self.index(index)
+ orig = self.choices[i]
+ if isinstance(orig, tuple):
+ self.choices[i] = (orig[0], value)
+ else:
+ self.choices[i] = value
+ else:
+ self.choices[index] = value
+
+#
+# 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.choices = choicesList(choices)
+
+ if default is None:
+ default = self.choices.default()
+
+ self._descr = None
+ self.default = self._value = self.last_value = default
+
+ def setChoices(self, choices, default = None):
+ self.choices = choicesList(choices)
+
+ if default is None:
+ default = self.choices.default()
+ self.default = default
+
+ if self.value not in self.choices:
+ self.value = default
+
+ def setValue(self, value):
+ if value in self.choices:
+ self._value = value
+ else:
+ self._value = self.default
+ self._descr = None
+ self.changed()
+
+ def tostring(self, val):
+ return val
+
+ def getValue(self):
+ return self._value
+
+ def setCurrentText(self, text):
+ i = self.choices.index(self.value)
+ self.choices[i] = text
+ self._descr = 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):
+ 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]
+
+ def selectNext(self):
+ nchoices = len(self.choices)
+ i = self.choices.index(self.value)
+ self.value = self.choices[(i + 1) % nchoices]
+
+ def getText(self):
+ if self._descr is not None:
+ return self._descr
+ descr = self._descr = self.description[self.value]
+ if descr:
+ return _(descr)
+ return descr
+
+ def getMulti(self, selected):
+ if self._descr is not None:
+ descr = self._descr
+ else:
+ descr = self._descr = self.description[self.value]
+ if descr:
+ return ("text", _(descr))
+ return ("text", descr)
+
+ # HTML
+ def getHTML(self, id):
+ res = ""
+ for v in self.choices:
+ descr = self.description[v]
+ if self.value == v:
+ checked = 'checked="checked" '
+ else:
+ checked = ''
+ res += '<input type="radio" name="' + id + '" ' + checked + 'value="' + v + '">' + descr + "</input></br>\n"
+ return res;
+
+ def unsafeAssign(self, value):
+ # setValue does check if value is in choices. This is safe enough.
+ self.value = value
+
+ description = property(lambda self: descriptionList(self.choices.choices, self.choices.type))
+
+# a binary decision.
+#
+# several customized versions exist for different
+# descriptions.
+#
+boolean_descriptions = {False: "false", True: "true"}
+class ConfigBoolean(ConfigElement):
+ def __init__(self, default = False, descriptions = boolean_descriptions):
+ ConfigElement.__init__(self)
+ self.descriptions = descriptions
+ self.value = self.last_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 descr:
+ return _(descr)
+ return descr
+
+ def getMulti(self, selected):
+ descr = self.descriptions[self.value]
+ if 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 '<input type="checkbox" name="' + id + '" value="1" ' + checked + " />"
+
+ # this is FLAWED. and must be fixed.
+ def unsafeAssign(self, value):
+ if value == "1":
+ self.value = True
+ else:
+ self.value = False
+
+ def onDeselect(self, session):
+ if not self.last_value == self.value:
+ self.changedFinal()
+ self.last_value = self.value
+
+yes_no_descriptions = {False: _("no"), True: _("yes")}
+class ConfigYesNo(ConfigBoolean):
+ def __init__(self, default = False):
+ ConfigBoolean.__init__(self, default = default, descriptions = yes_no_descriptions)
+
+on_off_descriptions = {False: _("off"), True: _("on")}
+class ConfigOnOff(ConfigBoolean):
+ def __init__(self, default = False):
+ ConfigBoolean.__init__(self, default = default, descriptions = on_off_descriptions)
+
+enable_disable_descriptions = {False: _("disable"), True: _("enable")}
+class ConfigEnableDisable(ConfigBoolean):
+ def __init__(self, default = False):
+ ConfigBoolean.__init__(self, default = default, descriptions = enable_disable_descriptions)
+
+class ConfigDateTime(ConfigElement):
+ def __init__(self, default, formatstring, increment = 86400):
+ ConfigElement.__init__(self)
+ self.increment = increment
+ self.formatstring = formatstring
+ self.value = self.last_value = self.default = int(default)
+
+ def handleKey(self, key):
+ 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
+
+ def getText(self):
+ return strftime(self.formatstring, localtime(self.value))
+
+ def getMulti(self, selected):
+ return ("text", strftime(self.formatstring, 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, default, censor_char = ""):
+ 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.last_value = self.default = default
+ self.value = copy_copy(default)
+ self.endNotifier = None
+
+ def validate(self):
+ max_pos = 0
+ num = 0
+ 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]
+
+ num += 1
+
+ if self.marked_pos >= max_pos:
+ if self.endNotifier:
+ 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
+
+ total_len = sum([len(str(x[1])) for x in self.limits])
+
+ if self.marked_pos >= total_len:
+ self.marked_pos = total_len - 1
+
+ def addEndNotifier(self, notifier):
+ if self.endNotifier is None:
+ self.endNotifier = []
+ self.endNotifier.append(notifier)
+
+ def handleKey(self, key):
+ if key == KEY_LEFT:
+ self.marked_pos -= 1
+ self.validatePos()
+
+ elif key == KEY_RIGHT:
+ self.marked_pos += 1
+ self.validatePos()
+
+ elif key == KEY_HOME:
+ self.marked_pos = 0
+ self.validatePos()
+
+ elif 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()
+
+ elif 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 = [len(str(x[1])) for x in self.limits]
+ 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
+ number_len = len(str(self.limits[blocknumber][1]))
+
+ # position in the block
+ posinblock = self.marked_pos - block_len_total[blocknumber]
+
+ 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)
+
+ self._value[blocknumber] = newvalue
+ self.marked_pos += 1
+
+ self.validate()
+ self.changed()
+
+ def genText(self):
+ value = ""
+ mPos = self.marked_pos
+ num = 0;
+ for i in self._value:
+ if value: #fixme no heading separator possible
+ value += self.seperator
+ if mPos >= len(value) - 1:
+ mPos += 1
+ if self.censor_char == "":
+ value += ("%0" + str(len(str(self.limits[num][1]))) + "d") % i
+ else:
+ 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!)
+ 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)]
+
+ def onDeselect(self, session):
+ if self.last_value != self._value:
+ self.changedFinal()
+ self.last_value = copy_copy(self._value)
+
+ip_limits = [(0,255),(0,255),(0,255),(0,255)]
+class ConfigIP(ConfigSequence):
+ def __init__(self, default, auto_jump = False):
+ ConfigSequence.__init__(self, seperator = ".", limits = ip_limits, default = default)
+ self.block_len = [len(str(x[1])) for x in self.limits]
+ self.marked_block = 0
+ self.overwrite = True
+ self.auto_jump = auto_jump
+
+ def handleKey(self, key):
+ if key == KEY_LEFT:
+ if self.marked_block > 0:
+ self.marked_block -= 1
+ self.overwrite = True
+
+ elif key == KEY_RIGHT:
+ if self.marked_block < len(self.limits)-1:
+ self.marked_block += 1
+ self.overwrite = True
+
+ elif key == KEY_HOME:
+ self.marked_block = 0
+ self.overwrite = True
+
+ elif key == KEY_END:
+ self.marked_block = len(self.limits)-1
+ self.overwrite = True
+
+ elif 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)
+ oldvalue = self._value[self.marked_block]
+
+ if self.overwrite:
+ self._value[self.marked_block] = number
+ self.overwrite = False
+ else:
+ oldvalue *= 10
+ newvalue = oldvalue + number
+ if self.auto_jump and newvalue > self.limits[self.marked_block][1] and self.marked_block < len(self.limits)-1:
+ self.handleKey(KEY_RIGHT)
+ self.handleKey(key)
+ return
+ else:
+ self._value[self.marked_block] = newvalue
+
+ if len(str(self._value[self.marked_block])) >= self.block_len[self.marked_block]:
+ self.handleKey(KEY_RIGHT)
+
+ self.validate()
+ self.changed()
+
+ def genText(self):
+ value = ""
+ block_strlen = []
+ for i in self._value:
+ block_strlen.append(len(str(i)))
+ if value:
+ value += self.seperator
+ value += str(i)
+ leftPos = sum(block_strlen[:(self.marked_block)])+self.marked_block
+ rightPos = sum(block_strlen[:(self.marked_block+1)])+self.marked_block
+ mBlock = range(leftPos, rightPos)
+ return (value, mBlock)
+
+ def getMulti(self, selected):
+ (value, mBlock) = self.genText()
+ if self.enabled:
+ return ("mtext"[1-selected:], value, mBlock)
+ else:
+ return ("text", value)
+
+ def getHTML(self, id):
+ # we definitely don't want leading zeros
+ return '.'.join(["%d" % d for d in self.value])
+
+mac_limits = [(1,255),(1,255),(1,255),(1,255),(1,255),(1,255)]
+class ConfigMAC(ConfigSequence):
+ def __init__(self, default):
+ ConfigSequence.__init__(self, seperator = ":", limits = mac_limits, 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)
+
+clock_limits = [(0,23),(0,59)]
+class ConfigClock(ConfigSequence):
+ def __init__(self, default):
+ t = localtime(default)
+ ConfigSequence.__init__(self, seperator = ":", limits = clock_limits, default = [t.tm_hour, t.tm_min])
+
+ def increment(self):
+ # Check if Minutes maxed out
+ if self._value[1] == 59:
+ # Increment Hour, reset Minutes
+ if self._value[0] < 23:
+ self._value[0] += 1
+ else:
+ self._value[0] = 0
+ self._value[1] = 0
+ else:
+ # Increment Minutes
+ self._value[1] += 1
+ # Trigger change
+ self.changed()
+
+ def decrement(self):
+ # Check if Minutes is minimum
+ if self._value[1] == 0:
+ # Decrement Hour, set Minutes to 59
+ if self._value[0] > 0:
+ self._value[0] -= 1
+ else:
+ self._value[0] = 23
+ self._value[1] = 59
+ else:
+ # Decrement Minutes
+ self._value[1] -= 1
+ # Trigger change
+ self.changed()
+
+integer_limits = (0, 9999999999)
+class ConfigInteger(ConfigSequence):
+ def __init__(self, default, limits = integer_limits):
+ 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)