diff options
author | Eitan Isaacson <eitan@monotonous.org> | 2010-12-09 00:03:32 -0800 |
---|---|---|
committer | Eitan Isaacson <eitan@monotonous.org> | 2010-12-09 00:03:32 -0800 |
commit | b656c2ff9ea269b382cba1224a93cb728ce591d7 (patch) | |
tree | 61330b4da2f97ea7587caad79262114ec2c03202 /caribou | |
parent | c35d19ab7d43328c33e9b1916f8508df944c2fc3 (diff) | |
download | caribou-b656c2ff9ea269b382cba1224a93cb728ce591d7.tar.gz |
Created new settings backend with MVC seperation.
Diffstat (limited to 'caribou')
-rw-r--r-- | caribou/common/Makefile.am | 5 | ||||
-rw-r--r-- | caribou/common/setting_types.py | 143 | ||||
-rw-r--r-- | caribou/common/settings.py | 181 | ||||
-rw-r--r-- | caribou/common/settings_manager.py | 77 |
4 files changed, 405 insertions, 1 deletions
diff --git a/caribou/common/Makefile.am b/caribou/common/Makefile.am index fade772..74805af 100644 --- a/caribou/common/Makefile.am +++ b/caribou/common/Makefile.am @@ -2,7 +2,10 @@ caribou_commondir = $(pkgpythondir)/common/ caribou_common_PYTHON = \ __init__.py \ - const.py + const.py \ + settings_manager.py \ + settings.py \ + setting_types.py clean-local: rm -rf *.pyc *.pyo diff --git a/caribou/common/setting_types.py b/caribou/common/setting_types.py new file mode 100644 index 0000000..17faa61 --- /dev/null +++ b/caribou/common/setting_types.py @@ -0,0 +1,143 @@ +import gobject + +GCONF_DIR="/apps/caribou/osk/" + +ENTRY_DEFAULT=0 +ENTRY_COMBO=1 +ENTRY_COLOR=2 +ENTRY_FONT=3 +ENTRY_SPIN=4 +ENTRY_SLIDER=5 +ENTRY_CHECKBOX=6 +ENTRY_RADIO=7 + +class Setting(gobject.GObject): + __gsignals__ = {'value-changed' : + (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)), + 'sensitivity-changed' : + (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_BOOLEAN,))} + def __init__(self, name, label, children=[]): + gobject.GObject.__init__(self) + self.name = name + self.label = label + self.children = children + + @property + def sensitive(self): + return getattr(self, '_sensitive', True) + + @sensitive.setter + def sensitive(self, sensitive): + changed = getattr(self, '_sensitive', sensitive) != sensitive + self._sensitive = sensitive + self.emit('sensitivity-changed', sensitive) + + + def __len__(self): + return len(self.children) + + def __getitem__(self, i): + return self.children[i] + + def __setitem__(self, i, v): + self.children[i] = v + + def __delitem__(self, i): + del self.children[i] + + def __iter__(self): + return self.children.__iter__() + +class SettingsGroup(Setting): + pass + +class ValueSetting(Setting): + gconf_type = '' + entry_type=ENTRY_DEFAULT + def __init__(self, name, label, default, short_desc="", long_desc="", + allowed=[], entry_type=ENTRY_DEFAULT, sensitive=None, + user_visible=True, children=[], + insensitive_when_false=[], insensitive_when_true=[]): + Setting.__init__(self, name, label, children) + self.short_desc = short_desc + self.long_desc = long_desc + self.allowed = allowed + self.entry_type = entry_type or self.__class__.entry_type + if sensitive is not None: + self.sensitive = sensitive + self.user_visible = user_visible + self.default = default + self.insensitive_when_false = insensitive_when_false + self.insensitive_when_true = insensitive_when_true + + @property + def value(self): + return getattr(self, '_value', self.default) + + @value.setter + def value(self, val): + _val = self.convert_value(val) + if self.allowed and _val not in [a for a, b in self.allowed]: + raise ValueError, "'%s' not a valid value" % _val + self._value = _val + self.emit('value-changed', _val) + + @property + def gconf_key(self): + return GCONF_DIR + self.name + + @property + def is_true(self): + return bool(self.value) + + @property + def gconf_default(self): + return self.default + +class BooleanSetting(ValueSetting): + gconf_type = 'boolean' + entry_type = ENTRY_CHECKBOX + def convert_value(self, val): + # Almost anything could be a boolean. + return bool(val) + + @property + def gconf_default(self): + str(self.default).lower() + +class IntegerSetting(ValueSetting): + gconf_type = 'int' + entry_type = ENTRY_SPIN + def __init__(self, *args, **kwargs): + self.min = kwargs.pop('min', gobject.G_MININT) + self.max = kwargs.pop('max', gobject.G_MAXINT) + ValueSetting.__init__(self, *args, **kwargs) + + def convert_value(self, val): + return int(val) + +class FloatSetting(ValueSetting): + gconf_type = 'float' + entry_type = ENTRY_SPIN + def __init__(self, *args, **kwargs): + self.min = kwargs.pop('min', gobject.G_MINFLOAT) + self.max = kwargs.pop('max', gobject.G_MAXFLOAT) + ValueSetting.__init__(self, *args, **kwargs) + + def convert_value(self, val): + return float(val) + +class StringSetting(ValueSetting): + gconf_type = 'string' + def convert_value(self, val): + return str(val) + +class ColorSetting(StringSetting): + entry_type = ENTRY_COLOR + +class FontSetting(StringSetting): + entry_type = ENTRY_FONT diff --git a/caribou/common/settings.py b/caribou/common/settings.py new file mode 100644 index 0000000..a3ffffc --- /dev/null +++ b/caribou/common/settings.py @@ -0,0 +1,181 @@ +import os +from setting_types import * +from gettext import gettext as _ +import caribou.common.const as const +import xml.dom.minidom + +try: + import json +except ImportError: + HAS_JSON = False +else: + HAS_JSON = True + +def fetch_keyboards(): + if True: + const.KEYBOARDS_DIR = os.path.abspath( + os.path.join(os.path.dirname(__file__), '../../data/keyboards')) + + try: + files = os.listdir(const.KEYBOARDS_DIR) + except: + files = [] + kbds = [] + for f in files: + if (HAS_JSON and f.endswith('.json')) or f.endswith('.xml'): + module = f.rsplit('.', 1)[0] + # TODO: verify keyboard before adding it to the list + kbds.append(module) + return kbds + +settings = SettingsGroup("_top", "", [ + SettingsGroup("keyboard", _("Keyboard"), [ + SettingsGroup("general", _("General"), [ + StringSetting( + "layout", _("Keyboard layout"), "qwerty", + _("The layout Caribou should use."), + _("The layout should be in the data directory of " + "Caribou (usually /usr/share/caribou/keyboards) " + "and should be a .xml or .json file."), + allowed=[(a,a) for a in fetch_keyboards()])]), + SettingsGroup("color", _("Color"), [ + BooleanSetting( + "default_colors", _("Use system theme"), True, + _("Use the default theme colors"), + insensitive_when_true=["normal_color", + "mouse_over_color"]), + ColorSetting( + "normal_color", _("Normal state"), "grey80", + _("Color of the keys when there is no " + "event on them")), + ColorSetting( + "mouse_over_color", _("Mouse over"), "yellow", + _("Color of the keys when the mouse goes " + "over the key"))]), + SettingsGroup("fontandsize", _("Font and size"), [ + BooleanSetting( + "default_font", _("Use system fonts"), True, + _("Use the default system font for keyboard"), + insensitive_when_true=["key_font"]), + FontSetting("key_font", _("Key font"), "Sans 12", + _("Custom font for keyboard")) + ]) + ]), + SettingsGroup("scanning", "Scanning", [ + BooleanSetting( + "scan_enabled", _("Enable scanning"), False, + _("Enable switch scanning"), + insensitive_when_false=["scanning_general", + "scanning_input", + "scanning_color"]), + SettingsGroup("scanning_general", "General", [ + StringSetting("scanning_type", _("Scanning mode"), + "block", + _("Scanning type, block or row"), + allowed=[("block", _("Block")), + ("row", _("Row"))]), + FloatSetting("step_time", _("Step time"), 1.0, + _("Time between key transitions"), + min=0.1, max=60.0), + BooleanSetting("reverse_scanning", + _("Reverse scanning"), False, + _("Scan in reverse order")) + ]), + SettingsGroup("scanning_input", "Input", [ + StringSetting("switch_type", _("Switch device"), + "keyboard", + _("Switch device, keyboard or mouse"), + entry_type=ENTRY_RADIO, + allowed=[("keyboard", _("Keyboard")), + ("mouse", _("Mouse"))], + children=[ + StringSetting("keyboard_key", "Switch key", + "Shift_R", + _( + "Key to use with scanning mode"), + allowed=[ + ("Shift_R", _("Right shift")), + ("Shift_L", _("Left shift")), + ("ISO_Level3_Shift", _("Alt Gr")), + ("Num_Lock", _("Num lock"))]), + StringSetting("mouse_button", "Switch button", + "2", + _( + "Mouse button to use in the scanning " + "mode"), + allowed=[("1", _("Button 1")), + ("2", _("Button 2")), + ("3", _("Button 3"))]) + ]), + ]), + SettingsGroup("scanning_color", "Color", [ + ColorSetting("block_scanning_color", _("Block color"), + "purple", _("Color of block scans")), + ColorSetting("row_scanning_color", _("Row color"), + "green", _("Color of row scans")), + ColorSetting("button_scanning_color", _("Key color"), + "cyan", _("Color of key scans")), + ColorSetting("cancel_scanning_color", + _("Cancel color"), + "red", _("Color of cancel scan")) + ]) + ]) + ]) + +if __name__ == "__main__": + class SchemasMaker: + def create_schemas(self): + doc = xml.dom.minidom.Document() + gconfschemafile = doc.createElement('gconfschemafile') + schemalist = doc.createElement('schemalist') + gconfschemafile.appendChild(schemalist) + self._create_schema(settings, doc, schemalist) + + self._pretty_xml(gconfschemafile) + + def _attribs(self, e): + if not e.attributes.items(): + return "" + return ' ' + ' '.join(['%s="%s"' % (k,v) \ + for k,v in e.attributes.items()]) + + def _pretty_xml(self, e, indent=0): + if not e.childNodes or \ + (len(e.childNodes) == 1 and \ + e.firstChild.nodeType == e.TEXT_NODE): + print '%s%s' % (' '*indent*2, e.toxml().strip()) + else: + print '%s<%s%s>' % (' '*indent*2, e.tagName, self._attribs(e)) + for c in e.childNodes: + self._pretty_xml(c, indent + 1) + print '%s</%s>' % (' '*indent*2, e.tagName) + + def _append_children_element_value_pairs(self, doc, element, pairs): + for e, t in pairs: + el = doc.createElement(e) + te = doc.createTextNode(str(t)) + el.appendChild(te) + element.appendChild(el) + + def _create_schema(self, setting, doc, schemalist): + if hasattr(setting, 'gconf_key'): + schema = doc.createElement('schema') + schemalist.appendChild(schema) + self._append_children_element_value_pairs( + doc, schema, [('key', '/schemas' + setting.gconf_key), + ('applyto', setting.gconf_key), + ('owner', 'caribou'), + ('type', setting.gconf_type), + ('default', setting.gconf_default)]) + locale = doc.createElement('locale') + locale.setAttribute('name', 'C') + schema.appendChild(locale) + self._append_children_element_value_pairs( + doc, locale, [('short', setting.short_desc), + ('long', setting.long_desc)]) + + for s in setting: + self._create_schema(s, doc, schemalist) + + maker = SchemasMaker() + maker.create_schemas() diff --git a/caribou/common/settings_manager.py b/caribou/common/settings_manager.py new file mode 100644 index 0000000..256ef76 --- /dev/null +++ b/caribou/common/settings_manager.py @@ -0,0 +1,77 @@ +import os +import gconf +from setting_types import * +from settings import settings +import const + +class _SettingsManager(object): + def __init__(self, settings): + self.groups = settings + self.gconf_client = gconf.client_get_default() + self.gconf_client.add_dir(const.CARIBOU_GCONF, + gconf.CLIENT_PRELOAD_NONE) + self._settings_map = {} + self._map_settings(self.groups) + + self._setup_settings() + + def __getattr__(self, name): + try: + return self._settings_map[name] + except KeyError: + raise AttributeError + + def _map_settings(self, setting): + if self._settings_map.has_key(setting.name): + raise ValueError, \ + "more than one setting has the name '%s'" % setting.name + self._settings_map[setting.name] = setting + + for s in setting: + self._map_settings(s) + + def _setup_settings(self): + for setting in self._settings_map.values(): + if isinstance(setting, SettingsGroup): + continue + try: + setting.value = self.gconf_client.get_value(setting.gconf_key) + except ValueError: + self.gconf_client.set_value(setting.gconf_key, setting.value) + + self._change_dependant_sensitivity(setting) + + handler_id = setting.connect('value-changed', + self._on_value_changed) + + self.gconf_client.notify_add(setting.gconf_key, + self._gconf_setting_changed_cb, + (setting, handler_id)) + + def _change_dependant_sensitivity(self, setting): + for name in setting.insensitive_when_false: + self._settings_map[name].sensitive = setting.is_true + for name in setting.insensitive_when_true: + self._settings_map[name].sensitive = not setting.is_true + if setting.allowed: + index = [a for a, b in setting.allowed].index(setting.value) + for i, child in enumerate(setting.children): + child.sensitive = i == index + + def _on_value_changed(self, setting, value): + if value != self.gconf_client.get_value(setting.gconf_key): + self.gconf_client.set_value(setting.gconf_key, value) + self._change_dependant_sensitivity(setting) + + def _gconf_setting_changed_cb(self, client, connection_id, entry, data): + setting, handler_id = data + new_value = client.get_value(setting.gconf_key) + if setting.value != new_value: + setting.handler_block(handler_id) + setting.value = new_value + setting.handler_unblock(handler_id) + + def __call__(self): + return self + +SettingsManager = _SettingsManager(settings) |