From 93dfdc30d815629a7e0c9393f0e8f0462844ff56 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 24 Dec 2020 17:26:51 +0100
Subject: Ported the panel update mechanisms to the Python API.

---
 configure.ac                                |   1 +
 plugins/pychrysalide/gui/Makefile.am        |   5 +-
 plugins/pychrysalide/gui/module.c           |   4 +-
 plugins/pychrysalide/gui/panel.c            |  90 ++++
 plugins/pychrysalide/gui/panels/Makefile.am |  19 +
 plugins/pychrysalide/gui/panels/module.c    | 101 ++++
 plugins/pychrysalide/gui/panels/module.h    |  42 ++
 plugins/pychrysalide/gui/panels/updating.c  | 734 ++++++++++++++++++++++++++++
 plugins/pychrysalide/gui/panels/updating.h  |  45 ++
 plugins/pychrysalide/helpers.c              |  46 ++
 src/gui/panels/bintree.c                    |  23 +-
 src/gui/panels/errors.c                     |  15 +-
 src/gui/panels/strings.c                    |  19 +-
 src/gui/panels/symbols.c                    |  19 +-
 src/gui/panels/symbols.ui                   |  93 ++--
 src/gui/panels/updating-int.h               |   4 +-
 src/gui/panels/updating.c                   |  56 ++-
 src/gui/panels/updating.h                   |   4 +-
 18 files changed, 1217 insertions(+), 103 deletions(-)
 create mode 100644 plugins/pychrysalide/gui/panels/Makefile.am
 create mode 100644 plugins/pychrysalide/gui/panels/module.c
 create mode 100644 plugins/pychrysalide/gui/panels/module.h
 create mode 100644 plugins/pychrysalide/gui/panels/updating.c
 create mode 100644 plugins/pychrysalide/gui/panels/updating.h

diff --git a/configure.ac b/configure.ac
index 3837315..e57fbb6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -488,6 +488,7 @@ AC_CONFIG_FILES([Makefile
                  plugins/pychrysalide/gtkext/graph/Makefile
                  plugins/pychrysalide/gui/Makefile
                  plugins/pychrysalide/gui/core/Makefile
+                 plugins/pychrysalide/gui/panels/Makefile
                  plugins/pychrysalide/mangling/Makefile
                  plugins/python/Makefile
                  plugins/python/abackup/Makefile
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;
 
 }
diff --git a/src/gui/panels/bintree.c b/src/gui/panels/bintree.c
index db5e774..c31bd88 100644
--- a/src/gui/panels/bintree.c
+++ b/src/gui/panels/bintree.c
@@ -190,7 +190,7 @@ struct _bintree_update_data
 static bool is_bintree_column_matching(const bintree_update_data *, GBinPortion *, gint, regmatch_t *);
 
 /* Prépare une opération de mise à jour de panneau. */
-static const char *g_bintree_panel_setup(const GBintreePanel *, unsigned int, size_t *, bintree_update_data **);
+static bool g_bintree_panel_setup(const GBintreePanel *, unsigned int, size_t *, bintree_update_data **, char **);
 
 /* Bascule l'affichage d'un panneau avant mise à jour. */
 static void g_bintree_panel_introduce(const GBintreePanel *, unsigned int, bintree_update_data *);
@@ -202,7 +202,7 @@ static void g_bintree_panel_process(const GBintreePanel *, unsigned int, GtkStat
 static void g_bintree_panel_conclude(GBintreePanel *, unsigned int, bintree_update_data *);
 
 /* Supprime les données dynamiques utilisées à la mise à jour. */
-static void g_bintree_panel_clean_data(GUpdatablePanel *, unsigned int, bintree_update_data *);
+static void g_bintree_panel_clean_data(const GUpdatablePanel *, unsigned int, bintree_update_data *);
 
 
 
@@ -1132,24 +1132,27 @@ static bool is_bintree_column_matching(const bintree_update_data *data, GBinPort
 *                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      : Description du message d'information.                        *
+*  Retour      : Bilan de la préparation.                                     *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static const char *g_bintree_panel_setup(const GBintreePanel *panel, unsigned int uid, size_t *count, bintree_update_data **data)
+static bool g_bintree_panel_setup(const GBintreePanel *panel, unsigned int uid, size_t *count, bintree_update_data **data, char **msg)
 {
-    const char *result;                     /* Message à retourner         */
+    bool result;                            /* Bilan à retourner           */
 #ifndef NDEBUG
     int ret;                                /* Bilan de mise en place      */
 #endif
     GtkBuilder *builder;                    /* Constructeur utilisé        */
     GtkTreeView *treeview;                  /* Arborescence graphique      */
 
+    result = true;
+
     *data = malloc(sizeof(bintree_update_data));
 
     switch (uid)
@@ -1159,7 +1162,7 @@ static const char *g_bintree_panel_setup(const GBintreePanel *panel, unsigned in
             *count = 0;
             (*data)->count = 0;
 
-            result = _("Loading portions contained in the binary format...");
+            *msg = strdup(_("Loading portions contained in the binary format..."));
 
             break;
 
@@ -1168,20 +1171,20 @@ static const char *g_bintree_panel_setup(const GBintreePanel *panel, unsigned in
             *count = panel->count;
             (*data)->count = panel->count;
 
-            result = _("Filtering portions contained in the binary format...");
+            *msg = strdup(_("Filtering portions contained in the binary format..."));
 
             break;
 
         default:    /* Pour GCC... */
             assert(false);
-            result = "";
+            result = false;
             break;
 
     }
 
     if (G_PANEL_ITEM(panel)->filter != NULL)
     {
-        (*data)->filter = (regex_t *)malloc(sizeof(regex_t));
+        (*data)->filter = malloc(sizeof(regex_t));
 
 #ifndef NDEBUG
         ret = regcomp((*data)->filter, G_PANEL_ITEM(panel)->filter, REG_EXTENDED | REG_ICASE);
@@ -1375,7 +1378,7 @@ static void g_bintree_panel_conclude(GBintreePanel *panel, unsigned int uid, bin
 *                                                                             *
 ******************************************************************************/
 
-static void g_bintree_panel_clean_data(GUpdatablePanel *panel, unsigned int uid, bintree_update_data *data)
+static void g_bintree_panel_clean_data(const GUpdatablePanel *panel, unsigned int uid, bintree_update_data *data)
 {
     size_t i;                               /* Boucle de parcours          */
 
diff --git a/src/gui/panels/errors.c b/src/gui/panels/errors.c
index d567511..2b5353f 100644
--- a/src/gui/panels/errors.c
+++ b/src/gui/panels/errors.c
@@ -178,7 +178,7 @@ struct _error_update_data
 
 
 /* Prépare une opération de mise à jour de panneau. */
-static const char *g_error_panel_setup(const GErrorPanel *, unsigned int, size_t *, error_update_data **);
+static bool g_error_panel_setup(const GErrorPanel *, unsigned int, size_t *, error_update_data **, char **);
 
 /* Bascule l'affichage d'un panneau avant mise à jour. */
 static void g_error_panel_introduce(const GErrorPanel *, unsigned int, error_update_data *);
@@ -1046,6 +1046,7 @@ static void on_error_selection_changed(GtkTreeSelection *selection, gpointer unu
 *                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.             *
 *                                                                             *
@@ -1055,14 +1056,16 @@ static void on_error_selection_changed(GtkTreeSelection *selection, gpointer unu
 *                                                                             *
 ******************************************************************************/
 
-static const char *g_error_panel_setup(const GErrorPanel *panel, unsigned int uid, size_t *count, error_update_data **data)
+static bool g_error_panel_setup(const GErrorPanel *panel, unsigned int uid, size_t *count, error_update_data **data, char **msg)
 {
-    const char *result;                     /* Message à retourner         */
+    bool result;                            /* Bilan à retourner           */
     GBinFormat *format;                     /* Format du binaire           */
     size_t fcount;                          /* Quantité d'erreurs #1       */
     GArchProcessor *proc;                   /* Architecture du binaire     */
     size_t pcount;                          /* Quantité d'erreurs #2       */
 
+    result = true;
+
     *data = malloc(sizeof(error_update_data));
 
     switch (uid)
@@ -1087,7 +1090,7 @@ static const char *g_error_panel_setup(const GErrorPanel *panel, unsigned int ui
 
             *count = fcount + pcount;
 
-            result = _("Loading errors occurred during the disassembling process...");
+            *msg = strdup(_("Loading errors occurred during the disassembling process..."));
 
             (*data)->count = *count;
             (*data)->kept = 0;
@@ -1098,7 +1101,7 @@ static const char *g_error_panel_setup(const GErrorPanel *panel, unsigned int ui
 
             *count = panel->count;
 
-            result = _("Filtering errors occurred during the disassembling process...");
+            *msg = strdup(_("Filtering errors occurred during the disassembling process..."));
 
             (*data)->count = panel->count;
             (*data)->kept = 0;
@@ -1107,7 +1110,7 @@ static const char *g_error_panel_setup(const GErrorPanel *panel, unsigned int ui
 
         default:    /* Pour GCC... */
             assert(false);
-            result = "";
+            result = false;
             break;
 
     }
diff --git a/src/gui/panels/strings.c b/src/gui/panels/strings.c
index 33b715b..f9d6b00 100644
--- a/src/gui/panels/strings.c
+++ b/src/gui/panels/strings.c
@@ -211,7 +211,7 @@ static bool is_string_name_matching(const strings_update_data *, GtkTreeModel *,
 static bool is_string_value_matching(const strings_update_data *, GtkTreeModel *, GtkTreeIter *, regmatch_t *);
 
 /* Prépare une opération de mise à jour de panneau. */
-static const char *g_strings_panel_setup(const GStringsPanel *, unsigned int, size_t *, strings_update_data **);
+static bool g_strings_panel_setup(const GStringsPanel *, unsigned int, size_t *, strings_update_data **, char **);
 
 /* Bascule l'affichage d'un panneau avant mise à jour. */
 static void g_strings_panel_introduce(const GStringsPanel *, unsigned int, strings_update_data *);
@@ -223,7 +223,7 @@ static void g_strings_panel_process(const GStringsPanel *, unsigned int, GtkStat
 static void g_strings_panel_conclude(GStringsPanel *, unsigned int, strings_update_data *);
 
 /* Supprime les données dynamiques utilisées à la mise à jour. */
-static void g_strings_panel_clean_data(GUpdatablePanel *, unsigned int, strings_update_data *);
+static void g_strings_panel_clean_data(const GUpdatablePanel *, unsigned int, strings_update_data *);
 
 
 
@@ -1526,6 +1526,7 @@ static bool is_string_value_matching(const strings_update_data *data, GtkTreeMod
 *                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.             *
 *                                                                             *
@@ -1535,14 +1536,16 @@ static bool is_string_value_matching(const strings_update_data *data, GtkTreeMod
 *                                                                             *
 ******************************************************************************/
 
-static const char *g_strings_panel_setup(const GStringsPanel *panel, unsigned int uid, size_t *count, strings_update_data **data)
+static bool g_strings_panel_setup(const GStringsPanel *panel, unsigned int uid, size_t *count, strings_update_data **data, char **msg)
 {
-    const char *result;                     /* Message à retourner         */
+    bool result;                            /* Bilan à retourner           */
     GBinFormat *format;                     /* Format du binaire           */
 #ifndef NDEBUG
     int ret;                                /* Bilan de mise en place      */
 #endif
 
+    result = true;
+
     *data = malloc(sizeof(strings_update_data));
 
     switch (uid)
@@ -1559,7 +1562,7 @@ static const char *g_strings_panel_setup(const GStringsPanel *panel, unsigned in
 
             (*data)->count = 0;
 
-            result = _("Loading strings available in the binary format...");
+            *msg = strdup(_("Loading strings available in the binary format..."));
 
             break;
 
@@ -1568,13 +1571,13 @@ static const char *g_strings_panel_setup(const GStringsPanel *panel, unsigned in
             *count = panel->count;
             (*data)->count = panel->count;
 
-            result = _("Filtering strings available in the binary format...");
+            *msg = strdup(_("Filtering strings available in the binary format..."));
 
             break;
 
         default:    /* Pour GCC... */
             assert(false);
-            result = "";
+            result = false;
             break;
 
     }
@@ -1735,7 +1738,7 @@ static void g_strings_panel_conclude(GStringsPanel *panel, unsigned int uid, str
 *                                                                             *
 ******************************************************************************/
 
-static void g_strings_panel_clean_data(GUpdatablePanel *panel, unsigned int uid, strings_update_data *data)
+static void g_strings_panel_clean_data(const GUpdatablePanel *panel, unsigned int uid, strings_update_data *data)
 {
     if (data->filter != NULL)
     {
diff --git a/src/gui/panels/symbols.c b/src/gui/panels/symbols.c
index 152d705..ab7a414 100644
--- a/src/gui/panels/symbols.c
+++ b/src/gui/panels/symbols.c
@@ -218,7 +218,7 @@ struct _symbols_update_data
 static bool is_symbol_matching(const symbols_update_data *, const GBinSymbol *, regmatch_t *);
 
 /* Prépare une opération de mise à jour de panneau. */
-static const char *g_symbols_panel_setup(const GSymbolsPanel *, unsigned int, size_t *, symbols_update_data **);
+static bool g_symbols_panel_setup(const GSymbolsPanel *, unsigned int, size_t *, symbols_update_data **, char **);
 
 /* Bascule l'affichage d'un panneau avant mise à jour. */
 static void g_symbols_panel_introduce(const GSymbolsPanel *, unsigned int, symbols_update_data *);
@@ -230,7 +230,7 @@ static void g_symbols_panel_process(const GSymbolsPanel *, unsigned int, GtkStat
 static void g_symbols_panel_conclude(GSymbolsPanel *, unsigned int, symbols_update_data *);
 
 /* Supprime les données dynamiques utilisées à la mise à jour. */
-static void g_symbols_panel_clean_data(GUpdatablePanel *, unsigned int, symbols_update_data *);
+static void g_symbols_panel_clean_data(const GUpdatablePanel *, unsigned int, symbols_update_data *);
 
 
 
@@ -1614,6 +1614,7 @@ static bool is_symbol_matching(const symbols_update_data *data, const GBinSymbol
 *                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.             *
 *                                                                             *
@@ -1623,9 +1624,9 @@ static bool is_symbol_matching(const symbols_update_data *data, const GBinSymbol
 *                                                                             *
 ******************************************************************************/
 
-static const char *g_symbols_panel_setup(const GSymbolsPanel *panel, unsigned int uid, size_t *count, symbols_update_data **data)
+static bool g_symbols_panel_setup(const GSymbolsPanel *panel, unsigned int uid, size_t *count, symbols_update_data **data, char **msg)
 {
-    const char *result;                     /* Message à retourner         */
+    bool result;                            /* Bilan à retourner           */
     GBinFormat *format;                     /* Format du binaire           */
 #ifndef NDEBUG
     int ret;                                /* Bilan de mise en place      */
@@ -1633,6 +1634,8 @@ static const char *g_symbols_panel_setup(const GSymbolsPanel *panel, unsigned in
     GtkBuilder *builder;                    /* Constructeur utilisé        */
     GtkTreeView *treeview;                  /* Arborescence graphique      */
 
+    result = true;
+
     *data = malloc(sizeof(symbols_update_data));
 
     switch (uid)
@@ -1649,7 +1652,7 @@ static const char *g_symbols_panel_setup(const GSymbolsPanel *panel, unsigned in
 
             (*data)->count = 0;
 
-            result = _("Loading symbols registered for the binary format...");
+            *msg = strdup(_("Loading symbols registered for the binary format..."));
 
             break;
 
@@ -1658,13 +1661,13 @@ static const char *g_symbols_panel_setup(const GSymbolsPanel *panel, unsigned in
             *count = panel->count;
             (*data)->count = panel->count;
 
-            result = _("Filtering symbols registered for the binary format...");
+            *msg = strdup(_("Filtering symbols registered for the binary format..."));
 
             break;
 
         default:    /* Pour GCC... */
             assert(false);
-            result = "";
+            result = false;
             break;
 
     }
@@ -1873,7 +1876,7 @@ static void g_symbols_panel_conclude(GSymbolsPanel *panel, unsigned int uid, sym
 *                                                                             *
 ******************************************************************************/
 
-static void g_symbols_panel_clean_data(GUpdatablePanel *panel, unsigned int uid, symbols_update_data *data)
+static void g_symbols_panel_clean_data(const GUpdatablePanel *panel, unsigned int uid, symbols_update_data *data)
 {
     size_t i;                               /* Boucle de parcours          */
 
diff --git a/src/gui/panels/symbols.ui b/src/gui/panels/symbols.ui
index 3c34a29..8e54656 100644
--- a/src/gui/panels/symbols.ui
+++ b/src/gui/panels/symbols.ui
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.21.0 -->
+<!-- Generated with glade 3.37.0 -->
 <interface>
   <requires lib="gtk+" version="3.20"/>
   <object class="GtkTreeStore" id="store">
@@ -28,51 +28,51 @@
     <property name="model">store</property>
   </object>
   <object class="GtkTreeModelFilter" id="filter">
-    <property name="child_model">sorter</property>
+    <property name="child-model">sorter</property>
   </object>
   <object class="GtkImage" id="symbol_class_classic.png">
     <property name="visible">True</property>
-    <property name="can_focus">False</property>
+    <property name="can-focus">False</property>
     <property name="resource">/org/chrysalide/gui/panels/../../../pixmaps/symbol_class_classic.png</property>
   </object>
   <object class="GtkImage" id="tbutton_collapse.png">
     <property name="visible">True</property>
-    <property name="can_focus">False</property>
+    <property name="can-focus">False</property>
     <property name="resource">/org/chrysalide/gui/panels/../../../pixmaps/tbutton_collapse.png</property>
   </object>
   <object class="GtkImage" id="tbutton_expand.png">
     <property name="visible">True</property>
-    <property name="can_focus">False</property>
+    <property name="can-focus">False</property>
     <property name="resource">/org/chrysalide/gui/panels/../../../pixmaps/tbutton_expand.png</property>
   </object>
   <object class="GtkImage" id="tbutton_list_view.png">
     <property name="visible">True</property>
-    <property name="can_focus">False</property>
+    <property name="can-focus">False</property>
     <property name="resource">/org/chrysalide/gui/panels/../../../pixmaps/tbutton_list_view.png</property>
   </object>
   <object class="GtkImage" id="tbutton_tree_view.png">
     <property name="visible">True</property>
-    <property name="can_focus">False</property>
+    <property name="can-focus">False</property>
     <property name="resource">/org/chrysalide/gui/panels/../../../pixmaps/tbutton_tree_view.png</property>
   </object>
   <object class="GtkOffscreenWindow">
-    <property name="can_focus">False</property>
+    <property name="can-focus">False</property>
     <child>
       <object class="GtkBox" id="box">
         <property name="visible">True</property>
-        <property name="can_focus">False</property>
+        <property name="can-focus">False</property>
         <property name="orientation">vertical</property>
         <child>
           <object class="GtkToolbar">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
+            <property name="can-focus">False</property>
             <child>
               <object class="GtkRadioToolButton" id="list_display">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="tooltip_text" translatable="yes">Show symbols using a list view</property>
-                <property name="use_underline">True</property>
-                <property name="icon_widget">tbutton_list_view.png</property>
+                <property name="can-focus">False</property>
+                <property name="tooltip-text" translatable="yes">Show symbols using a list view</property>
+                <property name="use-underline">True</property>
+                <property name="icon-widget">tbutton_list_view.png</property>
                 <property name="active">True</property>
                 <property name="group">tree_display</property>
                 <signal name="toggled" handler="on_symbols_list_display_toggle" swapped="no"/>
@@ -85,10 +85,10 @@
             <child>
               <object class="GtkRadioToolButton" id="tree_display">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="tooltip_text" translatable="yes">Show symbols using a tree view</property>
-                <property name="use_underline">True</property>
-                <property name="icon_widget">tbutton_tree_view.png</property>
+                <property name="can-focus">False</property>
+                <property name="tooltip-text" translatable="yes">Show symbols using a tree view</property>
+                <property name="use-underline">True</property>
+                <property name="icon-widget">tbutton_tree_view.png</property>
                 <signal name="toggled" handler="on_symbols_tree_display_toggle" swapped="no"/>
               </object>
               <packing>
@@ -99,7 +99,7 @@
             <child>
               <object class="GtkSeparatorToolItem">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
+                <property name="can-focus">False</property>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -109,10 +109,10 @@
             <child>
               <object class="GtkToolButton" id="collapse">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="tooltip_text" translatable="yes">Collapse all symbol nodes in the tree view</property>
-                <property name="use_underline">True</property>
-                <property name="icon_widget">tbutton_collapse.png</property>
+                <property name="can-focus">False</property>
+                <property name="tooltip-text" translatable="yes">Collapse all symbol nodes in the tree view</property>
+                <property name="use-underline">True</property>
+                <property name="icon-widget">tbutton_collapse.png</property>
                 <signal name="clicked" handler="reorganize_symbols_tree_view" swapped="no"/>
               </object>
               <packing>
@@ -123,10 +123,10 @@
             <child>
               <object class="GtkToolButton" id="expand">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="tooltip_text" translatable="yes">Expand all symbol nodes in the tree view</property>
-                <property name="use_underline">True</property>
-                <property name="icon_widget">tbutton_expand.png</property>
+                <property name="can-focus">False</property>
+                <property name="tooltip-text" translatable="yes">Expand all symbol nodes in the tree view</property>
+                <property name="use-underline">True</property>
+                <property name="icon-widget">tbutton_expand.png</property>
                 <signal name="clicked" handler="reorganize_symbols_tree_view" swapped="no"/>
               </object>
               <packing>
@@ -137,10 +137,10 @@
             <child>
               <object class="GtkToolButton" id="classes">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="tooltip_text" translatable="yes">Show all classes in the tree view</property>
-                <property name="use_underline">True</property>
-                <property name="icon_widget">symbol_class_classic.png</property>
+                <property name="can-focus">False</property>
+                <property name="tooltip-text" translatable="yes">Show all classes in the tree view</property>
+                <property name="use-underline">True</property>
+                <property name="icon-widget">symbol_class_classic.png</property>
                 <signal name="clicked" handler="reorganize_symbols_tree_view" swapped="no"/>
               </object>
               <packing>
@@ -151,7 +151,7 @@
             <child>
               <object class="GtkSeparatorToolItem">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
+                <property name="can-focus">False</property>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -161,15 +161,15 @@
             <child>
               <object class="GtkToolItem">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
+                <property name="can-focus">False</property>
                 <child>
                   <object class="GtkSearchEntry">
                     <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="tooltip_text" translatable="yes">Filter symbols using POSIX extended regular expressions</property>
-                    <property name="primary_icon_name">edit-find-symbolic</property>
-                    <property name="primary_icon_activatable">False</property>
-                    <property name="primary_icon_sensitive">False</property>
+                    <property name="can-focus">True</property>
+                    <property name="tooltip-text" translatable="yes">Filter symbols using POSIX extended regular expressions</property>
+                    <property name="primary-icon-name">edit-find-symbolic</property>
+                    <property name="primary-icon-activatable">False</property>
+                    <property name="primary-icon-sensitive">False</property>
                     <signal name="focus-in-event" handler="track_focus_change_in_text_area" swapped="no"/>
                     <signal name="focus-out-event" handler="track_focus_change_in_text_area" swapped="no"/>
                     <signal name="search-changed" handler="on_symbols_filter_changed" swapped="no"/>
@@ -191,20 +191,20 @@
         <child>
           <object class="GtkStack" id="stack">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
+            <property name="can-focus">False</property>
             <child>
               <object class="GtkScrolledWindow" id="content">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="shadow_type">in</property>
+                <property name="can-focus">True</property>
+                <property name="shadow-type">in</property>
                 <child>
                   <object class="GtkTreeView" id="treeview">
                     <property name="name">treeview</property>
                     <property name="visible">True</property>
-                    <property name="can_focus">True</property>
+                    <property name="can-focus">True</property>
                     <property name="model">filter</property>
-                    <property name="headers_visible">False</property>
-                    <property name="search_column">2</property>
+                    <property name="headers-visible">False</property>
+                    <property name="search-column">2</property>
                     <child internal-child="selection">
                       <object class="GtkTreeSelection">
                         <signal name="changed" handler="on_symbols_selection_change" swapped="no"/>
@@ -221,7 +221,7 @@
             <child>
               <object class="GtkSpinner" id="mask">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
+                <property name="can-focus">False</property>
                 <property name="active">True</property>
               </object>
               <packing>
@@ -239,8 +239,5 @@
         </child>
       </object>
     </child>
-    <child type="titlebar">
-      <placeholder/>
-    </child>
   </object>
 </interface>
diff --git a/src/gui/panels/updating-int.h b/src/gui/panels/updating-int.h
index f8d0760..1b742e8 100644
--- a/src/gui/panels/updating-int.h
+++ b/src/gui/panels/updating-int.h
@@ -30,7 +30,7 @@
 
 
 /* Prépare une opération de mise à jour de panneau. */
-typedef const char * (* setup_updatable_cb) (const GUpdatablePanel *, unsigned int, size_t *, void **);
+typedef bool (* setup_updatable_cb) (const GUpdatablePanel *, unsigned int, size_t *, void **, char **);
 
 /* Obtient le groupe de travail dédié à une mise à jour. */
 typedef wgroup_id_t (* get_updatable_group_cb) (const GUpdatablePanel *);
@@ -45,7 +45,7 @@ typedef void (* process_updatable_cb) (const GUpdatablePanel *, unsigned int, Gt
 typedef void (* conclude_updatable_cb) (GUpdatablePanel *, unsigned int, void *);
 
 /* Supprime les données dynamiques utilisées à la mise à jour. */
-typedef void (* clean_updatable_data_cb) (GUpdatablePanel *, unsigned int, void *);
+typedef void (* clean_updatable_data_cb) (const GUpdatablePanel *, unsigned int, void *);
 
 
 /* Mécanisme de mise à jour d'un panneau (interface) */
diff --git a/src/gui/panels/updating.c b/src/gui/panels/updating.c
index 31f42c3..c5d59e7 100644
--- a/src/gui/panels/updating.c
+++ b/src/gui/panels/updating.c
@@ -55,7 +55,7 @@ struct _GPanelUpdate
 
     size_t max;                             /* Marge de progression finale */
     void *data;                             /* Données utiles au traitement*/
-    const char *msg;                        /* Description d'activité      */
+    char *msg;                              /* Description d'activité      */
 
 };
 
@@ -120,24 +120,30 @@ static void g_updatable_panel_default_init(GUpdatablePanelInterface *iface)
 *                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      : Description du message d'information.                        *
+*  Retour      : Bilan de la préparation.                                     *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-const char *g_updatable_panel_setup(const GUpdatablePanel *panel, unsigned int uid, size_t *count, void **data)
+bool g_updatable_panel_setup(const GUpdatablePanel *panel, unsigned int uid, size_t *count, void **data, char **msg)
 {
+    bool result;                            /* Bilan à retourner           */
     GUpdatablePanelIface *iface;            /* Interface utilisée          */
 
     iface = G_UPDATABLE_PANEL_GET_IFACE(panel);
 
+    *count = 0;
     *data = NULL;
+    *msg = NULL;
 
-    return iface->setup(panel, uid, count, data);
+    result = iface->setup(panel, uid, count, data, msg);
+
+    return result;
 
 }
 
@@ -148,7 +154,7 @@ const char *g_updatable_panel_setup(const GUpdatablePanel *panel, unsigned int u
 *                                                                             *
 *  Description : Obtient le groupe de travail dédié à une mise à jour.        *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : Identifiant de groupe de travail.                            *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
@@ -259,7 +265,7 @@ void g_updatable_panel_conclude(GUpdatablePanel *panel, unsigned int uid, void *
 *                                                                             *
 ******************************************************************************/
 
-void g_updatable_panel_clean_data(GUpdatablePanel *panel, unsigned int uid, void *data)
+void g_updatable_panel_clean_data(const GUpdatablePanel *panel, unsigned int uid, void *data)
 {
     GUpdatablePanelIface *iface;            /* Interface utilisée          */
 
@@ -324,6 +330,12 @@ static void g_panel_update_class_init(GPanelUpdateClass *klass)
 
 static void g_panel_update_init(GPanelUpdate *update)
 {
+    update->panel = NULL;
+    update->uid = -1;
+
+    update->max = 0;
+    update->data = NULL;
+    update->msg = NULL;
 
 }
 
@@ -342,7 +354,10 @@ static void g_panel_update_init(GPanelUpdate *update)
 
 static void g_panel_update_dispose(GPanelUpdate *update)
 {
-    g_object_unref(G_OBJECT(update->panel));
+    if (update->panel != NULL)
+        g_updatable_panel_clean_data(update->panel, update->uid, update->data);
+
+    g_clear_object(&update->panel);
 
     G_OBJECT_CLASS(g_panel_update_parent_class)->dispose(G_OBJECT(update));
 
@@ -363,11 +378,12 @@ static void g_panel_update_dispose(GPanelUpdate *update)
 
 static void g_panel_update_finalize(GPanelUpdate *update)
 {
-    g_updatable_panel_clean_data(update->panel, update->uid, update->data);
-
     if (update->data != NULL)
         free(update->data);
 
+    if (update->msg != NULL)
+        free(update->msg);
+
     G_OBJECT_CLASS(g_panel_update_parent_class)->finalize(G_OBJECT(update));
 
 }
@@ -389,6 +405,7 @@ static void g_panel_update_finalize(GPanelUpdate *update)
 GPanelUpdate *g_panel_update_new(GUpdatablePanel *panel, unsigned int uid)
 {
     GPanelUpdate *result;                   /* Tâche à retourner           */
+    bool status;                            /* Bilan de la préparation     */
 
     result = g_object_new(G_TYPE_PANEL_UPDATE, NULL);
 
@@ -397,7 +414,10 @@ GPanelUpdate *g_panel_update_new(GUpdatablePanel *panel, unsigned int uid)
 
     result->uid = uid;
 
-    result->msg = g_updatable_panel_setup(panel, uid, &result->max, &result->data);
+    status = g_updatable_panel_setup(panel, uid, &result->max, &result->data, &result->msg);
+
+    if (!status)
+        g_clear_object(&result);
 
     return result;
 
@@ -477,15 +497,19 @@ void run_panel_update(GUpdatablePanel *panel, unsigned int uid)
 
     update = g_panel_update_new(panel, uid);
 
-    g_signal_connect_to_main(update, "work-completed", G_CALLBACK(conclude_panel_update), panel,
-                             g_cclosure_marshal_VOID__VOID);
+    if (update != NULL)
+    {
+        g_signal_connect_to_main(update, "work-completed", G_CALLBACK(conclude_panel_update), panel,
+                                 g_cclosure_marshal_VOID__VOID);
+
+        g_updatable_panel_introduce(panel, uid, update->data);
 
-    g_updatable_panel_introduce(panel, uid, update->data);
+        queue = get_work_queue();
 
-    queue = get_work_queue();
+        gid = g_updatable_panel_get_group(panel);
 
-    gid = g_updatable_panel_get_group(panel);
+        g_work_queue_schedule_work(queue, G_DELAYED_WORK(update), gid);
 
-    g_work_queue_schedule_work(queue, G_DELAYED_WORK(update), gid);
+    }
 
 }
diff --git a/src/gui/panels/updating.h b/src/gui/panels/updating.h
index 8c44795..2293a02 100644
--- a/src/gui/panels/updating.h
+++ b/src/gui/panels/updating.h
@@ -56,7 +56,7 @@ typedef struct _GUpdatablePanelIface GUpdatablePanelIface;
 GType g_updatable_panel_get_type(void) G_GNUC_CONST;
 
 /* Prépare une opération de mise à jour de panneau. */
-const char *g_updatable_panel_setup(const GUpdatablePanel *, unsigned int, size_t *, void **);
+bool g_updatable_panel_setup(const GUpdatablePanel *, unsigned int, size_t *, void **, char **);
 
 /* Obtient le groupe de travail dédié à une mise à jour. */
 wgroup_id_t g_updatable_panel_get_group(const GUpdatablePanel *);
@@ -71,7 +71,7 @@ void g_updatable_panel_process(const GUpdatablePanel *, unsigned int, GtkStatusS
 void g_updatable_panel_conclude(GUpdatablePanel *, unsigned int, void *);
 
 /* Supprime les données dynamiques utilisées à la mise à jour. */
-void g_updatable_panel_clean_data(GUpdatablePanel *, unsigned int, void *);
+void g_updatable_panel_clean_data(const GUpdatablePanel *, unsigned int, void *);
 
 
 
-- 
cgit v0.11.2-87-g4458