config_tools.py
author root
Wed, 26 Apr 2006 10:48:09 +0000
changeset 0 7710b138d4eb
permissions -rw-r--r--
forget the past. forget the past.

# Copyright (c) 2004 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program 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 General Public License for more details
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
"""This module contains some tools to handle main application and plugins
configuration.
"""

__revision__ = '$Id: config_tools.py,v 1.12 2006-04-23 13:53:52 nico Exp $'

from os.path import join

import gtk, gobject
from pigg.mvc import Controller
from pigg.form import PyFormModel
from pigg.wgenerator import notebook_generate

from logilab.common.configuration import Configuration, OptionValueError

from oobrother.uiutils.basemixins import WindowMixIn, PluggableFrameMixIn, \
     MenuControlledWindowMixIn
from oobrother.uiutils.trees import init_treeview_columns

from oobrother.sysutils import OOBROTHER_HOME
# from oobrother import get_path

__metaclass__ = type


def format_config_entry(opt_name, opt_dict):
    """format a config entry to a schema line"""
    opt_type = opt_dict['type']
    if opt_type in ('choice', 'multiple_choice'):
        extra_args = (opt_dict['choices'],)
    else:
        extra_args = ()
    return (opt_name, opt_type, opt_name.replace('_', ' '),
            extra_args, False, opt_dict.get('default'))

def schema_from_config(config):
    """return a schema as expected by `gtkmv.form.PyFormModel`, built
    from a `logilab.common.configuration.Configuration` instance.
    """
    return [format_config_entry(opt_name, opt_dict)
            for opt_name, opt_dict in config.options]
            
def notebook_from_config(config, model, ctrl):
    """generates and returns a gtk note book according to the given
    `logilab.common.configuration.Configuration` instance
    """
    nb_def = {}
    for opt_name, opt_dict in config.options:
        if opt_dict.get('type') is None:
            continue                
        section = opt_dict.get('group', config.name)
        attr_def = format_config_entry(opt_name, opt_dict)
        nb_def.setdefault(section, []).append(attr_def)
    return notebook_generate(nb_def.items(), model, ctrl)


def build_config_model(configurables):
    """creates an returns a tree model from a configuration tree"""
    store = gtk.TreeStore(gobject.TYPE_STRING,   # configuration section
                          gobject.TYPE_PYOBJECT, # The configuration object
                          )
    for configurable in configurables:
        fill_config_store(store, configurable, None)
    return store

def fill_config_store(store, configurable, tree_iter):
    """recursive build of a tree model for a configuration tree"""
    new_iter = store.append(tree_iter, [configurable.name, configurable])
    for subconfig in configurable.subconfiguration():
        fill_config_store(store, subconfig, new_iter)


def configuration_models(configurables):
    """return a list of configurable object (ie configuration node with
    a 'cfg' attribute set)
    """
    result = []
    for configurable in configurables:
        if configurable.cfg:
            result.append(configurable.cfg.model)
        result += configuration_models(configurable.subconfiguration())
    return result
    

class ConfigurableNode:
    
    def __init__(self, name, cfg=None, children=()):
        self.name = name
        self.cfg = cfg # optionel pluggable config object        
        self._children = children
        
    def subconfiguration(self):
        """return a list of configuration nodes for subconfiguration
        """
        return self._children

    
class PluggableConfig(Configuration):
    """extends `logilab.common.configuration.Configuration` to handle
    gui specific stuff (model, ctrl, pluggable widget
    """
    
    def __init__(self, name, config_file, options):
        if config_file:
            config_file = join(OOBROTHER_HOME, config_file)
        Configuration.__init__(self, config_file, options, name)
        self.wdg = None
        assert options
        assert config_file
        self.model = ConfigurationModel(self)
        try:
            self.load_file_configuration()
        except OptionValueError, ex:
            log(LOG_ERR, '%s: %s', (config_file, ex))
        self.model.update()
        self.ctrl = Controller(self.model)
        self.wdg = notebook_from_config(self, self.model, self.ctrl).wdg
        self.wdg.show_all()

class ConfigurationModel(PyFormModel):
    """a gtkmv model using a `logilab.common.configuration.Configuration`
    instance as backend
    """
    
    def __init__(self, config, schema=None):
        self.config = config
        if schema is None:
            schema = schema_from_config(config)
        self.schema = schema
        PyFormModel.__init__(self, self.schema, {})

    def update(self):
        """update the model values from the backend values"""
        for attr in self.schema:
            attr = attr[0]
            self.set_value(attr, self.config[attr])
        self._orig_data = self._data.copy()
                           
    def _commit(self):
        """write back the model's content (the form is completed at this point)
        """
        modifs = self.get_modifications()
        if not modifs:
            log(LOG_DEBUG, 'no modification to save to %s' %
                self.config.config_file)
            return
        for key, value in modifs.items():
            try:
                self.config.global_set_option(key, value)
            except KeyError:
                self.config.set_option(key, value)
        self.config.generate_config(open(self.config.config_file, 'w'))
        log(LOG_INFO, '%s written' % self.config.config_file)
        

class GlobalConfigurationModel:
    """the global configuration model is used to connect all the models
    related to application's configuration and editable by the
    configuration window.

    It only defines necessary methods from the Model interface.
    """
    
    def __init__(self, models):
        self._models = models
        
    def commit(self):
        """commit all models"""
        for model in self._models:
            model.commit()
        
    def rollback(self):
        """rollback all models"""
        for model in self._models:
            model.rollback()
            
    def notify_all(self):
        """ask each model to notify its observers"""
        for model in self._models:
            model.notify_all()
            
    def get_modifications(self):
        """return the modification of each model merged alltogether"""
        modifs = {}
        for model in self._models:
            modifs.update(model.get_modifications())
        return modifs