From 27c21356d494824850005932f3dee5f38d7a8e82 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Mon, 4 Mar 2019 21:52:50 +0100
Subject: Provided access to the graph layout from Python.

---
 configure.ac                                  |   1 +
 plugins/pychrysalide/analysis/binary.c        |  45 ++
 plugins/pychrysalide/analysis/binary.h        |   3 +
 plugins/pychrysalide/analysis/block.c         | 140 +++++
 plugins/pychrysalide/analysis/block.h         |   6 +
 plugins/pychrysalide/gtkext/Makefile.am       |   6 +
 plugins/pychrysalide/gtkext/graph/Makefile.am |  20 +
 plugins/pychrysalide/gtkext/graph/cluster.c   | 718 ++++++++++++++++++++++++++
 plugins/pychrysalide/gtkext/graph/cluster.h   |  45 ++
 plugins/pychrysalide/gtkext/graph/edge.c      | 353 +++++++++++++
 plugins/pychrysalide/gtkext/graph/edge.h      |  45 ++
 plugins/pychrysalide/gtkext/graph/module.c    |  99 ++++
 plugins/pychrysalide/gtkext/graph/module.h    |  42 ++
 plugins/pychrysalide/gtkext/module.c          |   8 +
 plugins/pychrysalide/helpers.c                |  30 ++
 plugins/pychrysalide/helpers.h                |  23 +
 src/analysis/block.c                          |  18 +-
 src/analysis/block.h                          |   4 +-
 src/analysis/disass/dragon.c                  |   2 -
 src/gtkext/graph/cluster.c                    | 364 ++++++++++++-
 src/gtkext/graph/cluster.h                    |  20 +-
 src/gtkext/graph/edge.c                       |  49 ++
 src/gtkext/graph/edge.h                       |   8 +-
 23 files changed, 2009 insertions(+), 40 deletions(-)
 create mode 100644 plugins/pychrysalide/gtkext/graph/Makefile.am
 create mode 100644 plugins/pychrysalide/gtkext/graph/cluster.c
 create mode 100644 plugins/pychrysalide/gtkext/graph/cluster.h
 create mode 100644 plugins/pychrysalide/gtkext/graph/edge.c
 create mode 100644 plugins/pychrysalide/gtkext/graph/edge.h
 create mode 100644 plugins/pychrysalide/gtkext/graph/module.c
 create mode 100644 plugins/pychrysalide/gtkext/graph/module.h

diff --git a/configure.ac b/configure.ac
index 11a6a85..1f50fd0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -422,6 +422,7 @@ AC_CONFIG_FILES([Makefile
                  plugins/pychrysalide/format/Makefile
                  plugins/pychrysalide/glibext/Makefile
                  plugins/pychrysalide/gtkext/Makefile
+                 plugins/pychrysalide/gtkext/graph/Makefile
                  plugins/pychrysalide/gui/Makefile
                  plugins/pychrysalide/gui/core/Makefile
                  plugins/pychrysalide/gui/panels/Makefile
diff --git a/plugins/pychrysalide/analysis/binary.c b/plugins/pychrysalide/analysis/binary.c
index f73684b..bc57ab4 100644
--- a/plugins/pychrysalide/analysis/binary.c
+++ b/plugins/pychrysalide/analysis/binary.c
@@ -330,3 +330,48 @@ bool ensure_python_loaded_binary_is_registered(void)
     return true;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en ensemble de binaire chargé.            *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_loaded_binary(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_loaded_binary_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 loaded binary");
+            break;
+
+        case 1:
+            *((GLoadedBinary **)dst) = G_LOADED_BINARY(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/analysis/binary.h b/plugins/pychrysalide/analysis/binary.h
index 235e018..d4ab311 100644
--- a/plugins/pychrysalide/analysis/binary.h
+++ b/plugins/pychrysalide/analysis/binary.h
@@ -37,6 +37,9 @@ PyTypeObject *get_python_loaded_binary_type(void);
 /* Prend en charge l'objet 'pychrysalide.analysis.LoadedBinary'. */
 bool ensure_python_loaded_binary_is_registered(void);
 
+/* Tente de convertir en ensemble de binaire chargé. */
+int convert_to_loaded_binary(PyObject *, void *);
+
 
 
 #endif  /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_BINARY_H */
diff --git a/plugins/pychrysalide/analysis/block.c b/plugins/pychrysalide/analysis/block.c
index ca4ac09..e282d14 100644
--- a/plugins/pychrysalide/analysis/block.c
+++ b/plugins/pychrysalide/analysis/block.c
@@ -332,6 +332,51 @@ bool ensure_python_code_block_is_registered(void)
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  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 bloc de code.                          *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_code_block(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_code_block_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 code block");
+            break;
+
+        case 1:
+            *((GCodeBlock **)dst) = G_CODE_BLOCK(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                           REGROUPEMENT EN LISTE DE BLOCS                           */
@@ -542,3 +587,98 @@ bool ensure_python_block_list_is_registered(void)
     return true;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en liste de blocs de code.                *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_block_list_with_ref(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+    GBlockList *new;                        /* Nouvelle liste à constituer */
+    PyObject *item;                         /* Elément issu de l'itération */
+    int ret;                                /* Bilan d'une conversion      */
+    GCodeBlock *block;                      /* Bloc de code à intégrer     */
+
+    if (arg == NULL)
+    {
+        g_clear_object((void **)dst);
+        result = 1;
+    }
+
+    else
+    {
+        result = PyObject_IsInstance(arg, (PyObject *)get_python_block_list_type());
+
+        switch (result)
+        {
+            case -1:
+                /* L'exception est déjà fixée par Python */
+                result = 0;
+                break;
+
+            case 0:
+
+                if (PyIter_Check(arg))
+                {
+                    new = g_block_list_new(0);
+
+                    result = Py_CLEANUP_SUPPORTED;
+
+                    for (item = PyIter_Next(arg); item != NULL; item = PyIter_Next(arg))
+                    {
+                        ret = convert_to_code_block(item, &block);
+
+                        if (ret == 1)
+                            g_object_ref(G_OBJECT(block));
+
+                        Py_DECREF(item);
+
+                        if (ret != 1)
+                        {
+                            result = 0;
+                            break;
+                        }
+
+                        g_block_list_append_block(new, block);
+
+                    }
+
+                    if (result != Py_CLEANUP_SUPPORTED)
+                        g_object_unref(G_OBJECT(new));
+
+                    else
+                        *((GBlockList **)dst) = new;
+
+                }
+
+                else
+                    PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to graph cluster");
+
+                break;
+
+            case 1:
+                *((GBlockList **)dst) = G_BLOCK_LIST(pygobject_get(arg));
+                break;
+
+            default:
+                assert(false);
+                break;
+
+        }
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/analysis/block.h b/plugins/pychrysalide/analysis/block.h
index 8bae464..438157c 100644
--- a/plugins/pychrysalide/analysis/block.h
+++ b/plugins/pychrysalide/analysis/block.h
@@ -40,6 +40,9 @@ PyTypeObject *get_python_code_block_type(void);
 /* Prend en charge l'objet 'pychrysalide.analysis.CodeBlock'. */
 bool ensure_python_code_block_is_registered(void);
 
+/* Tente de convertir en bloc de code. */
+int convert_to_code_block(PyObject *, void *);
+
 
 
 /* ------------------------- REGROUPEMENT EN LISTE DE BLOCS ------------------------- */
@@ -51,6 +54,9 @@ PyTypeObject *get_python_block_list_type(void);
 /* Prend en charge l'objet 'pychrysalide.analysis.BlockList'. */
 bool ensure_python_block_list_is_registered(void);
 
+/* Tente de convertir en liste de blocs de code. */
+int convert_to_block_list_with_ref(PyObject *, void *);
+
 
 
 #endif  /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_BLOCK_H */
diff --git a/plugins/pychrysalide/gtkext/Makefile.am b/plugins/pychrysalide/gtkext/Makefile.am
index e881aa4..5895f24 100644
--- a/plugins/pychrysalide/gtkext/Makefile.am
+++ b/plugins/pychrysalide/gtkext/Makefile.am
@@ -8,6 +8,9 @@ libpychrysagtkext_la_SOURCES =			\
 	dockable.h dockable.c				\
 	module.h module.c
 
+libpychrysagtkext_la_LIBADD =			\
+	graph/libpychrysagtkextgraph.la
+
 libpychrysagtkext_la_LDFLAGS = 
 
 
@@ -20,3 +23,6 @@ AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJE
 	-I$(top_srcdir)/src
 
 AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS)
+
+
+SUBDIRS = graph
diff --git a/plugins/pychrysalide/gtkext/graph/Makefile.am b/plugins/pychrysalide/gtkext/graph/Makefile.am
new file mode 100644
index 0000000..c30c07c
--- /dev/null
+++ b/plugins/pychrysalide/gtkext/graph/Makefile.am
@@ -0,0 +1,20 @@
+
+noinst_LTLIBRARIES = libpychrysagtkextgraph.la
+
+libpychrysagtkextgraph_la_SOURCES =		\
+	cluster.h cluster.c					\
+	edge.h edge.c						\
+	module.h module.c
+
+libpychrysagtkextgraph_la_LDFLAGS = 
+
+
+devdir = $(includedir)/chrysalide-$(subdir)
+
+dev_HEADERS = $(libpychrysagtkextgraph_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/gtkext/graph/cluster.c b/plugins/pychrysalide/gtkext/graph/cluster.c
new file mode 100644
index 0000000..1c664a0
--- /dev/null
+++ b/plugins/pychrysalide/gtkext/graph/cluster.c
@@ -0,0 +1,718 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * cluster.c - équivalent Python du fichier "glibext/gtkext/graph/cluster.c"
+ *
+ * Copyright (C) 2019 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 "cluster.h"
+
+
+#include <malloc.h>
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <gtkext/graph/cluster.h>
+#include <plugins/dt.h>
+
+
+#include "../../access.h"
+#include "../../helpers.h"
+#include "../../struct.h"
+#include "../../analysis/binary.h"
+#include "../../analysis/block.h"
+
+
+
+/* Recherche le groupe de blocs avec un bloc donné comme chef. */
+static PyObject *py_graph_cluster_find_by_block(PyObject *, PyObject *);
+
+/* Recherche le groupe de blocs avec un composant comme chef. */
+static PyObject *py_graph_cluster_find_by_widget(PyObject *, PyObject *);
+
+/* Recherche le groupe de blocs avec une cible particulière. */
+static PyObject *py_graph_cluster_find(PyObject *, PyObject *);
+
+/* Construit un graphique à partir de blocs basiques. */
+static PyObject *py_graph_cluster_bootstrap(PyObject *, PyObject *);
+
+/* Collecte tous les chefs de file de blocs de code. */
+static PyObject *py_graph_cluster_collect(PyObject *, PyObject *);
+
+/* Collecte tous les liens de chefs de file de blocs de code. */
+static PyObject *py_graph_cluster_collect_edges(PyObject *, PyObject *);
+
+/* Fournit le bloc de code principal du groupe. */
+static PyObject *py_graph_cluster_get_block(PyObject *, void *);
+
+/* Fournit le composant graphique principal du groupe. */
+static PyObject *py_graph_cluster_get_widget(PyObject *, void *);
+
+/* Fournit l'emplacement prévu pour un chef de file de blocs. */
+static PyObject *py_graph_cluster_get_allocation(PyObject *, void *);
+
+/* Détermine l'emplacement requis d'un ensemble de blocs. */
+static PyObject *py_graph_cluster_get_needed_alloc(PyObject *, void *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = arguments fournis pour l'appel.                       *
+*                                                                             *
+*  Description : Recherche le groupe de blocs avec un bloc donné comme chef.  *
+*                                                                             *
+*  Retour      : Groupe trouvé ou None en cas d'échec.                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_find_by_block(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Instance à retourner        */
+    GCodeBlock *block;                      /* Bloc de code à retrouver    */
+    int ret;                                /* Bilan de lecture des args.  */
+    GGraphCluster *cluster;                 /* Ensemble mis en place       */
+    GGraphCluster *found;                   /* Ensemble graphique trouvé   */
+
+    ret = PyArg_ParseTuple(args, "O&",
+                           convert_to_code_block, &block);
+    if (!ret) return NULL;
+
+    cluster = G_GRAPH_CLUSTER(pygobject_get(self));
+
+    found = g_graph_cluster_find_by_block(cluster, block);
+
+    if (found != NULL)
+    {
+        result = pygobject_new(G_OBJECT(found));
+        g_object_unref(G_OBJECT(found));
+    }
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = arguments fournis pour l'appel.                       *
+*                                                                             *
+*  Description : Recherche le groupe de blocs avec un composant comme chef.   *
+*                                                                             *
+*  Retour      : Groupe trouvé ou None en cas d'échec.                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_find_by_widget(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Instance à retourner        */
+    PyObject *gtk_mod;                      /* Module Python Gtk           */
+    PyObject *type;                         /* Module "GtkWidget"          */
+    PyObject *widget_obj;                   /* Composant GTK en Python     */
+    int ret;                                /* Bilan de lecture des args.  */
+    GGraphCluster *cluster;                 /* Ensemble mis en place       */
+    GtkWidget *widget;                      /* Composant GTK à retrouver   */
+    GGraphCluster *found;                   /* Ensemble graphique trouvé   */
+
+    gtk_mod = PyImport_ImportModule("gi.repository.Gtk");
+
+    if (gtk_mod == NULL)
+    {
+        PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module");
+        return NULL;
+    }
+
+    type = PyObject_GetAttrString(gtk_mod, "Widget");
+
+    Py_DECREF(gtk_mod);
+
+    ret = PyArg_ParseTuple(args, "O!",
+                           type, &widget_obj);
+
+    Py_DECREF(type);
+
+    if (!ret) return NULL;
+
+    if (!GTK_IS_WIDGET(pygobject_get(widget_obj)))
+    {
+        PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GTK widget");
+        return NULL;
+    }
+
+    cluster = G_GRAPH_CLUSTER(pygobject_get(self));
+
+    widget = GTK_WIDGET(pygobject_get(widget_obj));
+
+    found = g_graph_cluster_find_by_widget(cluster, widget);
+
+    if (found != NULL)
+    {
+        result = pygobject_new(G_OBJECT(found));
+        g_object_unref(G_OBJECT(found));
+    }
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = arguments fournis pour l'appel.                       *
+*                                                                             *
+*  Description : Recherche le groupe de blocs avec une cible particulière.    *
+*                                                                             *
+*  Retour      : Groupe trouvé ou None en cas d'échec.                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_find(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Instance à retourner        */
+    PyObject *block_or_widget;              /* Objet Python fourni         */
+    int ret;                                /* Bilan de lecture des args.  */
+
+    ret = PyArg_ParseTuple(args, "O", &block_or_widget);
+    if (!ret) return NULL;
+
+    ret = PyObject_IsInstance(block_or_widget, (PyObject *)get_python_code_block_type());
+
+    if (ret == 1)
+        result = py_graph_cluster_find_by_block(self, args);
+
+    else
+    {
+        PyErr_Clear();
+        result = py_graph_cluster_find_by_widget(self, args);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = arguments fournis pour l'appel.                       *
+*                                                                             *
+*  Description : Construit un graphique à partir de blocs basiques.           *
+*                                                                             *
+*  Retour      : Structure mise en place.                                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_bootstrap(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Instance à retourner        */
+    GLoadedBinary *binary;                  /* Binaire chargé avec contenu */
+    GBlockList *list;                       /* Liste de blocs de code      */
+    int ret;                                /* Bilan de lecture des args.  */
+    GGraphCluster *cluster;                 /* Ensemble mis en place       */
+
+    ret = PyArg_ParseTuple(args, "O&O&",
+                           convert_to_loaded_binary, &binary,
+                           convert_to_block_list_with_ref, &list);
+    if (!ret) return NULL;
+
+    cluster = bootstrap_graph_cluster(binary, list, NULL);
+
+    if (cluster != NULL)
+    {
+        result = pygobject_new(G_OBJECT(cluster));
+        g_object_unref(G_OBJECT(cluster));
+    }
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    g_object_unref(G_OBJECT(list));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = arguments fournis pour l'appel.                       *
+*                                                                             *
+*  Description : Collecte tous les chefs de file de blocs de code.            *
+*                                                                             *
+*  Retour      : Liste de graphiques de blocs rassemblés.                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_collect(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Liste à retourner           */
+    GGraphCluster *root;                    /* Chef de file à analyser     */
+    size_t count;                           /* Taille de la liste          */
+    GGraphCluster **list;                   /* Liste constituée            */
+    size_t i;                               /* Boucle de parcours          */
+    PyObject *item;                         /* Instance à transmettre      */
+
+    root = G_GRAPH_CLUSTER(pygobject_get(self));
+
+    list = collect_graph_clusters(root, &count);
+
+    result = PyTuple_New(count);
+
+    for (i = 0; i < count; i++)
+    {
+        item = pygobject_new(G_OBJECT(list[i]));
+        g_object_unref(G_OBJECT(list[i]));
+
+        PyTuple_SetItem(result, i, item);
+
+    }
+
+    if (list != NULL)
+        free(list);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = arguments fournis pour l'appel.                       *
+*                                                                             *
+*  Description : Collecte tous les liens de chefs de file de blocs de code.   *
+*                                                                             *
+*  Retour      : Liste de liens graphiques de blocs rassemblés.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_collect_edges(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Liste à retourner           */
+    GGraphCluster *root;                    /* Chef de file à analyser     */
+    size_t count;                           /* Taille de la liste          */
+    GGraphEdge **list;                      /* Liste constituée            */
+    size_t i;                               /* Boucle de parcours          */
+    PyObject *item;                         /* Instance à transmettre      */
+
+    root = G_GRAPH_CLUSTER(pygobject_get(self));
+
+    list = collect_graph_cluster_edges(root, &count);
+
+    result = PyTuple_New(count);
+
+    for (i = 0; i < count; i++)
+    {
+        item = pygobject_new(G_OBJECT(list[i]));
+        g_object_unref(G_OBJECT(list[i]));
+
+        PyTuple_SetItem(result, i, item);
+
+    }
+
+    if (list != NULL)
+        free(list);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Fournit le bloc de code principal du groupe.                 *
+*                                                                             *
+*  Retour      : Bloc de code associé.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_get_block(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Construction à retourner    */
+    GGraphCluster *cluster;                 /* Version GLib du type        */
+    GCodeBlock *block;                      /* Bloc de code associé        */
+
+    cluster = G_GRAPH_CLUSTER(pygobject_get(self));
+
+    block = g_graph_cluster_get_block(cluster);
+
+    result = pygobject_new(G_OBJECT(block));
+
+    g_object_unref(G_OBJECT(block));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Fournit le composant graphique principal du groupe.          *
+*                                                                             *
+*  Retour      : Composant graphique principal utilisé.                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_get_widget(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Construction à retourner    */
+    GGraphCluster *cluster;                 /* Version GLib du type        */
+    GtkWidget *widget;                      /* Composant graphique associé */
+
+    cluster = G_GRAPH_CLUSTER(pygobject_get(self));
+
+    widget = g_graph_cluster_get_widget(cluster);
+
+    result = new_pygobject_widget(widget);
+
+    g_object_unref(G_OBJECT(widget));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Fournit l'emplacement prévu pour un chef de file de blocs.   *
+*                                                                             *
+*  Retour      : Emplacement idéal pour l'affichage.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_get_allocation(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Construction à retourner    */
+    GGraphCluster *cluster;                 /* Version GLib du type        */
+    GtkAllocation alloc;                    /* Aire à convertir en Python  */
+    PyTypeObject *base;                     /* Modèle d'objet à créer      */
+    PyObject *attrib;                       /* Attribut à constituer       */
+    int ret;                                /* Bilan d'une mise en place   */
+
+    cluster = G_GRAPH_CLUSTER(pygobject_get(self));
+
+    g_graph_cluster_get_allocation(cluster, &alloc);
+
+    base = get_python_py_struct_type();
+
+    result = PyObject_CallFunction((PyObject *)base, NULL);
+    assert(result != NULL);
+
+#define TRANSLATE_ALLOC_FIELD(_n, _v)                       \
+    do                                                      \
+    {                                                       \
+        attrib = PyLong_FromUnsignedLongLong(_v);           \
+        ret = PyDict_SetItemString(result, _n, attrib);     \
+        if (ret != 0) goto failed;                          \
+    }                                                       \
+    while (0);
+
+    TRANSLATE_ALLOC_FIELD("x", alloc.x);
+    TRANSLATE_ALLOC_FIELD("y", alloc.y);
+    TRANSLATE_ALLOC_FIELD("width", alloc.width);
+    TRANSLATE_ALLOC_FIELD("height", alloc.height);
+
+    return result;
+
+ failed:
+
+    Py_DECREF(result);
+
+    return NULL;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Détermine l'emplacement requis d'un ensemble de blocs.       *
+*                                                                             *
+*  Retour      : Emplacement idéal pour l'affichage.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_cluster_get_needed_alloc(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Construction à retourner    */
+    GGraphCluster *cluster;                 /* Version GLib du type        */
+    GtkAllocation alloc;                    /* Aire à convertir en Python  */
+    PyTypeObject *base;                     /* Modèle d'objet à créer      */
+    PyObject *attrib;                       /* Attribut à constituer       */
+    int ret;                                /* Bilan d'une mise en place   */
+
+    cluster = G_GRAPH_CLUSTER(pygobject_get(self));
+
+    g_graph_cluster_compute_needed_alloc(cluster, &alloc);
+
+    base = get_python_py_struct_type();
+
+    result = PyObject_CallFunction((PyObject *)base, NULL);
+    assert(result != NULL);
+
+#define TRANSLATE_ALLOC_FIELD(_n, _v)                       \
+    do                                                      \
+    {                                                       \
+        attrib = PyLong_FromUnsignedLongLong(_v);           \
+        ret = PyDict_SetItemString(result, _n, attrib);     \
+        if (ret != 0) goto failed;                          \
+    }                                                       \
+    while (0);
+
+    TRANSLATE_ALLOC_FIELD("x", alloc.x);
+    TRANSLATE_ALLOC_FIELD("y", alloc.y);
+    TRANSLATE_ALLOC_FIELD("width", alloc.width);
+    TRANSLATE_ALLOC_FIELD("height", alloc.height);
+
+    return result;
+
+ failed:
+
+    Py_DECREF(result);
+
+    return NULL;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Fournit un accès à une définition de type à diffuser.        *
+*                                                                             *
+*  Retour      : Définition d'objet pour Python.                              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+PyTypeObject *get_python_graph_cluster_type(void)
+{
+    static PyMethodDef py_graph_cluster_methods[] = {
+        {
+            "find_by_block", py_graph_cluster_find_by_block,
+            METH_VARARGS,
+            "find_by_block(block, /)\n--\n\nFind the cluster associated with a given code block."
+        },
+        {
+            "find_by_widget", py_graph_cluster_find_by_widget,
+            METH_VARARGS,
+            "find_by_widget(widget, /)\n--\n\nFind the cluster associated with a given GTK widget."
+        },
+        {
+            "find", py_graph_cluster_find,
+            METH_VARARGS,
+            "find(block_or_widget, /)\n--\n\nFind a cluster depending on the provided property."
+            "\n"
+            "Alias for find_by_block() or find_by_widget()."
+        },
+        {
+            "bootstrap", py_graph_cluster_bootstrap,
+            METH_VARARGS | METH_STATIC,
+            "bootstrap(binary, list, /)\n--\n\nBuild a graph cluster from a binary and a list of code blocks."
+        },
+        {
+            "collect_clusters", py_graph_cluster_collect,
+            METH_NOARGS,
+            "collect_clusters()\n--\n\nCollect all clusters involvded in a graph view clustering."
+        },
+        {
+            "collect_edges", py_graph_cluster_collect_edges,
+            METH_NOARGS,
+            "collect_edges()\n--\n\nCollect all cluster edges involvded in a graph view clustering."
+        },
+        { NULL }
+    };
+
+    static PyGetSetDef py_graph_cluster_getseters[] = {
+        {
+            "block", py_graph_cluster_get_block, NULL,
+            "Main code block linked to the cluster.", NULL
+        },
+        {
+            "widget", py_graph_cluster_get_widget, NULL,
+            "GTK widget built to display the code block linked to the cluster.", NULL
+        },
+        {
+            "allocation", py_graph_cluster_get_allocation, NULL,
+            "Area allocated for the cluster code block.", NULL
+        },
+        {
+            "needed_alloc", py_graph_cluster_get_needed_alloc, NULL,
+            "Area needed for the cluster code block and all its children.", NULL
+        },
+        { NULL }
+    };
+
+    static PyTypeObject py_graph_cluster_type = {
+
+        PyVarObject_HEAD_INIT(NULL, 0)
+
+        .tp_name        = "pychrysalide.gtkext.graph.GraphCluster",
+        .tp_basicsize   = sizeof(PyGObject),
+
+        .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+        .tp_doc         = "Graphical cluster used in the graph view.\n" \
+                          "\n" \
+                          "The aim of this object is to provide a read-only " \
+                          "access to the information relative to graphical " \
+                          "cluster contained in a layout.",
+
+        .tp_methods     = py_graph_cluster_methods,
+        .tp_getset      = py_graph_cluster_getseters,
+
+        .tp_new         = no_python_constructor_allowed,
+
+    };
+
+    return &py_graph_cluster_type;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : module = module dont la définition est à compléter.          *
+*                                                                             *
+*  Description : Prend en charge l'objet 'pychrysalide.gtkext..GraphCluster'. *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool ensure_python_graph_cluster_is_registered(void)
+{
+    PyTypeObject *type;                     /* Type Python 'BinPortion'    */
+    PyObject *module;                       /* Module à recompléter        */
+    PyObject *dict;                         /* Dictionnaire du module      */
+
+    type = get_python_graph_cluster_type();
+
+    if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+    {
+        module = get_access_to_python_module("pychrysalide.gtkext.graph");
+
+        dict = PyModule_GetDict(module);
+
+        if (!register_class_for_pygobject(dict, G_TYPE_GRAPH_CLUSTER, type, &PyGObject_Type))
+            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 ensemble de blocs de code.             *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_graph_cluster(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_graph_cluster_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 graph cluster");
+            break;
+
+        case 1:
+            *((GGraphCluster **)dst) = G_GRAPH_CLUSTER(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/gtkext/graph/cluster.h b/plugins/pychrysalide/gtkext/graph/cluster.h
new file mode 100644
index 0000000..f9bbaa2
--- /dev/null
+++ b/plugins/pychrysalide/gtkext/graph/cluster.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * cluster.h - prototypes pour l'équivalent Python du fichier "gtkext/graph/cluster.h"
+ *
+ * Copyright (C) 2019 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_GTKEXT_GRAPH_CLUSTER_H
+#define _PLUGINS_PYCHRYSALIDE_GTKEXT_GRAPH_CLUSTER_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_graph_cluster_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.gtkext.graph.GraphCluster'. */
+bool ensure_python_graph_cluster_is_registered(void);
+
+/* Tente de convertir en ensemble de blocs de code. */
+int convert_to_graph_cluster(PyObject *, void *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_GTKEXT_GRAPH_CLUSTER_H */
diff --git a/plugins/pychrysalide/gtkext/graph/edge.c b/plugins/pychrysalide/gtkext/graph/edge.c
new file mode 100644
index 0000000..3f24ea4
--- /dev/null
+++ b/plugins/pychrysalide/gtkext/graph/edge.c
@@ -0,0 +1,353 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * edge.c - équivalent Python du fichier "glibext/gtkext/graph/edge.c"
+ *
+ * Copyright (C) 2019 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 "edge.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <gtkext/graph/edge.h>
+#include <plugins/dt.h>
+
+
+#include "../../access.h"
+#include "../../helpers.h"
+
+
+
+/* Fournit les deux blocs aux extrémités d'un lien. */
+static PyObject *py_graph_edge_get_boundaries(PyObject *, void *);
+
+/* Fournit la couleur de rendu d'un lien graphique. */
+static PyObject *py_graph_edge_get_color(PyObject *, void *);
+
+/* Fournit l'ensemble des points constituant un lien graphique. */
+static PyObject *py_graph_edge_get_points(PyObject *, void *);
+
+/* Définit les constantes pour les liens graphiques. */
+static bool py_graph_edge_define_constants(PyTypeObject *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Fournit les deux blocs aux extrémités d'un lien.             *
+*                                                                             *
+*  Retour      : Blocs d'origine et de destination du lien.                   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_edge_get_boundaries(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Résultat à retourner        */
+    GGraphEdge *edge;                       /* Version GLib du type        */
+    GCodeBlock *src;                        /* Bloc d'origine              */
+    GCodeBlock *dst;                        /* Bloc de destination         */
+    int ret;                                /* Bilan d'une insertion       */
+
+    edge = G_GRAPH_EDGE(pygobject_get(self));
+
+    g_graph_edge_get_boundaries(edge, &src, &dst);
+
+    result = PyTuple_New(2);
+
+#ifndef NDEBUG
+    ret = PyTuple_SetItem(result, 0, pygobject_new(G_OBJECT(src)));
+    assert(ret == 0);
+#else
+    PyTuple_SetItem(result, 0, pygobject_new(G_OBJECT(src)));
+#endif
+
+#ifndef NDEBUG
+    ret = PyTuple_SetItem(result, 1, pygobject_new(G_OBJECT(dst)));
+    assert(ret == 0);
+#else
+    PyTuple_SetItem(result, 1, pygobject_new(G_OBJECT(dst)));
+#endif
+
+    g_object_unref(G_OBJECT(src));
+    g_object_unref(G_OBJECT(dst));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Fournit la couleur de rendu d'un lien graphique.             *
+*                                                                             *
+*  Retour      : Identifiant de couleur de rendu.                             *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_edge_get_color(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Résultat à retourner        */
+    GGraphEdge *edge;                       /* Version GLib du type        */
+    EdgeColor color;                        /* Couleur de rendu courante   */
+
+    edge = G_GRAPH_EDGE(pygobject_get(self));
+
+    color = g_graph_edge_get_color(edge);
+
+    result = PyLong_FromUnsignedLong(color);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Fournit l'ensemble des points constituant un lien graphique. *
+*                                                                             *
+*  Retour      : Liste de points utilisés pour le dessin d'un lien.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_graph_edge_get_points(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Résultat à retourner        */
+    GGraphEdge *edge;                       /* Version GLib du type        */
+    size_t count;                           /* Quantité à considérer       */
+    const GdkPoint *points;                 /* Ensemble de points du lien  */
+    size_t i;                               /* Boucle de parcours          */
+    PyObject *obj;                          /* Objet Python à insérer      */
+    int ret;                                /* Bilan d'une insertion       */
+
+    edge = G_GRAPH_EDGE(pygobject_get(self));
+
+    points = g_graph_edge_get_points(edge, &count);
+
+    result = PyTuple_New(count);
+
+    for (i = 0; i < count; i++)
+    {
+        obj = Py_BuildValue("(ii)", points[i].x, points[i].y);
+
+#ifndef NDEBUG
+        ret = PyTuple_SetItem(result, i, obj);
+        assert(ret == 0);
+#else
+        PyTuple_SetItem(result, i, obj);
+#endif
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : obj_type = type dont le dictionnaire est à compléter.        *
+*                                                                             *
+*  Description : Définit les constantes pour les liens graphiques.            *
+*                                                                             *
+*  Retour      : true en cas de succès de l'opération, false sinon.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool py_graph_edge_define_constants(PyTypeObject *obj_type)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = true;
+
+    if (result) result = PyDict_AddULongMacro(obj_type, EGC_DEFAULT);
+    if (result) result = PyDict_AddULongMacro(obj_type, EGC_GREEN);
+    if (result) result = PyDict_AddULongMacro(obj_type, EGC_RED);
+    if (result) result = PyDict_AddULongMacro(obj_type, EGC_BLUE);
+    if (result) result = PyDict_AddULongMacro(obj_type, EGC_DASHED_GRAY);
+    if (result) result = PyDict_AddULongMacro(obj_type, EGC_COUNT);
+
+    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_graph_edge_type(void)
+{
+    static PyMethodDef py_graph_edge_methods[] = {
+        { NULL }
+    };
+
+    static PyGetSetDef py_graph_edge_getseters[] = {
+        {
+            "boundaries", py_graph_edge_get_boundaries, NULL,
+            "Origin and destination blocks for the graphical edge.", NULL
+        },
+        {
+            "color", py_graph_edge_get_color, NULL,
+            "Rendering color of the graphical edge.", NULL
+        },
+        {
+            "points", py_graph_edge_get_points, NULL,
+            "Points of the lines rendered for the graphical edge.", NULL
+        },
+        { NULL }
+    };
+
+    static PyTypeObject py_graph_edge_type = {
+
+        PyVarObject_HEAD_INIT(NULL, 0)
+
+        .tp_name        = "pychrysalide.gtkext.graph.GraphEdge",
+        .tp_basicsize   = sizeof(PyGObject),
+
+        .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+        .tp_doc         = "Graphical edge used in the graph view.\n" \
+                          "\n" \
+                          "The aim of this object is to provide a read-only " \
+                          "access to the information relative to graphical " \
+                          "edge contained in a layout.",
+
+        .tp_methods     = py_graph_edge_methods,
+        .tp_getset      = py_graph_edge_getseters,
+
+        .tp_new         = no_python_constructor_allowed,
+
+    };
+
+    return &py_graph_edge_type;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : module = module dont la définition est à compléter.          *
+*                                                                             *
+*  Description : Prend en charge l'objet 'pychrysalide.gtkext.....GraphEdge'. *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool ensure_python_graph_edge_is_registered(void)
+{
+    PyTypeObject *type;                     /* Type Python 'BinPortion'    */
+    PyObject *module;                       /* Module à recompléter        */
+    PyObject *dict;                         /* Dictionnaire du module      */
+
+    type = get_python_graph_edge_type();
+
+    if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+    {
+        module = get_access_to_python_module("pychrysalide.gtkext.graph");
+
+        dict = PyModule_GetDict(module);
+
+        if (!register_class_for_pygobject(dict, G_TYPE_GRAPH_EDGE, type, &PyGObject_Type))
+            return false;
+
+        if (!py_graph_edge_define_constants(type))
+            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 lien graphique entre noeuds.           *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_graph_edge(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_graph_edge_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 graph edge");
+            break;
+
+        case 1:
+            *((GGraphEdge **)dst) = G_GRAPH_EDGE(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/gtkext/graph/edge.h b/plugins/pychrysalide/gtkext/graph/edge.h
new file mode 100644
index 0000000..db61655
--- /dev/null
+++ b/plugins/pychrysalide/gtkext/graph/edge.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * edge.h - prototypes pour l'équivalent Python du fichier "gtkext/graph/edge.h"
+ *
+ * Copyright (C) 2019 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_GTKEXT_GRAPH_EDGE_H
+#define _PLUGINS_PYCHRYSALIDE_GTKEXT_GRAPH_EDGE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_graph_edge_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.gtkext.graph.GraphEdge'. */
+bool ensure_python_graph_edge_is_registered(void);
+
+/* Tente de convertir en lien graphique entre noeuds. */
+int convert_to_graph_edge(PyObject *, void *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_GTKEXT_GRAPH_EDGE_H */
diff --git a/plugins/pychrysalide/gtkext/graph/module.c b/plugins/pychrysalide/gtkext/graph/module.c
new file mode 100644
index 0000000..49ed6ef
--- /dev/null
+++ b/plugins/pychrysalide/gtkext/graph/module.c
@@ -0,0 +1,99 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.c - intégration du répertoire graph en tant que module
+ *
+ * Copyright (C) 2019 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 "cluster.h"
+#include "edge.h"
+#include "../../helpers.h"
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : super = module dont la définition est à compléter.           *
+*                                                                             *
+*  Description : Ajoute le module 'gtkext.graph' à un module Python.          *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool add_gtkext_graph_module(PyObject *super)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *module;                       /* Sous-module mis en place    */
+
+    static PyModuleDef py_chrysalide_gtkext_graph_module = {
+
+        .m_base = PyModuleDef_HEAD_INIT,
+
+        .m_name = "pychrysalide.gtkext.graph",
+        .m_doc = "Python module for Chrysalide.gtkext.graph",
+
+        .m_size = -1,
+
+    };
+
+    module = build_python_module(super, &py_chrysalide_gtkext_graph_module);
+
+    result = (module != NULL);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Intègre les objets du module 'gtkext.graph'.                 *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool populate_gtkext_graph_module(void)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = true;
+
+    if (result) result = ensure_python_graph_cluster_is_registered();
+    if (result) result = ensure_python_graph_edge_is_registered();
+
+    assert(result);
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/gtkext/graph/module.h b/plugins/pychrysalide/gtkext/graph/module.h
new file mode 100644
index 0000000..ca855ce
--- /dev/null
+++ b/plugins/pychrysalide/gtkext/graph/module.h
@@ -0,0 +1,42 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.h - prototypes pour l'intégration du répertoire graph en tant que module
+ *
+ * Copyright (C) 2019 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_GTKEXT_GRAPH_MODULE_H
+#define _PLUGINS_PYCHRYSALIDE_GTKEXT_GRAPH_MODULE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Ajoute le module 'gtkext.graph' à un module Python. */
+bool add_gtkext_graph_module(PyObject *);
+
+/* Intègre les objets du module 'gtkext.graph'. */
+bool populate_gtkext_graph_module(void);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_GTKEXT_GRAPH_MODULE_H */
diff --git a/plugins/pychrysalide/gtkext/module.c b/plugins/pychrysalide/gtkext/module.c
index 2108b04..8b6954f 100644
--- a/plugins/pychrysalide/gtkext/module.c
+++ b/plugins/pychrysalide/gtkext/module.c
@@ -32,6 +32,7 @@
 #include "bufferdisplay.h"
 #include "displaypanel.h"
 #include "dockable.h"
+#include "graph/module.h"
 #include "../helpers.h"
 
 
@@ -68,6 +69,11 @@ bool add_gtkext_module(PyObject *super)
 
     result = (module != NULL);
 
+    if (result) result = add_gtkext_graph_module(module);
+
+    if (!result)
+        Py_XDECREF(module);
+
     return result;
 
 }
@@ -96,6 +102,8 @@ bool populate_gtkext_module(void)
     if (result) result = ensure_python_display_panel_is_registered();
     if (result) result = ensure_python_gtk_dockable_is_registered();
 
+    if (result) result = populate_gtkext_graph_module();
+
     assert(result);
 
     return result;
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index 48b805c..674221b 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -52,6 +52,9 @@ static bool include_python_type_into_features(PyObject *, PyTypeObject *);
 /* --------------------------- CONFORTS CIBLANT PYGOBJECT --------------------------- */
 
 
+/* Message d'erreur affiché. */
+#define NO_CONSTRUCTOR_MSG _("Chrysalide does not allow building this kind of object from Python")
+
 /* Message d'erreur affiché puis recherché. */
 #define NOT_IMPLEMENTED_MSG _("Chrysalide method implementation is missing")
 
@@ -548,6 +551,33 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : type = type du nouvel objet à mettre en place.               *
+*                args = éventuelle liste d'arguments.                         *
+*                kwds = éventuel dictionnaire de valeurs mises à disposition. *
+*                                                                             *
+*  Description : Marque l'interdiction d'une instanciation depuis Python.     *
+*                                                                             *
+*  Retour      : NULL pour la levée d'exception.                              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+PyObject *no_python_constructor_allowed(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyObject *result;                       /* Exception à retourner       */
+
+    result = NULL;
+
+    PyErr_SetString(PyExc_NotImplementedError, NO_CONSTRUCTOR_MSG);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : self = objet quelconque dont le code Python hérite.          *
 *                args = série d'arguments si présents.                        *
 *                                                                             *
diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h
index edc6686..7bc7577 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -89,12 +89,35 @@ bool register_python_module_object(PyObject *, PyTypeObject *);
 #define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new
 
 
+/* Marque l'interdiction d'une instanciation depuis Python. */
+PyObject *no_python_constructor_allowed(PyTypeObject *, PyObject *, PyObject *);
+
 /* Marque l'absence d'implémentation pour une méthode donnée. */
 PyObject *not_yet_implemented_method(PyObject *, PyObject *);
 
 /* Définit dans le tas de Python un nouveau type. */
 PyTypeObject *define_python_dynamic_type(const PyTypeObject *);
 
+
+/**
+ * pygobject_new() prend en compte les références flottantes au moment de la
+ * construction d'un objet Python.
+ *
+ * Et cela pose problème dans la mesure où on ne veut pas transférer à Python
+ * la propriété d'un objet, même si celui-ci a une référence flotante.
+ *
+ * Dans les faits, c'est toujours uniquement le cas pour les composants graphiques,
+ * puisque GtkWidget hérite de GInitiallyUnowned, qui force le décompte flottant
+ * dans g_initially_unowned_init().
+ */
+#define new_pygobject_widget(o)             \
+    ({                                      \
+        if (g_object_is_floating(o))        \
+            g_object_ref_sink(o);           \
+        pygobject_new(G_OBJECT(o));         \
+    })
+
+
 /* Enregistre correctement une surcouche de conversion GObject. */
 bool _register_class_for_pygobject(PyObject *, GType, PyTypeObject *, PyTypeObject *, ...);
 
diff --git a/src/analysis/block.c b/src/analysis/block.c
index 85c8017..8dd76cf 100644
--- a/src/analysis/block.c
+++ b/src/analysis/block.c
@@ -636,7 +636,10 @@ GBlockList *g_block_list_new(size_t count)
 
     result = g_object_new(G_TYPE_BLOCK_LIST, NULL);
 
-    result->blocks = calloc(count, sizeof(GCodeBlock *));
+    if (count == 0)
+        result->blocks = NULL;
+    else
+        result->blocks = calloc(count, sizeof(GCodeBlock *));
 
     result->count = count;
 
@@ -696,12 +699,12 @@ void g_block_list_add_block(GBlockList *list, GCodeBlock *block, size_t index)
 }
 
 
-
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : list = ensemble des blocs de code à traiter.                 *
+*  Paramètres  : list  = liste de blocs de code à compléter.                  *
+*                block = bloc de code à intégrer.                             *
 *                                                                             *
-*  Description : Etablit les liens entre les blocs de code et leurs voisins.  *
+*  Description : Ajoute un bloc de code à une liste en construction.          *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -709,8 +712,13 @@ void g_block_list_add_block(GBlockList *list, GCodeBlock *block, size_t index)
 *                                                                             *
 ******************************************************************************/
 
-void g_block_list_resolve_links(const GBlockList *list)
+void g_block_list_append_block(GBlockList *list, GCodeBlock *block)
 {
+    list->blocks = realloc(list->blocks, ++list->count * sizeof(GCodeBlock *));
+
+    list->blocks[list->count - 1] = block;
+
+    g_code_block_set_index(block, list->count - 1);
 
 }
 
diff --git a/src/analysis/block.h b/src/analysis/block.h
index bb3db3e..d2a7b21 100644
--- a/src/analysis/block.h
+++ b/src/analysis/block.h
@@ -142,8 +142,8 @@ size_t g_block_list_count_blocks(const GBlockList *);
 /* Ajoute un bloc de code à une liste définie. */
 void g_block_list_add_block(GBlockList *, GCodeBlock *, size_t);
 
-/* Etablit les liens entre les blocs de code et leurs voisins. */
-void g_block_list_resolve_links(const GBlockList *);
+/* Ajoute un bloc de code à une liste en construction. */
+void g_block_list_append_block(GBlockList *, GCodeBlock *);
 
 /* Fournit le bloc de code correspondant à un indice de liste. */
 GCodeBlock *g_block_list_get_block(const GBlockList *, size_t);
diff --git a/src/analysis/disass/dragon.c b/src/analysis/disass/dragon.c
index 89dc784..5e8c2d3 100644
--- a/src/analysis/disass/dragon.c
+++ b/src/analysis/disass/dragon.c
@@ -833,8 +833,6 @@ GBlockList *translate_dragon_knight(const dragon_knight *knight, GLoadedBinary *
 
     }
 
-    g_block_list_resolve_links(result);
-
     return result;
 
 }
diff --git a/src/gtkext/graph/cluster.c b/src/gtkext/graph/cluster.c
index 8220bfe..84f3b38 100644
--- a/src/gtkext/graph/cluster.c
+++ b/src/gtkext/graph/cluster.c
@@ -264,9 +264,19 @@ static void set_y_for_graph_rank(const graph_rank_t *, gint *);
 /* Détermine les ordonnées de tous les liens en place. */
 static void compute_loop_link_with_graph_rank(const graph_rank_t *, const GtkAllocation *);
 
+/* Recherche le groupe de blocs avec un bloc donné comme chef. */
+static GGraphCluster *find_cluster_by_block_in_graph_rank(const graph_rank_t *, GCodeBlock *);
+
 /* Recherche le groupe de blocs avec un composant comme chef. */
 static GGraphCluster *find_cluster_by_widget_in_graph_rank(const graph_rank_t *, GtkWidget *);
 
+/* Collecte tous les chefs de file de blocs de code. */
+static GGraphCluster **collect_graph_ranks_clusters(const graph_rank_t *, GGraphCluster **, size_t *);
+
+/* Collecte tous les liens de chefs de file de blocs de code. */
+static GGraphEdge **collect_graph_ranks_cluster_edges(const graph_rank_t *, GGraphEdge **, size_t *);
+
+
 
 /* -------------------------- DEFINITION D'UN CHEF DE FILE -------------------------- */
 
@@ -402,6 +412,12 @@ static void g_graph_cluster_compute_link_y_positions(GGraphCluster *);
 /* Applique les positions calculées pour chaque lien graphique. */
 static void g_graph_cluster_resolve_links(const GGraphCluster *);
 
+/* Collecte tous les chefs de file de blocs de code. */
+static GGraphCluster **g_graph_cluster_collect(GGraphCluster *, GGraphCluster **, size_t *);
+
+/* Collecte tous les liens de chefs de file de blocs de code. */
+static GGraphEdge **g_graph_cluster_collect_edges(GGraphCluster *, GGraphEdge **, size_t *);
+
 
 
 /* ------------------------- CALCUL DE REPARTITION DE BLOCS ------------------------- */
@@ -1791,7 +1807,35 @@ static void compute_loop_link_with_graph_rank(const graph_rank_t *grank, const G
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : grank = ensemble de blocs de même rang à analyser.           *
-*                widget  = composant graphique à retrouver.                   *
+*                block = bloc de code à retrouver.                            *
+*                                                                             *
+*  Description : Recherche le groupe de blocs avec un bloc donné comme chef.  *
+*                                                                             *
+*  Retour      : Groupe trouvé ou NULL en cas d'échec.                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static GGraphCluster *find_cluster_by_block_in_graph_rank(const graph_rank_t *grank, GCodeBlock *block)
+{
+    GGraphCluster *result;                  /* Trouvaille à retourner      */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = NULL;
+
+    for (i = 0; i < grank->count && result == NULL; i++)
+        result = g_graph_cluster_find_by_block(grank->clusters[i], block);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : grank  = ensemble de blocs de même rang à analyser.          *
+*                widget = composant graphique à retrouver.                    *
 *                                                                             *
 *  Description : Recherche le groupe de blocs avec un composant comme chef.   *
 *                                                                             *
@@ -1816,6 +1860,64 @@ static GGraphCluster *find_cluster_by_widget_in_graph_rank(const graph_rank_t *g
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : grank = ensemble de blocs de même rang à analyser.           *
+*                list  = liste en cours de constitution. [OUT]                *
+*                count = taille de cette liste. [OUT]                         *
+*                                                                             *
+*  Description : Collecte tous les chefs de file de blocs de code.            *
+*                                                                             *
+*  Retour      : Liste de graphiques de blocs rassemblés.                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static GGraphCluster **collect_graph_ranks_clusters(const graph_rank_t *grank, GGraphCluster **list, size_t *count)
+{
+    GGraphCluster **result;                 /* Liste complétée à renvoyer  */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = list;
+
+    for (i = 0; i < grank->count; i++)
+        result = g_graph_cluster_collect(grank->clusters[i], result, count);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : grank = ensemble de blocs de même rang à analyser.           *
+*                list  = liste en cours de constitution. [OUT]                *
+*                count = taille de cette liste. [OUT]                         *
+*                                                                             *
+*  Description : Collecte tous les liens de chefs de file de blocs de code.   *
+*                                                                             *
+*  Retour      : Liste de liens graphiques de blocs rassemblés.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static GGraphEdge **collect_graph_ranks_cluster_edges(const graph_rank_t *grank, GGraphEdge **list, size_t *count)
+{
+    GGraphEdge **result;                    /* Liste complétée à renvoyer  */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = list;
+
+    for (i = 0; i < grank->count; i++)
+        result = g_graph_cluster_collect_edges(grank->clusters[i], result, count);
+
+    return result;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                            DEFINITION D'UN CHEF DE FILE                            */
@@ -2773,7 +2875,77 @@ static void g_graph_cluster_set_y(GGraphCluster *cluster, gint base)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : cluster = encapsulation à consulter.                         *
-*                alloc   = emplacement idéal pour l'affichage.                *
+*                                                                             *
+*  Description : Fournit le bloc de code principal du groupe.                 *
+*                                                                             *
+*  Retour      : Bloc de code associé.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GCodeBlock *g_graph_cluster_get_block(GGraphCluster *cluster)
+{
+    GCodeBlock *result;                     /* Bloc de code à retourner    */
+
+    result = cluster->block;
+
+    g_object_ref(G_OBJECT(result));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cluster = encapsulation à consulter.                         *
+*                                                                             *
+*  Description : Fournit le composant graphique principal du groupe.          *
+*                                                                             *
+*  Retour      : Composant graphique principal utilisé.                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GtkWidget *g_graph_cluster_get_widget(GGraphCluster *cluster)
+{
+    GtkWidget *result;                      /* Composant à retourner       */
+
+    result = cluster->display;
+
+    g_object_ref(G_OBJECT(result));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cluster = encapsulation à consulter.                         *
+*                alloc   = emplacement idéal pour l'affichage. [OUT]          *
+*                                                                             *
+*  Description : Fournit l'emplacement prévu pour un chef de file de blocs.   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_graph_cluster_get_allocation(const GGraphCluster *cluster, GtkAllocation *alloc)
+{
+    *alloc = cluster->alloc;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cluster = encapsulation à consulter.                         *
+*                alloc   = emplacement idéal pour l'affichage. [OUT]          *
 *                                                                             *
 *  Description : Détermine l'emplacement requis d'un ensemble de blocs.       *
 *                                                                             *
@@ -2819,31 +2991,6 @@ void g_graph_cluster_compute_needed_alloc(const GGraphCluster *cluster, GtkAlloc
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : cluster = encapsulation à consulter.                         *
-*                                                                             *
-*  Description : Fournit le composant graphique principal du groupe.          *
-*                                                                             *
-*  Retour      : Composant graphique principal utilisé.                       *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-GtkWidget *g_graph_cluster_get_widget(GGraphCluster *cluster)
-{
-    GtkWidget *result;
-
-    result = cluster->display;
-
-    g_object_ref(G_OBJECT(result));
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
 *  Paramètres  : cluster = encapsulation à traiter.                           *
 *                display = support de destination finale.                     *
 *                                                                             *
@@ -3394,6 +3541,44 @@ static void g_graph_cluster_resolve_links(const GGraphCluster *cluster)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : cluster = graphique de blocs à analyser.                     *
+*                block   = bloc de code à retrouver.                          *
+*                                                                             *
+*  Description : Recherche le groupe de blocs avec un bloc donné comme chef.  *
+*                                                                             *
+*  Retour      : Groupe trouvé ou NULL en cas d'échec.                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GGraphCluster *g_graph_cluster_find_by_block(GGraphCluster *cluster, GCodeBlock *block)
+{
+    GGraphCluster *result;                  /* Trouvaille à retourner      */
+    size_t i;                               /* Boucle de parcours          */
+
+    if (cluster->block == block)
+    {
+        result = cluster;
+        g_object_ref(G_OBJECT(result));
+    }
+
+    else
+    {
+        result = NULL;
+
+        for (i = 0; i < cluster->ranks_count && result == NULL; i++)
+            result = find_cluster_by_block_in_graph_rank(&cluster->ranks[i], block);
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cluster = graphique de blocs à analyser.                     *
 *                widget  = composant graphique à retrouver.                   *
 *                                                                             *
 *  Description : Recherche le groupe de blocs avec un composant comme chef.   *
@@ -3429,6 +3614,75 @@ GGraphCluster *g_graph_cluster_find_by_widget(GGraphCluster *cluster, GtkWidget
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cluster = graphique de blocs à analyser.                     *
+*                list    = liste en cours de constitution. [OUT]              *
+*                count   = taille de cette liste. [OUT]                       *
+*                                                                             *
+*  Description : Collecte tous les chefs de file de blocs de code.            *
+*                                                                             *
+*  Retour      : Liste de graphiques de blocs rassemblés.                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static GGraphCluster **g_graph_cluster_collect(GGraphCluster *cluster, GGraphCluster **list, size_t *count)
+{
+    GGraphCluster **result;                 /* Liste complétée à renvoyer  */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = realloc(list, ++(*count) * sizeof(GGraphCluster *));
+
+    result[*count - 1] = cluster;
+    g_object_ref(G_OBJECT(cluster));
+
+    for (i = 0; i < cluster->ranks_count; i++)
+        result = collect_graph_ranks_clusters(&cluster->ranks[i], result, count);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cluster = graphique de blocs à analyser.                     *
+*                list    = liste en cours de constitution. [OUT]              *
+*                count   = taille de cette liste. [OUT]                       *
+*                                                                             *
+*  Description : Collecte tous les liens de chefs de file de blocs de code.   *
+*                                                                             *
+*  Retour      : Liste de liens graphiques de blocs rassemblés.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static GGraphEdge **g_graph_cluster_collect_edges(GGraphCluster *cluster, GGraphEdge **list, size_t *count)
+{
+    GGraphEdge **result;                    /* Liste complétée à renvoyer  */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = realloc(list, (*count + cluster->ta_count) * sizeof(GGraphEdge *));
+
+    for (i = 0; i < cluster->ta_count; i++)
+    {
+        result[*count + i] = cluster->top_anchors[i]->edge;
+        g_object_ref(G_OBJECT(result[*count + i]));
+    }
+
+    *count += cluster->ta_count;
+
+    for (i = 0; i < cluster->ranks_count; i++)
+        result = collect_graph_ranks_cluster_edges(&cluster->ranks[i], result, count);
+
+    return result;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                           CALCUL DE REPARTITION DE BLOCS                           */
@@ -3603,7 +3857,7 @@ static GGraphCluster *setup_graph_clusters(GLoadedBinary *binary, const GBlockLi
 *                                                                             *
 *  Description : Construit un graphique à partir de blocs basiques.           *
 *                                                                             *
-*  Retour      : Adresse de la structure mise en place.                       *
+*  Retour      : Structure mise en place.                                     *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
@@ -3721,3 +3975,57 @@ GGraphCluster *bootstrap_graph_cluster(GLoadedBinary *binary, const GBlockList *
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : root  = graphique de blocs à analyser.                       *
+*                count = taille de cette liste. [OUT]                         *
+*                                                                             *
+*  Description : Collecte tous les chefs de file de blocs de code.            *
+*                                                                             *
+*  Retour      : Liste de graphiques de blocs rassemblés.                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GGraphCluster **collect_graph_clusters(GGraphCluster *root, size_t *count)
+{
+    GGraphCluster **result;                 /* Liste à retourner           */
+
+    result = NULL;
+    *count = 0;
+
+    result = g_graph_cluster_collect(root, result, count);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : root  = graphique de blocs à analyser.                       *
+*                count = taille de cette liste. [OUT]                         *
+*                                                                             *
+*  Description : Collecte tous les liens de chefs de file de blocs de code.   *
+*                                                                             *
+*  Retour      : Liste de liens graphiques de blocs rassemblés.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GGraphEdge **collect_graph_cluster_edges(GGraphCluster *root, size_t *count)
+{
+    GGraphEdge **result;                    /* Liste à retourner           */
+
+    result = NULL;
+    *count = 0;
+
+    result = g_graph_cluster_collect_edges(root, result, count);
+
+    return result;
+
+}
diff --git a/src/gtkext/graph/cluster.h b/src/gtkext/graph/cluster.h
index 2a48b25..ee4249e 100644
--- a/src/gtkext/graph/cluster.h
+++ b/src/gtkext/graph/cluster.h
@@ -25,6 +25,7 @@
 #define _GTKEXT_GRAPH_CLUSTER_H
 
 
+#include "edge.h"
 #include "../gtkgraphdisplay.h"
 #include "../../analysis/binary.h"
 #include "../../analysis/disass/block.h"
@@ -55,15 +56,24 @@ GType g_graph_cluster_get_type(void);
 /* Construit un graphique à partir de blocs de code. */
 GGraphCluster *g_graph_cluster_new(GCodeBlock *, segcnt_list *, GLoadedBinary *);
 
-/* Détermine l'emplacement requis d'un ensemble de blocs. */
-void g_graph_cluster_compute_needed_alloc(const GGraphCluster *, GtkAllocation *);
+/* Fournit le bloc de code principal du groupe. */
+GCodeBlock *g_graph_cluster_get_block(GGraphCluster *);
 
 /* Fournit le composant graphique principal du groupe. */
 GtkWidget *g_graph_cluster_get_widget(GGraphCluster *);
 
+/* Fournit l'emplacement prévu pour un chef de file de blocs. */
+void g_graph_cluster_get_allocation(const GGraphCluster *, GtkAllocation *);
+
+/* Détermine l'emplacement requis d'un ensemble de blocs. */
+void g_graph_cluster_compute_needed_alloc(const GGraphCluster *, GtkAllocation *);
+
 /* Dispose chaque noeud sur la surface de destination donnée. */
 void g_graph_cluster_place(GGraphCluster *, GtkGraphDisplay *);
 
+/* Recherche le groupe de blocs avec un bloc donné comme chef. */
+GGraphCluster *g_graph_cluster_find_by_block(GGraphCluster *, GCodeBlock *);
+
 /* Recherche le groupe de blocs avec un composant comme chef. */
 GGraphCluster *g_graph_cluster_find_by_widget(GGraphCluster *, GtkWidget *);
 
@@ -75,6 +85,12 @@ GGraphCluster *g_graph_cluster_find_by_widget(GGraphCluster *, GtkWidget *);
 /* Construit un graphique à partir de blocs basiques. */
 GGraphCluster *bootstrap_graph_cluster(GLoadedBinary *, const GBlockList *, segcnt_list *);
 
+/* Collecte tous les chefs de file de blocs de code. */
+GGraphCluster **collect_graph_clusters(GGraphCluster *, size_t *);
+
+/* Collecte tous les liens de chefs de file de blocs de code. */
+GGraphEdge **collect_graph_cluster_edges(GGraphCluster *, size_t *);
+
 
 
 #endif  /* _GTKEXT_GRAPH_CLUSTER_H */
diff --git a/src/gtkext/graph/edge.c b/src/gtkext/graph/edge.c
index 20f1988..93f020b 100644
--- a/src/gtkext/graph/edge.c
+++ b/src/gtkext/graph/edge.c
@@ -237,6 +237,29 @@ void g_graph_edge_get_boundaries(const GGraphEdge *edge, GCodeBlock **src, GCode
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : edge = ligne de rendu à consulter.                           *
+*                                                                             *
+*  Description : Fournit la couleur de rendu d'un lien graphique.             *
+*                                                                             *
+*  Retour      : Identifiant de couleur de rendu.                             *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+EdgeColor g_graph_edge_get_color(const GGraphEdge *edge)
+{
+    EdgeColor result;                       /* Couleur à retourner         */
+
+    result = edge->color;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : edge = ligne de rendu à consulter.                           *
 *                x1   = abscisse du point de départ de la ligne. [OUT]        *
 *                x2   = abscisse du point d'arrivée de la ligne. [OUT]        *
 *                                                                             *
@@ -322,6 +345,32 @@ void g_graph_edge_offset(GGraphEdge *edge, gint dx, gint dy)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : edge  = ligne de rendu avec positions à consulter.           *
+*                count = quantité de points représentés. [OUT]                *
+*                                                                             *
+*  Description : Fournit l'ensemble des points constituant un lien graphique. *
+*                                                                             *
+*  Retour      : Liste de points utilisés pour le dessin d'un lien.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+const GdkPoint *g_graph_edge_get_points(const GGraphEdge *edge, size_t *count)
+{
+    const GdkPoint *result;                 /* Liste de points à renvoyer  */
+
+    result = edge->points;
+
+    *count = edge->count;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : edge = ligne de rendu avec positions à consulter.            *
 *                x    = emplacement de la souris sur l'axe des abscisses.     *
 *                y    = emplacement de la souris sur l'axe des ordonnées.     *
diff --git a/src/gtkext/graph/edge.h b/src/gtkext/graph/edge.h
index ec33544..5342766 100644
--- a/src/gtkext/graph/edge.h
+++ b/src/gtkext/graph/edge.h
@@ -30,7 +30,7 @@
 #include <gtk/gtk.h>
 
 
-#include "../../analysis/disass/block.h"
+#include "../../analysis/block.h"
 
 
 
@@ -88,6 +88,9 @@ GGraphEdge *_g_graph_edge_new(GCodeBlock *, GCodeBlock *, const GdkPoint **, siz
 /* Fournit les deux blocs aux extrémités d'un lien. */
 void g_graph_edge_get_boundaries(const GGraphEdge *, GCodeBlock **, GCodeBlock **);
 
+/* Fournit la couleur de rendu d'un lien graphique. */
+EdgeColor g_graph_edge_get_color(const GGraphEdge *);
+
 /* Fournit les abscisses des points extrèmes de la ligne. */
 void g_graph_edge_get_x_borders(const GGraphEdge *, gint *, gint *);
 
@@ -97,6 +100,9 @@ void g_graph_edge_resolve(GGraphEdge *);
 /* Opère un décalage du lien dans une direction donnée. */
 void g_graph_edge_offset(GGraphEdge *, gint, gint);
 
+/* Fournit l'ensemble des points constituant un lien graphique. */
+const GdkPoint *g_graph_edge_get_points(const GGraphEdge *, size_t *);
+
 /* Opère un décalage du lien dans une direction donnée. */
 bool g_graph_edge_detect_at(const GGraphEdge *, gint, gint);
 
-- 
cgit v0.11.2-87-g4458