diff --git a/cream/config/Makefile b/cream/config/Makefile
new file mode 100644
index 0000000..ec1b27b
--- /dev/null
+++ b/cream/config/Makefile
@@ -0,0 +1,3 @@
+settings:
+ sudo cp org.cream.melange.gschema.xml /usr/share/glib-2.0/schemas/
+ sudo glib-compile-schemas /usr/share/glib-2.0/schemas/
diff --git a/cream/config/__init__.py b/cream/config/__init__.py
index a3b970e..7bf996a 100644
--- a/cream/config/__init__.py
+++ b/cream/config/__init__.py
@@ -15,302 +15,27 @@
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-# TODO: Rewrite this.
+from cream.config.frontend import Frontend
+from cream.config.backend import Backend
-from gpyconf import Configuration as _Configuration
-from gpyconf.fields import Field
-from .backend import CreamXMLBackend, CONFIGURATION_SCHEME_FILE
-from cream.util import flatten, cached_property
+class Configuration(object):
-PROFILE_EXISTS_MARKUP = ''' \
-Sorry! A profile with the name {0} already exists!
+ def __init__(self):
-Please choose a different name!'''
+ self.backend = Backend('org.cream.melange')
+ self.frontend = Frontend(self.backend.profiles)
-class MissingConfigurationDefinitionFile(Exception):
- """
- Raised if one tries to access a module's configuration
- but that module hasn't defined any.
- """
- pass
+ self.frontend.connect('profile-selected', lambda f, p: self.backend.set_profile(p))
+ self.frontend.connect('profile-added', lambda f, p: self.backend.add_profile(p))
+ self.frontend.connect('profile-removed', lambda f, p: self.backend.remove_profile(p))
+ self.frontend.connect('value-changed', lambda f, k, v: self.backend.set_value(k, v))
-class ProfileNotEditable(Exception):
- pass
+ self.frontend.run()
-class ConfigurationProfile(object):
- """ A configuration profile. Holds name and assigned values. """
- is_editable = True
+ self.backend.save()
- def __init__(self, name, values, editable=True):
- self.name = name
- self.default_values = values
- self.is_editable = editable
- self._values = values
- @classmethod
- def fromdict(cls, dct, default_profile):
- values = default_profile.values.copy()
- values.update(dct.get('values', ()))
- return cls(dct.pop('name'), values, dct.pop('editable', True))
-
- @property
- def values(self):
- return self._values
-
- # TODO: Very repetitive
- @values.setter
- def values(self, value):
- if not self.is_editable:
- raise ProfileNotEditable(self)
- else:
- self._values = value
-
- def update(self, iterable):
- if not self.is_editable:
- raise ProfileNotEditable(self)
- self.values.update(iterable)
-
- def set(self, name, value):
- if not self.is_editable:
- raise ProfileNotEditable(self)
- self.values[name] = value
-
- def __repr__(self):
- return "" % (self.name,
- not self.is_editable and ' (not editable)' or '')
-
-class DefaultProfile(ConfigurationProfile):
- """ Default configuration profile (using in-code defined values) """
- def __init__(self, values):
- ConfigurationProfile.__init__(self, 'Default Profile',
- values, editable=False)
-
-
-class ProfileExistsError(Exception):
- def __init__(self, name):
- Exception.__init__(self, "A profile named '%s' already exists" % name)
-
-class ProfileList(list):
- """ List of profiles """
- default = None
- active = None
- active_index = 0
-
- def __init__(self, default_profile):
- list.__init__(self)
- list.append(self, default_profile)
- self.default = default_profile
-
- def insert(self, index, profile, overwrite=False):
- assert index
-
- if not isinstance(profile, ConfigurationProfile):
- profile = ConfigurationProfile.fromdict(profile, self.default)
-
- _, old_profile = self.find_by_name(profile.name)
- if old_profile is not None:
- if not overwrite:
- raise ProfileExistsError(profile)
- else:
- old_profile.values = profile.values
- else:
- list.insert(self, index, profile)
-
- def append(self, *args, **kwargs):
- self.insert(len(self), *args, **kwargs)
- add = append
-
- def find_by_name(self, name):
- """
- Returns a `(index, profile)` tuple being the `Profile` instance holding
- `name` as `name` attribute and its index in this list.
-
- If no such profile exists, returns a `(None, None)` tuple instead.
- """
- for index, profile in enumerate(self):
- if profile.name == name:
- return index, profile
- return None, None
-
- def _use(self, profile):
- if isinstance(profile, int):
- try:
- self.active = self[profile]
- self.active_index = profile
- except IndexError:
- self._use(0)
- else:
- self.active = profile
- self.active_index = self.index(profile)
-
-
-class Configuration(_Configuration):
- """
- Base class for all cream configurations.
- """
- backend = CreamXMLBackend
- profiles = ()
-
- @cached_property
- def frontend(self):
- from .frontend import CreamFrontend
- return CreamFrontend
-
-
- def __init__(self, scheme_path, path, **kwargs):
- # Make sure this instance's `fields` dict is *not* the classes'
- # `fields` dict (hence, the `fields` attribute of class `cls`),
- # but a copy of it.
- # TODO: There has to be a better way.
- self.fields = self.fields.copy()
-
- backend = CreamXMLBackend(scheme_path, path)
-
- try:
- configuration_scheme = backend.read_scheme()
- for name, field in configuration_scheme.iteritems():
- self._add_field(name, field)
- except MissingConfigurationDefinitionFile:
- pass
-
- _Configuration.__init__(self, backend_instance=backend, **kwargs)
-
-
- def _add_field(self, name, field):
- field.field_var = name
- self.fields[name] = field
- field.connect('value-changed', self.on_field_value_changed)
-
- def read(self):
- if not self.initially_read:
- predefined_profiles = self.profiles
- else:
- predefined_profiles = ()
-
- self.profiles = ProfileList(DefaultProfile(self.fields.name_value_dict)) # TODO: remove static options
-
- static_options, profiles = self.backend_instance.read()
-
- for field_name, value in static_options.iteritems():
- setattr(self, field_name, value)
-
- active_profile = 0
- for profile in flatten((profiles, predefined_profiles)):
- position = profile.pop('position')
- self.profiles.insert(position, profile, overwrite=True)
- if profile.pop('selected', False):
- active_profile = position
-
- self.use_profile(active_profile)
-
- self.initially_read = True
-
- def __setattr__(self, attr, value):
- new_value = super(Configuration, self).__setattr__(attr, value)
- if new_value is not None and not self.fields[attr].static:
- self.profiles.active.set(attr, new_value)
-
- def __getattr__(self, name):
- field = self.fields.get(name)
- if field is not None:
- if field.static:
- return field.value
- else:
- return self.profiles.active.values[name]
- else:
- raise AttributeError("No such attribute '%s'" % name)
-
- def use_profile(self, profile):
- self.profiles._use(profile)
- for name, instance in self.fields.iteritems():
- if instance.static: continue
- instance.value = self.profiles.active.values[name]
- self.profiles.active.values[name] = instance.value
-
-
- # FRONTEND:
- def _init_frontend(self, fields):
- _Configuration._init_frontend(self, fields)
- self.window = self.frontend_instance
-
- self.window.add_profiles(self.profiles)
-
- self.window.connect('profile-changed', self.frontend_profile_changed)
- self.window.connect('add-profile', self.frontend_add_profile)
- self.window.connect('remove-profile', self.frontend_remove_profile)
-
- self.window.set_active_profile_index(self.profiles.active_index)
-
- def frontend_field_value_changed(self, *args):
- if not self._ignore_frontend:
- _Configuration.frontend_field_value_changed(self, *args)
-
-
- def frontend_profile_changed(self, sender, profile_name, index):
- """ Profile selection was changed by the frontend (user) """
- self._ignore_frontend = True
- self.use_profile(index)
- self._ignore_frontend = False
- self.window.editable = self.profiles.active.is_editable
- self.save()
-
- def frontend_add_profile(self, sender, profile_name, position):
- """ User added a profile using the "add profile" button """
- profile = ConfigurationProfile(profile_name, self.fields.name_value_dict)
- try:
- self.profiles.insert(position, profile)
- except ProfileExistsError:
- import gtk
- dialog = gtk.MessageDialog(
- parent=None,
- flags=gtk.DIALOG_MODAL,
- type=gtk.MESSAGE_ERROR,
- buttons=gtk.BUTTONS_CLOSE
- )
- dialog.set_markup(PROFILE_EXISTS_MARKUP.format(profile.name))
- dialog.run()
- dialog.destroy()
- else:
- self.window.insert_profile(profile, position)
- self.window.set_active_profile_index(position)
- self.save()
-
- def frontend_remove_profile(self, sender, position):
- """ User removed a profile using the "remove profile" button """
- del self.profiles[position]
- self.window.remove_profile(position)
- self.save()
-
-
- def run_frontend(self):
- _Configuration.run_frontend(self)
- del self.frontend_instance
-
- def show_dialog(self):
- self.run_frontend()
-
-
- # BACKEND
- def save(self):
- self.emit('pre-save')
- self.backend_instance.save(self.profiles, self.fields)
-
-
-
-# Patch the `Field` class to make it accept the
-# cream-specific `static` keyword:
-def inject_method(klass, method_name):
- def wrapper(func):
- original_meth = getattr(klass, method_name)
- def chained_method(*args, **kwargs):
- original_meth(*args, **kwargs)
- func(*args, **kwargs)
- setattr(klass, method_name, chained_method)
- return func
- return wrapper
-
-@inject_method(Field, '_external_on_initialized')
-def on_initialized(self, kwargs):
- self.static = kwargs.pop('static', False)
+if __name__ == '__main__':
+ Configuration()
diff --git a/cream/config/backend.py b/cream/config/backend.py
index b3be734..966efbb 100644
--- a/cream/config/backend.py
+++ b/cream/config/backend.py
@@ -15,158 +15,211 @@
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-import os
+from gi.repository import GObject as gobject, Gio as gio, GLib as glib
-from lxml.etree import XMLSyntaxError, parse as parse_xml
-from cream.util.string import slugify
+from cream.util.dicts import ordereddict
-from gpyconf.backends import Backend
-from xmlserialize import unserialize_file, unserialize_atomic, serialize_to_file
-import gpyconf.fields
-import gpyconf.contrib.gtk
-import cream.config.fields
-from cream.util.dicts import ordereddict
-FIELD_TYPE_MAP = {
- 'char' : 'str',
- 'color' : 'str',
- 'font' : 'str',
- 'file' : 'tuple',
- 'integer' : 'int',
- 'hotkey' : 'str',
- 'boolean' : 'bool',
- 'multioption' : 'tuple'
-}
+NAME_DEFAULT = 'Default'
+
+KEY_PROFILES = 'profiles'
+KEY_SELECTED = 'profile-selected'
+
+IGNORE_KEYS = [KEY_PROFILES, KEY_SELECTED]
+
+
+def variant_from_python(obj):
+
+ if isinstance(obj, bool):
+ return glib.Variant('b', obj)
+ elif isinstance(obj, int):
+ return glib.Variant('i', obj)
+ elif isinstance(obj, float):
+ return glib.Variant('d', obj)
+ elif isinstance(obj, basestring):
+ return glib.Variant('s', obj)
+
+
+
+def variant_to_python(variant):
+
+ if variant.get_type_string() == 'b':
+ return variant.get_boolean()
+ elif variant.get_type_string() == 'i':
+ return variant.get_int32()
+ elif variant.get_type_string() == 'd':
+ return variant.get_double()
+ elif variant.get_type_string() == 's':
+ return variant.get_string()
+ elif variant.get_type_string() == 'as':
+ return list(variant)
+
+
+
+class Profiles(object):
+
+ def __init__(self, schema):
+
+ self.schema = schema
+ self.profiles = ordereddict()
+ self._selected_profile = None
+
+ self.default_profile = self.add_profile(NAME_DEFAULT)
+
+ profile_names = self.default_profile.settings.get_value(KEY_PROFILES)
+ profile_names = filter(lambda n: n != NAME_DEFAULT, list(profile_names))
+
+ if not profile_names:
+ # only default profile available
+ self.default_profile.selected = True
+
+
+ for name in profile_names:
+ self.add_profile(name)
+
+ for profile in self.profiles.itervalues():
+ if profile.selected:
+ self.selected_profile = profile
+
+
+ def __iter__(self):
+ return iter(self.profiles.itervalues())
+
+
+ def __len__(self):
+ return len(self.profiles)
+
+
+ @property
+ def selected_profile(self):
+ return self._selected_profile
+
+ @selected_profile.setter
+ def selected_profile(self, profile):
+
+ if self._selected_profile:
+ self._selected_profile.selected = False
+
+ profile.selected = True
+ self._selected_profile = profile
+
+
+ def add_profile(self, name):
+
+ if name == NAME_DEFAULT:
+ profile = DefaultProfile(name, self.schema)
+ else:
+ profile = Profile(name, self.schema)
+ self.profiles[name] = profile
+
+ return profile
+
+
+ def remove_profile(self, name):
+
+ profile = self.profiles[name]
+ if profile == self.selected_profile:
+ index = self.profiles.values().index(profile)
+ self.selected_profile = self.profiles.values()[index-1]
+
+ return self.profiles.pop(name)
+
+
+ def set_profile(self, name):
+
+ profile = self.profiles[name]
+ self.selected_profile = profile
+
+
+ def set_value(self, key, value):
+
+ variant = variant_from_python(value)
+ self.selected_profile.set_value(key, variant)
+
+
+ def save(self):
+
+ profiles = glib.Variant('as', self.profiles.keys())
+ self.default_profile.set_value(KEY_PROFILES, profiles)
+
+
+class Profile(object):
+
+ def __init__(self, name, schema):
+
+ self.name = name
+ self.schema = schema
+ self.path = '/' + name.lower()
+ self.settings = gio.Settings.new_with_path(self.schema, self.path)
+
+ self.is_default = False
+ self._selected = self.settings.get_boolean(KEY_SELECTED)
+
+
+ @property
+ def keys(self):
+ return (key for key in self.settings.list_keys() if key not in IGNORE_KEYS)
+
+
+ @property
+ def writeable(self):
+ return self.name != NAME_DEFAULT
+
+
+ @property
+ def selected(self):
+ return self._selected
+
+ @selected.setter
+ def selected(self, value):
+ self._selected = value
+ self.settings.set_boolean(KEY_SELECTED, value)
+
+
+ def get_value(self, key):
+ return variant_to_python(self.settings.get_value(key))
+
+
+ def set_value(self, key, value):
+ self.settings.set_value(key, value)
+
+
+class DefaultProfile(Profile):
+
+ def __init__(self, name, schema):
+
+ Profile.__init__(self, name, schema)
+
+ self.is_default = True
+
+
+class Backend(gobject.GObject):
+ def __init__(self, schema):
-CONFIGURATION_DIRECTORY = 'configuration'
-STATIC_OPTIONS_FILE = 'static-options.xml'
-CONFIGURATION_SCHEME_FILE = 'scheme.xml'
-PROFILE_ROOT_NODE = 'configuration_profile'
-STATIC_OPTIONS_ROOT_NODE = 'static_options'
-PROFILE_DIR = 'profiles'
+ gobject.GObject.__init__(self)
+ self.profiles = Profiles(schema)
-def get_field(name):
- if not name.endswith('Field'):
- name = name.title() + 'Field'
- try: return getattr(cream.config.fields, name)
- except AttributeError: pass
- try: return getattr(gpyconf.fields, name)
- except AttributeError: pass
- try: return getattr(gpyconf.contrib.gtk, name)
- except AttributeError:
- raise FieldNotFound(name)
+ def set_profile(self, profile):
+ self.profiles.set_profile(profile)
-class FieldNotFound(Exception):
- pass
+ def add_profile(self, profile):
+ self.profiles.add_profile(profile)
-class CreamXMLBackend(dict, Backend):
- compatibility_mode = False
-
- def __init__(self, scheme_path, path):
- Backend.__init__(self, None)
- dict.__init__(self)
- self.scheme_path = scheme_path
- self.path = path
-
- self.profile_dir = os.path.join(self.path, PROFILE_DIR)
-
-
- def read_scheme(self):
-
- if not os.path.isfile(self.scheme_path):
- from . import MissingConfigurationDefinitionFile
- raise MissingConfigurationDefinitionFile("Could not find %r." % self.scheme_path)
-
- tree = parse_xml(self.scheme_path)
- root = tree.getroot()
- scheme = ordereddict()
-
- for child in root.getchildren():
- option_name = child.tag
- attributes = dict(child.attrib)
- option_type = attributes.pop('type')
- if option_type.startswith('multioption'):
- # TODO: Hrm
- attributes['default'] = child.attrib.pop('default', None)
- attributes['options'] = unserialize_atomic(child, FIELD_TYPE_MAP)
- else:
- if not (
- FIELD_TYPE_MAP.get(option_type) in ('list', 'tuple', 'dict')
- and not child.getchildren()
- ):
- attributes['default'] = unserialize_atomic(child, FIELD_TYPE_MAP)
- scheme[option_name] = get_field(option_type)(**attributes)
-
- return scheme
-
-
- def read(self):
-
- static_options = {}
- profiles = []
-
- try:
- obj = unserialize_file(os.path.join(self.path, 'static-options.xml'))
- static_options.update(obj)
- except:
- pass
-
- if not os.path.exists(self.profile_dir):
- return dict(), tuple()
-
- for profile in os.listdir(self.profile_dir):
- if os.path.isdir(os.path.join(self.profile_dir, profile)):
- continue
- try:
- obj = unserialize_file(os.path.join(self.profile_dir, profile))
- except XMLSyntaxError, err:
- self.warn("Could not parse XML configuration file '{file}': {error}".format(
- file=profile, error=err))
- else:
- profiles.append(obj)
-
- return static_options, profiles
-
-
- def save(self, profile_list, fields):
-
- if not os.path.exists(self.profile_dir):
- os.makedirs(self.profile_dir)
- # get all saved profiles
- saved_profiles = {}
- for profile in os.listdir(self.profile_dir):
- name = os.path.splitext(profile)[0]
- saved_profiles[name] = os.path.join(self.profile_dir, profile)
+ def remove_profile(self, profile):
+ self.profiles.remove_profile(profile)
- for index, profile in enumerate(profile_list):
- if not profile.is_editable: continue
- filename = os.path.join(os.path.join(self.path, PROFILE_DIR), slugify(profile.name)+'.xml')
-
- serialize_to_file({
- 'name' : profile.name,
- 'values' : profile.values,
- 'position' : index,
- 'selected' : profile_list.active == profile
- }, filename, tag=PROFILE_ROOT_NODE)
+ def set_value(self, key, value):
+ self.profiles.set_value(key, value)
- if profile.name.lower() in saved_profiles:
- del saved_profiles[profile.name.lower()]
- # `saved_profiles` now contains profiles, which have been removed
- # but are still present in the filesystem. Remove them.
- for profile in saved_profiles.values():
- os.remove(profile)
+ def save(self):
- static_options = dict((name, field.value) for name, field in
- fields.iteritems() if field.static)
- if static_options:
- serialize_to_file(static_options,
- os.path.join(self.path, STATIC_OPTIONS_FILE),
- tag=STATIC_OPTIONS_ROOT_NODE)
+ self.profiles.save()
+ gio.Settings.sync()
diff --git a/cream/config/fields.py b/cream/config/fields.py
deleted file mode 100644
index fea1fd6..0000000
--- a/cream/config/fields.py
+++ /dev/null
@@ -1,45 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# This library is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
-
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from gpyconf.fields import FontField as _FontField, MultiOptionField
-from gpyconf.frontends.gtk import font_description_to_dict, dict_to_font_description
-
-
-MultioptionField = MultiOptionField
-
-class FontDict(dict):
- def to_string(self):
- return dict_to_font_description(self)
-
- @classmethod
- def fromstring(cls, string):
- d = font_description_to_dict(string)
- d['color'] = '#000000'
- return cls(d)
-
-class FontField(_FontField):
- def custom_default(self):
- return FontDict(_FontField.custom_default(self))
-
- def to_python(self, value):
- if isinstance(value, basestring):
- return FontDict.fromstring(value)
- else:
- return FontDict(_FontField.to_python(self, value))
-
- def conf_to_python(self, value):
- return FontDict(_FontField.conf_to_python(self, value))
\ No newline at end of file
diff --git a/cream/config/frontend.py b/cream/config/frontend.py
index 12efcf2..653ca0c 100644
--- a/cream/config/frontend.py
+++ b/cream/config/frontend.py
@@ -15,156 +15,230 @@
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-import os
-import gtk
-from gpyconf.frontends.gtk import ConfigurationDialog
+
+from gi.repository import GObject as gobject, Gtk as gtk
+
+from cream.config import widgets
from cream.util import joindir
+
MODE_NORMAL = 1
-MODE_EDIT = 2
-
-class CreamFrontend(ConfigurationDialog):
- _editable = True
- _new_events = ('profile-changed', 'add-profile', 'remove-profile')
-
- def __init__(self, *args, **kwargs):
-
- self.profiles = []
- self._mode = MODE_NORMAL
-
- ConfigurationDialog.__init__(self, title='Configuration', *args, **kwargs)
- self.add_events(self._new_events)
-
- self.interface = gtk.Builder()
- self.interface.add_from_file(joindir(__file__, 'interface/profiles.ui'))
-
- self.profile_box_edit = self.interface.get_object('profile_box_edit')
- self.profile_entry = self.interface.get_object('profile_entry')
- self.profile_save = self.interface.get_object('profile_save')
- self.profile_cancel = self.interface.get_object('profile_cancel')
-
- self.profile_box_normal = self.interface.get_object('profile_box_normal')
- self.profile_selector = self.interface.get_object('profile_selector')
- self.profile_add = self.interface.get_object('profile_add')
- self.profile_remove = self.interface.get_object('profile_remove')
-
- self.profiles_storage = self.interface.get_object('profiles_storage')
-
- self.profile_selector.connect('changed', self.on_profile_changed)
- self.profile_entry.connect('activate', self.change_mode)
- self.profile_entry.connect('activate', self.on_new_profile_added)
- self.profile_add.connect('clicked', self.change_mode)
- self.profile_save.connect('clicked', self.change_mode)
- self.profile_save.connect('clicked', self.on_new_profile_added)
- self.profile_cancel.connect('clicked', self.change_mode)
- self.profile_remove.connect('clicked', self.on_remove_profile)
-
- self.alignment = gtk.Alignment(1, 0.5, 1, 1)
- self.alignment.add(self.profile_box_normal)
-
- self.layout.pack_start(self.alignment, False, False, 0)
- self.layout.reorder_child(self.alignment, 0)
-
- def add_profiles(self, profiles):
- """ Add a list or tuple of `Profile`s to the profile selector """
- for profile in profiles:
- self.add_profile(profile)
-
- def add_profile(self, profile):
- """ Add a `Profile` instance to the profile selector """
- self.profiles_storage.append([profile.name])
- self.profiles.append(profile.name)
-
- def insert_profile(self, profile, position):
- """ Insert `profile` at `position` into the profile selector """
- self.profiles_storage.insert(position, [profile.name])
- self.profiles.insert(position, profile.name)
-
- def remove_profile(self, position):
- """ Remove entry at `position` from the profile selector """
- iter = self.profiles_storage.get_iter_from_string(str(position))
- # huaa?
- self.profiles_storage.remove(iter)
- self.profile_selector.set_active(position-1)
-
-
- # CALLBACKS
- def on_profile_changed(self, widget):
- """ User changed the profile-selector dropdown """
- index = widget.get_active()
- if index < 0:
- # empty profile selector (should only happen at startup)
- return
- self.emit('profile-changed',
- self.profiles_storage.get_value(widget.get_active_iter(), 0),
- index)
-
-
- def on_remove_profile(self, sender):
- """ User clicked the "remove profile" button """
-
- dialog = gtk.MessageDialog(
- parent=None,
- flags=gtk.DIALOG_MODAL,
- type=gtk.MESSAGE_QUESTION,
- buttons=gtk.BUTTONS_YES_NO)
-
- dialog.set_markup("Are you sure that you want to remove profile {0}?\n\nYou will lose all data connected to this profile and won't be able to restore a previously removed profile!".format(self.profiles[self.profile_selector.get_active()]))
-
- res = dialog.run()
- dialog.destroy()
- if res == gtk.RESPONSE_YES:
- self.emit('remove-profile', self.profile_selector.get_active())
-
- def on_new_profile_added(self, sender):
- """ User is done with editing the Entry """
- name = self.profile_entry.get_text()
- index = self.profile_selector.get_active() + 1
- if name:
- self.emit('add-profile', name, index)
-
-
- def change_mode(self, sender):
- """ User clicked on add or save button. Change the mode. """
- max_height = max(self.profile_entry.get_allocation()[3],
- self.profile_selector.get_allocation()[3]
- )
- self.alignment.set_size_request(-1, max_height)
-
- box = [widget for widget in self.alignment][0]
- if self._mode == MODE_NORMAL:
+MODE_ADD = 2
+
+
+def get_widget_for_value(value):
+
+ if isinstance(value, bool):
+ return widgets.BooleanWidget
+ elif isinstance(value, int):
+ return widgets.IntegerWidget
+ elif isinstance(value, float):
+ return widgets.FloatWidget
+ elif isinstance(value, basestring):
+ return widgets.CharWidget
+ elif isinstance(value, list):
+ return widgets.MultiOptionWidget
+
+
+class ConfigurationDialog(gobject.GObject):
+
+ __gsignals__ = {
+ 'value-changed': (gobject.SignalFlags.RUN_LAST, None, (str, object)),
+ 'profile-selected': (gobject.SignalFlags.RUN_LAST, None, (str, )),
+ 'profile-added': (gobject.SignalFlags.RUN_LAST, None, (str, )),
+ 'profile-removed': (gobject.SignalFlags.RUN_LAST, None, (str, ))
+ }
+
+ def __init__(self, profiles):
+
+ gobject.GObject.__init__(self)
+
+ self.profiles = profiles
+ self.widgets = {}
+
+ interface = gtk.Builder()
+ interface.add_from_file(joindir(__file__, 'interface/dialog.ui'))
+
+ self.dialog = interface.get_object('dialog')
+ self.profile_box = interface.get_object('profile_box')
+ self.settings_grid = interface.get_object('settings_grid')
+ self.profile_store = interface.get_object('profile_store')
+
+ self.profile_normal = interface.get_object('profile_normal')
+ self.profile_selector = interface.get_object('profile_selector')
+ self.button_add = interface.get_object('button_add')
+ self.button_remove = interface.get_object('button_remove')
+
+ self.profile_add = interface.get_object('profile_add')
+ self.profile_entry = interface.get_object('profile_entry')
+ self.button_save = interface.get_object('button_save')
+ self.button_cancel = interface.get_object('button_cancel')
+
+
+ # TODO: do this in Glade, stupid thing is crashing
+ cell = gtk.CellRendererText()
+ self.profile_selector.pack_start(cell, False)
+ self.profile_selector.add_attribute(cell, "text", 0)
+
+
+ self.dialog.connect('delete-event', lambda *x: self.dialog.hide())
+
+ self.profile_selector.connect('changed', self.on_profile_selected)
+ self.button_add.connect('clicked', self.change_mode, MODE_ADD)
+ self.button_remove.connect('clicked', self.on_profile_removed)
+ self.button_save.connect('clicked', self.on_profile_added)
+ self.button_cancel.connect('clicked', self.change_mode, MODE_NORMAL)
+ self.profile_entry.connect('activate', self.on_profile_added)
+
+
+ self.profile_box.pack_start(self.profile_normal, True, True, 0)
+
+ self.dialog.set_default_size(250, 150)
+
+
+ for row, key in enumerate(sorted(self.profiles.default_profile.keys)):
+ widget = self.init_widget(key)
+
+ widget.connect('value-changed', self.on_value_changed, key)
+ self.widgets[key] = widget
+
+ self.settings_grid.attach(gtk.Label(key), 0, row+1, 1, 1)
+ self.settings_grid.attach(widget.widget, 1, row+1, 1, 1)
+
+ self.settings_grid.show_all()
+ self.update_profile_list()
+
+
+ def init_widget(self, key):
+
+ value = self.profiles.selected_profile.get_value(key)
+ widget = get_widget_for_value(value)
+
+ return widget(value)
+
+
+ @property
+ def selected_profile(self):
+ index = self.profile_selector.get_active()
+ return list(self.profiles)[index]
+
+
+ def change_mode(self, widget, mode):
+
+ if mode == MODE_NORMAL:
self.profile_entry.set_text('')
- self.alignment.remove(box)
- self.alignment.add(self.profile_box_edit)
+ self.profile_box.remove(self.profile_add)
+ self.profile_box.pack_start(self.profile_normal, True, True, 0)
+ else:
+ self.profile_box.remove(self.profile_normal)
+ self.profile_box.pack_start(self.profile_add, True, True, 0)
self.profile_entry.grab_focus()
- self._mode = MODE_EDIT
+
+
+ def update_profile_list(self):
+
+ self.profile_store.clear()
+
+ for i, profile in enumerate(self.profiles):
+ if profile.selected:
+ selected_index = i
+ self.profile_store.append((profile.name,))
+ self.profile_selector.set_active(selected_index)
+
+ if len(self.profiles) == 1:
+ self.profile_selector.set_sensitive(False)
else:
- self.alignment.remove(box)
- self.alignment.add(self.profile_box_normal)
- self._mode = MODE_NORMAL
+ self.profile_selector.set_sensitive(True)
- @property
- def editable(self):
- """
- `True` if the window is 'editable' (an editable profile is selected)
- """
- return self._editable
-
- @editable.setter
- def editable(self, value):
- # set widgets sensitive (or not)
- if value:
- if not self.editable:
- self.content.set_sensitive(True)
- self.profile_remove.set_sensitive(True)
+
+ def update_values(self):
+
+ for key in self.profiles.selected_profile.keys:
+ value = self.profiles.selected_profile.get_value(key)
+ self.widgets[key].set_value(value)
+
+
+ def update_sensitivity(self):
+
+ if self.profiles.selected_profile.is_default:
+ self.button_remove.set_sensitive(False)
+ self.settings_grid.set_sensitive(False)
else:
- self.content.set_sensitive(False)
- self.profile_remove.set_sensitive(False)
- self._editable = value
+ self.button_remove.set_sensitive(True)
+ self.settings_grid.set_sensitive(True)
+
+
+ def on_profile_selected(self, widget):
+
+ self.emit('profile-selected', self.selected_profile.name)
+
+ self.update_sensitivity()
+ self.update_values()
+
+
+ def on_profile_added(self, widget):
+
+ self.emit('profile-added', self.profile_entry.get_text())
+
+ self.change_mode(None, MODE_NORMAL)
+ self.update_profile_list()
+
+
+ def on_profile_removed(self, widget):
+
+ self.emit('profile-removed', self.selected_profile.name)
+ self.update_profile_list()
+
+
+ def on_value_changed(self, widget, value, key):
+ self.emit('value-changed', key, value)
- def set_active_profile_index(self, index):
- self.profile_selector.set_active(index)
def run(self):
- ConfigurationDialog.run(self)
- self.dialog.destroy()
+ return self.dialog.run()
+
+
+class Frontend(gobject.GObject):
+
+ __gsignals__ = {
+ 'value-changed': (gobject.SignalFlags.RUN_LAST, None, (str, object)),
+ 'profile-selected': (gobject.SignalFlags.RUN_LAST, None, (str, )),
+ 'profile-added': (gobject.SignalFlags.RUN_LAST, None, (str, )),
+ 'profile-removed': (gobject.SignalFlags.RUN_LAST, None, (str, )),
+ }
+
+ def __init__(self, profiles):
+
+ gobject.GObject.__init__(self)
+
+
+ self.profiles = profiles
+ self.dialog = ConfigurationDialog(profiles)
+
+ self.dialog.connect('profile-selected', self.on_profile_selected)
+ self.dialog.connect('profile-added', self.on_profile_added)
+ self.dialog.connect('profile-removed', self.on_profile_removed)
+ self.dialog.connect('value-changed', self.on_value_changed)
+
+
+ def run(self):
+ return self.dialog.run()
+
+
+ def on_profile_selected(self, dialog, profile):
+ self.emit('profile-selected', profile)
+
+
+ def on_profile_added(self, dialog, profile):
+
+ profiles = map(lambda p: p.name, self.profiles)
+ if profile not in profiles:
+ self.emit('profile-added', profile)
+
+
+ def on_profile_removed(self, dialog, profile):
+ self.emit('profile-removed', profile)
+
+
+ def on_value_changed(self, dialog, key, value):
+ self.emit('value-changed', key, value)
diff --git a/cream/config/interface/dialog.ui b/cream/config/interface/dialog.ui
new file mode 100644
index 0000000..143a984
--- /dev/null
+++ b/cream/config/interface/dialog.ui
@@ -0,0 +1,215 @@
+
+
+
+
+
+ True
+ False
+
+
+ True
+ True
+ ●
+
+
+ False
+ True
+ 0
+
+
+
+
+ False
+ True
+ True
+ True
+ False
+
+
+ True
+ False
+ gtk-ok
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ True
+ True
+ False
+
+
+ True
+ False
+ gtk-cancel
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ profile_store
+
+
+ True
+ True
+ 0
+
+
+
+
+ False
+ True
+ True
+ True
+ False
+
+
+ True
+ False
+ gtk-add
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ True
+ True
+ False
+
+
+ True
+ False
+ gtk-remove
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
diff --git a/cream/config/interface/profiles.ui b/cream/config/interface/profiles.ui
deleted file mode 100644
index 076c1a3..0000000
--- a/cream/config/interface/profiles.ui
+++ /dev/null
@@ -1,110 +0,0 @@
-
-
-
-
-
- True
-
-
- True
- True
- •
-
-
- 0
-
-
-
-
- True
- True
- True
-
-
- True
- gtk-ok
-
-
-
-
- False
- 1
-
-
-
-
- True
- True
- True
-
-
- True
- gtk-cancel
-
-
-
-
- False
- 2
-
-
-
-
- True
-
-
- True
- profiles_storage
-
-
-
- 0
-
-
-
-
- 0
-
-
-
-
- True
- True
- True
-
-
- True
- gtk-add
-
-
-
-
- False
- 1
-
-
-
-
- True
- True
- True
-
-
- True
- gtk-remove
-
-
-
-
- False
- 2
-
-
-
-
-
-
-
-
-
-
diff --git a/cream/config/org.cream.melange.gschema.xml b/cream/config/org.cream.melange.gschema.xml
new file mode 100644
index 0000000..32d1a64
--- /dev/null
+++ b/cream/config/org.cream.melange.gschema.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ []
+
+
+ false
+
+
+
+
+ false
+
+
+
+ 10
+
+
+ 'Text'
+
+
+ 3.14
+
+
+
diff --git a/cream/config/widgets.py b/cream/config/widgets.py
new file mode 100644
index 0000000..bae5a46
--- /dev/null
+++ b/cream/config/widgets.py
@@ -0,0 +1,125 @@
+from gi.repository import Gtk as gtk, GObject as gobject
+
+
+class Widget(gobject.GObject):
+
+ __gsignals__ = {
+ 'value-changed': (gobject.SignalFlags.RUN_LAST, None, (object,)),
+ }
+
+ def __init__(self, gtk_widget):
+
+ gobject.GObject.__init__(self)
+
+ self.widget = gtk_widget
+
+
+ def on_value_changed(self, *args):
+
+ self.emit('value-changed', self.get_value())
+
+
+
+class BooleanWidget(Widget):
+
+ def __init__(self, value):
+
+ Widget.__init__(self, gtk.CheckButton())
+
+ self.widget.connect('toggled', self.on_value_changed)
+
+ self.set_value(value)
+
+
+ def get_value(self):
+ return self.widget.get_active()
+
+ def set_value(self, value):
+ self.widget.set_active(value)
+
+
+class IntegerWidget(Widget):
+
+ def __init__(self, value):
+
+ Widget.__init__(self, gtk.SpinButton())
+
+ self.widget.set_range(0, 100)
+ self.widget.set_increments(1, self.widget.get_increments()[0])
+ self.widget.connect('value-changed', self.on_value_changed)
+
+ self.set_value(value)
+
+
+ def get_value(self):
+ return int(self.widget.get_value())
+
+ def set_value(self, value):
+ self.widget.set_value(value)
+
+
+class FloatWidget(Widget):
+
+ def __init__(self, value):
+
+ Widget.__init__(self, gtk.SpinButton())
+
+ self.widget.set_range(0, 100)
+ self.widget.set_increments(0.01, self.widget.get_increments()[0])
+ self.widget.set_digits(2)
+ self.widget.connect('value-changed', self.on_value_changed)
+
+ self.set_value(value)
+
+
+ def get_value(self):
+ return self.widget.get_value()
+
+ def set_value(self, value):
+ self.widget.set_value(value)
+
+
+class CharWidget(Widget):
+
+ def __init__(self, value):
+
+ Widget.__init__(self, gtk.Entry())
+
+ self.widget.connect('changed', self.on_value_changed)
+
+ self.set_value(value)
+
+
+ def get_value(self):
+ return self.widget.get_text()
+
+ def set_value(self, value):
+ self.widget.set_text(value)
+
+
+"""class MultiOptionWidget(Widget):
+
+ def __init__(self, value):
+
+ class ComboBoxText(gtk.ComboBox):
+ def __init__(self):
+ gtk.ComboBox.__init__(self)
+ self.liststore = gtk.ListStore(str)
+ self.set_model(self.liststore)
+ cell = gtk.CellRendererText()
+ self.pack_start(cell, True)
+ self.add_attribute(cell, 'text', 0)
+
+ Widget.__init__(self, ComboBoxText())
+
+ for v in value:
+ self.widget.liststore.append((v,))
+
+ self.widget.connect('changed', self.on_value_changed)
+
+
+ def get_value(self):
+ pass
+
+ def set_value(self, value):
+ pass"""