From a6a88792bc866d8a1d7cabd50a93374da5dd1e7a Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Mon, 28 Dec 2020 22:44:27 +0100 Subject: Improved the API for configuration and its Python documentation. --- plugins/pychrysalide/glibext/configuration.c | 869 ++++++++++++++++++++------- plugins/pychrysalide/glibext/configuration.h | 6 + plugins/pychrysalide/glibext/constants.c | 116 ++++ plugins/pychrysalide/glibext/constants.h | 6 + plugins/pychrysalide/gtkext/easygtk.c | 40 +- plugins/pychrysalide/helpers.c | 124 ++++ plugins/pychrysalide/helpers.h | 7 + src/glibext/Makefile.am | 1 + src/glibext/configuration-int.h | 121 ++++ src/glibext/configuration.c | 209 ++++--- src/glibext/configuration.h | 2 +- tests/glibext/configuration.py | 71 +++ 12 files changed, 1218 insertions(+), 354 deletions(-) create mode 100644 src/glibext/configuration-int.h create mode 100644 tests/glibext/configuration.py 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 -#include +#include +#include +#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", ¶m); +#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, ¶m, &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 #include +#include #include #include #include @@ -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 #include #include +#include @@ -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 . + */ + + +#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 +#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)) -- cgit v0.11.2-87-g4458