summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2020-12-28 21:44:27 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2020-12-28 21:44:27 (GMT)
commita6a88792bc866d8a1d7cabd50a93374da5dd1e7a (patch)
tree748d5a29339f0ccd09b1d5ef691a988c30eb5fca
parentcd59150b26173fc4caa44b604d9e0989de331b3d (diff)
Improved the API for configuration and its Python documentation.
-rw-r--r--plugins/pychrysalide/glibext/configuration.c869
-rw-r--r--plugins/pychrysalide/glibext/configuration.h6
-rw-r--r--plugins/pychrysalide/glibext/constants.c116
-rw-r--r--plugins/pychrysalide/glibext/constants.h6
-rw-r--r--plugins/pychrysalide/gtkext/easygtk.c40
-rw-r--r--plugins/pychrysalide/helpers.c124
-rw-r--r--plugins/pychrysalide/helpers.h7
-rw-r--r--src/glibext/Makefile.am1
-rw-r--r--src/glibext/configuration-int.h121
-rw-r--r--src/glibext/configuration.c209
-rw-r--r--src/glibext/configuration.h2
-rw-r--r--tests/glibext/configuration.py71
12 files changed, 1218 insertions, 354 deletions
diff --git a/plugins/pychrysalide/glibext/configuration.c b/plugins/pychrysalide/glibext/configuration.c
index 192f602..b7d8f82 100644
--- a/plugins/pychrysalide/glibext/configuration.c
+++ b/plugins/pychrysalide/glibext/configuration.c
@@ -28,9 +28,11 @@
#include <pygobject.h>
-#include <glibext/configuration.h>
+#include <glibext/configuration-int.h>
+#include <plugins/dt.h>
+#include "constants.h"
#include "../access.h"
#include "../helpers.h"
@@ -42,6 +44,9 @@
/* Crée un nouvel objet Python de type 'ConfigParam'. */
static PyObject *py_config_param_new(PyTypeObject *, PyObject *, PyObject *);
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_config_param_init(PyObject *, PyObject *, PyObject *);
+
/* Efface toute valeur courante d'un paramètre de configuration. */
static PyObject *py_config_param_make_empty(PyObject *, PyObject *);
@@ -63,9 +68,6 @@ static PyObject *py_config_param_get_value(PyObject *, void *);
/* Modifie la valeur courante d'un paramètre de configuration. */
static int py_config_param_set_value(PyObject *, PyObject *, void *);
-/* Définit les constantes pour les paramètres. */
-static bool py_config_param_define_constants(PyObject *);
-
/* ----------------------------- PARCOURS DE PARAMETRES ----------------------------- */
@@ -98,9 +100,15 @@ static int py_config_param_iterator_init(PyObject *, PyObject *, PyObject *);
/* ----------------------- GESTION GENERIQUE DE CONFIGURATION ----------------------- */
-/* Crée un nouvel objet Python de type 'GenConfig'. */
+/* Accompagne la création d'une instance dérivée en Python. */
static PyObject *py_generic_config_new(PyTypeObject *, PyObject *, PyObject *);
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_generic_config_init(PyObject *, PyObject *, PyObject *);
+
+/* Met à disposition un encadrement des accès aux paramètres. */
+static PyObject *py_generic_config_lock_unlock(PyObject *, PyObject *);
+
/* Lit la configuration depuis un fichier. */
static PyObject *py_generic_config_read(PyObject *, PyObject *);
@@ -116,9 +124,12 @@ static PyObject *py_generic_config_add(PyObject *, PyObject *);
/* Retire un paramètre d'une configuration. */
static PyObject *py_generic_config_delete(PyObject *, PyObject *);
-/* Fournit le chemin d'accès au binaire représenté. */
+/* Indique le fichier utilisé pour l'enregistrement XML. */
static PyObject *py_generic_config_get_filename(PyObject *, void *);
+/* Renvoie la liste des paramètres de configuration. */
+static PyObject *py_generic_config_get_params(PyObject *, void *);
+
/* ---------------------------------------------------------------------------------- */
@@ -128,73 +139,173 @@ static PyObject *py_generic_config_get_filename(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : type = type de l'objet à instancier. *
+* Paramètres : type = type du nouvel objet à mettre en place. *
+* args = éventuelle liste d'arguments. *
+* kwds = éventuel dictionnaire de valeurs mises à disposition. *
+* *
+* Description : Accompagne la création d'une instance dérivée en Python. *
+* *
+* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_config_param_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyObject *result; /* Objet à retourner */
+ PyTypeObject *base; /* Type de base à dériver */
+ bool first_time; /* Evite les multiples passages*/
+ GType gtype; /* Nouveau type de processeur */
+ bool status; /* Bilan d'un enregistrement */
+
+ /* Validations diverses */
+
+ base = get_python_config_param_type();
+
+ if (type == base)
+ goto simple_way;
+
+ /* Mise en place d'un type dédié */
+
+ first_time = (g_type_from_name(type->tp_name) == 0);
+
+ gtype = build_dynamic_type(G_TYPE_CFG_PARAM, type->tp_name, NULL, NULL, NULL);
+
+ if (first_time)
+ {
+ status = register_class_for_dynamic_pygobject(gtype, type, base);
+
+ if (!status)
+ {
+ result = NULL;
+ goto exit;
+ }
+
+ }
+
+ /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
+
+ simple_way:
+
+ result = PyType_GenericNew(type, args, kwds);
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
* args = arguments fournis à l'appel. *
* kwds = arguments de type key=val fournis. *
* *
-* Description : Crée un nouvel objet Python de type 'ConfigParam'. *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
* *
-* Retour : Instance Python mise en place. *
+* Retour : 0. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_config_param_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int py_config_param_init(PyObject *self, PyObject *args, PyObject *kwds)
{
- PyObject *result; /* Instance à retourner */
const char *path; /* Accès au paramètre */
- unsigned int ptype; /* Type de paramètre */
- PyObject *value; /* Valeur par défaut éventuelle*/
+ ConfigParamType ptype; /* Type de paramètre */
+ PyObject *py_value; /* Valeur par défaut éventuelle*/
int ret; /* Bilan de lecture des args. */
+ bool valid; /* Validité des transmissions */
+ param_value value; /* Valeur de paramètre */
GCfgParam *param; /* Paramètre mis en place */
- value = NULL;
-
- ret = PyArg_ParseTuple(args, "sI|O", &path, &ptype, &value);
- if (!ret) return NULL;
-
- if (value == NULL || value == Py_None)
- param = g_config_param_new_empty(path, ptype);
+#define CONFIG_PARAM_DOC \
+ "ConfigParam holds a configuration parameter with its default and current" \
+ " values.\n" \
+ "\n" \
+ "Parameters are aimed to join a pychrysalide.glibext.GenConfig instance.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " ConfigParam(path, type, value=None)" \
+ "\n" \
+ "Where *path* is dot separated string items serving as a parameter key," \
+ " *type* is a pychrysalide.glibext.ConfigParam.ConfigParamType value and" \
+ " *value* is an optional default value if the parameter initial value" \
+ " has not to be empty."
+
+ /* Récupération des paramètres */
+
+ py_value = NULL;
+
+ ret = PyArg_ParseTuple(args, "sO&|O", &path, convert_to_config_param_type, &ptype, &py_value);
+ if (!ret) return -1;
- else
+ if (py_value != NULL && py_value != Py_None)
+ {
switch (ptype)
{
case CPT_BOOLEAN:
- if (PyBool_Check(value))
- param = g_config_param_new(path, CPT_BOOLEAN, (bool)(value == Py_True));
- else
- param = NULL;
+ valid = PyBool_Check(py_value);
+ if (valid)
+ value.boolean = (bool)(py_value == Py_True);
break;
case CPT_INTEGER:
- if (PyLong_Check(value))
- param = g_config_param_new(path, CPT_INTEGER, (int)PyLong_AsLong(value));
- else
- param = NULL;
+ valid = PyLong_Check(py_value);
+ if (valid)
+ value.integer = (int)PyLong_AsLong(py_value);
+ break;
+
+ case CPT_ULONG:
+ valid = PyLong_Check(py_value);
+ if (valid)
+ value.ulong = (unsigned long)PyLong_AsUnsignedLong(py_value);
break;
case CPT_STRING:
- if (PyUnicode_Check(value))
- param = g_config_param_new(path, CPT_STRING, PyUnicode_DATA(value));
- else
- param = NULL;
+ valid = PyUnicode_Check(py_value);
+ if (valid)
+ value.string = PyUnicode_DATA(py_value);
+ break;
+
+ case CPT_COLOR:
+ valid = (convert_to_gdk_rgba(py_value, &value.color) == 1);
break;
default:
- param = NULL;
+ assert(false);
+ valid = false;
break;
}
- if (param != NULL)
- {
- result = pygobject_new(G_OBJECT(param));
- g_object_unref(param);
+ if (!valid)
+ {
+ PyErr_SetString(PyExc_TypeError, "invalid value for the specified parameter type");
+ return -1;
+ }
+
}
- else result = NULL;
- return result;
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ param = G_CFG_PARAM(pygobject_get(self));
+
+ if (py_value == NULL || py_value == Py_None)
+ g_config_param_build_empty(param, path, ptype);
+
+ else
+ g_config_param_build(param, path, ptype, &value);
+
+ return 0;
}
@@ -216,6 +327,13 @@ static PyObject *py_config_param_make_empty(PyObject *self, PyObject *args)
{
GCfgParam *param; /* Paramètre visé par l'opérat°*/
+#define CONFIG_PARAM_MAKE_EMPTY_METHOD PYTHON_METHOD_DEF \
+( \
+ make_empty, "$self, /", \
+ METH_NOARGS, py_config_param, \
+ "Unset the value of the current parameter." \
+)
+
param = G_CFG_PARAM(pygobject_get(self));
g_config_param_make_empty(param);
@@ -242,6 +360,13 @@ static PyObject *py_config_param_reset(PyObject *self, PyObject *args)
{
GCfgParam *param; /* Paramètre visé par l'opérat°*/
+#define CONFIG_PARAM_RESET_METHOD PYTHON_METHOD_DEF \
+( \
+ reset, "$self, /", \
+ METH_NOARGS, py_config_param, \
+ "Reset the content of the current parameter." \
+)
+
param = G_CFG_PARAM(pygobject_get(self));
g_config_param_reset(param);
@@ -269,6 +394,13 @@ static PyObject *py_config_param_get_path(PyObject *self, void *closure)
GCfgParam *param; /* Paramètre visé par l'opérat°*/
const char *path; /* Chemin d'accès à diffuser */
+#define CONFIG_PARAM_PATH_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ path, py_config_param, \
+ "Dot separated string items used as key for a" \
+ " configuration parameter." \
+)
+
param = G_CFG_PARAM(pygobject_get(self));
path = g_config_param_get_path(param);
@@ -292,13 +424,25 @@ static PyObject *py_config_param_get_path(PyObject *self, void *closure)
static PyObject *py_config_param_get_type(PyObject *self, void *closure)
{
+ PyObject *result; /* Type de paramètre à renvoyer*/
GCfgParam *param; /* Paramètre visé par l'opérat°*/
ConfigParamType type; /* Type de paramètre */
+#define CONFIG_PARAM_TYPE_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ type, py_config_param, \
+ "Type of value provided by a configuration parameter.\n" \
+ "\n" \
+ "The attribute carries a" \
+ " pychrysalide.glibext.ConfigParam.ConfigParamType value." \
+)
+
param = G_CFG_PARAM(pygobject_get(self));
type = g_config_param_get_ptype(param);
- return PyLong_FromLong(type);
+ result = cast_with_constants_group_from_type(get_python_config_param_type(), "ConfigParamType", type);
+
+ return result;
}
@@ -318,13 +462,25 @@ static PyObject *py_config_param_get_type(PyObject *self, void *closure)
static PyObject *py_config_param_get_state(PyObject *self, void *closure)
{
+ PyObject *result; /* Etat à retourner */
GCfgParam *param; /* Paramètre visé par l'opérat°*/
ConfigParamState state; /* Statut de paramètre */
+#define CONFIG_PARAM_STATE_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ state, py_config_param, \
+ "State of a configuration parameter.\n" \
+ "\n" \
+ "The attribute carries a" \
+ " pychrysalide.glibext.ConfigParam.ConfigParamState value." \
+)
+
param = G_CFG_PARAM(pygobject_get(self));
state = g_config_param_get_state(param);
- return PyLong_FromLong(state);
+ result = cast_with_constants_group_from_type(get_python_config_param_type(), "ConfigParamState", state);
+
+ return result;
}
@@ -347,9 +503,16 @@ static PyObject *py_config_param_get_value(PyObject *self, void *closure)
PyObject *result; /* Valeur à retourner */
GCfgParam *param; /* Paramètre visé par l'opérat°*/
ConfigParamType type; /* Type de paramètre manipulé */
- bool boolean; /* Valeur booléenne */
- int integer; /* Valeur entière */
- char *string; /* Chaîne de caractères */
+ param_value value; /* Valeur de paramètre */
+
+#define CONFIG_PARAM_VALUE_ATTRIB PYTHON_GETSET_DEF_FULL \
+( \
+ value, py_config_param, \
+ "Value of a configuration parameter.\n" \
+ "\n" \
+ "The type of the value carried by the attribute depends on" \
+ " pychrysalide.glibext.ConfigParam.type value." \
+)
param = G_CFG_PARAM(pygobject_get(self));
type = g_config_param_get_ptype(param);
@@ -357,20 +520,25 @@ static PyObject *py_config_param_get_value(PyObject *self, void *closure)
switch (type)
{
case CPT_BOOLEAN:
- g_config_param_get_value(param, &boolean);
- result = (boolean ? Py_True : Py_False);
+ g_config_param_get_value(param, &value.boolean);
+ result = (value.boolean ? Py_True : Py_False);
Py_INCREF(result);
break;
case CPT_INTEGER:
- g_config_param_get_value(param, &integer);
- result = PyLong_FromLong(integer);
+ g_config_param_get_value(param, &value.integer);
+ result = PyLong_FromLong(value.integer);
+ break;
+
+ case CPT_ULONG:
+ g_config_param_get_value(param, &value.ulong);
+ result = PyLong_FromUnsignedLong(value.ulong);
break;
case CPT_STRING:
- g_config_param_get_value(param, &string);
- if (string != NULL)
- result = PyUnicode_FromString(string);
+ g_config_param_get_value(param, &value.string);
+ if (value.string != NULL)
+ result = PyUnicode_FromString(value.string);
else
{
result = Py_None;
@@ -378,6 +546,11 @@ static PyObject *py_config_param_get_value(PyObject *self, void *closure)
}
break;
+ case CPT_COLOR:
+ g_config_param_get_value(param, &value.color);
+ result = create_gdk_rgba(&value.color);
+ break;
+
default:
result = NULL;
break;
@@ -408,6 +581,7 @@ static int py_config_param_set_value(PyObject *self, PyObject *value, void *clos
int result; /* Conclusion à remonter */
GCfgParam *param; /* Paramètre visé par l'opérat°*/
ConfigParamType type; /* Type de paramètre manipulé */
+ param_value pvalue; /* Valeur de paramètre */
result = -1;
@@ -428,7 +602,7 @@ static int py_config_param_set_value(PyObject *self, PyObject *value, void *clos
case CPT_BOOLEAN:
if (PyBool_Check(value))
{
- g_config_param_set_value(param, (bool)(value == Py_True));
+ pvalue.boolean = (value == Py_True);
result = 0;
}
break;
@@ -436,7 +610,15 @@ static int py_config_param_set_value(PyObject *self, PyObject *value, void *clos
case CPT_INTEGER:
if (PyLong_Check(value))
{
- g_config_param_set_value(param, (int)PyLong_AsLong(value));
+ pvalue.integer = PyLong_AsLong(value);
+ result = 0;
+ }
+ break;
+
+ case CPT_ULONG:
+ if (PyLong_Check(value))
+ {
+ pvalue.ulong = PyLong_AsUnsignedLong(value);
result = 0;
}
break;
@@ -444,16 +626,25 @@ static int py_config_param_set_value(PyObject *self, PyObject *value, void *clos
case CPT_STRING:
if (PyUnicode_Check(value))
{
- g_config_param_set_value(param, PyUnicode_DATA(value));
+ pvalue.string = PyUnicode_DATA(value);
result = 0;
}
break;
+ case CPT_COLOR:
+ if (convert_to_gdk_rgba(value, &pvalue.color) == 1)
+ result = 0;
+ break;
+
default:
+ assert(false);
break;
}
+ if (result == 0)
+ g_config_param_set_value(param, &pvalue);
+
}
return result;
@@ -476,36 +667,17 @@ static int py_config_param_set_value(PyObject *self, PyObject *value, void *clos
PyTypeObject *get_python_config_param_type(void)
{
static PyMethodDef py_config_param_methods[] = {
- {
- "make_empty", py_config_param_make_empty,
- METH_NOARGS,
- "make_empty($self, /)\n--\n\nUnset the value of the current parameter."
- },
- {
- "reset", py_config_param_reset,
- METH_NOARGS,
- "reset($self, /)\n--\n\nReset the content of the current parameter."
- },
+ CONFIG_PARAM_MAKE_EMPTY_METHOD,
+ CONFIG_PARAM_RESET_METHOD,
{ NULL }
};
static PyGetSetDef py_config_param_getseters[] = {
- {
- "path", py_config_param_get_path, NULL,
- "Show the path used as key for a configuration parameter.", NULL
- },
- {
- "type", py_config_param_get_type, NULL,
- "Show the type of value provided by a configuration parameter.", NULL
- },
- {
- "state", py_config_param_get_state, NULL,
- "Show the state of a configuration parameter.", NULL
- },
- {
- "value", py_config_param_get_value, py_config_param_set_value,
- "Handle the value of a configuration parameter.", NULL
- },
+ CONFIG_PARAM_PATH_ATTRIB,
+ CONFIG_PARAM_TYPE_ATTRIB,
+ CONFIG_PARAM_STATE_ATTRIB,
+ CONFIG_PARAM_VALUE_ATTRIB,
+ CONFIG_PARAM_VALUE_ATTRIB,
{ NULL }
};
@@ -518,10 +690,12 @@ PyTypeObject *get_python_config_param_type(void)
.tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = "PyChrysalide generic configuration",
+ .tp_doc = CONFIG_PARAM_DOC,
.tp_methods = py_config_param_methods,
.tp_getset = py_config_param_getseters,
+
+ .tp_init = py_config_param_init,
.tp_new = py_config_param_new
};
@@ -533,33 +707,37 @@ PyTypeObject *get_python_config_param_type(void)
/******************************************************************************
* *
-* Paramètres : dict = dictionnaire à compléter. *
+* Paramètres : module = module dont la définition est à compléter. *
* *
-* Description : Définit les constantes pour les paramètres. *
+* Description : Prend en charge l'objet 'pychrysalide.glibext.ConfigParam'. *
* *
-* Retour : - *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool py_config_param_define_constants(PyObject *dict)
+bool ensure_python_config_param_is_registered(void)
{
- int ret; /* Bilan d'un ajout */
+ PyTypeObject *type; /* Type Python 'ConfigParam' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
-#define DEF_ULONG_CONST(name) \
- ret = PyDict_SetItemString(dict, #name, PyLong_FromUnsignedLong(name)); \
- if (ret == -1) return false;
+ type = get_python_config_param_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
- DEF_ULONG_CONST(CPT_BOOLEAN);
- DEF_ULONG_CONST(CPT_INTEGER);
- DEF_ULONG_CONST(CPT_STRING);
- DEF_ULONG_CONST(CPT_COUNT);
+ dict = PyModule_GetDict(module);
- DEF_ULONG_CONST(CPS_UNDEFINED);
- DEF_ULONG_CONST(CPS_CHANGED);
- DEF_ULONG_CONST(CPS_DEFAULT);
- DEF_ULONG_CONST(CPS_EMPTY);
+ if (!register_class_for_pygobject(dict, G_TYPE_CFG_PARAM, type, &PyGObject_Type))
+ return false;
+
+ if (!define_config_param_constants(type))
+ return false;
+
+ }
return true;
@@ -568,39 +746,45 @@ static bool py_config_param_define_constants(PyObject *dict)
/******************************************************************************
* *
-* Paramètres : module = module dont la définition est à compléter. *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
* *
-* Description : Prend en charge l'objet 'pychrysalide.glibext.ConfigParam'. *
+* Description : Tente de convertir en paramètre de configuration. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
* *
* Remarques : - *
* *
******************************************************************************/
-bool ensure_python_config_param_is_registered(void)
+int convert_to_config_param(PyObject *arg, void *dst)
{
- PyTypeObject *type; /* Type Python 'ConfigParam' */
- PyObject *module; /* Module à recompléter */
- PyObject *dict; /* Dictionnaire du module */
+ int result; /* Bilan à retourner */
- type = get_python_config_param_type();
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_config_param_type());
- if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ switch (result)
{
- module = get_access_to_python_module("pychrysalide.glibext");
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
- dict = PyModule_GetDict(module);
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to configuration parameter");
+ break;
- if (!register_class_for_pygobject(dict, G_TYPE_CFG_PARAM, type, &PyGObject_Type))
- return false;
+ case 1:
+ *((GCfgParam **)dst) = G_CFG_PARAM(pygobject_get(arg));
+ break;
- if (!py_config_param_define_constants(type->tp_dict))
- return false;
+ default:
+ assert(false);
+ break;
}
- return true;
+ return result;
}
@@ -701,19 +885,22 @@ static PyObject *py_config_param_iterator_next(PyObject *self)
static int py_config_param_iterator_init(PyObject *self, PyObject *args, PyObject *kwds)
{
- pyConfigParamIterator *iterator; /* Références pour le parcours */
- PyObject *config; /* Configuration format Python */
+ GGenConfig *config; /* Configuration format natif */
int ret; /* Bilan de lecture des args. */
+ pyConfigParamIterator *iterator; /* Références pour le parcours */
- ret = PyArg_ParseTuple(args, "O", &config);
- if (!ret) return -1;
+#define CONFIG_PARAM_ITERATOR_DOC \
+ "ConfigParamIterator is an iterator for configuration parameters.\n" \
+ "\n" \
+ "This kind of iterator is provided by the" \
+ " pychrysalide.glibext.GenConfig.params attribute."
- ret = PyObject_IsInstance(config, (PyObject *)get_python_generic_config_type());
+ ret = PyArg_ParseTuple(args, "O&", convert_to_generic_config, &config);
if (!ret) return -1;
iterator = (pyConfigParamIterator *)self;
- iterator->config = G_GEN_CONFIG(pygobject_get(config));
+ iterator->config = config;
g_object_ref(G_OBJECT(iterator->config));
g_generic_config_rlock(iterator->config);
@@ -752,7 +939,7 @@ PyTypeObject *get_python_config_param_iterator_type(void)
.tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = "Iterator for configuration parameters",
+ .tp_doc = CONFIG_PARAM_ITERATOR_DOC,
.tp_iter = PyObject_SelfIter,
.tp_iternext = py_config_param_iterator_next,
@@ -814,38 +1001,163 @@ bool ensure_python_config_param_iterator_is_registered(void)
/******************************************************************************
* *
-* Paramètres : type = type de l'objet à instancier. *
+* Paramètres : type = type du nouvel objet à mettre en place. *
+* args = éventuelle liste d'arguments. *
+* kwds = éventuel dictionnaire de valeurs mises à disposition. *
+* *
+* Description : Accompagne la création d'une instance dérivée en Python. *
+* *
+* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_generic_config_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyObject *result; /* Objet à retourner */
+ PyTypeObject *base; /* Type de base à dériver */
+ bool first_time; /* Evite les multiples passages*/
+ GType gtype; /* Nouveau type de processeur */
+ bool status; /* Bilan d'un enregistrement */
+
+ /* Validations diverses */
+
+ base = get_python_generic_config_type();
+
+ if (type == base)
+ goto simple_way;
+
+ /* Mise en place d'un type dédié */
+
+ first_time = (g_type_from_name(type->tp_name) == 0);
+
+ gtype = build_dynamic_type(G_TYPE_GEN_CONFIG, type->tp_name, NULL, NULL, NULL);
+
+ if (first_time)
+ {
+ status = register_class_for_dynamic_pygobject(gtype, type, base);
+
+ if (!status)
+ {
+ result = NULL;
+ goto exit;
+ }
+
+ }
+
+ /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
+
+ simple_way:
+
+ result = PyType_GenericNew(type, args, kwds);
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
* args = arguments fournis à l'appel. *
* kwds = arguments de type key=val fournis. *
* *
-* Description : Crée un nouvel objet Python de type 'GenConfig'. *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
* *
-* Retour : Instance Python mise en place. *
+* Retour : 0. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_generic_config_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int py_generic_config_init(PyObject *self, PyObject *args, PyObject *kwds)
{
- PyObject *result; /* Instance à retourner */
- const char *name; /* Nom du fichier à charger */
+ const char *name; /* Désignation de configuration*/
int ret; /* Bilan de lecture des args. */
- GGenConfig *config; /* Version GLib du format */
+ GGenConfig *config; /* Configuration en place */
+
+#define GENERIC_CONFIG_DOC \
+ "The GenConfig class defines a generic way to load, provide and store" \
+ " configuration items. Each of these items is handled with a" \
+ " pychrysalide.glibext.ConfigParam object.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " GenConfig(name=None)" \
+ "\n" \
+ "Where *name* is a suitable storage filename for the configuration. If" \
+ " no *name* is defined, the configuration is expected to be" \
+ " memory-only resident."
+
+ /* Récupération des paramètres */
name = NULL;
- ret = PyArg_ParseTuple(args, "|s", &name);
+ ret = PyArg_ParseTuple(args, "s", &name);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ config = G_GEN_CONFIG(pygobject_get(self));
+
+ g_generic_config_build(config, name);
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = configuration à manipuler. *
+* args = paramètres liés à l'appel. *
+* *
+* Description : Met à disposition un encadrement des accès aux paramètres. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_generic_config_lock_unlock(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Instance à retourner */
+ bool write; /* Accès en lecture / écriture */
+ bool lock; /* Pose ou retrait du verrou ? */
+ int ret; /* Bilan de lecture des args. */
+ GGenConfig *config; /* Version GLib de la config. */
+
+#define GENERIC_CONFIG_LOCK_UNLOCK_METHOD PYTHON_METHOD_DEF \
+( \
+ lock_unlock, "$self, write, lock", \
+ METH_VARARGS, py_generic_config, \
+ "Lock or unlock access to the configuration internals.\n" \
+ "\n" \
+ "The *write* argument states if the operation targets read" \
+ " or write accesses, and the *lock* value defines the" \
+ " state to achieve.\n" \
+ "\n" \
+ "Both arguments are boolean values." \
+)
+
+ ret = PyArg_ParseTuple(args, "pp", &write, &lock);
if (!ret) return NULL;
- if (name == NULL)
- config = g_generic_config_new();
- else
- config = g_generic_config_new_from_file(name);
+ config = G_GEN_CONFIG(pygobject_get(self));
+
+ g_generic_config_lock_unlock(config, write, lock);
- g_object_ref_sink(G_OBJECT(config));
- result = pygobject_new(G_OBJECT(config));
- g_object_unref(G_OBJECT(config));
+ result = Py_None;
+ Py_INCREF(result);
return result;
@@ -868,9 +1180,19 @@ static PyObject *py_generic_config_new(PyTypeObject *type, PyObject *args, PyObj
static PyObject *py_generic_config_read(PyObject *self, PyObject *args)
{
PyObject *result; /* Instance à retourner */
- GGenConfig *config; /* Version GLib du format */
+ GGenConfig *config; /* Version GLib de la config. */
bool status; /* Bilan de l'opération */
+#define GENERIC_CONFIG_READ_METHOD PYTHON_METHOD_DEF \
+( \
+ read, "$self, /", \
+ METH_NOARGS, py_generic_config, \
+ "Read the configuration from its relative XML file.\n" \
+ "\n" \
+ "The returned value is True if the operation terminated" \
+ " with success, or False in case of failure." \
+)
+
config = G_GEN_CONFIG(pygobject_get(self));
status = g_generic_config_read(config);
@@ -899,9 +1221,19 @@ static PyObject *py_generic_config_read(PyObject *self, PyObject *args)
static PyObject *py_generic_config_write(PyObject *self, PyObject *args)
{
PyObject *result; /* Instance à retourner */
- GGenConfig *config; /* Version GLib du format */
+ GGenConfig *config; /* Version GLib de la config. */
bool status; /* Bilan de l'opération */
+#define GENERIC_CONFIG_WRITE_METHOD PYTHON_METHOD_DEF \
+( \
+ write, "$self, /", \
+ METH_NOARGS, py_generic_config, \
+ "Write the configuration to its relative XML file.\n" \
+ "\n" \
+ "The returned value is True if the operation terminated" \
+ " with success, or False in case of failure." \
+)
+
config = G_GEN_CONFIG(pygobject_get(self));
status = g_generic_config_write(config);
@@ -930,20 +1262,49 @@ static PyObject *py_generic_config_write(PyObject *self, PyObject *args)
static PyObject *py_generic_config_search(PyObject *self, PyObject *args)
{
PyObject *result; /* Instance à retourner */
- GGenConfig *config; /* Version GLib du format */
+ int lock; /* Ordre de pose de verrou */
const char *path; /* Chemin d'accès du paramètre */
int ret; /* Bilan de lecture des args. */
+ GGenConfig *config; /* Version GLib de la config. */
GCfgParam *param; /* Paramètre trouvé ou NULL */
- config = G_GEN_CONFIG(pygobject_get(self));
-
- ret = PyArg_ParseTuple(args, "s", &path);
+#define GENERIC_CONFIG_SEARCH_METHOD PYTHON_METHOD_DEF \
+( \
+ search, "$self, path, /, lock=True", \
+ METH_VARARGS, py_generic_config, \
+ "Look for a given configuration parameter.\n" \
+ "\n" \
+ "The *path* argument is a string used as key pointing to a parameter." \
+ " The *lock* boolean value is an optional order handling the way" \
+ " configuration parameters are accessed.\n" \
+ "\n" \
+ "The configuration has to be locked while accessing its content. This" \
+ " lock can be managed with the *lock* argument of this function or" \
+ " thanks to the pychrysalide.glibext.GenConfig.lock_unlock method().\n" \
+ "\n" \
+ "The returned value is a pychrysalide.glibext.ConfigParam instance in" \
+ " case of success or None if the parameter is not found." \
+)
+
+ lock = 1;
+
+ ret = PyArg_ParseTuple(args, "s|p", &path, &lock);
if (!ret) return NULL;
- param = g_generic_config_search(config, path);
+ config = G_GEN_CONFIG(pygobject_get(self));
+
+ param = _g_generic_config_search(config, path, lock);
- result = pygobject_new(G_OBJECT(param));
- Py_XINCREF(result);
+ if (param == NULL)
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+ else
+ {
+ result = pygobject_new(G_OBJECT(param));
+ g_object_unref(G_OBJECT(param));
+ }
return result;
@@ -966,28 +1327,50 @@ static PyObject *py_generic_config_search(PyObject *self, PyObject *args)
static PyObject *py_generic_config_add(PyObject *self, PyObject *args)
{
PyObject *result; /* Instance à retourner */
- GGenConfig *config; /* Version GLib du format */
- PyObject *param; /* Paramètre transmis */
+ int lock; /* Ordre de pose de verrou */
+ GCfgParam *param; /* Paramètre GLib transmis */
int ret; /* Bilan de lecture des args. */
+ GGenConfig *config; /* Version GLib de la config. */
GCfgParam *added; /* Elément ajouté ou NULL */
- config = G_GEN_CONFIG(pygobject_get(self));
-
- ret = PyArg_ParseTuple(args, "O", &param);
+#define GENERIC_CONFIG_ADD_METHOD PYTHON_METHOD_DEF \
+( \
+ add, "$self, param, /, lock=True", \
+ METH_VARARGS, py_generic_config, \
+ "Add an existing parameter to a configuration.\n" \
+ "\n" \
+ "The *param* argument has to be a pychrysalide.glibext.ConfigParam" \
+ " instance. The *lock* boolean value is an optional order handling" \
+ " the way configuration parameters are accessed.\n" \
+ "\n" \
+ "The configuration has to be locked while accessing its content. This" \
+ " lock can be managed with the *lock* argument of this function or" \
+ " thanks to the pychrysalide.glibext.GenConfig.lock_unlock method().\n" \
+ "\n" \
+ "The returned value is a pychrysalide.glibext.ConfigParam instance in" \
+ " case of success or None if the parameter already exists in the" \
+ " configuration." \
+)
+
+ lock = 1;
+
+ ret = PyArg_ParseTuple(args, "O&|p", convert_to_config_param, &param, &lock);
if (!ret) return NULL;
- ret = PyObject_IsInstance(param, (PyObject *)get_python_config_param_type());
- if (!ret) Py_RETURN_NONE;
+ config = G_GEN_CONFIG(pygobject_get(self));
- added = g_generic_config_add_param(config, G_CFG_PARAM(pygobject_get(param)));
+ added = _g_generic_config_add_param(config, param, lock);
- if (added != NULL)
+ if (added == NULL)
{
- result = pygobject_new(G_OBJECT(added));
- Py_XINCREF(result);
+ result = Py_None;
+ Py_INCREF(result);
}
else
- result = NULL;
+ {
+ result = pygobject_new(G_OBJECT(added));
+ g_object_unref(G_OBJECT(added));
+ }
return result;
@@ -1009,15 +1392,25 @@ static PyObject *py_generic_config_add(PyObject *self, PyObject *args)
static PyObject *py_generic_config_delete(PyObject *self, PyObject *args)
{
- GGenConfig *config; /* Version GLib du format */
const char *path; /* Chemin d'accès du paramètre */
int ret; /* Bilan de lecture des args. */
-
- config = G_GEN_CONFIG(pygobject_get(self));
+ GGenConfig *config; /* Version GLib de la config. */
+
+#define GENERIC_CONFIG_DELETE_METHOD PYTHON_METHOD_DEF \
+( \
+ delete, "$self, path", \
+ METH_VARARGS, py_generic_config, \
+ "Delete an existing parameter from a configuration.\n" \
+ "\n" \
+ "The *path* argument is a string used as key pointing to the parameter" \
+ " to process." \
+)
ret = PyArg_ParseTuple(args, "s", &path);
if (!ret) return NULL;
+ config = G_GEN_CONFIG(pygobject_get(self));
+
g_generic_config_delete_param(config, path);
Py_RETURN_NONE;
@@ -1027,33 +1420,37 @@ static PyObject *py_generic_config_delete(PyObject *self, PyObject *args)
/******************************************************************************
* *
-* Paramètres : self = configuration à manipuler. *
-* args = non utilisé ici. *
+* Paramètres : self = NULL car méthode statique. *
+* closure = non utilisé ici. *
* *
-* Description : Renvoie la liste des paramètres de configuration. *
+* Description : Indique le fichier utilisé pour l'enregistrement XML. *
* *
-* Retour : Itérateur pour la liste des paramètres. *
+* Retour : Chemin d'accès, potentiellement non existant. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_generic_config_list_params(PyObject *self, PyObject *args)
+static PyObject *py_generic_config_get_filename(PyObject *self, void *closure)
{
- PyObject *result; /* Instance à retourner */
- PyTypeObject *iterator_type; /* Type Python de l'itérateur */
- PyObject *args_list; /* Arguments de mise en place */
-
- iterator_type = get_python_config_param_iterator_type();
+ GGenConfig *config; /* Version GLib de la config. */
+ const char *filename; /* Chemin d'accès au fichier */
- Py_INCREF(self);
+#define GENERIC_CONFIG_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ filename, py_generic_config, \
+ "Path to the file used as storage backend for the" \
+ " configuration.\n" \
+ "\n" \
+ "The result is a string pointing to a file which may not" \
+ " (yet) exist." \
+)
- args_list = Py_BuildValue("(O)", self);
- result = PyObject_CallObject((PyObject *)iterator_type, args_list);
+ config = G_GEN_CONFIG(pygobject_get(self));
- Py_DECREF(args_list);
+ filename = g_generic_config_get_filename(config);
- return result;
+ return PyUnicode_FromString(filename);
}
@@ -1063,24 +1460,39 @@ static PyObject *py_generic_config_list_params(PyObject *self, PyObject *args)
* Paramètres : self = NULL car méthode statique. *
* closure = non utilisé ici. *
* *
-* Description : Fournit le chemin d'accès au binaire représenté. *
+* Description : Renvoie la liste des paramètres de configuration. *
* *
-* Retour : Chemin d'accès en Python. *
+* Retour : Liste d'éléments à parcourir. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_generic_config_get_filename(PyObject *self, void *closure)
+static PyObject *py_generic_config_get_params(PyObject *self, void *closure)
{
- GGenConfig *config; /* Version GLib du format */
- const char *filename; /* Chemin d'accès au fichier */
+ PyObject *result; /* Instance à retourner */
+ PyTypeObject *iterator_type; /* Type Python de l'itérateur */
+ PyObject *args_list; /* Arguments de mise en place */
- config = G_GEN_CONFIG(pygobject_get(self));
+#define GENERIC_CONFIG_PARAMS_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ params, py_generic_config, \
+ "List of all registered configuration parameters.\n" \
+ "\n" \
+ "The result is a pychrysalide.glibext.ConfigParamIterator" \
+ " over pychrysalide.glibext.ConfigParam instances." \
+)
- filename = g_generic_config_get_filename(config);
+ iterator_type = get_python_config_param_iterator_type();
- return PyUnicode_FromString(filename);
+ Py_INCREF(self);
+
+ args_list = Py_BuildValue("(O)", self);
+ result = PyObject_CallObject((PyObject *)iterator_type, args_list);
+
+ Py_DECREF(args_list);
+
+ return result;
}
@@ -1100,44 +1512,18 @@ static PyObject *py_generic_config_get_filename(PyObject *self, void *closure)
PyTypeObject *get_python_generic_config_type(void)
{
static PyMethodDef py_generic_config_methods[] = {
- {
- "read", py_generic_config_read,
- METH_NOARGS,
- "read(, /)\n--\n\nRead the configuration from its relative XML file."
- },
- {
- "write", py_generic_config_write,
- METH_NOARGS,
- "write(, /)\n--\n\nWrite the configuration to its relative XML file."
- },
- {
- "search", py_generic_config_search,
- METH_VARARGS,
- "search($self, path, /)\n--\n\nLook for a given configuration parameter."
- },
- {
- "add", py_generic_config_add,
- METH_VARARGS,
- "add($self, param, /)\n--\n\nAdd an existing parameter to a configuration."
- },
- {
- "delete", py_generic_config_delete,
- METH_VARARGS,
- "delete($self, path, /)\n--\n\nDelete an existing parameter from a configuration."
- },
- {
- "params", py_generic_config_list_params,
- METH_NOARGS,
- "params($self, /)\n--\n\nList all registered configuration parameters."
- },
+ GENERIC_CONFIG_LOCK_UNLOCK_METHOD,
+ GENERIC_CONFIG_READ_METHOD,
+ GENERIC_CONFIG_WRITE_METHOD,
+ GENERIC_CONFIG_SEARCH_METHOD,
+ GENERIC_CONFIG_ADD_METHOD,
+ GENERIC_CONFIG_DELETE_METHOD,
{ NULL }
};
static PyGetSetDef py_generic_config_getseters[] = {
- {
- "filename", py_generic_config_get_filename, NULL,
- "Show the filename of the loaded binary file.", NULL
- },
+ GENERIC_CONFIG_FILENAME_ATTRIB,
+ GENERIC_CONFIG_PARAMS_ATTRIB,
{ NULL }
};
@@ -1150,10 +1536,12 @@ PyTypeObject *get_python_generic_config_type(void)
.tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = "PyChrysalide generic configuration",
+ .tp_doc = GENERIC_CONFIG_DOC,
.tp_methods = py_generic_config_methods,
.tp_getset = py_generic_config_getseters,
+
+ .tp_init = py_generic_config_init,
.tp_new = py_generic_config_new
};
@@ -1197,3 +1585,48 @@ bool ensure_python_generic_config_is_registered(void)
return true;
}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en configuration générique. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_generic_config(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_generic_config_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to generic configuration");
+ break;
+
+ case 1:
+ *((GGenConfig **)dst) = G_GEN_CONFIG(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/configuration.h b/plugins/pychrysalide/glibext/configuration.h
index a4e141a..a82b177 100644
--- a/plugins/pychrysalide/glibext/configuration.h
+++ b/plugins/pychrysalide/glibext/configuration.h
@@ -40,6 +40,9 @@ PyTypeObject *get_python_config_param_type(void);
/* Prend en charge l'objet 'pychrysalide.glibext.ConfigParam'. */
bool ensure_python_config_param_is_registered(void);
+/* Tente de convertir en paramètre de configuration. */
+int convert_to_config_param(PyObject *, void *);
+
/* ----------------------------- PARCOURS DE PARAMETRES ----------------------------- */
@@ -61,6 +64,9 @@ PyTypeObject *get_python_generic_config_type(void);
/* Prend en charge l'objet 'pychrysalide.glibext.GenConfig'. */
bool ensure_python_generic_config_is_registered(void);
+/* Tente de convertir en configuration générique. */
+int convert_to_generic_config(PyObject *, void *);
+
#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONFIGURATION_H */
diff --git a/plugins/pychrysalide/glibext/constants.c b/plugins/pychrysalide/glibext/constants.c
index 3f657e2..373d1bf 100644
--- a/plugins/pychrysalide/glibext/constants.c
+++ b/plugins/pychrysalide/glibext/constants.c
@@ -27,6 +27,7 @@
#include <i18n.h>
#include <glibext/bufferline.h>
+#include <glibext/configuration.h>
#include <glibext/linesegment.h>
#include <glibext/gbinportion.h>
#include <glibext/gloadedpanel.h>
@@ -249,6 +250,121 @@ int convert_to_buffer_line_flags(PyObject *arg, void *dst)
* *
* Paramètres : type = type dont le dictionnaire est à compléter. *
* *
+* Description : Définit les constantes relatives aux paramètres de config. *
+* *
+* Retour : true en cas de succès de l'opération, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool define_config_param_constants(PyTypeObject *type)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *values; /* Groupe de valeurs à établir */
+
+ values = PyDict_New();
+
+ result = add_const_to_group(values, "BOOLEAN", CPT_BOOLEAN);
+ if (result) result = add_const_to_group(values, "INTEGER", CPT_INTEGER);
+ if (result) result = add_const_to_group(values, "ULONG", CPT_ULONG);
+ if (result) result = add_const_to_group(values, "STRING", CPT_STRING);
+ if (result) result = add_const_to_group(values, "COLOR", CPT_COLOR);
+ if (result) result = add_const_to_group(values, "COUNT", CPT_COUNT);
+
+ if (!result)
+ {
+ Py_DECREF(values);
+ goto exit;
+ }
+
+ result = attach_constants_group_to_type(type, false, "ConfigParamType", values,
+ "Kind of value available for configuration parameter types.");
+
+ values = PyDict_New();
+
+ result = add_const_to_group(values, "UNDEFINED", CPS_UNDEFINED);
+ if (result) result = add_const_to_group(values, "CHANGED", CPS_CHANGED);
+ if (result) result = add_const_to_group(values, "DEFAULT", CPS_DEFAULT);
+ if (result) result = add_const_to_group(values, "EMPTY", CPS_EMPTY);
+
+ if (!result)
+ {
+ Py_DECREF(values);
+ goto exit;
+ }
+
+ result = attach_constants_group_to_type(type, true, "ConfigParamState", values,
+ "States of a value carried by a configuration parameter.");
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en constante ConfigParamType. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_config_param_type(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+ unsigned long value; /* Valeur récupérée */
+
+ result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type);
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to ConfigParamType");
+ break;
+
+ case 1:
+
+ value = PyLong_AsUnsignedLong(arg);
+
+ if (value > CPT_COUNT)
+ {
+ result = 0;
+ PyErr_SetString(PyExc_ValueError, _("invalid configuration parameter type"));
+ }
+
+ else
+ *((ConfigParamType *)dst) = value;
+
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : type = type dont le dictionnaire est à compléter. *
+* *
* Description : Définit les constantes relatives aux segments de ligne. *
* *
* Retour : true en cas de succès de l'opération, false sinon. *
diff --git a/plugins/pychrysalide/glibext/constants.h b/plugins/pychrysalide/glibext/constants.h
index f509866..342b7ad 100644
--- a/plugins/pychrysalide/glibext/constants.h
+++ b/plugins/pychrysalide/glibext/constants.h
@@ -43,6 +43,12 @@ bool define_buffer_line_constants(PyTypeObject *);
/* Tente de convertir en constante BufferLineFlags. */
int convert_to_buffer_line_flags(PyObject *, void *);
+/* Définit les constantes relatives aux paramètres de configuration. */
+bool define_config_param_constants(PyTypeObject *);
+
+/* Tente de convertir en constante ConfigParamType. */
+int convert_to_config_param_type(PyObject *, void *);
+
/* Définit les constantes relatives aux segments de ligne. */
bool define_line_segment_constants(PyTypeObject *);
diff --git a/plugins/pychrysalide/gtkext/easygtk.c b/plugins/pychrysalide/gtkext/easygtk.c
index a489cd1..34b917f 100644
--- a/plugins/pychrysalide/gtkext/easygtk.c
+++ b/plugins/pychrysalide/gtkext/easygtk.c
@@ -67,19 +67,16 @@ static PyObject *py_easygtk_get_nth_contained_child(PyObject *, PyObject *);
static PyObject *py_easygtk_get_color_from_style(PyObject *self, PyObject *args)
{
- PyObject *result; /* Désignation à retourner */
+ PyObject *result; /* Coloration à retourner */
const char *class; /* Classe de style GTK */
int background; /* Nature du traitement */
int ret; /* Bilan de lecture des args. */
GdkRGBA color; /* Couleur obtenue */
bool status; /* Bilan de la récupération */
- PyObject *gdk_mod; /* Module Python Gdk */
- PyObject *rgba_type; /* Classe "GtkRGBA" */
- PyObject *rgba_args; /* Arguments pour l'appel */
-#define EASYGTK_GET_COLOR_FROM_STYLE_METHOD PYTHON_METHOD_DEF \
+#define EASYGTK_GET_COLOR_FROM_STYLE_METHOD PYTHON_METHOD_DEF \
( \
- get_color_from_style, "cls, background, /", \
+ get_color_from_style, "cls, background, /", \
METH_VARARGS | METH_STATIC, py_easygtk, \
"Find the index of a given child widget inside a GTK container" \
" children.\n" \
@@ -96,37 +93,10 @@ static PyObject *py_easygtk_get_color_from_style(PyObject *self, PyObject *args)
status = get_color_from_style(class, background, &color);
if (status)
- {
- gdk_mod = PyImport_ImportModule("gi.repository.Gdk");
-
- if (gdk_mod == NULL)
- {
- PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module");
- goto done;
- }
-
- rgba_type = PyObject_GetAttrString(gdk_mod, "RGBA");
-
- Py_DECREF(gdk_mod);
-
- rgba_args = PyTuple_New(4);
- PyTuple_SetItem(rgba_args, 0, PyFloat_FromDouble(color.red));
- PyTuple_SetItem(rgba_args, 1, PyFloat_FromDouble(color.green));
- PyTuple_SetItem(rgba_args, 2, PyFloat_FromDouble(color.blue));
- PyTuple_SetItem(rgba_args, 3, PyFloat_FromDouble(color.alpha));
-
- result = PyObject_CallObject(rgba_type, rgba_args);
-
- Py_DECREF(rgba_args);
-
- }
+ result = create_gdk_rgba(&color);
else
- {
- done:
- result = Py_None;
- Py_INCREF(result);
- }
+ result = NULL;
return result;
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index b2ecbec..92a5db9 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -1299,6 +1299,130 @@ int convert_to_gtk_container(PyObject *arg, void *dst)
}
+/******************************************************************************
+* *
+* Paramètres : color = couleur dans sa définition native à copier. *
+* *
+* Description : Construit un objet Python pour une couleur RGBA. *
+* *
+* Retour : Objet Python prêt à emploi ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyObject *create_gdk_rgba(const GdkRGBA *color)
+{
+ PyObject *result; /* Coloration à retourner */
+ PyObject *gdk_mod; /* Module Python Gdk */
+ PyObject *rgba_type; /* Classe "GtkRGBA" */
+ PyObject *rgba_args; /* Arguments pour l'appel */
+
+ result = NULL;
+
+ gdk_mod = PyImport_ImportModule("gi.repository.Gdk");
+
+ if (gdk_mod == NULL)
+ {
+ PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module");
+ goto done;
+ }
+
+ rgba_type = PyObject_GetAttrString(gdk_mod, "RGBA");
+
+ Py_DECREF(gdk_mod);
+
+ rgba_args = PyTuple_New(4);
+ PyTuple_SetItem(rgba_args, 0, PyFloat_FromDouble(color->red));
+ PyTuple_SetItem(rgba_args, 1, PyFloat_FromDouble(color->green));
+ PyTuple_SetItem(rgba_args, 2, PyFloat_FromDouble(color->blue));
+ PyTuple_SetItem(rgba_args, 3, PyFloat_FromDouble(color->alpha));
+
+ result = PyObject_CallObject(rgba_type, rgba_args);
+
+ Py_DECREF(rgba_args);
+
+ done:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en instance de couleur RGBA. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_gdk_rgba(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+ PyObject *gdk_mod; /* Module Python Gdk */
+ PyObject *rgba_type; /* Module "RGBA" */
+ int ret; /* Bilan d'une conversion */
+ PyObject *value; /* Valeur d'une composante */
+
+ result = 0;
+
+ gdk_mod = PyImport_ImportModule("gi.repository.Gdk");
+
+ if (gdk_mod == NULL)
+ {
+ PyErr_SetString(PyExc_TypeError, "unable to find the Gdk Python module");
+ goto done;
+ }
+
+ rgba_type = PyObject_GetAttrString(gdk_mod, "RGBA");
+
+ Py_DECREF(gdk_mod);
+
+ ret = PyObject_TypeCheck(arg, (PyTypeObject *)rgba_type);
+
+ Py_DECREF(rgba_type);
+
+ if (!ret)
+ {
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GDK RGBA color");
+ goto done;
+ }
+
+ value = PyObject_GetAttrString(arg, "red");
+ assert(PyFloat_Check(value));
+
+ ((GdkRGBA *)dst)->red = PyFloat_AsDouble(value);
+
+ value = PyObject_GetAttrString(arg, "blue");
+ assert(PyFloat_Check(value));
+
+ ((GdkRGBA *)dst)->blue = PyFloat_AsDouble(value);
+
+ value = PyObject_GetAttrString(arg, "green");
+ assert(PyFloat_Check(value));
+
+ ((GdkRGBA *)dst)->green = PyFloat_AsDouble(value);
+
+ value = PyObject_GetAttrString(arg, "alpha");
+ assert(PyFloat_Check(value));
+
+ ((GdkRGBA *)dst)->alpha = PyFloat_AsDouble(value);
+
+ result = 1;
+
+ done:
+
+ return result;
+
+}
+
+
/* ---------------------------------------------------------------------------------- */
/* TRANSFERT DES VALEURS CONSTANTES */
diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h
index 226071c..72a5489 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -29,6 +29,7 @@
#include <assert.h>
#include <glib-object.h>
#include <stdbool.h>
+#include <gdk/gdk.h>
@@ -225,6 +226,12 @@ int convert_to_gtk_widget(PyObject *, void *);
/* Tente de convertir en instance de conteneur GTK. */
int convert_to_gtk_container(PyObject *, void *);
+/* Construit un objet Python pour une couleur RGBA. */
+PyObject *create_gdk_rgba(const GdkRGBA *);
+
+/* Tente de convertir en instance de couleur RGBA. */
+int convert_to_gdk_rgba(PyObject *, void *);
+
/* ----------------------- TRANSFERT DES VALEURS CONSTANTES ------------------------- */
diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am
index df05de3..2ab27f0 100644
--- a/src/glibext/Makefile.am
+++ b/src/glibext/Makefile.am
@@ -9,6 +9,7 @@ libglibext_la_SOURCES = \
bufferline.h bufferline.c \
bufferview.h bufferview.c \
chrysamarshal.h chrysamarshal.c \
+ configuration-int.h \
configuration.h configuration.c \
delayed-int.h \
delayed.h delayed.c \
diff --git a/src/glibext/configuration-int.h b/src/glibext/configuration-int.h
new file mode 100644
index 0000000..2ddeb4f
--- /dev/null
+++ b/src/glibext/configuration-int.h
@@ -0,0 +1,121 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * configuration-int.h - accès interne aux éléments de configuration du programme
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_CONFIGURATION_INT_H
+#define _GLIBEXT_CONFIGURATION_INT_H
+
+
+#include "configuration.h"
+
+
+#include "../common/fnv1a.h"
+
+
+
+/* ---------------------------- ELEMENT DE CONFIGURATION ---------------------------- */
+
+
+/* Valeurs supportées par les paramètres */
+typedef union _param_value
+{
+ bool boolean; /* Valeur booléenne */
+ int integer; /* Valeur entière */
+ unsigned long ulong; /* Valeur entière positive */
+ char *string; /* Chaîne de caractères */
+ GdkRGBA color; /* Couleur avec transparence */
+
+} param_value;
+
+/* Configuration générique quelconque (instance) */
+struct _GCfgParam
+{
+ GObject parent; /* A laisser en premier */
+
+ char *path; /* Chemin d'accès XML */
+ fnv64_t hash; /* Empreinte pour accès rapide */
+
+ ConfigParamType type; /* Type de valeur */
+
+ ConfigParamState cached_state; /* Etat du paramétrage */
+
+ param_value def; /* Valeur par défaut */
+ bool def_empty; /* Non défini par défaut ? */
+ param_value cur; /* Valeur courante */
+ bool cur_empty; /* Actuellement non défini ? */
+
+};
+
+/* Configuration générique quelconque (classe) */
+struct _GCfgParamClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+ /* Signaux */
+
+ void (* modified) (GCfgParam *);
+
+};
+
+
+/* Construit un paramètre de configuration. */
+void g_config_param_build(GCfgParam *, const char *, ConfigParamType, const param_value *);
+
+/* Construit un paramètre de configuration sans valeur. */
+void g_config_param_build_empty(GCfgParam *, const char *, ConfigParamType);
+
+
+
+/* ----------------------- GESTION GENERIQUE DE CONFIGURATION ----------------------- */
+
+
+/* Configuration générique quelconque (instance) */
+struct _GGenConfig
+{
+ GObject parent; /* A laisser en premier */
+
+ char *filename; /* CHemin d'accès complet */
+
+ GList *groups; /* Groupes d'éléments non fixés*/
+ GList *params; /* Eléments de configuration */
+ GRWLock params_access; /* Verrou de protection */
+
+};
+
+/* Configuration générique quelconque (classe) */
+struct _GGenConfigClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+ /* Signaux */
+
+ void (* modified) (GGenConfig *, GCfgParam *);
+
+};
+
+
+/* Crée un gestionnaire configuration générique. */
+void g_generic_config_build(GGenConfig *, const char *);
+
+
+
+#endif /* _GLIBEXT_CONFIGURATION_INT_H */
diff --git a/src/glibext/configuration.c b/src/glibext/configuration.c
index 7fd9a98..1fa2968 100644
--- a/src/glibext/configuration.c
+++ b/src/glibext/configuration.c
@@ -33,9 +33,9 @@
#include <unistd.h>
+#include "configuration-int.h"
#include "../common/cpp.h"
#include "../common/extstr.h"
-#include "../common/fnv1a.h"
#include "../common/io.h"
#include "../common/xdg.h"
#include "../common/xml.h"
@@ -45,48 +45,6 @@
/* ---------------------------- ELEMENT DE CONFIGURATION ---------------------------- */
-/* Valeurs supportées par les paramètres */
-typedef union _param_value
-{
- bool boolean; /* Valeur booléenne */
- int integer; /* Valeur entière */
- unsigned long ulong; /* Valeur entière positive */
- char *string; /* Chaîne de caractères */
- GdkRGBA color; /* Couleur avec transparence */
-
-} param_value;
-
-/* Configuration générique quelconque (instance) */
-struct _GCfgParam
-{
- GObject parent; /* A laisser en premier */
-
- char *path; /* Chemin d'accès XML */
- fnv64_t hash; /* Empreinte pour accès rapide */
-
- ConfigParamType type; /* Type de valeur */
-
- ConfigParamState cached_state; /* Etat du paramétrage */
-
- param_value def; /* Valeur par défaut */
- bool def_empty; /* Non défini par défaut ? */
- param_value cur; /* Valeur courante */
- bool cur_empty; /* Actuellement non défini ? */
-
-};
-
-/* Configuration générique quelconque (classe) */
-struct _GCfgParamClass
-{
- GObjectClass parent; /* A laisser en premier */
-
- /* Signaux */
-
- void (* modified) (GCfgParam *);
-
-};
-
-
/* Initialise la classe des blocs de données binaires. */
static void g_config_param_class_init(GCfgParamClass *);
@@ -148,31 +106,6 @@ static void g_config_group_load(GCfgGroup *, GGenConfig *, xmlXPathContextPtr);
/* ----------------------- GESTION GENERIQUE DE CONFIGURATION ----------------------- */
-/* Configuration générique quelconque (instance) */
-struct _GGenConfig
-{
- GObject parent; /* A laisser en premier */
-
- char *filename; /* CHemin d'accès complet */
-
- GList *groups; /* Groupes d'éléments non fixés*/
- GList *params; /* Eléments de configuration */
- GRWLock params_access; /* Verrou de protection */
-
-};
-
-/* Configuration générique quelconque (classe) */
-struct _GGenConfigClass
-{
- GObjectClass parent; /* A laisser en premier */
-
- /* Signaux */
-
- void (* modified) (GGenConfig *, GCfgParam *);
-
-};
-
-
/* Initialise la classe des blocs de données binaires. */
static void g_generic_config_class_init(GGenConfigClass *);
@@ -321,51 +254,50 @@ GCfgParam *g_config_param_new(const char *path, ConfigParamType type, ...)
{
GCfgParam *result; /* Structure à retourner */
va_list ap; /* Liste d'arguments */
+ param_value value; /* Valeur par défaut */
- result = g_object_new(G_TYPE_CFG_PARAM, NULL);
-
- result->path = strdup(path);
- result->hash = fnv_64a_hash(path);
-
- result->type = type;
+ result = NULL;
va_start(ap, type);
- switch (result->type)
+ switch (type)
{
case CPT_BOOLEAN:
- result->def.boolean = va_arg(ap, /*bool*/int);
+ value.boolean = va_arg(ap, /*bool*/int);
break;
case CPT_INTEGER:
- result->def.integer = va_arg(ap, int);
+ value.integer = va_arg(ap, int);
break;
case CPT_ULONG:
- result->def.ulong = va_arg(ap, unsigned long);
+ value.ulong = va_arg(ap, unsigned long);
break;
case CPT_STRING:
- result->def.string = va_arg(ap, char *);
- if (result->def.string != NULL)
- result->def.string = strdup(result->def.string);
+ value.string = va_arg(ap, char *);
+ if (value.string != NULL)
+ value.string = strdup(value.string);
break;
case CPT_COLOR:
- result->def.color = *va_arg(ap, GdkRGBA *);
+ value.color = *va_arg(ap, GdkRGBA *);
break;
default:
- g_object_unref(G_OBJECT(result));
- result = NULL;
assert(false);
+ goto quick_exit;
break;
}
va_end(ap);
- g_config_param_reset(result);
+ result = g_object_new(G_TYPE_CFG_PARAM, NULL);
+
+ g_config_param_build(result, path, type, &value);
+
+ quick_exit:
return result;
@@ -374,8 +306,37 @@ GCfgParam *g_config_param_new(const char *path, ConfigParamType type, ...)
/******************************************************************************
* *
-* Paramètres : path = chemin d'accès à un paramètre en guise de clef. *
-* type = type de paramètre à installer. *
+* Paramètres : param = paramètre de configuration à construire. *
+* path = chemin d'accès à un paramètre en guise de clef. *
+* type = type de paramètre à installer. *
+* value = valeur par défaut à appliquer. *
+* *
+* Description : Construit un paramètre de configuration. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_config_param_build(GCfgParam *param, const char *path, ConfigParamType type, const param_value *value)
+{
+ param->path = strdup(path);
+ param->hash = fnv_64a_hash(path);
+
+ param->type = type;
+
+ param->def = *value;
+
+ g_config_param_reset(param);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : path = chemin d'accès à un paramètre en guise de clef. *
+* type = type de paramètre à installer. *
* *
* Description : Crée un paramètre de configuration sans valeur. *
* *
@@ -391,17 +352,38 @@ GCfgParam *g_config_param_new_empty(const char *path, ConfigParamType type)
result = g_object_new(G_TYPE_CFG_PARAM, NULL);
- result->path = strdup(path);
- result->hash = fnv_64a_hash(path);
+ g_config_param_build_empty(result, path, type);
- result->type = type;
+ return result;
- g_config_param_make_empty(result);
+}
- result->def = result->cur;
- result->def_empty = true;
- return result;
+/******************************************************************************
+* *
+* Paramètres : param = paramètre de configuration à construire. *
+* path = chemin d'accès à un paramètre en guise de clef. *
+* type = type de paramètre à installer. *
+* *
+* Description : Construit un paramètre de configuration sans valeur. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_config_param_build_empty(GCfgParam *param, const char *path, ConfigParamType type)
+{
+ param->path = strdup(path);
+ param->hash = fnv_64a_hash(path);
+
+ param->type = type;
+
+ g_config_param_make_empty(param);
+
+ param->def = param->cur;
+ param->def_empty = true;
}
@@ -645,7 +627,11 @@ const char *g_config_param_get_path(const GCfgParam *param)
ConfigParamType g_config_param_get_ptype(const GCfgParam *param)
{
- return param->type;
+ ConfigParamType result; /* Type de paramètre à renvoyer*/
+
+ result = param->type;
+
+ return result;
}
@@ -892,6 +878,7 @@ void g_config_param_set_value(GCfgParam *param, ...)
case CPT_STRING:
old_string = param->cur.string;
+
param->cur.string = va_arg(ap, char *);
if (param->cur.string != NULL)
param->cur.string = strdup(param->cur.string);
@@ -1261,7 +1248,8 @@ static void g_generic_config_dispose(GGenConfig *config)
static void g_generic_config_finalize(GGenConfig *config)
{
- free(config->filename);
+ if (config->filename != NULL)
+ free(config->filename);
g_rw_lock_clear(&config->params_access);
@@ -1308,21 +1296,42 @@ GGenConfig *g_generic_config_new(void)
GGenConfig *g_generic_config_new_from_file(const char *name)
{
GGenConfig *result; /* Structure à retourner */
- char *suffix; /* Fin du nom de fichier */
result = g_object_new(G_TYPE_GEN_CONFIG, NULL);
+ g_generic_config_build(result, name);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : config = ensemble de paramètres de configuration à manipuler.*
+* name = désignation de la configuration. *
+* *
+* Description : Crée un gestionnaire configuration générique. *
+* *
+* Retour : Elément mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_generic_config_build(GGenConfig *config, const char *name)
+{
+ char *suffix; /* Fin du nom de fichier */
+
suffix = strdup("chrysalide");
suffix = stradd(suffix, G_DIR_SEPARATOR_S);
suffix = stradd(suffix, name);
suffix = stradd(suffix, ".xml");
- result->filename = get_xdg_config_dir(suffix);
+ config->filename = get_xdg_config_dir(suffix);
free(suffix);
- return result;
-
}
diff --git a/src/glibext/configuration.h b/src/glibext/configuration.h
index 49a289b..afefed1 100644
--- a/src/glibext/configuration.h
+++ b/src/glibext/configuration.h
@@ -52,7 +52,7 @@ typedef enum _ConfigParamType
typedef enum _ConfigParamState
{
CPS_UNDEFINED = (0 << 0), /* Etat non déterminé */
- CPS_CHANGED = (0 << 1), /* Modification utilisateur */
+ CPS_CHANGED = (1 << 0), /* Modification utilisateur */
CPS_DEFAULT = (1 << 1), /* Valeur par défaut */
CPS_EMPTY = (1 << 2) /* Valeur vide */
diff --git a/tests/glibext/configuration.py b/tests/glibext/configuration.py
new file mode 100644
index 0000000..786fc9e
--- /dev/null
+++ b/tests/glibext/configuration.py
@@ -0,0 +1,71 @@
+
+import gi
+gi.require_version('Gdk', '3.0')
+from gi.repository import Gdk
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.glibext import ConfigParam
+
+
+class TestConfiguration(ChrysalideTestCase):
+ """TestCase for configuration related items.*"""
+
+
+ def testCfgParamValues(self):
+ """Set and unset configuration parameter values."""
+
+ color = Gdk.RGBA()
+ color.parse('#3465A4')
+
+ param = ConfigParam('config.color', ConfigParam.ConfigParamType.COLOR, color)
+
+ self.assertEqual(param.value, color)
+
+ param.make_empty()
+
+ void = Gdk.RGBA(red=0, green=0, blue=0, alpha=0)
+ self.assertEqual(param.value, void)
+
+ param.value = color
+
+ self.assertEqual(param.value, color)
+
+
+ def testCfgParamStates(self):
+ """Validate all states of an evolving parameter."""
+
+ param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER)
+
+ self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT)
+
+ param.make_empty()
+
+ self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT)
+
+ param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER, 0x123)
+
+ self.assertEqual(param.value, 0x123)
+
+ self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT)
+
+ param.make_empty()
+
+ self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.CHANGED)
+
+ param.value = 0x1
+
+ self.assertEqual(param.state, ConfigParam.ConfigParamState.CHANGED)
+
+ param.reset()
+
+ self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT)
+
+
+ def testCfgParamDesc(self):
+ """Export types and states as strings when needed."""
+
+ param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER)
+
+ self.assertTrue('|' in str(param.state))
+
+ self.assertTrue('.' in str(param.type))