fix python part of ePythonConfig::getConfigQuery... so now default ac3 and 5V
[vuplus_dvbapp] / lib / python / Components / config.py
1 import time
2 from Tools.NumericalTextInput import NumericalTextInput
3 from Tools.Directories import resolveFilename, SCOPE_CONFIG
4 import copy
5
6
7 # ConfigElement, the base class of all ConfigElements.
8
9 # it stores:
10 #   value    the current value, usefully encoded.
11 #            usually a property which retrieves _value,
12 #            and maybe does some reformatting
13 #   _value   the value as it's going to be saved in the configfile,
14 #            though still in non-string form.
15 #            this is the object which is actually worked on.
16 #   default  the initial value. If _value is equal to default,
17 #            it will not be stored in the config file
18 #   saved_value is a text representation of _value, stored in the config file
19 #
20 # and has (at least) the following methods:
21 #   save()   stores _value into saved_value, 
22 #            (or stores 'None' if it should not be stored)
23 #   load()   loads _value from saved_value, or loads
24 #            the default if saved_value is 'None' (default)
25 #            or invalid.
26 #
27 class ConfigElement(object):
28         def __init__(self):
29
30                 object.__init__(self)
31                 self.saved_value = None
32                 self.save_disabled = False
33                 self.notifiers = []
34                 self.enabled = True
35
36         # you need to override this to do input validation
37         def setValue(self, value):
38                 self._value = value
39                 self.changed()
40
41         def getValue(self):
42                 return self._value
43         
44         value = property(getValue, setValue)
45
46         # you need to override this if self.value is not a string
47         def fromstring(self, value):
48                 return value
49
50         # you can overide this for fancy default handling
51         def load(self):
52                 if self.saved_value is None:
53                         self.value = self.default
54                 else:
55                         self.value = self.fromstring(self.saved_value)
56
57         def tostring(self, value):
58                 return str(value)
59
60         # you need to override this if str(self.value) doesn't work
61         def save(self):
62                 if self.save_disabled or self.value == self.default:
63                         self.saved_value = None
64                 else:
65                         self.saved_value = self.tostring(self.value)
66
67         def cancel(self):
68                 self.load()
69
70         def changed(self):
71                 for x in self.notifiers:
72                         x(self)
73                         
74         def addNotifier(self, notifier, initial_call = True):
75                 assert callable(notifier), "notifiers must be callable"
76                 self.notifiers.append(notifier)
77
78                 # CHECKME:
79                 # do we want to call the notifier
80                 #  - at all when adding it? (yes, though optional)
81                 #  - when the default is active? (yes)
82                 #  - when no value *yet* has been set,
83                 #    because no config has ever been read (currently yes)
84                 #    (though that's not so easy to detect.
85                 #     the entry could just be new.)
86                 if initial_call:
87                         notifier(self)
88
89         def disableSave(self):
90                 self.save_disabled = True
91
92         def __call__(self, selected):
93                 return self.getMulti(selected)
94
95         def helpWindow(self):
96                 return None
97
98 KEY_LEFT = 0
99 KEY_RIGHT = 1
100 KEY_OK = 2
101 KEY_DELETE = 3
102 KEY_TIMEOUT = 4
103 KEY_NUMBERS = range(12, 12+10)
104 KEY_0 = 12
105 KEY_9 = 12+9
106
107 def getKeyNumber(key):
108         assert key in KEY_NUMBERS
109         return key - KEY_0
110
111 #
112 # ConfigSelection is a "one of.."-type.
113 # it has the "choices", usually a list, which contains
114 # (id, desc)-tuples (or just only the ids, in case the id
115 # will be used as description)
116 #
117 # all ids MUST be plain strings.
118 #
119 class ConfigSelection(ConfigElement):
120         def __init__(self, choices, default = None):
121                 ConfigElement.__init__(self)
122                 self.choices = []
123                 self.description = {}
124                 
125                 if isinstance(choices, list):
126                         for x in choices:
127                                 if isinstance(x, tuple):
128                                         self.choices.append(x[0])
129                                         self.description[x[0]] = x[1]
130                                 else:
131                                         self.choices.append(x)
132                                         self.description[x] = x
133                 elif isinstance(choices, dict):
134                         for (key, val) in choices.items():
135                                 self.choices.append(key)
136                                 self.description[key] = val
137                 else:
138                         assert False, "ConfigSelection choices must be dict or list!"
139                 
140                 #assert len(self.choices), "you can't have an empty configselection"
141                 if len(self.choices) == 0:
142                         self.choices = [""]
143                         self.description[""] = ""
144
145                 if default is None:
146                         default = self.choices[0]
147
148                 assert default in self.choices, "default must be in choice list, but " + repr(default) + " is not!"
149                 for x in self.choices:
150                         assert isinstance(x, str), "ConfigSelection choices must be strings"
151                 
152                 self.value = self.default = default
153
154         def setValue(self, value):
155                 if value in self.choices:
156                         self._value = value
157                 else:
158                         self._value = self.default
159                 
160                 self.changed()
161
162         def tostring(self, val):
163                 return val
164
165         def getValue(self):
166                 return self._value
167
168         def setCurrentText(self, text):
169                 i = self.choices.index(self.value)
170                 del self.description[self.choices[i]]
171                 self.choices[i] = text
172                 self.description[text] = text
173                 self._value = text
174
175         value = property(getValue, setValue)
176         
177         def getIndex(self):
178                 return self.choices.index(self.value)
179         
180         index = property(getIndex)
181
182         # GUI
183         def handleKey(self, key):
184                 nchoices = len(self.choices)
185                 i = self.choices.index(self.value)
186                 if key == KEY_LEFT:
187                         self.value = self.choices[(i + nchoices - 1) % nchoices]
188                 elif key == KEY_RIGHT:
189                         self.value = self.choices[(i + 1) % nchoices]
190
191         def getMulti(self, selected):
192                 return ("text", self.description[self.value])
193
194         # HTML
195         def getHTML(self, id):
196                 res = ""
197                 for v in self.choices:
198                         if self.value == v:
199                                 checked = 'checked="checked" '
200                         else:
201                                 checked = ''
202                         res += '<input type="radio" name="' + id + '" ' + checked + 'value="' + v + '">' + self.description[v] + "</input></br>\n"
203                 return res;
204
205         def unsafeAssign(self, value):
206                 # setValue does check if value is in choices. This is safe enough.
207                 self.value = value
208
209 # a binary decision.
210 #
211 # several customized versions exist for different
212 # descriptions.
213 #
214 class ConfigBoolean(ConfigElement):
215         def __init__(self, default = False, descriptions = {False: "false", True: "true"}):
216                 ConfigElement.__init__(self)
217                 self.descriptions = descriptions
218                 self.value = self.default = default
219         def handleKey(self, key):
220                 if key in [KEY_LEFT, KEY_RIGHT]:
221                         self.value = not self.value
222
223         def getMulti(self, selected):
224                 return ("text", _(self.descriptions[self.value]))
225
226         def tostring(self, value):
227                 if not value:
228                         return "false"
229                 else:
230                         return "true"
231
232         def fromstring(self, val):
233                 if val == "true":
234                         return True
235                 else:
236                         return False
237
238         def getHTML(self, id):
239                 if self.value:
240                         checked = ' checked="checked"'
241                 else:
242                         checked = ''
243                 return '<input type="checkbox" name="' + id + '" value="1" ' + checked + " />"
244
245         # this is FLAWED. and must be fixed.
246         def unsafeAssign(self, value):
247                 if value == "1":
248                         self.value = True
249                 else:
250                         self.value = False
251
252 class ConfigYesNo(ConfigBoolean):
253         def __init__(self, default = False):
254                 ConfigBoolean.__init__(self, default = default, descriptions = {False: _("no"), True: _("yes")})
255
256 class ConfigOnOff(ConfigBoolean):
257         def __init__(self, default = False):
258                 ConfigBoolean.__init__(self, default = default, descriptions = {False: _("off"), True: _("on")})
259
260 class ConfigEnableDisable(ConfigBoolean):
261         def __init__(self, default = False):
262                 ConfigBoolean.__init__(self, default = default, descriptions = {False: _("disable"), True: _("enable")})
263
264 class ConfigDateTime(ConfigElement):
265         def __init__(self, default, formatstring, increment = 86400):
266                 ConfigElement.__init__(self)
267                 self.increment = increment
268                 self.formatstring = formatstring
269                 self.value = self.default = int(default)
270
271         def handleKey(self, key):
272                 if key == KEY_LEFT:
273                         self.value = self.value - self.increment
274                 if key == KEY_RIGHT:
275                         self.value = self.value + self.increment
276
277         def getMulti(self, selected):
278                 return ("text", time.strftime(self.formatstring, time.localtime(self.value)))
279
280         def fromstring(self, val):
281                 return int(val)
282
283 # *THE* mighty config element class
284 #
285 # allows you to store/edit a sequence of values.
286 # can be used for IP-addresses, dates, plain integers, ...
287 # several helper exist to ease this up a bit.
288 #
289 class ConfigSequence(ConfigElement):
290         def __init__(self, seperator, limits, censor_char = "", default = None):
291                 ConfigElement.__init__(self)
292                 assert isinstance(limits, list) and len(limits[0]) == 2, "limits must be [(min, max),...]-tuple-list"
293                 assert censor_char == "" or len(censor_char) == 1, "censor char must be a single char (or \"\")"
294                 #assert isinstance(default, list), "default must be a list"
295                 #assert isinstance(default[0], int), "list must contain numbers"
296                 #assert len(default) == len(limits), "length must match"
297
298                 self.marked_pos = 0
299                 self.seperator = seperator
300                 self.limits = limits
301                 self.censor_char = censor_char
302                 
303                 self.default = default
304                 self.value = copy.copy(default)
305
306         def validate(self):
307                 max_pos = 0
308                 num = 0
309                 for i in self._value:
310                         max_pos += len(str(self.limits[num][1]))
311
312                         while self._value[num] < self.limits[num][0]:
313                                 self.value[num] += 1
314
315                         while self._value[num] > self.limits[num][1]:
316                                 self._value[num] -= 1
317
318                         num += 1
319
320                 if self.marked_pos >= max_pos:
321                         self.marked_pos = max_pos - 1
322
323                 if self.marked_pos < 0:
324                         self.marked_pos = 0
325
326         def validatePos(self):
327                 if self.marked_pos < 0:
328                         self.marked_pos = 0
329                         
330                 total_len = sum([len(str(x[1])) for x in self.limits])
331
332                 if self.marked_pos >= total_len:
333                         self.marked_pos = total_len - 1
334
335         def handleKey(self, key):
336                 if key == KEY_LEFT:
337                         self.marked_pos -= 1
338                         self.validatePos()
339
340                 if key == KEY_RIGHT:
341                         self.marked_pos += 1
342                         self.validatePos()
343                 
344                 if key in KEY_NUMBERS:
345                         block_len = []
346                         for x in self.limits:
347                                 block_len.append(len(str(x[1])))
348                         
349                         total_len = sum(block_len)
350
351                         pos = 0
352                         blocknumber = 0
353                         block_len_total = [0, ]
354                         for x in block_len:
355                                 pos += block_len[blocknumber]
356                                 block_len_total.append(pos)
357                                 if pos - 1 >= self.marked_pos:
358                                         pass
359                                 else:
360                                         blocknumber += 1
361
362                         number = getKeyNumber(key)
363                         
364                         # length of numberblock
365                         number_len = len(str(self.limits[blocknumber][1]))
366
367                         # position in the block
368                         posinblock = self.marked_pos - block_len_total[blocknumber]
369                         
370                         oldvalue = self._value[blocknumber]
371                         olddec = oldvalue % 10 ** (number_len - posinblock) - (oldvalue % 10 ** (number_len - posinblock - 1))
372                         newvalue = oldvalue - olddec + (10 ** (number_len - posinblock - 1) * number)
373                         
374                         self._value[blocknumber] = newvalue
375                         self.marked_pos += 1
376                 
377                         self.validate()
378                         self.changed()
379                         
380         def getMulti(self, selected):
381                 value = ""
382                 mPos = self.marked_pos
383                 num = 0;
384                 for i in self._value:
385                         if len(value):  #fixme no heading separator possible
386                                 value += self.seperator
387                                 if mPos >= len(value) - 1:
388                                         mPos += 1
389
390                         if self.censor_char == "":
391                                 value += ("%0" + str(len(str(self.limits[num][1]))) + "d") % i
392                         else:
393                                 value += (self.censor_char * len(str(self.limits[num][1])))
394                         num += 1
395
396                         # only mark cursor when we are selected
397                         # (this code is heavily ink optimized!)
398                 if self.enabled:
399                         return ("mtext"[1-selected:], value, [mPos])
400                 else:
401                         return ("text", value)
402
403         def tostring(self, val):
404                 return self.seperator.join([self.saveSingle(x) for x in val])
405         
406         def saveSingle(self, v):
407                 return str(v)
408
409         def fromstring(self, value):
410                 return [int(x) for x in self.saved_value.split(self.seperator)]
411
412 class ConfigIP(ConfigSequence):
413         def __init__(self, default):
414                 ConfigSequence.__init__(self, seperator = ".", limits = [(0,255),(0,255),(0,255),(0,255)], default = default)
415
416 class ConfigMAC(ConfigSequence):
417         def __init__(self, default):
418                 ConfigSequence.__init__(self, seperator = ":", limits = [(1,255),(1,255),(1,255),(1,255),(1,255),(1,255)], default = default)
419
420 class ConfigPosition(ConfigSequence):
421         def __init__(self, default, args):
422                 ConfigSequence.__init__(self, seperator = ",", limits = [(0,args[0]),(0,args[1]),(0,args[2]),(0,args[3])], default = default)
423
424 class ConfigClock(ConfigSequence):
425         def __init__(self, default):
426                 import time
427                 t = time.localtime(default)
428                 ConfigSequence.__init__(self, seperator = ":", limits = [(0,23),(0,59)], default = [t.tm_hour, t.tm_min])
429
430 class ConfigInteger(ConfigSequence):
431         def __init__(self, default, limits = (0, 10000000000)):
432                 ConfigSequence.__init__(self, seperator = ":", limits = [limits], default = default)
433         
434         # you need to override this to do input validation
435         def setValue(self, value):
436                 self._value = [value]
437                 self.changed()
438
439         def getValue(self):
440                 return self._value[0]
441         
442         value = property(getValue, setValue)
443
444         def fromstring(self, value):
445                 return int(value)
446
447         def tostring(self, value):
448                 return str(value)
449
450 class ConfigPIN(ConfigInteger):
451         def __init__(self, default, len = 4, censor = ""):
452                 assert isinstance(default, int), "ConfigPIN default must be an integer"
453                 if default == -1:
454                         default = "aaaa"
455                 ConfigSequence.__init__(self, seperator = ":", limits = [(0, (10**len)-1)], censor_char = censor, default = default)
456                 self.len = len
457
458         def getLength(self):
459                 return self.len
460
461 class ConfigFloat(ConfigSequence):
462         def __init__(self, default, limits):
463                 ConfigSequence.__init__(self, seperator = ".", limits = limits, default = default)
464
465         def getFloat(self):
466                 return float(self.value[1] / float(self.limits[1][1] + 1) + self.value[0])
467
468         float = property(getFloat)
469
470 # an editable text...
471 class ConfigText(ConfigElement, NumericalTextInput):
472         def __init__(self, default = "", fixed_size = True):
473                 ConfigElement.__init__(self)
474                 NumericalTextInput.__init__(self, nextFunc = self.nextFunc, handleTimeout = False)
475                 
476                 self.marked_pos = 0
477                 self.fixed_size = fixed_size
478
479                 self.value = self.default = default
480
481         def validateMarker(self):
482                 if self.marked_pos < 0:
483                         self.marked_pos = 0
484                 if self.marked_pos >= len(self.text):
485                         self.marked_pos = len(self.text) - 1
486
487         #def nextEntry(self):
488         #       self.vals[1](self.getConfigPath())
489
490         def handleKey(self, key):
491                 # this will no change anything on the value itself
492                 # so we can handle it here in gui element
493                 if key == KEY_DELETE:
494                         self.text = self.text[0:self.marked_pos] + self.text[self.marked_pos + 1:]
495                 elif key == KEY_LEFT:
496                         self.marked_pos -= 1
497                 elif key == KEY_RIGHT:
498                         self.marked_pos += 1
499                         if not self.fixed_size:
500                                 if self.marked_pos >= len(self.text):
501                                         self.text = self.text.ljust(len(self.text) + 1)
502                 elif key in KEY_NUMBERS:
503                         number = self.getKey(getKeyNumber(key))
504                         self.text = self.text[0:self.marked_pos] + unicode(number) + self.text[self.marked_pos + 1:]
505                 elif key == KEY_TIMEOUT:
506                         self.timeout()
507                         return
508
509                 self.validateMarker()
510                 self.changed()
511
512         def nextFunc(self):
513                 self.marked_pos += 1
514                 self.validateMarker()
515                 self.changed()
516
517         def getValue(self):
518                 return self.text.encode("utf-8")
519                 
520         def setValue(self, val):
521                 try:
522                         self.text = val.decode("utf-8")
523                 except UnicodeDecodeError:
524                         self.text = val
525                         print "Broken UTF8!"
526
527         value = property(getValue, setValue)
528         _value = property(getValue, setValue)
529
530         def getMulti(self, selected):
531                 return ("mtext"[1-selected:], self.value, [self.marked_pos])
532
533         def helpWindow(self):
534                 from Screens.NumericalTextInputHelpDialog import NumericalTextInputHelpDialog
535                 return (NumericalTextInputHelpDialog,self)
536
537         def getHTML(self, id):
538                 return '<input type="text" name="' + id + '" value="' + self.value + '" /><br>\n'
539
540         def unsafeAssign(self, value):
541                 self.value = str(value)
542
543 # a slider.
544 class ConfigSlider(ConfigElement):
545         def __init__(self, default = 0, increment = 1, limits = (0, 100)):
546                 ConfigElement.__init__(self)
547                 self.value = self.default = default
548                 self.min = limits[0]
549                 self.max = limits[1]
550                 self.increment = increment
551
552         def checkValues(self):
553                 if self.value < self.min:
554                         self.value = self.min
555
556                 if self.value > self.max:
557                         self.value = self.max
558
559         def handleKey(self, key):
560                 if key == KEY_LEFT:
561                         self.value -= self.increment
562                 elif key == KEY_RIGHT:
563                         self.value += self.increment
564                 else:
565                         return
566
567                 self.checkValues()
568                 self.changed()
569
570         def getMulti(self, selected):
571                 self.checkValues()
572                 return ("slider", self.value, self.max)
573
574         def fromstring(self, value):
575                 return int(value)
576
577 # a satlist. in fact, it's a ConfigSelection.
578 class ConfigSatlist(ConfigSelection):
579         def __init__(self, list, default = None):
580                 if default is not None:
581                         default = str(default)
582                 ConfigSelection.__init__(self, choices = [(str(orbpos), desc) for (orbpos, desc) in list], default = default)
583
584         def getOrbitalPosition(self):
585                 if self.value == "":
586                         return None
587                 return int(self.value)
588         
589         orbital_position = property(getOrbitalPosition)
590
591 # nothing.
592 class ConfigNothing(ConfigSelection):
593         def __init__(self):
594                 ConfigSelection.__init__(self, choices = [""])
595
596 # until here, 'saved_value' always had to be a *string*.
597 # now, in ConfigSubsection, and only there, saved_value
598 # is a dict, essentially forming a tree.
599 #
600 # config.foo.bar=True
601 # config.foobar=False
602 #
603 # turns into:
604 # config.saved_value == {"foo": {"bar": "True"}, "foobar": "False"}
605 #
606
607
608 class ConfigSubsectionContent(object):
609         pass
610
611 # we store a backup of the loaded configuration
612 # data in self.stored_values, to be able to deploy
613 # them when a new config element will be added,
614 # so non-default values are instantly available
615
616 # A list, for example:
617 # config.dipswitches = ConfigSubList()
618 # config.dipswitches.append(ConfigYesNo())
619 # config.dipswitches.append(ConfigYesNo())
620 # config.dipswitches.append(ConfigYesNo())
621 class ConfigSubList(list, object):
622         def __init__(self):
623                 object.__init__(self)
624                 list.__init__(self)
625                 self.stored_values = {}
626
627         def save(self):
628                 for x in self:
629                         x.save()
630         
631         def load(self):
632                 for x in self:
633                         x.load()
634
635         def getSavedValue(self):
636                 res = {}
637                 for i in range(len(self)):
638                         sv = self[i].saved_value
639                         if sv is not None:
640                                 res[str(i)] = sv
641                 return res
642
643         def setSavedValue(self, values):
644                 self.stored_values = dict(values)
645                 for (key, val) in self.stored_values.items():
646                         if int(key) < len(self):
647                                 self[int(key)].saved_value = val
648
649         saved_value = property(getSavedValue, setSavedValue)
650         
651         def append(self, item):
652                 i = str(len(self))
653                 list.append(self, item)
654                 if i in self.stored_values:
655                         item.saved_value = self.stored_values[i]
656                         item.load()
657
658 # same as ConfigSubList, just as a dictionary.
659 # care must be taken that the 'key' has a proper
660 # str() method, because it will be used in the config
661 # file.
662 class ConfigSubDict(dict, object):
663         def __init__(self):
664                 object.__init__(self)
665                 dict.__init__(self)
666                 self.stored_values = {}
667
668         def save(self):
669                 for x in self.values():
670                         x.save()
671         
672         def load(self):
673                 for x in self.values():
674                         x.load()
675
676         def getSavedValue(self):
677                 res = {}
678                 for (key, val) in self.items():
679                         if val.saved_value is not None:
680                                 res[str(key)] = val.saved_value
681                 return res
682
683         def setSavedValue(self, values):
684                 self.stored_values = dict(values)
685                 for (key, val) in self.items():
686                         if str(key) in self.stored_values:
687                                 val = self.stored_values[str(key)]
688
689         saved_value = property(getSavedValue, setSavedValue)
690
691         def __setitem__(self, key, item):
692                 dict.__setitem__(self, key, item)
693                 if str(key) in self.stored_values:
694                         item.saved_value = self.stored_values[str(key)]
695                         item.load()
696
697 # Like the classes above, just with a more "native"
698 # syntax.
699 #
700 # some evil stuff must be done to allow instant
701 # loading of added elements. this is why this class
702 # is so complex.
703 #
704 # we need the 'content' because we overwrite 
705 # __setattr__.
706 # If you don't understand this, try adding
707 # __setattr__ to a usual exisiting class and you will.
708 class ConfigSubsection(object):
709         def __init__(self):
710                 object.__init__(self)
711                 self.__dict__["content"] = ConfigSubsectionContent()
712                 self.content.items = { }
713                 self.content.stored_values = { }
714         
715         def __setattr__(self, name, value):
716                 if name == "saved_value":
717                         return self.setSavedValue(value)
718                 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"
719                 self.content.items[name] = value
720                 if name in self.content.stored_values:
721                         #print "ok, now we have a new item,", name, "and have the following value for it:", self.content.stored_values[name]
722                         value.saved_value = self.content.stored_values[name]
723                         value.load()
724
725         def __getattr__(self, name):
726                 return self.content.items[name]
727
728         def getSavedValue(self):
729                 res = self.content.stored_values
730                 for (key, val) in self.content.items.items():
731                         if val.saved_value is not None:
732                                 res[key] = val.saved_value
733                         elif key in res:
734                                 del res[key]
735                                 
736                 return res
737
738         def setSavedValue(self, values):
739                 values = dict(values)
740                 
741                 self.content.stored_values = values
742                 
743                 for (key, val) in self.content.items.items():
744                         if key in values:
745                                 val.setSavedValue(values[key])
746
747         saved_value = property(getSavedValue, setSavedValue)
748
749         def save(self):
750                 for x in self.content.items.values():
751                         x.save()
752
753         def load(self):
754                 for x in self.content.items.values():
755                         x.load()
756
757 # the root config object, which also can "pickle" (=serialize)
758 # down the whole config tree.
759 #
760 # we try to keep non-existing config entries, to apply them whenever
761 # a new config entry is added to a subsection
762 # also, non-existing config entries will be saved, so they won't be
763 # lost when a config entry disappears.
764 class Config(ConfigSubsection):
765         def __init__(self):
766                 ConfigSubsection.__init__(self)
767
768         def pickle_this(self, prefix, topickle, result):
769                 for (key, val) in topickle.items():
770                         name = prefix + "." + key
771                         
772                         if isinstance(val, dict):
773                                 self.pickle_this(name, val, result)
774                         elif isinstance(val, tuple):
775                                 result.append(name + "=" + val[0]) # + " ; " + val[1])
776                         else:
777                                 result.append(name + "=" + val)
778
779         def pickle(self):
780                 result = [ ]
781                 self.pickle_this("config", self.saved_value, result)
782                 return '\n'.join(result) + "\n"
783
784         def unpickle(self, lines):
785                 tree = { }
786                 for l in lines:
787                         if not len(l) or l[0] == '#':
788                                 continue
789                         
790                         n = l.find('=')
791                         val = l[n+1:].strip()
792
793                         names = l[:n].split('.')
794 #                       if val.find(' ') != -1:
795 #                               val = val[:val.find(' ')]
796
797                         base = tree
798                         
799                         for n in names[:-1]:
800                                 base = base.setdefault(n, {})
801                         
802                         base[names[-1]] = val
803
804                 # we inherit from ConfigSubsection, so ...
805                 #object.__setattr__(self, "saved_value", tree["config"])
806                 if "config" in tree:
807                         self.setSavedValue(tree["config"])
808
809         def saveToFile(self, filename):
810                 f = open(filename, "w")
811                 f.write(self.pickle())
812                 f.close()
813
814         def loadFromFile(self, filename):
815                 f = open(filename, "r")
816                 self.unpickle(f.readlines())
817                 f.close()
818
819 config = Config()
820 config.misc = ConfigSubsection()
821
822 class ConfigFile:
823         CONFIG_FILE = resolveFilename(SCOPE_CONFIG, "settings")
824
825         def load(self):
826                 try:
827                         config.loadFromFile(self.CONFIG_FILE)
828                 except IOError, e:
829                         print "unable to load config (%s), assuming defaults..." % str(e)
830         
831         def save(self):
832                 config.save()
833                 config.saveToFile(self.CONFIG_FILE)
834         
835         def __resolveValue(self, pickles, cmap):
836                 if cmap.has_key(pickles[0]):
837                         if len(pickles) > 1:
838                                 return self.__resolveValue(pickles[1:], cmap[pickles[0]].content.items)
839                         else:
840                                 return str(cmap[pickles[0]].value)
841                 return None
842         
843         def getResolvedKey(self, key):
844                 names = key.split('.')
845                 if len(names) > 1:
846                         if names[0] == "config":
847                                 ret=self.__resolveValue(names[1:], config.content.items)
848                                 if ret and len(ret):
849                                         return ret
850                 print "getResolvedKey", key, "failed !! (Typo??)"
851                 return ""
852
853 def NoSave(element):
854         element.disableSave()
855         return element
856
857 configfile = ConfigFile()
858
859 configfile.load()
860
861 def getConfigListEntry(desc, config):
862         return (desc, config)
863
864 #def _(x):
865 #       return x
866 #
867 #config.bla = ConfigSubsection()
868 #config.bla.test = ConfigYesNo()
869 #config.nim = ConfigSubList()
870 #config.nim.append(ConfigSubsection())
871 #config.nim[0].bla = ConfigYesNo()
872 #config.nim.append(ConfigSubsection())
873 #config.nim[1].bla = ConfigYesNo()
874 #config.nim[1].blub = ConfigYesNo()
875 #config.arg = ConfigSubDict()
876 #config.arg["Hello"] = ConfigYesNo()
877 #
878 #config.arg["Hello"].handleKey(KEY_RIGHT)
879 #config.arg["Hello"].handleKey(KEY_RIGHT)
880 #
881 ##config.saved_value
882 #
883 ##configfile.save()
884 #config.save()
885 #print config.pickle()