summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEitan Isaacson <eitan@monotonous.org>2010-12-09 00:03:32 -0800
committerEitan Isaacson <eitan@monotonous.org>2010-12-09 00:03:32 -0800
commitb656c2ff9ea269b382cba1224a93cb728ce591d7 (patch)
tree61330b4da2f97ea7587caad79262114ec2c03202
parentc35d19ab7d43328c33e9b1916f8508df944c2fc3 (diff)
downloadcaribou-b656c2ff9ea269b382cba1224a93cb728ce591d7.tar.gz
Created new settings backend with MVC seperation.
-rw-r--r--caribou/common/Makefile.am5
-rw-r--r--caribou/common/setting_types.py143
-rw-r--r--caribou/common/settings.py181
-rw-r--r--caribou/common/settings_manager.py77
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)