diff options
Diffstat (limited to 'plugins/pychrysalide')
-rw-r--r-- | plugins/pychrysalide/gui/Makefile.am | 5 | ||||
-rw-r--r-- | plugins/pychrysalide/gui/module.c | 4 | ||||
-rw-r--r-- | plugins/pychrysalide/gui/panel.c | 90 | ||||
-rw-r--r-- | plugins/pychrysalide/gui/panels/Makefile.am | 19 | ||||
-rw-r--r-- | plugins/pychrysalide/gui/panels/module.c | 101 | ||||
-rw-r--r-- | plugins/pychrysalide/gui/panels/module.h | 42 | ||||
-rw-r--r-- | plugins/pychrysalide/gui/panels/updating.c | 734 | ||||
-rw-r--r-- | plugins/pychrysalide/gui/panels/updating.h | 45 | ||||
-rw-r--r-- | plugins/pychrysalide/helpers.c | 46 |
9 files changed, 1083 insertions, 3 deletions
diff --git a/plugins/pychrysalide/gui/Makefile.am b/plugins/pychrysalide/gui/Makefile.am index ca6bee5..cd8f05d 100644 --- a/plugins/pychrysalide/gui/Makefile.am +++ b/plugins/pychrysalide/gui/Makefile.am @@ -9,7 +9,8 @@ libpychrysagui_la_SOURCES = \ panel.h panel.c libpychrysagui_la_LIBADD = \ - core/libpychrysaguicore.la + core/libpychrysaguicore.la \ + panels/libpychrysaguipanels.la libpychrysagui_la_LDFLAGS = @@ -24,4 +25,4 @@ AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJE AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) -SUBDIRS = core +SUBDIRS = core panels diff --git a/plugins/pychrysalide/gui/module.c b/plugins/pychrysalide/gui/module.c index 1ff7b1b..365cab6 100644 --- a/plugins/pychrysalide/gui/module.c +++ b/plugins/pychrysalide/gui/module.c @@ -28,11 +28,11 @@ #include <assert.h> - #include "item.h" #include "menubar.h" #include "panel.h" #include "core/module.h" +#include "panels/module.h" #include "../helpers.h" @@ -74,6 +74,7 @@ bool add_gui_module(PyObject *super) result = (module != NULL); if (result) result = add_gui_core_module(module); + if (result) result = add_gui_panels_module(module); if (!result) Py_XDECREF(module); @@ -106,6 +107,7 @@ bool populate_gui_module(void) if (result) result = ensure_python_panel_item_is_registered(); if (result) result = populate_gui_core_module(); + if (result) result = populate_gui_panels_module(); assert(result); diff --git a/plugins/pychrysalide/gui/panel.c b/plugins/pychrysalide/gui/panel.c index 160a7a3..2afb2a1 100644 --- a/plugins/pychrysalide/gui/panel.c +++ b/plugins/pychrysalide/gui/panel.c @@ -82,6 +82,12 @@ static PyObject *py_panel_item_dock(PyObject *, PyObject *); /* Supprime un panneau de l'ensemble affiché. */ static PyObject *py_panel_item_undock(PyObject *, PyObject *); +/* Bascule l'affichage d'un panneau après sa mise à jour. */ +static PyObject *py_panel_item_switch_to_updated_content(PyObject *, PyObject *); + +/* Bascule l'affichage d'un panneau avant sa mise à jour. */ +static PyObject *py_panel_item_switch_to_updating_mask(PyObject *, PyObject *); + /* Fournit une indication sur la personnalité du panneau. */ static PyObject *py_panel_item_get_personality(PyObject *, void *); @@ -670,6 +676,88 @@ static PyObject *py_panel_item_undock(PyObject *self, PyObject *args) /****************************************************************************** * * +* Paramètres : self = panneau ciblé par une mise à jour. * +* args = arguments fournis à l'appel. * +* * +* Description : Bascule l'affichage d'un panneau avant sa mise à jour. * +* * +* Retour : Py_None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_panel_item_switch_to_updating_mask(PyObject *self, PyObject *args) +{ + GPanelItem *item; /* Panneau à manipuler */ + +#define PANEL_ITEM_SWITCH_TO_UPDATING_MASK_METHOD PYTHON_METHOD_DEF \ +( \ + switch_to_updating_mask, "$self, /", \ + METH_NOARGS, py_panel_item, \ + "Switch the panel content display before its update." \ + "\n" \ + "The *Gtk.Builder* helper linked to the panel has to define the" \ + " following widgets:\n" \ + "* 'stack': a *Gtk.Stack* instance containing the other widget;\n" \ + "* 'content': the main *Gtk.Widget* used to show the main panel" \ + " content;\n" \ + "* 'mask': a widget displayed during computing, like a" \ + " *Gtk.Spinner* instance." \ +) + + item = G_PANEL_ITEM(pygobject_get(self)); + + g_panel_item_switch_to_updating_mask(item); + + Py_RETURN_NONE; + +} + + +/****************************************************************************** +* * +* Paramètres : self = panneau ciblé par une mise à jour. * +* args = arguments fournis à l'appel. * +* * +* Description : Bascule l'affichage d'un panneau après sa mise à jour. * +* * +* Retour : Py_None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_panel_item_switch_to_updated_content(PyObject *self, PyObject *args) +{ + GPanelItem *item; /* Panneau à manipuler */ + +#define PANEL_ITEM_SWITCH_TO_UPDATED_CONTENT_METHOD PYTHON_METHOD_DEF \ +( \ + switch_to_updated_content, "$self, /", \ + METH_NOARGS, py_panel_item, \ + "Switch the panel content display after its update." \ + "\n" \ + "The *Gtk.Builder* helper linked to the panel has to define the" \ + " following widgets:\n" \ + "* 'stack': a *Gtk.Stack* instance containing the other widget;\n" \ + "* 'content': the main *Gtk.Widget* used to show the main panel" \ + " content;\n" \ + "* 'mask': a widget displayed during computing, like a" \ + " *Gtk.Spinner* instance." \ +) + + item = G_PANEL_ITEM(pygobject_get(self)); + + g_panel_item_switch_to_updated_content(item); + + Py_RETURN_NONE; + +} + + +/****************************************************************************** +* * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * @@ -1023,6 +1111,8 @@ PyTypeObject *get_python_panel_item_type(void) static PyMethodDef py_panel_item_methods[] = { PANEL_ITEM_DOCK_METHOD, PANEL_ITEM_UNDOCK_METHOD, + PANEL_ITEM_SWITCH_TO_UPDATING_MASK_METHOD, + PANEL_ITEM_SWITCH_TO_UPDATED_CONTENT_METHOD, { NULL } }; diff --git a/plugins/pychrysalide/gui/panels/Makefile.am b/plugins/pychrysalide/gui/panels/Makefile.am new file mode 100644 index 0000000..a6e3635 --- /dev/null +++ b/plugins/pychrysalide/gui/panels/Makefile.am @@ -0,0 +1,19 @@ + +noinst_LTLIBRARIES = libpychrysaguipanels.la + +libpychrysaguipanels_la_SOURCES = \ + module.h module.c \ + updating.h updating.c + +libpychrysaguipanels_la_LDFLAGS = + + +devdir = $(includedir)/chrysalide-$(subdir) + +dev_HEADERS = $(libpychrysaguipanels_la_SOURCES:%c=) + + +AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + -I$(top_srcdir)/src + +AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) diff --git a/plugins/pychrysalide/gui/panels/module.c b/plugins/pychrysalide/gui/panels/module.c new file mode 100644 index 0000000..f904188 --- /dev/null +++ b/plugins/pychrysalide/gui/panels/module.c @@ -0,0 +1,101 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire panels en tant que module + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module.h" + + +#include <assert.h> + + +#include "updating.h" +#include "../../helpers.h" + + + +/****************************************************************************** +* * +* Paramètres : super = module dont la définition est à compléter. * +* * +* Description : Ajoute le module 'gui.panels' à un module Python. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool add_gui_panels_module(PyObject *super) +{ + bool result; /* Bilan à retourner */ + PyObject *module; /* Sous-module mis en place */ + +#define PYCHRYSALIDE_GUI_PANELS_DOC \ + "This module defines all the core panels and their helper class" \ + " pychrysalide.gui.panels.UpdatablePanel as well." + + static PyModuleDef py_chrysalide_gui_panels_module = { + + .m_base = PyModuleDef_HEAD_INIT, + + .m_name = "pychrysalide.gui.panels", + .m_doc = PYCHRYSALIDE_GUI_PANELS_DOC, + + .m_size = -1, + + }; + + module = build_python_module(super, &py_chrysalide_gui_panels_module); + + result = (module != NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'gui.panels'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_gui_panels_module(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_updatable_panel_is_registered(); + + assert(result); + + return result; + +} diff --git a/plugins/pychrysalide/gui/panels/module.h b/plugins/pychrysalide/gui/panels/module.h new file mode 100644 index 0000000..e926107 --- /dev/null +++ b/plugins/pychrysalide/gui/panels/module.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire panels en tant que module + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GUI_PANELS_MODULE_H +#define _PLUGINS_PYCHRYSALIDE_GUI_PANELS_MODULE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Ajoute le module 'gui.panels' à un module Python. */ +bool add_gui_panels_module(PyObject *); + +/* Intègre les objets du module 'gui.panels'. */ +bool populate_gui_panels_module(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GUI_PANELS_MODULE_H */ diff --git a/plugins/pychrysalide/gui/panels/updating.c b/plugins/pychrysalide/gui/panels/updating.c new file mode 100644 index 0000000..5898575 --- /dev/null +++ b/plugins/pychrysalide/gui/panels/updating.c @@ -0,0 +1,734 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * updating.c - équivalent Python du fichier "gui/panels/updating.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "updating.h" + + +#include <pygobject.h> + + +#include <core/queue.h> +#include <gui/panels/updating-int.h> + + +#include "../../access.h" +#include "../../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface de génération. */ +static void py_updatable_panel_interface_init(GUpdatablePanelIface *, gpointer *); + +/* Prépare une opération de mise à jour de panneau. */ +static bool py_updatable_panel_setup_wrapper(const GUpdatablePanel *, unsigned int, size_t *, void **, char **); + +/* Obtient le groupe de travail dédié à une mise à jour. */ +static wgroup_id_t py_updatable_panel_get_group_wrapper(const GUpdatablePanel *); + +/* Bascule l'affichage d'un panneau avant mise à jour. */ +static void py_updatable_panel_introduce_wrapper(const GUpdatablePanel *, unsigned int, void *); + +/* Réalise une opération de mise à jour de panneau. */ +static void py_updatable_panel_process_wrapper(const GUpdatablePanel *, unsigned int, GtkStatusStack *, activity_id_t, void *); + +/* Bascule l'affichage d'un panneau après mise à jour. */ +static void py_updatable_panel_conclude_wrapper(GUpdatablePanel *, unsigned int, void *); + +/* Supprime les données dynamiques utilisées à la mise à jour. */ +static void py_updatable_panel_clean_data_wrapper(const GUpdatablePanel *, unsigned int, void *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Prépare et lance l'actualisation d'un panneau. */ +static PyObject *py_updatable_panel_run_update(PyObject *, PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface de génération. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_updatable_panel_interface_init(GUpdatablePanelIface *iface, gpointer *unused) +{ + +#define UPDATABLE_PANEL_DOC \ + "UpdatablePanel defines an interface as helper for panels updates." \ + " Panels contents can thus get hidden then restored easily once data" \ + " is fully processed.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, UpdatablePanel):\n" \ + " ...\n" \ + "\n" \ + "The following methods have to be defined for new implementations:\n" \ + "* pychrysalide.gui.panels.UpdatablePanel._setup();\n" \ + "* pychrysalide.gui.panels.UpdatablePanel._introduce();\n" \ + "* pychrysalide.gui.panels.UpdatablePanel._process();\n" \ + "* pychrysalide.gui.panels.UpdatablePanel._conclude();\n" \ + "* pychrysalide.gui.panels.UpdatablePanel._clean_data().\n" \ + "\n" \ + "The following attribute has to be defined for new implementations:\n" \ + "* pychrysalide.gui.panels.UpdatablePanel._working_group_id.\n" \ + + iface->setup = py_updatable_panel_setup_wrapper; + iface->get_group = py_updatable_panel_get_group_wrapper; + iface->introduce = py_updatable_panel_introduce_wrapper; + iface->process = py_updatable_panel_process_wrapper; + iface->conclude = py_updatable_panel_conclude_wrapper; + iface->clean = py_updatable_panel_clean_data_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau ciblé par une mise à jour. * +* uid = identifiant de la phase de traitement. * +* count = nombre d'étapes à prévoir dans le traitement. [OUT] * +* data = données sur lesquelles s'appuyer ensuite. [OUT] * +* msg = description du message d'information. [OUT] * +* * +* Description : Prépare une opération de mise à jour de panneau. * +* * +* Retour : Bilan de la préparation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_updatable_panel_setup_wrapper(const GUpdatablePanel *panel, unsigned int uid, size_t *count, void **data, char **msg) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + PyObject *item; /* Elément obtenu */ + +#define UPDATABLE_PANEL_SETUP_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _setup, "$self, uid, /", \ + METH_VARARGS, \ + "Abstract method used to prepare an update process for a panel.\n" \ + "\n" \ + "The *uid* identifier is an arbitrary number identifying the update" \ + " process.\n" \ + "\n" \ + "The expected result is a tuple containing three items:\n" \ + "* the number of items to be processed, in order to synchronize with" \ + " the progress shown in the status bar;\n" \ + "* an optional object used to store final result (or None);\n" \ + "* a text message to display as the name of the update operation." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(panel)); + + if (has_python_method(pyobj, "_setup")) + { + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(uid)); + + pyret = run_python_method(pyobj, "_setup", args); + + if (!PyTuple_Check(pyret) || PyTuple_Size(pyret) != 3) + { + PyErr_SetString(PyExc_ValueError, "the provided quantity has to be a tuple with three items"); + goto exit; + } + + item = PyTuple_GetItem(pyret, 0); + if (!PyLong_Check(item)) goto exit; + + *count = PyLong_AsUnsignedLongLong(item); + + item = PyTuple_GetItem(pyret, 1); + + Py_INCREF(item); + *data = item; + + item = PyTuple_GetItem(pyret, 2); + if (!PyUnicode_Check(item)) + { + Py_DECREF(item); + *data = NULL; + goto exit; + } + + *msg = strdup(PyUnicode_AsUTF8(item)); + + result = true; + + exit: + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau ciblé par une mise à jour. * +* * +* Description : Obtient le groupe de travail dédié à une mise à jour. * +* * +* Retour : Identifiant de groupe de travail. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static wgroup_id_t py_updatable_panel_get_group_wrapper(const GUpdatablePanel *panel) +{ + wgroup_id_t result; /* Identifiant à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyattr; /* Attribut de l'objet Python */ + int ret; /* Bilan d'une conversion */ + +#define UPDATABLE_PANEL_WORKING_GROUP_ID_ATTRIB_WRAPPER PYTHON_GETTER_WRAPPER_DEF \ +( \ + _working_group_id, \ + "Identifier of a dedicated working group processing panel update jobs.\n" \ + "\n" \ + "The result has to be an integer." \ +) + + result = DEFAULT_WORK_GROUP; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(panel)); + + if (PyObject_HasAttrString(pyobj, "_working_group_id")) + { + pyattr = PyObject_GetAttrString(pyobj, "_working_group_id"); + + if (pyattr != NULL) + { + ret = PyLong_Check(pyattr); + + if (ret) + result = PyLong_AsUnsignedLongLong(pyattr); + + Py_DECREF(pyattr); + + } + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau ciblé par une mise à jour. * +* uid = identifiant de la phase de traitement. * +* data = données préparées par l'appelant. * +* * +* Description : Bascule l'affichage d'un panneau avant mise à jour. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_updatable_panel_introduce_wrapper(const GUpdatablePanel *panel, unsigned int uid, void *data) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pydata; /* Données au format Python */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define UPDATABLE_PANEL_INTRODUCE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _introduce, "$self, uid, data, /", \ + METH_VARARGS, \ + "Abstract method used to introduce the update process; display switch" \ + " is here an option.\n" \ + "\n" \ + "The *uid* identifier is the same identifier provided for a previous" \ + " call to pychrysalide.gui.panels.UpdatablePanel._setup(), and *data*" \ + " is an optional object instance." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(panel)); + pydata = (PyObject *)data; + + if (has_python_method(pyobj, "_introduce")) + { + Py_INCREF(pydata); + + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(uid)); + PyTuple_SetItem(args, 1, pydata); + + pyret = run_python_method(pyobj, "_introduce", args); + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau ciblé par une mise à jour. * +* uid = identifiant de la phase de traitement. * +* status = barre de statut à tenir informée. * +* id = identifiant pour le suivi de la progression. * +* data = données préparées par l'appelant. * +* * +* Description : Réalise une opération de mise à jour de panneau. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_updatable_panel_process_wrapper(const GUpdatablePanel *panel, unsigned int uid, GtkStatusStack *status, activity_id_t id, void *data) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pydata; /* Données au format Python */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define UPDATABLE_PANEL_PROCESS_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _process, "$self, uid, status, id, data, /", \ + METH_VARARGS, \ + "Abstract method used to perform the computing of data to render.\n" \ + "\n" \ + "The *uid* identifier is the same identifier provided for a previous" \ + " call to pychrysalide.gui.panels.UpdatablePanel._setup(), *status* is" \ + " a pychrysalide.gtkext.StatusStack instance, *id* refers to the" \ + " identifier for message display inside the status bar and *data* is" \ + " an optional object instance.\n" \ + "\n" \ + "The method is called from a dedicated processing thread." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(panel)); + pydata = (PyObject *)data; + + if (has_python_method(pyobj, "_process")) + { + Py_INCREF(pydata); + + args = PyTuple_New(4); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(uid)); + PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(status))); + PyTuple_SetItem(args, 2, PyLong_FromUnsignedLong(id)); + PyTuple_SetItem(args, 3, pydata); + + pyret = run_python_method(pyobj, "_process", args); + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau ciblé par une mise à jour. * +* uid = identifiant de la phase de traitement. * +* data = données préparées par l'appelant. * +* * +* Description : Bascule l'affichage d'un panneau après mise à jour. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_updatable_panel_conclude_wrapper(GUpdatablePanel *panel, unsigned int uid, void *data) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pydata; /* Données au format Python */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define UPDATABLE_PANEL_CONCLUDE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _conclude, "$self, uid, data, /", \ + METH_VARARGS, \ + "Abstract method used to conclude the update process and to display" \ + " the computed data.\n" \ + "\n" \ + "The *uid* identifier is the same identifier provided for a previous" \ + " call to pychrysalide.gui.panels.UpdatablePanel._setup(), and *data*" \ + " is an optional object instance." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(panel)); + pydata = (PyObject *)data; + + if (has_python_method(pyobj, "_conclude")) + { + Py_INCREF(pydata); + + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(uid)); + PyTuple_SetItem(args, 1, pydata); + + pyret = run_python_method(pyobj, "_conclude", args); + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : panel = panneau ciblé par une mise à jour. * +* uid = identifiant de la phase de traitement. * +* data = données en place à nettoyer avant suppression. * +* * +* Description : Supprime les données dynamiques utilisées à la mise à jour. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_updatable_panel_clean_data_wrapper(const GUpdatablePanel *panel, unsigned int uid, void *data) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pydata; /* Données au format Python */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define UPDATABLE_PANEL_CLEAN_DATA_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _clean_data, "$self, uid, data, /", \ + METH_VARARGS, \ + "Abstract method used to delete dynamically generated objects for the" \ + " panel update.\n" \ + "\n" \ + "The *uid* identifier is the same identifier provided for a previous" \ + " call to pychrysalide.gui.panels.UpdatablePanel._setup(), and *data*" \ + " is an optional object instance.\n" \ + "\n" \ + "As the user *data* reference counter is decreased automatically after" \ + " this wrapper is called (if existing), there should be no need to" \ + " define such a wrapper, except if the panel needs some kind of" \ + " notification at the end of the update or if it still owns a reference"\ + " to this *data*." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(panel)); + pydata = (PyObject *)data; + + if (has_python_method(pyobj, "_clean_data")) + { + Py_INCREF(pydata); + + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(uid)); + PyTuple_SetItem(args, 1, pydata); + + pyret = run_python_method(pyobj, "_clean_data", args); + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pydata); + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = NULL car méthode statique. * +* args = paramètres à transmettre à l'appel natif. * +* * +* Description : Prépare et lance l'actualisation d'un panneau. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_updatable_panel_run_update(PyObject *self, PyObject *args) +{ + PyObject *result; /* Désignation à retourner */ + unsigned int uid; /* Identifiant de mise à jour */ + int ret; /* Bilan de lecture des args. */ + GUpdatablePanel *panel; /* Instance à manipuler */ + +#define UPDATABLE_PANEL_RUN_UPDATE_METHOD PYTHON_METHOD_DEF \ +( \ + run_update, "self, uid, /", \ + METH_VARARGS, py_updatable_panel, \ + "Prepare and run an update for the panel.\n" \ + "\n" \ + "The *uid* argument is an arbitrary integer provided" \ + " as internal identifier for the caller." \ +) + + ret = PyArg_ParseTuple(args, "I", &uid); + if (!ret) return NULL; + + panel = G_UPDATABLE_PANEL(pygobject_get(self)); + + run_panel_update(panel, uid); + + result = Py_None; + Py_INCREF(result); + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_updatable_panel_type(void) +{ + static PyMethodDef py_updatable_panel_methods[] = { + UPDATABLE_PANEL_SETUP_WRAPPER, + UPDATABLE_PANEL_INTRODUCE_WRAPPER, + UPDATABLE_PANEL_PROCESS_WRAPPER, + UPDATABLE_PANEL_CONCLUDE_WRAPPER, + UPDATABLE_PANEL_CLEAN_DATA_WRAPPER, + UPDATABLE_PANEL_RUN_UPDATE_METHOD, + { NULL } + }; + + static PyGetSetDef py_updatable_panel_getseters[] = { + UPDATABLE_PANEL_WORKING_GROUP_ID_ATTRIB_WRAPPER, + { NULL } + }; + + static PyTypeObject py_updatable_panel_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.gui.panels.UpdatablePanel", + .tp_basicsize = sizeof(PyObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = UPDATABLE_PANEL_DOC, + + .tp_methods = py_updatable_panel_methods, + .tp_getset = py_updatable_panel_getseters, + + }; + + return &py_updatable_panel_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.gui....UpdatablePanel'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_updatable_panel_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'LineGenerator' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_updatable_panel_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_updatable_panel_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.gui.panels"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_UPDATABLE_PANEL, type, &info)) + return false; + + } + + 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 mécanisme de mise à jour de panneau. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_updatable_panel(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_updatable_panel_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 updatable panel"); + break; + + case 1: + *((GUpdatablePanel **)dst) = G_UPDATABLE_PANEL(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/gui/panels/updating.h b/plugins/pychrysalide/gui/panels/updating.h new file mode 100644 index 0000000..a9e5799 --- /dev/null +++ b/plugins/pychrysalide/gui/panels/updating.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * updating.h - prototypes pour l'équivalent Python du fichier "gui/panels/updating.h" + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GUI_PANELS_UPDATING_H +#define _PLUGINS_PYCHRYSALIDE_GUI_PANELS_UPDATING_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_updatable_panel_type(void); + +/* Prend en charge l'objet 'pychrysalide.gui.panels.UpdatablePanel'. */ +bool ensure_python_updatable_panel_is_registered(void); + +/* Tente de convertir en générateur de lignes. */ +int convert_to_updatable_panel(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GUI_PANELS_UPDATING_H */ diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c index f811ad3..b2ecbec 100644 --- a/plugins/pychrysalide/helpers.c +++ b/plugins/pychrysalide/helpers.c @@ -977,6 +977,11 @@ bool register_class_for_dynamic_pygobject(GType gtype, PyTypeObject *type, PyTyp PyObject *modname; /* Nom du module du type */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire dudit module */ + Py_ssize_t i; /* Boucle de parcours */ + PyObject *mro_base; /* Base finale effective */ + GType itype; /* Type d'interface implémentée*/ + GQuark pyginterface_info_key; /* Clef d'accès non déclarée */ + const GInterfaceInfo *iinfo; /* Informations associées */ /** * Lors de l'appel à pygobject_register_class(), PyGObject remplace systématiquement @@ -1023,6 +1028,47 @@ bool register_class_for_dynamic_pygobject(GType gtype, PyTypeObject *type, PyTyp Py_TYPE(type) = legacy_parent; + /** + * Comme la mise en place dynamique de nouveau GType court-circuite les + * mécanismes internes de pygobject, les interfaces implémentées ne sont + * nominalement plus complétées. + * + * On reprend donc la logique copiée depuis le contenu de la fonction + * pyg_type_add_interfaces() du fichier "pygobject-3.22.0/gi/gobjectmodule.c". + */ + + for (i = 0; i < PyTuple_GET_SIZE(type->tp_mro); i++) + { + mro_base = PyTuple_GET_ITEM(type->tp_mro, i); + + if (!PyType_Check(mro_base)) + continue; + + itype = pyg_type_from_object(mro_base); + + if (itype == G_TYPE_INTERFACE) + continue; + + if (!G_TYPE_IS_INTERFACE(itype)) + continue; + + if (!g_type_is_a(gtype, itype)) + { + /** + * Reproduction de pyg_lookup_interface_info(). + */ + + pyginterface_info_key = g_quark_from_static_string("PyGInterface::info"); + + iinfo = g_type_get_qdata(itype, pyginterface_info_key); + assert(iinfo != NULL); + + g_type_add_interface_static(gtype, itype, iinfo); + + } + + } + return result; } |