[caribou] Created new settings backend with MVC seperation.



commit b656c2ff9ea269b382cba1224a93cb728ce591d7
Author: Eitan Isaacson <eitan monotonous org>
Date:   Thu Dec 9 00:03:32 2010 -0800

    Created new settings backend with MVC seperation.

 caribou/common/Makefile.am         |    5 +-
 caribou/common/setting_types.py    |  143 ++++++++++++++++++++++++++++
 caribou/common/settings.py         |  181 ++++++++++++++++++++++++++++++++++++
 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)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]