From 333e68541e376a7b86703fad8e917f71c0f243d0 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sat, 5 Dec 2020 11:28:15 +0100
Subject: Extended the plugin API to include some panel events.

---
 plugins/pychrysalide/constants.c |   3 +
 plugins/pychrysalide/plugin.c    | 139 +++++++++++++++++++++++++++++++++++++++
 src/gui/panel.c                  |   7 ++
 src/plugins/pglist.h             |   8 +++
 src/plugins/plugin-def.h         |  11 ++++
 src/plugins/plugin-int.h         |   8 +++
 src/plugins/plugin.c             |  95 ++++++++++++++++++++++++++
 src/plugins/plugin.h             |   7 ++
 8 files changed, 278 insertions(+)

diff --git a/plugins/pychrysalide/constants.c b/plugins/pychrysalide/constants.c
index e31a8fe..ded25c3 100644
--- a/plugins/pychrysalide/constants.c
+++ b/plugins/pychrysalide/constants.c
@@ -57,7 +57,10 @@ bool define_plugin_module_constants(PyTypeObject *type)
     if (result) result = add_const_to_group(values, "PLUGIN_INIT", PGA_PLUGIN_INIT);
     if (result) result = add_const_to_group(values, "PLUGIN_EXIT", PGA_PLUGIN_EXIT);
     if (result) result = add_const_to_group(values, "NATIVE_LOADED", PGA_NATIVE_LOADED);
+    if (result) result = add_const_to_group(values, "TYPE_BUILDING", PGA_TYPE_BUILDING);
     if (result) result = add_const_to_group(values, "GUI_THEME", PGA_GUI_THEME);
+    if (result) result = add_const_to_group(values, "PANEL_CREATION", PGA_PANEL_CREATION);
+    if (result) result = add_const_to_group(values, "PANEL_DOCKING", PGA_PANEL_DOCKING);
     if (result) result = add_const_to_group(values, "CONTENT_EXPLORER", PGA_CONTENT_EXPLORER);
     if (result) result = add_const_to_group(values, "CONTENT_RESOLVER", PGA_CONTENT_RESOLVER);
     if (result) result = add_const_to_group(values, "CONTENT_ANALYZED", PGA_CONTENT_ANALYZED);
diff --git a/plugins/pychrysalide/plugin.c b/plugins/pychrysalide/plugin.c
index a552aaa..b3bcce5 100644
--- a/plugins/pychrysalide/plugin.c
+++ b/plugins/pychrysalide/plugin.c
@@ -63,6 +63,12 @@ static void py_plugin_module_notify_native_loaded_wrapper(GPluginModule *, Plugi
 /* Complète une liste de resources pour thème. */
 static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, gboolean, char ***, size_t *);
 
+/* Rend compte de la création d'un panneau. */
+static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *, PluginAction, GPanelItem *);
+
+/* Rend compte d'un affichage ou d'un retrait de panneau. */
+static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *, PluginAction, GPanelItem *, bool);
+
 /* Procède à une opération liée à un contenu binaire. */
 static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *);
 
@@ -228,6 +234,8 @@ static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unu
     class->native_loaded = py_plugin_module_notify_native_loaded_wrapper;
 
     class->include_theme = py_plugin_module_include_theme_wrapper;
+    class->notify_panel = py_plugin_module_notify_panel_creation_wrapper;
+    class->notify_docking = py_plugin_module_notify_panel_docking_wrapper;
 
     class->handle_content = py_plugin_module_handle_binary_content_wrapper;
     class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper;
@@ -287,6 +295,8 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
     " have to be defined for new classes:\n"                                \
     "* pychrysalide.PluginModule._notify_native_loaded();\n"                \
     "* pychrysalide.PluginModule._include_theme();\n"                       \
+    "* pychrysalide.PluginModule._on_panel_creation;\n"                     \
+    "* pychrysalide.PluginModule._on_panel_docking();\n"                    \
     "* pychrysalide.PluginModule._handle_binary_content();\n"               \
     "* pychrysalide.PluginModule._handle_loaded_content();\n"               \
     "* pychrysalide.PluginModule._handle_format_analysis();\n"              \
@@ -530,6 +540,133 @@ static void py_plugin_module_include_theme_wrapper(const GPluginModule *plugin,
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                item   = nouveau panneau créé.                               *
+*                                                                             *
+*  Description : Rend compte de la création d'un panneau.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *plugin, PluginAction action, GPanelItem *item)
+{
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *pyret;                        /* Bilan d'exécution           */
+
+#define PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER PYTHON_WRAPPER_DEF      \
+(                                                                       \
+    _on_panel_creation, "$self, action, item, /",                       \
+    METH_VARARGS,                                                       \
+    "Abstract method called when a new instance of panel is created.\n" \
+    "\n"                                                                \
+    "The expected *action* is a pychrysalide.PluginModule.PluginAction" \
+    " value and the *item* is a pychrysalide.gui.PanelItem instance.\n" \
+    "\n"                                                                \
+    "This method has to be defined in order to handle action such as"   \
+    " *PANEL_CREATION*."                                                \
+)
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(plugin));
+
+    if (has_python_method(pyobj, "_on_panel_creation"))
+    {
+        args = PyTuple_New(2);
+
+        PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
+        PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(item)));
+
+        pyret = run_python_method(pyobj, "_on_panel_creation", args);
+
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
+
+    }
+
+    Py_DECREF(pyobj);
+
+    PyGILState_Release(gstate);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                item   = panneau marqué par un changement d'affichage.       *
+*                dock   = indique une accroche et non un décrochage.          *
+*                                                                             *
+*  Description : Rend compte d'un affichage ou d'un retrait de panneau.       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *plugin, PluginAction action, GPanelItem *item, bool dock)
+{
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *pydock;                       /* Valeur booléenne à joindre  */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *pyret;                        /* Bilan d'exécution           */
+
+#define PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER PYTHON_WRAPPER_DEF       \
+(                                                                       \
+    _on_panel_docking, "$self, action, item, dock, /",                  \
+    METH_VARARGS,                                                       \
+    "Abstract method called when a panel is docked or undocked into"    \
+    " the Chrysalide main graphical interface.\n"                       \
+    "\n"                                                                \
+    "The expected *action* is a pychrysalide.PluginModule.PluginAction" \
+    " value, the *item* is a pychrysalide.gui.PanelItem instance and"   \
+    " the *dock* parameter indicates if the panel request a docking"    \
+    " operation or an undocking one.\n"                                 \
+    "\n"                                                                \
+    "This method has to be defined in order to handle action such as"   \
+    " *PANEL_DOCKING*."                                                 \
+)
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(plugin));
+
+    if (has_python_method(pyobj, "_on_panel_docking"))
+    {
+        args = PyTuple_New(3);
+
+        pydock = (dock ? Py_True : Py_False);
+        Py_INCREF(pydock);
+
+        PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
+        PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(item)));
+        PyTuple_SetItem(args, 2, pydock);
+
+        pyret = run_python_method(pyobj, "_on_panel_docking", args);
+
+        Py_XDECREF(pyret);
+        Py_DECREF(args);
+
+    }
+
+    Py_DECREF(pyobj);
+
+    PyGILState_Release(gstate);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : plugin  = greffon à manipuler.                               *
 *                action  = type d'action attendue.                            *
 *                content = contenu binaire à traiter.                         *
@@ -1494,6 +1631,8 @@ PyTypeObject *get_python_plugin_module_type(void)
     static PyMethodDef py_plugin_module_methods[] = {
         PLUGIN_MODULE_NOTIFY_NATIVE_LOADED_WRAPPER,
         PLUGIN_MODULE_INCLUDE_THEME_WRAPPER,
+        PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER,
+        PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER,
         PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER,
         PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER,
         PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER,
diff --git a/src/gui/panel.c b/src/gui/panel.c
index a509f40..976a2d7 100644
--- a/src/gui/panel.c
+++ b/src/gui/panel.c
@@ -38,6 +38,7 @@
 #include "../gtkext/gtkdockable-int.h"
 #include "../gtkext/named.h"
 #include "../plugins/dt.h"
+#include "../plugins/pglist.h"
 
 
 
@@ -601,6 +602,8 @@ GPanelItem *g_panel_item_new(GType type, const char *path)
 
     register_editor_item(G_EDITOR_ITEM(result));
 
+    notify_panel_creation(result);
+
  singleton:
 
     if (path != NULL)
@@ -818,6 +821,8 @@ void g_panel_item_dock(GPanelItem *item)
     if (G_PANEL_ITEM_GET_CLASS(item)->ack_dock != NULL)
         G_PANEL_ITEM_GET_CLASS(item)->ack_dock(item);
 
+    notify_panel_docking(item, true);
+
 }
 
 
@@ -895,6 +900,8 @@ void g_panel_item_undock(GPanelItem *item)
     if (G_PANEL_ITEM_GET_CLASS(item)->ack_undock != NULL)
         G_PANEL_ITEM_GET_CLASS(item)->ack_undock(item);
 
+    notify_panel_docking(item, false);
+
     personality = gtk_panel_item_class_get_personality(G_PANEL_ITEM_GET_CLASS(item));
 
     if (personality != PIP_PERSISTENT_SINGLETON)
diff --git a/src/plugins/pglist.h b/src/plugins/pglist.h
index fd8e30a..ccf854f 100644
--- a/src/plugins/pglist.h
+++ b/src/plugins/pglist.h
@@ -118,6 +118,14 @@ GPluginModule **get_all_plugins_for_action(PluginAction, size_t *);
 #define include_plugin_theme(d, r, c) \
     process_all_plugins_for(PGA_GUI_THEME, g_plugin_module_include_theme, d, r, c)
 
+/* DPS_RUNNING */
+
+#define notify_panel_creation(i) \
+    process_all_plugins_for(PGA_PANEL_CREATION, g_plugin_module_notify_panel_creation, i)
+
+#define notify_panel_docking(i, d) \
+    process_all_plugins_for(PGA_PANEL_DOCKING, g_plugin_module_notify_panel_docking, i, d)
+
 /* DPS_CONTENT */
 
 #define handle_binary_content(a, c, i, s) \
diff --git a/src/plugins/plugin-def.h b/src/plugins/plugin-def.h
index 5a2dc54..e99d7b1 100644
--- a/src/plugins/plugin-def.h
+++ b/src/plugins/plugin-def.h
@@ -79,6 +79,7 @@ typedef uint32_t plugin_action_t;
 /* DPC_GUI */
 
 #define DPS_SETUP               DEFINE_PLUGIN_SUB_CATEGORY(0)
+#define DPS_RUNNING             DEFINE_PLUGIN_SUB_CATEGORY(1)
 
 /* DPC_BINARY_PROCESSING */
 
@@ -133,6 +134,16 @@ typedef enum _PluginAction
     PGA_GUI_THEME = DPC_GUI | DPS_SETUP | DEFINE_PLUGIN_ACTION(0),
 
     /**
+     * DPC_GUI | DPS_RUNNING
+     */
+
+    /* Accrochage / décrochage de panneaux */
+    PGA_PANEL_CREATION = DPC_GUI | DPS_RUNNING | DEFINE_PLUGIN_ACTION(0),
+
+    /* Accrochage / décrochage de panneaux */
+    PGA_PANEL_DOCKING = DPC_GUI | DPS_RUNNING | DEFINE_PLUGIN_ACTION(1),
+
+    /**
      * DPC_BINARY_PROCESSING | DPS_CONTENT
      */
 
diff --git a/src/plugins/plugin-int.h b/src/plugins/plugin-int.h
index 129e155..9dd9173 100644
--- a/src/plugins/plugin-int.h
+++ b/src/plugins/plugin-int.h
@@ -60,6 +60,12 @@ typedef void (* pg_handle_loaded_fc) (const GPluginModule *, PluginAction, GLoad
 /* Complète une liste de resources pour thème. */
 typedef void (* pg_include_theme_fc) (const GPluginModule *, PluginAction, gboolean, char ***, size_t *);
 
+/* Rend compte de la création d'un panneau. */
+typedef void (* pg_notify_panel_fc) (const GPluginModule *, PluginAction, GPanelItem *);
+
+/* Rend compte d'un affichage ou d'un retrait de panneau. */
+typedef void (* pg_notify_docking_fc) (const GPluginModule *, PluginAction, GPanelItem *, bool);
+
 /* Assure l'interprétation d'un format en différé. */
 typedef bool (* pg_handle_format_analysis_fc) (const GPluginModule *, PluginAction, GKnownFormat *, wgroup_id_t, GtkStatusStack *);
 
@@ -107,6 +113,8 @@ struct _GPluginModuleClass
     pg_get_modname_fc get_modname;          /* Fourniture du nom brut      */
 
     pg_include_theme_fc include_theme;      /* Extension d'un thème        */
+    pg_notify_panel_fc notify_panel;        /* Création de panneau         */
+    pg_notify_docking_fc notify_docking;    /* Affichage ou retrait        */
 
     pg_handle_content_fc handle_content;    /* Explorations ou résolutions */
     pg_handle_loaded_fc handle_loaded;      /* Traitement de contenu chargé*/
diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c
index df593ea..8b3654e 100644
--- a/src/plugins/plugin.c
+++ b/src/plugins/plugin.c
@@ -373,6 +373,28 @@ GPluginModule *g_plugin_module_new(const gchar *filename)
 
                         break;
 
+                    case DPS_RUNNING:
+
+                        switch (action)
+                        {
+                            case PGA_PANEL_CREATION:
+                                valid = check_plugin_symbol(module, "chrysalide_plugin_on_panel_creation");
+                                break;
+
+                            case PGA_PANEL_DOCKING:
+                                valid = check_plugin_symbol(module, "chrysalide_plugin_on_panel_docking");
+                                break;
+
+                            default:
+                                log_variadic_message(LMT_WARNING,
+                                                     _("Unknown action '0x%02x' in plugin '%s'..."),
+                                                     interface->actions[i], filename);
+                                break;
+
+                        }
+
+                        break;
+
                     default:
                         log_variadic_message(LMT_WARNING,
                                              _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename);
@@ -640,6 +662,28 @@ static void g_plugin_module_init_gclass(GPluginModuleClass *class, GModule *modu
 
                         break;
 
+                    case DPS_RUNNING:
+
+                        switch (action)
+                        {
+                            case PGA_PANEL_CREATION:
+                                load_plugin_symbol(module, "chrysalide_plugin_on_panel_creation",
+                                                   &class->notify_panel);
+                                break;
+
+                            case PGA_PANEL_DOCKING:
+                                load_plugin_symbol(module, "chrysalide_plugin_on_panel_docking",
+                                                   &class->notify_docking);
+                                break;
+
+                            default:
+                                assert(false);
+                                break;
+
+                        }
+
+                        break;
+
                     default:
                         assert(false);
                         break;
@@ -1270,6 +1314,57 @@ void g_plugin_module_include_theme(const GPluginModule *plugin, PluginAction act
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                item   = nouveau panneau créé.                               *
+*                                                                             *
+*  Description : Rend compte de la création d'un panneau.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_plugin_module_notify_panel_creation(const GPluginModule *plugin, PluginAction action, GPanelItem *item)
+{
+    GPluginModuleClass *class;              /* Classe de l'instance active */
+
+    class = G_PLUGIN_MODULE_GET_CLASS(plugin);
+
+    class->notify_panel(plugin, action, item);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                item   = panneau marqué par un changement d'affichage.       *
+*                dock   = indique une accroche et non un décrochage.          *
+*                                                                             *
+*  Description : Rend compte d'un affichage ou d'un retrait de panneau.       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_plugin_module_notify_panel_docking(const GPluginModule *plugin, PluginAction action, GPanelItem *item, bool dock)
+{
+    GPluginModuleClass *class;              /* Classe de l'instance active */
+
+    class = G_PLUGIN_MODULE_GET_CLASS(plugin);
+
+    class->notify_docking(plugin, action, item, dock);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : plugin  = greffon à manipuler.                               *
 *                action  = type d'action attendue.                            *
 *                content = contenu binaire à traiter.                         *
diff --git a/src/plugins/plugin.h b/src/plugins/plugin.h
index f65d0eb..3e8d9c1 100644
--- a/src/plugins/plugin.h
+++ b/src/plugins/plugin.h
@@ -36,6 +36,7 @@
 #include "../format/known.h"
 #include "../format/preload.h"
 #include "../gtkext/gtkstatusstack.h"
+#include "../gui/panel.h"
 
 
 
@@ -114,6 +115,12 @@ gpointer g_plugin_module_build_type_instance(GPluginModule *, PluginAction, GTyp
 /* Complète une liste de resources pour thème. */
 void g_plugin_module_include_theme(const GPluginModule *, PluginAction, gboolean, char ***, size_t *);
 
+/* Rend compte de la création d'un panneau. */
+void g_plugin_module_notify_panel_creation(const GPluginModule *, PluginAction, GPanelItem *);
+
+/* Rend compte d'un affichage ou d'un retrait de panneau. */
+void g_plugin_module_notify_panel_docking(const GPluginModule *, PluginAction, GPanelItem *, bool);
+
 /* Procède à une opération liée à un contenu binaire. */
 void g_plugin_module_handle_binary_content(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *);
 
-- 
cgit v0.11.2-87-g4458