From d2861533cc53fbcc217048bafebf34b1f70ba3aa Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Mon, 11 May 2020 23:33:36 +0200
Subject: Updated the API for buffer caches.

---
 plugins/pychrysalide/analysis/content.c    |  31 +
 plugins/pychrysalide/analysis/content.h    |   3 +
 plugins/pychrysalide/format/symbol.c       |   1 +
 plugins/pychrysalide/glibext/buffercache.c | 947 ++++++++++++++++++++++++++++-
 plugins/pychrysalide/glibext/buffercache.h |   3 +
 plugins/pychrysalide/glibext/bufferline.c  |  24 +-
 plugins/pychrysalide/glibext/constants.c   | 100 +++
 plugins/pychrysalide/glibext/constants.h   |   6 +
 plugins/pychrysalide/glibext/linegen.c     | 734 ++++++++++++++++++----
 plugins/pychrysalide/glibext/linegen.h     |   3 +
 plugins/pychrysalide/helpers.c             |  45 ++
 plugins/pychrysalide/helpers.h             |   5 +-
 src/glibext/Makefile.am                    |   1 +
 src/glibext/gbuffercache-int.h             |  96 +++
 src/glibext/gbuffercache.c                 | 111 ++--
 src/glibext/gbuffercache.h                 |  15 +-
 src/glibext/gbufferline.c                  |   5 +-
 src/glibext/gbufferline.h                  |  14 +-
 src/glibext/linegen.c                      |  22 +-
 src/glibext/linegen.h                      |   4 +-
 src/gtkext/hexdisplay.c                    |   3 -
 tests/glibext/buffercache.py               |  49 ++
 22 files changed, 1956 insertions(+), 266 deletions(-)
 create mode 100644 src/glibext/gbuffercache-int.h
 create mode 100644 tests/glibext/buffercache.py

diff --git a/plugins/pychrysalide/analysis/content.c b/plugins/pychrysalide/analysis/content.c
index a0eb11c..8a404ed 100644
--- a/plugins/pychrysalide/analysis/content.c
+++ b/plugins/pychrysalide/analysis/content.c
@@ -813,3 +813,34 @@ int convert_to_binary_content(PyObject *arg, void *dst)
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en contenu binaire ou NULL.               *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_binary_content_or_none(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    if (arg == Py_None)
+    {
+        *((GBinContent **)dst) = NULL;
+        result = 1;
+    }
+
+    else
+        result = convert_to_binary_content(arg, dst);
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/analysis/content.h b/plugins/pychrysalide/analysis/content.h
index e56e29f..fab6b1b 100644
--- a/plugins/pychrysalide/analysis/content.h
+++ b/plugins/pychrysalide/analysis/content.h
@@ -40,6 +40,9 @@ bool ensure_python_binary_content_is_registered(void);
 /* Tente de convertir en contenu binaire. */
 int convert_to_binary_content(PyObject *, void *);
 
+/* Tente de convertir en contenu binaire ou NULL. */
+int convert_to_binary_content_or_none(PyObject *, void *);
+
 
 
 #endif  /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_CONTENT_H */
diff --git a/plugins/pychrysalide/format/symbol.c b/plugins/pychrysalide/format/symbol.c
index 6f1c1d2..71832cb 100644
--- a/plugins/pychrysalide/format/symbol.c
+++ b/plugins/pychrysalide/format/symbol.c
@@ -306,6 +306,7 @@ static int py_binary_symbol_init(PyObject *self, PyObject *args, PyObject *kwds)
 }
 
 
+
 /* ---------------------------------------------------------------------------------- */
 /*                       FONCTIONNALITES BASIQUES POUR SYMBOLES                       */
 /* ---------------------------------------------------------------------------------- */
diff --git a/plugins/pychrysalide/glibext/buffercache.c b/plugins/pychrysalide/glibext/buffercache.c
index 9ce06c1..fec5844 100644
--- a/plugins/pychrysalide/glibext/buffercache.c
+++ b/plugins/pychrysalide/glibext/buffercache.c
@@ -28,63 +28,897 @@
 #include <pygobject.h>
 
 
-#include <glibext/gbuffercache.h>
+#include <glibext/gbuffercache-int.h>
+#include <plugins/dt.h>
 
 
+#include "constants.h"
+#include "bufferline.h"
+#include "linegen.h"
 #include "../access.h"
 #include "../helpers.h"
-#include "../arch/vmpa.h"
+#include "../analysis/content.h"
 
 
 
-#if 0
-/* Retrouve une ligne au sein d'un tampon avec une adresse. */
-static PyObject *py_code_buffer_find_line_by_addr(PyObject *, PyObject *);
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
 
 
+/* Accompagne la création d'une instance dérivée en Python. */
+static PyObject *py_buffer_cache_new(PyTypeObject *, PyObject *, PyObject *);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_buffer_cache_init(PyObject *, PyObject *, PyObject *);
+
+
+
+/* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */
+
+
+/* Insère un générateur dans des lignes à une position donnée. */
+static PyObject *py_buffer_cache_insert_at(PyObject *, PyObject *);
+
+/* Retire une ligne du tampon. */
+static PyObject *py_buffer_cache_delete_at(PyObject *, PyObject *);
+
+/* Retire un type de générateur de lignes. */
+static PyObject *py_buffer_cache_delete_type_at(PyObject *, PyObject *);
+
+/* Ajoute en fin de tampon un générateur de lignes. */
+static PyObject *py_buffer_cache_append(PyObject *, PyObject *);
+
+/* Etend un tampon avec un générateur de lignes unique. */
+static PyObject *py_buffer_cache_extend_with(PyObject *, PyObject *);
+
+/* Réduit le tampon à une quantité de lignes précise. */
+static PyObject *py_buffer_cache_truncate(PyObject *, PyObject *);
+
+/* Détermine l'ensemble des propriétés attachées à une ligne. */
+static PyObject *py_buffer_cache_get_line_flags(PyObject *, PyObject *);
+
+/* Retrouve une ligne au sein d'un tampon avec un indice. */
+static PyObject *py_buffer_cache_find_line_by_index(PyObject *, PyObject *);
+
+/* Avance autant que possible vers une ligne idéale. */
+static PyObject *py_buffer_cache_look_for_flag(PyObject *, PyObject *);
+
+/* Indique l'éventuel contenu binaire associé au cache. */
+static PyObject *py_buffer_cache_get_content(PyObject *, void *);
+
+/* Fournit la hauteur d'impression d'une ligne visualisée. */
+static PyObject *py_buffer_cache_get_line_height(PyObject *, void *);
+
+/* Fournit la taille réservée pour la marge gauche. */
+static PyObject *py_buffer_cache_get_left_margin(PyObject *, void *);
+
+/* Fournit la position de départ pour l'impression de texte. */
+static PyObject *py_buffer_cache_get_text_position(PyObject *, void *);
+
+/* Compte le nombre de lignes rassemblées dans un tampon. */
+static PyObject *py_buffer_cache_get_lines_count(PyObject *, void *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                          GLUE POUR CREATION DEPUIS PYTHON                          */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type du nouvel objet à mettre en place.               *
+*                args = éventuelle liste d'arguments.                         *
+*                kwds = éventuel dictionnaire de valeurs mises à disposition. *
+*                                                                             *
+*  Description : Accompagne la création d'une instance dérivée en Python.     *
+*                                                                             *
+*  Retour      : Nouvel objet Python mis en place ou NULL en cas d'échec.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyObject *result;                       /* Objet à retourner           */
+    PyTypeObject *base;                     /* Type de base à dériver      */
+    bool first_time;                        /* Evite les multiples passages*/
+    GType gtype;                            /* Nouveau type de processeur  */
+    bool status;                            /* Bilan d'un enregistrement   */
+
+    /* Validations diverses */
+
+    base = get_python_buffer_cache_type();
+
+    if (type == base)
+        goto simple_way;
+
+    /* Mise en place d'un type dédié */
+
+    first_time = (g_type_from_name(type->tp_name) == 0);
+
+    gtype = build_dynamic_type(G_TYPE_BUFFER_CACHE, type->tp_name, NULL, NULL, NULL);
+
+    if (first_time)
+    {
+        status = register_class_for_dynamic_pygobject(gtype, type, base);
+
+        if (!status)
+        {
+            result = NULL;
+            goto exit;
+        }
+
+    }
+
+    /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
+
+ simple_way:
+
+    result = PyType_GenericNew(type, args, kwds);
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet à initialiser (théoriquement).                  *
+*                args = arguments fournis à l'appel.                          *
+*                kwds = arguments de type key=val fournis.                    *
+*                                                                             *
+*  Description : Initialise une instance sur la base du dérivé de GObject.    *
+*                                                                             *
+*  Retour      : 0.                                                           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int py_buffer_cache_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    GBinContent *content;                   /* Instance GLib du contenu    */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Version GLib du tampon      */
+
+#define BUFFER_CACHE_DOC                                                    \
+    "The BufferCache object manages a group of lines intended to get"       \
+    " printed onto the screen or into a file.\n"                            \
+    "\n"                                                                    \
+    "These lines are cached and rebuilt when needed.\n"                     \
+    "\n"                                                                    \
+    "Instances can be created using the following constructor:\n"           \
+    "\n"                                                                    \
+    "    BufferCache(content=None)"                                         \
+    "\n"                                                                    \
+    "Where content is a pychrysalide.analysis.BinContent instance, if"      \
+    " defined. This content is provided to lines as argument when the"      \
+    " lines get printed, so it may not be always useful and thus can be"    \
+    " discarded when creating a new buffer cache."                          \
+
+    /* Récupération des paramètres */
+
+    content = NULL;
+
+    ret = PyArg_ParseTuple(args, "|O&", convert_to_binary_content_or_none, &content);
+    if (!ret) return -1;
+
+    /* Initialisation d'un objet GLib */
+
+    ret = forward_pygobjet_init(self);
+    if (ret == -1) return -1;
+
+    /* Eléments de base */
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    if (content != NULL)
+    {
+        cache->content = content;
+        g_object_ref(G_OBJECT(content));
+    }
+
+    return 0;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                            TAMPON POUR CODE DESASSEMBLE                            */
+/* ---------------------------------------------------------------------------------- */
+
 
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : self = classe représentant un tampon de code.                *
 *                args = arguments fournis à l'appel.                          *
 *                                                                             *
-*  Description : Retrouve une ligne au sein d'un tampon avec une adresse.     *
+*  Description : Insère un générateur dans des lignes à une position donnée.  *
 *                                                                             *
-*  Retour      : Instance de la ligne trouvée.                                *
+*  Retour      : -                                                            *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static PyObject *py_code_buffer_find_line_by_addr(PyObject *self, PyObject *args)
+static PyObject *py_buffer_cache_insert_at(PyObject *self, PyObject *args)
 {
     PyObject *result;                       /* Trouvailles à retourner     */
-    PyObject *py_vmpa;                      /* Localisation version Python */
+    size_t index;                           /* Indice de ligne à supprimer */
+    GLineGenerator *generator;              /* Générateur à associer       */
+    BufferLineFlags flags;                  /* Particularités nominales    */
+    int before;                             /* Insertion avant ?           */
+    int after;                              /* Insertion après ?           */
     int ret;                                /* Bilan de lecture des args.  */
-    vmpa2t *addr;                           /* Adresse visée par l'opérat° */
-    GBuffercache *buffer;                   /* Version native              */
-    GBufferLine *line;                      /* Ligne trouvée               */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+
+#define BUFFER_CACHE_INSERT_AT_METHOD PYTHON_METHOD_DEF             \
+(                                                                   \
+    insert_at, "$self, index, generator, /, flags="                 \
+    "pychrysalide.glibext.BufferLine.BufferLineFlags.NONE,"         \
+    " before=False, after=False",                                   \
+    METH_VARARGS, py_buffer_cache,                                  \
+    "Add an extra content generator to a given line.\n"             \
+    "\n"                                                            \
+    "The new content generator for the line at, before or after"    \
+    " the provided index is a pychrysalide.glibext.LineGenerator"   \
+    " instance. Nominal properties can be set for this line as"     \
+    " extra pychrysalide.glibext.BufferLine.BufferLineFlags"        \
+    " values.\n"                                                    \
+    "\n"                                                            \
+    "An access lock has to be held for the cache; see the"          \
+    " pychrysalide.glibext.BufferCache.lock() function."            \
+)
 
-    ret = PyArg_ParseTuple(args, "O", &py_vmpa);
+    flags = BLF_NONE;
+    before = 0;
+    after = 0;
+
+    ret = PyArg_ParseTuple(args, "nO&|O&pp", &index, convert_to_line_generator, &generator,
+                           convert_to_buffer_line_flags, &flags, &before, &after);
     if (!ret) return NULL;
 
-    ret = PyObject_IsInstance(py_vmpa, (PyObject *)get_python_vmpa_type());
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    g_buffer_cache_insert_at(cache, index, generator, flags, before, after);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = classe représentant un tampon de code.                *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Retire une ligne du tampon.                                  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_delete_at(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    size_t index;                           /* Indice de ligne à supprimer */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+
+#define BUFFER_CACHE_DELETE_AT_METHOD PYTHON_METHOD_DEF                 \
+(                                                                       \
+    delete_at, "$self, index, /",                                       \
+    METH_VARARGS, py_buffer_cache,                                      \
+    "Delete the line at the *index* position from a buffer cache.\n"    \
+    "\n"                                                                \
+    "An access lock has to be held for the cache; see the"              \
+    " pychrysalide.glibext.BufferCache.lock() function."                \
+)
+
+    ret = PyArg_ParseTuple(args, "n", &index);
     if (!ret) return NULL;
 
-    addr = get_internal_vmpa(py_vmpa);
-    if (addr == NULL) return NULL;
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    g_buffer_cache_delete_at(cache, index);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = classe représentant un tampon de code.                *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Retire un type de générateur de lignes.                      *
+*                                                                             *
+*  Retour      : Générateur éventuellement trouvé ou NULL si aucun.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_delete_type_at(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    size_t index;                           /* Indice de ligne à supprimer */
+    PyObject *py_gtype;                     /* Version Python d'un GType   */
+    int before;                             /* Insertion avant ?           */
+    int after;                              /* Insertion après ?           */
+    int ret;                                /* Bilan de lecture des args.  */
+    GType gtype;                            /* Type de générateur visé     */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+    GLineGenerator *generator;              /* Générateur retiré ou NULL   */
+
+#define BUFFER_CACHE_DELETE_TYPE_AT_METHOD PYTHON_METHOD_DEF                \
+(                                                                           \
+    delete_type_at, "$self, index, gtype, /, before=False, after=False",    \
+    METH_VARARGS, py_buffer_cache,                                          \
+    "Delete the first generator of a given type found inside a line of a"   \
+    " buffer cache.\n"                                                      \
+    "\n"                                                                    \
+    "The target type has to be a gobject.GType, usually provided by the"    \
+    " *__gtype__* attribute of a generator interface. The generator is"     \
+    " deleted from the line at, before or after the provided index.\n"      \
+    "\n"                                                                    \
+    "The function returns the deleted generator as a"                       \
+    " pychrysalide.glibext.LineGenerator instance, or None if none found.\n"\
+    "\n"                                                                    \
+    "An access lock has to be held for the cache; see the"                  \
+    " pychrysalide.glibext.BufferCache.lock() function."                    \
+)
+
+    before = 0;
+    after = 0;
+
+    ret = PyArg_ParseTuple(args, "nO|pp", &index, &py_gtype, &before, &after);
+    if (!ret) return NULL;
 
-    buffer = G_CODE_BUFFER(pygobject_get(self));
+    gtype = pyg_type_from_object(py_gtype);
+    if (gtype == 0) return NULL;
 
-    line = g_code_buffer_find_line_by_addr(buffer, addr, 0, NULL);
-    if (line == NULL) Py_RETURN_NONE;
+    cache = G_BUFFER_CACHE(pygobject_get(self));
 
-    result = pygobject_new(G_OBJECT(line));
+    generator = g_buffer_cache_delete_type_at(cache, index, gtype, before, after);
+
+    if (generator != NULL)
+    {
+        result = pygobject_new(G_OBJECT(generator));
+        g_object_unref(G_OBJECT(generator));
+    }
+
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = classe représentant un tampon de code.                *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Ajoute en fin de tampon un générateur de lignes.             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_append(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    GLineGenerator *generator;              /* Générateur à associer       */
+    BufferLineFlags flags;                  /* Particularités nominales    */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+
+#define BUFFER_CACHE_APPEND_METHOD PYTHON_METHOD_DEF                \
+(                                                                   \
+    append, "$self, generator, /, flags="                           \
+    "pychrysalide.glibext.BufferLine.BufferLineFlags.NONE",         \
+    METH_VARARGS, py_buffer_cache,                                  \
+    "Append a new line at the end of a buffer cache.\n"             \
+    "\n"                                                            \
+    "The content generator for this new line is a"                  \
+    " pychrysalide.glibext.LineGenerator instance. Nominal"         \
+    " properties can be set for the line as extra"                  \
+    " pychrysalide.glibext.BufferLine.BufferLineFlags values.\n"    \
+    "\n"                                                            \
+    "An access lock has to be held for the cache; see the"          \
+    " pychrysalide.glibext.BufferCache.lock() function."            \
+)
+
+    flags = BLF_NONE;
+
+    ret = PyArg_ParseTuple(args, "O&|O&", convert_to_line_generator, &generator,
+                           convert_to_buffer_line_flags, &flags);
+    if (!ret) return NULL;
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    g_buffer_cache_append(cache, generator, flags);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = classe représentant un tampon de code.                *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Etend un tampon avec un générateur de lignes unique.         *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_extend_with(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    size_t count;                           /* Quantité de lignes à créer  */
+    GLineGenerator *generator;              /* Générateur à associer       */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+
+#define BUFFER_CACHE_EXTEND_WITH_METHOD PYTHON_METHOD_DEF           \
+(                                                                   \
+    extend_with, "$self, count, generator, /",                      \
+    METH_VARARGS, py_buffer_cache,                                  \
+    "Extend the buffer cache so it will contain *count* lines.\n"   \
+    "\n"                                                            \
+    "The *count* number should be greater than the current line"    \
+    " quantity, otherwise the call makes no sense. The generator"   \
+    " is a pychrysalide.glibext.LineGenerator instance used to"     \
+    " produce the extra new lines on demand."                       \
+    "\n"                                                                \
+    "An access lock has to be held for the cache; see the"              \
+    " pychrysalide.glibext.BufferCache.lock() function."                \
+)
+
+    ret = PyArg_ParseTuple(args, "nO&", &count, convert_to_line_generator, &generator);
+    if (!ret) return NULL;
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    g_buffer_cache_extend_with(cache, count, generator);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = classe représentant un tampon de code.                *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Réduit le tampon à une quantité de lignes précise.           *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_truncate(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    size_t max;                             /* Nombre de lignes au maximum */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+
+#define BUFFER_CACHE_TRUNCATE_METHOD PYTHON_METHOD_DEF                  \
+(                                                                       \
+    truncate, "$self, max, /",                                          \
+    METH_VARARGS, py_buffer_cache,                                      \
+    "Shrink the buffer cache so it will contain at most *max* lines.\n" \
+    "\n"                                                                \
+    "The *max* number should be lesser than the current line quantity," \
+    " otherwise no effect will happen."                                 \
+    "\n"                                                                \
+    "An access lock has to be held for the cache; see the"              \
+    " pychrysalide.glibext.BufferCache.lock() function."                \
+)
+
+    ret = PyArg_ParseTuple(args, "n", &max);
+    if (!ret) return NULL;
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    g_buffer_cache_truncate(cache, max);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = classe représentant un tampon de code.                *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Détermine l'ensemble des propriétés attachées à une ligne.   *
+*                                                                             *
+*  Retour      : Somme de toutes les propriétés enregistrées.                 *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_get_line_flags(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    size_t index;                           /* Indice de la ligne visée    */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+    BufferLineFlags flags;                  /* Fanions conservés           */
+
+#define BUFFER_CACHE_GET_LINE_FLAGS_METHOD PYTHON_METHOD_DEF            \
+(                                                                       \
+    get_line_flags, "$self, index, /",                                  \
+    METH_VARARGS, py_buffer_cache,                                      \
+    "Provide the optional flags assigned to a given buffer line. The"   \
+    " line is located using the *index* argument.\n"                    \
+    "\n"                                                                \
+    "The result is a pychrysalide.glibext.BufferLine.BufferLineFlags"   \
+    " value.\n"                                                         \
+    "\n"                                                                \
+    "An access lock has to be held for the cache; see the"              \
+    " pychrysalide.glibext.BufferCache.lock() function."                \
+)
+
+    ret = PyArg_ParseTuple(args, "n", &index);
+    if (!ret) return NULL;
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    flags = g_buffer_cache_get_line_flags(cache, index);
+
+    result = cast_with_constants_group_from_type(get_python_buffer_line_type(), "BufferLineFlags", flags);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = classe représentant un tampon de code.                *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Retrouve une ligne au sein d'un tampon avec un indice.       *
+*                                                                             *
+*  Retour      : Line retrouvée ou None en cas d'échec.                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_find_line_by_index(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    size_t index;                           /* Indice de la ligne visée    */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+    GBufferLine *line;                      /* Ligne trouvée ?             */
+
+#define BUFFER_CACHE_FIND_LINE_BY_INDEX_METHOD PYTHON_METHOD_DEF        \
+(                                                                       \
+    find_line_by_index, "$self, index, /",                              \
+    METH_VARARGS, py_buffer_cache,                                      \
+    "Retrieve the line contained in a buffer cache at a given index.\n" \
+    "\n"                                                                \
+    "The result is a pychrysalide.glibext.BufferLine instance or"       \
+    " None.\n"                                                          \
+    "\n"                                                                \
+    "An access lock has to be held for the cache; see the"              \
+    " pychrysalide.glibext.BufferCache.lock() function."                \
+)
+
+    ret = PyArg_ParseTuple(args, "n", &index);
+    if (!ret) return NULL;
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    line = g_buffer_cache_find_line_by_index(cache, index);
+
+    if (line != NULL)
+    {
+        result = pygobject_new(G_OBJECT(line));
+        g_object_unref(G_OBJECT(line));
+    }
+
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = classe représentant un tampon de code.                *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Avance autant que possible vers une ligne idéale.            *
+*                                                                             *
+*  Retour      : Indice de la ligne recherchée, si elle existe.               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_look_for_flag(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    size_t index;                           /* Indice de la ligne visée    */
+    BufferLineFlags flag;                   /* Particularité à retrouver   */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+    size_t found;                           /* Indice de la ligne trouvée  */
+
+#define BUFFER_CACHE_LOOK_FOR_FLAG_METHOD PYTHON_METHOD_DEF             \
+(                                                                       \
+    look_for_flag, "$self, index, flag, /",                             \
+    METH_VARARGS, py_buffer_cache,                                      \
+    "Iterate the buffer cache lines from a starting index until a"      \
+    " line with flags matching the provided flag is met.\n"             \
+    "\n"                                                                \
+    "The *flag* argument has to be a"                                   \
+    " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n"         \
+    "\n"                                                                \
+    "The result is an index equal or greater than the starting index"   \
+    " or, if no match is found, the number of lines in the buffer"      \
+    " cache.\n"                                                         \
+    "\n"                                                                \
+    "An access lock has to be held for the cache; see the"              \
+    " pychrysalide.glibext.BufferCache.lock() function."                \
+)
+
+    ret = PyArg_ParseTuple(args, "nO&", &index, convert_to_buffer_line_flags, &flag);
+    if (!ret) return NULL;
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    found = g_buffer_cache_look_for_flag(cache, index, flag);
+
+    result = PyLong_FromSize_t(found);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Indique l'éventuel contenu binaire associé au cache.         *
+*                                                                             *
+*  Retour      : Eventuel contenu renseigné ou None.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_get_content(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Contenu binaire à retourner */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+    GBinContent *content;                   /* Contenu éventuel à renvoyer */
+
+#define BUFFER_CACHE_CONTENT_ATTRIB PYTHON_GET_DEF_FULL     \
+(                                                           \
+    content, py_buffer_cache,                               \
+    "Binary content linked to the buffer cache, as a"       \
+    " pychrysalide.analysis.BinContent instance, or None."  \
+)
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    content = g_buffer_cache_get_content(cache);
+
+    if (content != NULL)
+    {
+        result = pygobject_new(G_OBJECT(content));
+        g_object_unref(G_OBJECT(content));
+    }
+
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Fournit la hauteur d'impression d'une ligne visualisée.      *
+*                                                                             *
+*  Retour      : Hauteur de ligne en pixels.                                  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_get_line_height(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+    gint height;                            /* Valeur obtenue du cache     */
+
+#define BUFFER_CACHE_LINE_HEIGHT_ATTRIB PYTHON_GET_DEF_FULL \
+(                                                           \
+    line_height, py_buffer_cache,                           \
+    "Height of a line printed from the buffer cache."       \
+)
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    height = g_buffer_cache_get_line_height(cache);
+
+    result = PyLong_FromLong(height);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Fournit la taille réservée pour la marge gauche.             *
+*                                                                             *
+*  Retour      : Largeur en pixels.                                           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_get_left_margin(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+    gint width;                             /* Valeur obtenue du cache     */
+
+#define BUFFER_CACHE_LEFT_MARGIN_ATTRIB PYTHON_GET_DEF_FULL     \
+(                                                               \
+    left_margin, py_buffer_cache,                               \
+    "Width of the left margin inside of a buffer cache output." \
+)
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    width = g_buffer_cache_get_left_margin(cache);
+
+    result = PyLong_FromLong(width);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Fournit la position de départ pour l'impression de texte.    *
+*                                                                             *
+*  Retour      : Position en pixels.                                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_get_text_position(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+    gint pos;                               /* Valeur obtenue du cache     */
+
+#define BUFFER_CACHE_TEXT_POSITION_ATTRIB PYTHON_GET_DEF_FULL               \
+(                                                                           \
+    text_position, py_buffer_cache,                                         \
+    "Starting position of the text on the left of a buffer cache output."   \
+)
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    pos = g_buffer_cache_get_text_position(cache);
+
+    result = PyLong_FromLong(pos);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Compte le nombre de lignes rassemblées dans un tampon.       *
+*                                                                             *
+*  Retour      : Nombre de lignes constituant le tampon.                      *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_get_lines_count(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+    size_t count;                           /* Décompte de première main   */
+
+#define BUFFER_CACHE_LINES_COUNT_ATTRIB PYTHON_GET_DEF_FULL     \
+(                                                               \
+    lines_count, py_buffer_cache,                               \
+    "Count the number of lines contained in a buffer cache.\n"  \
+    "\n"                                                        \
+    "An access lock has to be held for the cache; see the"      \
+    " pychrysalide.glibext.BufferCache.lock() function."        \
+)
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    count = g_buffer_cache_count_lines(cache);
+
+    result = PyLong_FromSize_t(count);
 
     return result;
 
 }
-#endif
 
 
 /******************************************************************************
@@ -102,17 +936,24 @@ static PyObject *py_code_buffer_find_line_by_addr(PyObject *self, PyObject *args
 PyTypeObject *get_python_buffer_cache_type(void)
 {
     static PyMethodDef py_buffer_cache_methods[] = {
-#if 0
-        {
-            "find_line_by_addr", py_buffer_cache_find_line_by_addr,
-            METH_VARARGS,
-            "find_line_by_addr($self, addr, /)\n--\n\nFind a buffer line with a given address."
-        },
-#endif
+        BUFFER_CACHE_INSERT_AT_METHOD,
+        BUFFER_CACHE_DELETE_AT_METHOD,
+        BUFFER_CACHE_DELETE_TYPE_AT_METHOD,
+        BUFFER_CACHE_APPEND_METHOD,
+        BUFFER_CACHE_EXTEND_WITH_METHOD,
+        BUFFER_CACHE_TRUNCATE_METHOD,
+        BUFFER_CACHE_GET_LINE_FLAGS_METHOD,
+        BUFFER_CACHE_FIND_LINE_BY_INDEX_METHOD,
+        BUFFER_CACHE_LOOK_FOR_FLAG_METHOD,
         { NULL }
     };
 
     static PyGetSetDef py_buffer_cache_getseters[] = {
+        BUFFER_CACHE_CONTENT_ATTRIB,
+        BUFFER_CACHE_LINE_HEIGHT_ATTRIB,
+        BUFFER_CACHE_LEFT_MARGIN_ATTRIB,
+        BUFFER_CACHE_TEXT_POSITION_ATTRIB,
+        BUFFER_CACHE_LINES_COUNT_ATTRIB,
         { NULL }
     };
 
@@ -125,11 +966,14 @@ PyTypeObject *get_python_buffer_cache_type(void)
 
         .tp_flags       = Py_TPFLAGS_DEFAULT,
 
-        .tp_doc         = "PyChrysalide code buffer",
+        .tp_doc         = BUFFER_CACHE_DOC,
 
         .tp_methods     = py_buffer_cache_methods,
         .tp_getset      = py_buffer_cache_getseters,
 
+        .tp_init        = py_buffer_cache_init,
+        .tp_new         = py_buffer_cache_new
+
     };
 
     return &py_buffer_cache_type;
@@ -171,3 +1015,48 @@ bool ensure_python_buffer_cache_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 tampon de lignes.                      *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_buffer_cache(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_buffer_cache_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 buffer cache");
+            break;
+
+        case 1:
+            *((GBufferCache **)dst) = G_BUFFER_CACHE(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/buffercache.h b/plugins/pychrysalide/glibext/buffercache.h
index 3135e22..1908434 100644
--- a/plugins/pychrysalide/glibext/buffercache.h
+++ b/plugins/pychrysalide/glibext/buffercache.h
@@ -37,6 +37,9 @@ PyTypeObject *get_python_buffer_cache_type(void);
 /* Prend en charge l'objet 'pychrysalide.glibext.BufferCache'. */
 bool ensure_python_buffer_cache_is_registered(void);
 
+/* Tente de convertir en tampon de lignes. */
+int convert_to_buffer_cache(PyObject *, void *);
+
 
 
 #endif  /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_BUFFERCACHE_H */
diff --git a/plugins/pychrysalide/glibext/bufferline.c b/plugins/pychrysalide/glibext/bufferline.c
index 47b2bc4..af3cf1d 100644
--- a/plugins/pychrysalide/glibext/bufferline.c
+++ b/plugins/pychrysalide/glibext/bufferline.c
@@ -155,7 +155,7 @@ static PyObject *py_buffer_line_new(PyTypeObject *type, PyObject *args, PyObject
 static PyObject *py_buffer_line_append_text(PyObject *self, PyObject *args)
 {
     PyObject *result;                       /* Trouvailles à retourner     */
-    unsigned long column;                   /* Indice de colonne           */
+    size_t column;                          /* Indice de colonne           */
     const char *text;                       /* Texte à ajouter             */
     RenderingTagType type;                  /* Type de rendu attendu       */
     GObject *creator;                       /* Eventuel créateur à associer*/
@@ -177,7 +177,7 @@ static PyObject *py_buffer_line_append_text(PyObject *self, PyObject *args)
 
     creator = NULL;
 
-    ret = PyArg_ParseTuple(args, "ksO&|O&", &column, &text,
+    ret = PyArg_ParseTuple(args, "nsO&|O&", &column, &text,
                            convert_to_rendering_tag_type, &type, convert_to_gobject, &creator);
     if (!ret) return NULL;
 
@@ -228,9 +228,17 @@ static PyObject *py_buffer_line_get_text(PyObject *self, PyObject *args)
     line = G_BUFFER_LINE(pygobject_get(self));
     text = g_buffer_line_get_text(line, first, end, markup);
 
-    result = PyUnicode_FromString(text);
+    if (text != NULL)
+    {
+        result = PyUnicode_FromString(text);
+        free(text);
+    }
 
-    free(text);
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
 
     return result;
 
@@ -398,11 +406,6 @@ static bool py_buffer_line_define_constants(PyTypeObject *obj_type)
     result &= PyDict_AddULongMacro(obj_type, BLC_FIRST);
     result &= PyDict_AddULongMacro(obj_type, BLC_DISPLAY);
 
-    result &= PyDict_AddULongMacro(obj_type, BLF_NONE);
-    result &= PyDict_AddULongMacro(obj_type, BLF_HAS_CODE);
-    result &= PyDict_AddULongMacro(obj_type, BLF_ENTRYPOINT);
-    result &= PyDict_AddULongMacro(obj_type, BLF_BOOKMARK);
-
     return result;
 
 }
@@ -509,6 +512,9 @@ bool ensure_python_buffer_line_is_registered(void)
         if (!define_line_segment_constants(type))
             return false;
 
+        if (!define_buffer_line_constants(type))
+            return false;
+
         if (!py_buffer_line_define_constants(type))
             return false;
 
diff --git a/plugins/pychrysalide/glibext/constants.c b/plugins/pychrysalide/glibext/constants.c
index d18e051..823465a 100644
--- a/plugins/pychrysalide/glibext/constants.c
+++ b/plugins/pychrysalide/glibext/constants.c
@@ -26,6 +26,7 @@
 
 
 #include <glibext/linesegment.h>
+#include <glibext/gbufferline.h>
 
 
 #include "../helpers.h"
@@ -140,3 +141,102 @@ int convert_to_rendering_tag_type(PyObject *arg, void *dst)
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type dont le dictionnaire est à compléter.            *
+*                                                                             *
+*  Description : Définit les constantes relatives aux lignes de tampon.       *
+*                                                                             *
+*  Retour      : true en cas de succès de l'opération, false sinon.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool define_buffer_line_constants(PyTypeObject *type)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *values;                       /* Groupe de valeurs à établir */
+
+    values = PyDict_New();
+
+    result = add_const_to_group(values, "NONE", BLF_NONE);
+    if (result) result = add_const_to_group(values, "HAS_CODE", BLF_HAS_CODE);
+    if (result) result = add_const_to_group(values, "IS_LABEL", BLF_IS_LABEL);
+    if (result) result = add_const_to_group(values, "ENTRYPOINT", BLF_ENTRYPOINT);
+    if (result) result = add_const_to_group(values, "BOOKMARK", BLF_BOOKMARK);
+    if (result) result = add_const_to_group(values, "WIDTH_MANAGER", BLF_WIDTH_MANAGER);
+    if (result) result = add_const_to_group(values, "ALL", BLF_ALL);
+
+    if (!result)
+    {
+        Py_DECREF(values);
+        goto exit;
+    }
+
+    result = attach_constants_group_to_type(type, true, "BufferLineFlags", values,
+                                            "Optional flags linked to a rendering line.");
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en constante BufferLineFlags.             *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_buffer_line_flags(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+    unsigned long value;                    /* Valeur transcrite           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type);
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to BufferLineFlags");
+            break;
+
+        case 1:
+            value = PyLong_AsUnsignedLong(arg);
+
+            if ((value & BLF_ALL) != value)
+            {
+                PyErr_SetString(PyExc_TypeError, "invalid value for BufferLineFlags");
+                result = 0;
+            }
+
+            else
+                *((BufferLineFlags *)dst) = value;
+
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/constants.h b/plugins/pychrysalide/glibext/constants.h
index 472507b..a1c56af 100644
--- a/plugins/pychrysalide/glibext/constants.h
+++ b/plugins/pychrysalide/glibext/constants.h
@@ -37,6 +37,12 @@ bool define_line_segment_constants(PyTypeObject *);
 /* Tente de convertir en constante RenderingTagType. */
 int convert_to_rendering_tag_type(PyObject *, void *);
 
+/* Définit les constantes relatives aux lignes de tampon. */
+bool define_buffer_line_constants(PyTypeObject *);
+
+/* Tente de convertir en constante BufferLineFlags. */
+int convert_to_buffer_line_flags(PyObject *, void *);
+
 
 
 #endif  /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_H */
diff --git a/plugins/pychrysalide/glibext/linegen.c b/plugins/pychrysalide/glibext/linegen.c
index 3ca7fcc..4d6d60d 100644
--- a/plugins/pychrysalide/glibext/linegen.c
+++ b/plugins/pychrysalide/glibext/linegen.c
@@ -28,26 +28,49 @@
 #include <pygobject.h>
 
 
-#include <common/cpp.h>
-#include <glibext/linegen.h>
+#include <glibext/linegen-int.h>
 
 
+#include "bufferline.h"
+#include "constants.h"
+#include "linecursor.h"
 #include "../access.h"
 #include "../helpers.h"
 #include "../analysis/content.h"
-#include "../arch/vmpa.h"
-#include "../glibext/bufferline.h"
 
 
 
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+/* Procède à l'initialisation de l'interface de génération. */
+static void py_line_generator_interface_init(GLineGeneratorIface *, gpointer *);
+
 /* Indique le nombre de ligne prêtes à être générées. */
-static PyObject *py_line_generator_count_lines(PyObject *, PyObject *);
+static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *);
 
 /* Retrouve l'emplacement correspondant à une position donnée. */
-//static PyObject *py_line_generator_compute_addr(PyObject *, PyObject *);
+static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *, gint, size_t, size_t, GLineCursor **);
 
 /* Détermine si le conteneur s'inscrit dans une plage donnée. */
-//static PyObject *py_line_generator_contains_addr(PyObject *, PyObject *);
+static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *, size_t, size_t, const GLineCursor *);
+
+/* Renseigne sur les propriétés liées à un générateur. */
+static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator *, size_t, size_t);
+
+/* Imprime dans une ligne de rendu le contenu représenté. */
+static void py_line_generator_print_wrapper(GLineGenerator *, GBufferLine *, size_t, size_t, const GBinContent *);
+
+
+
+/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
+
+
+/* Retrouve l'emplacement correspondant à une position donnée. */
+static PyObject *py_line_generator_compute_cursor(PyObject *, PyObject *);
+
+/* Détermine si le conteneur s'inscrit dans une plage donnée. */
+static PyObject *py_line_generator_contain_cursor(PyObject *, PyObject *);
 
 /* Renseigne sur les propriétés liées à un générateur. */
 static PyObject *py_line_generator_get_flags(PyObject *, PyObject *);
@@ -55,12 +78,61 @@ static PyObject *py_line_generator_get_flags(PyObject *, PyObject *);
 /* Imprime dans une ligne de rendu le contenu représenté. */
 static PyObject *py_line_generator_print(PyObject *, PyObject *);
 
+/* Indique le nombre de ligne prêtes à être générées. */
+static PyObject *py_line_generator_get_lines_count(PyObject *, void *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                          GLUE POUR CREATION DEPUIS PYTHON                          */
+/* ---------------------------------------------------------------------------------- */
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : self = classe représentant un générateur à manipuler.        *
-*                args = arguments fournis à l'appel.                          *
+*  Paramètres  : iface  = interface GLib à initialiser.                       *
+*                unused = adresse non utilisée ici.                           *
+*                                                                             *
+*  Description : Procède à l'initialisation de l'interface de génération.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void py_line_generator_interface_init(GLineGeneratorIface *iface, gpointer *unused)
+{
+
+#define LINE_GENERATOR_DOC                                                  \
+    "LineGenerator gives an interface to all objects which aim to produce"  \
+    " content for rendering lines. Such lines can be exported to graphical" \
+    " interfaces or text files.\n"                                          \
+    "\n"                                                                    \
+    "A typical class declaration for a new implementation looks like:\n"    \
+    "\n"                                                                    \
+    "    class NewImplem(GObject.Object, LineGenerator):\n"                 \
+    "        ...\n"                                                         \
+    "\n"                                                                    \
+    "The following methods have to be defined for new implementations:\n"   \
+    "* pychrysalide.glibext.LineGenerator._count_lines();\n"                \
+    "* pychrysalide.glibext.LineGenerator._compute_cursor();\n"             \
+    "* pychrysalide.glibext.LineGenerator._contain_cursor();\n"             \
+    "* pychrysalide.glibext.LineGenerator._get_flags();\n"                  \
+    "* pychrysalide.glibext.LineGenerator._print();\n"                      \
+
+    iface->count = py_line_generator_count_lines_wrapper;
+    iface->compute = py_line_generator_compute_cursor_wrapper;
+    iface->contains = py_line_generator_contain_cursor_wrapper;
+    iface->get_flags = py_line_generator_get_flags_wrapper;
+    iface->print = py_line_generator_print_wrapper;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : generator = générateur à consulter.                          *
 *                                                                             *
 *  Description : Indique le nombre de ligne prêtes à être générées.           *
 *                                                                             *
@@ -70,17 +142,46 @@ static PyObject *py_line_generator_print(PyObject *, PyObject *);
 *                                                                             *
 ******************************************************************************/
 
-static PyObject *py_line_generator_count_lines(PyObject *self, PyObject *args)
+static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *generator)
 {
-    PyObject *result;                       /* Décompte à retourner        */
-    GLineGenerator *generator;              /* Version native              */
-    size_t count;                           /* Nombre de lignes présentes  */
+    size_t result;                          /* Décompte à retourner        */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *pyret;                        /* Bilan de consultation       */
+    int ret;                                /* Bilan d'une conversion      */
 
-    generator = G_LINE_GENERATOR(pygobject_get(self));
+#define LINE_GENERATOR_COUNT_LINES_WRAPPER PYTHON_WRAPPER_DEF       \
+(                                                                   \
+    _count_lines, "$self, /",                                       \
+    METH_NOARGS,                                                    \
+    "Abstract method used to count the number of lines produced"    \
+    " by the current generator."                                    \
+)
 
-    count = g_line_generator_count_lines(generator);
+    result = 0;
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(generator));
+
+    if (has_python_method(pyobj, "_count_lines"))
+    {
+        pyret = run_python_method(pyobj, "_count_lines", NULL);
+
+        if (pyret != NULL)
+        {
+            ret = PyLong_Check(pyret);
+
+            if (ret)
+                result = PyLong_AsSize_t(pyret);
+
+            Py_DECREF(pyret);
+
+        }
+
+    }
 
-    result = Py_BuildValue("k", count);
+    PyGILState_Release(gstate);
 
     return result;
 
@@ -89,40 +190,358 @@ static PyObject *py_line_generator_count_lines(PyObject *self, PyObject *args)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : generator = générateur à consulter.                          *
+*                x         = position géographique sur la ligne concernée.    *
+*                index     = indice de cette même ligne dans le tampon global.*
+*                repeat    = indice d'utilisations successives du générateur. *
+*                                                                             *
+*  Description : Retrouve l'emplacement correspondant à une position donnée.  *
+*                                                                             *
+*  Retour      : Emplacement constitué.                                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *generator, gint x, size_t index, size_t repeat, GLineCursor **cursor)
+{
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *pyret;                        /* Bilan de consultation       */
+    int ret;                                /* Bilan d'une conversion      */
+
+#define LINE_GENERATOR_COMPUTE_CURSOR_WRAPPER PYTHON_WRAPPER_DEF    \
+(                                                                   \
+    _compute_cursor, "$self, x, index, repeat, /",                  \
+    METH_VARARGS,                                                   \
+    "Abstract method used to create a new cursor for a given"       \
+    " location inside displayed lines.\n"                           \
+    "\n"                                                            \
+    "The position on the horizontal axis, the line index and the"   \
+    " number of repetitions (only relevant if the generator"        \
+    " produces several lines) give indications about the active"    \
+    " position.\n"                                                  \
+    "\n"                                                            \
+    "The result has to be a pychrysalide.glibext.LineCursor"        \
+    " instance."                                                    \
+)
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(generator));
+
+    if (has_python_method(pyobj, "_compute_cursor"))
+    {
+        args = PyTuple_New(3);
+        PyTuple_SetItem(args, 0, PyLong_FromSize_t(x));
+        PyTuple_SetItem(args, 1, PyLong_FromSize_t(index));
+        PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat));
+
+        pyret = run_python_method(pyobj, "_compute_cursor", args);
+
+        if (pyret != NULL)
+        {
+            ret = convert_to_line_cursor(pyret, cursor);
+
+            if (ret != 1)
+                *cursor = NULL;
+
+            Py_DECREF(pyret);
+
+        }
+
+        Py_DECREF(args);
+
+    }
+
+    PyGILState_Release(gstate);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : generator = générateur à consulter.                          *
+*                index     = indice de cette même ligne dans le tampon global.*
+*                repeat    = indice d'utilisations successives du générateur. *
+*                cursor    = emplacement à analyser.                          *
+*                                                                             *
+*  Description : Détermine si le conteneur s'inscrit dans une plage donnée.   *
+*                                                                             *
+*  Retour      : Bilan de la détermination, utilisable en comparaisons.       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *generator, size_t index, size_t repeat, const GLineCursor *cursor)
+{
+    int result;                             /* Bilan d'analyse à retourner */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *pyret;                        /* Bilan de consultation       */
+    int ret;                                /* Bilan d'une conversion      */
+
+#define LINE_GENERATOR_CONTAIN_CURSOR_WRAPPER PYTHON_WRAPPER_DEF    \
+(                                                                   \
+    _contain_cursor, "$self, index, repeat, cursor, /",             \
+    METH_VARARGS,                                                   \
+    "Abstract method used to check the position of a cursor in"     \
+    " relation to rendering lines.\n"                               \
+    "\n"                                                            \
+    "The line index and the number of repetitions (only relevant"   \
+    " if the generator produces several lines) give indications"    \
+    " about the active position. The cursor is a"                   \
+    " pychrysalide.glibext.LineCursor instance.\n"                  \
+    "\n"                                                            \
+    "The result has to be an integer less than, equal to, or"       \
+    " greater than zero if the cursor is, respectively, before,"    \
+    " inside or after the area covered by the generator."           \
+)
+
+    result = 0;
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(generator));
+
+    if (has_python_method(pyobj, "_contain_cursor"))
+    {
+        args = PyTuple_New(3);
+        PyTuple_SetItem(args, 0, PyLong_FromSize_t(index));
+        PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat));
+        PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(cursor)));
+
+        pyret = run_python_method(pyobj, "_contain_cursor", args);
+
+        if (pyret != NULL)
+        {
+            ret = PyLong_Check(pyret);
+
+            if (ret)
+                result = PyLong_AsLong(pyret);
+
+            Py_DECREF(pyret);
+
+        }
+
+        Py_DECREF(args);
+
+    }
+
+    PyGILState_Release(gstate);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : generator = générateur à consulter.                          *
+*                index     = indice de cette même ligne dans le tampon global.*
+*                repeat    = indice d'utilisations successives du générateur. *
+*                                                                             *
+*  Description : Renseigne sur les propriétés liées à un générateur.          *
+*                                                                             *
+*  Retour      : Propriétés particulières associées.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator *generator, size_t index, size_t repeat)
+{
+    BufferLineFlags result;                 /* Fanions à retourner         */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *pyret;                        /* Bilan de consultation       */
+    int ret;                                /* Bilan d'une conversion      */
+
+#define LINE_GENERATOR_GET_FLAGS_WRAPPER PYTHON_WRAPPER_DEF         \
+(                                                                   \
+    _get_flags, "$self, index, repeat, /",                          \
+    METH_VARARGS,                                                   \
+    "Abstract method used to provide flags for a given rendering"   \
+    " line.\n"                                                      \
+    "\n"                                                            \
+    "The line index and the number of repetitions (only relevant"   \
+    " if the generator produces several lines) give indications"    \
+    " about the active position.\n"                                 \
+    "\n"                                                            \
+    "The result has to be a"                                        \
+    " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n"     \
+)
+
+    result = BLF_NONE;
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(generator));
+
+    if (has_python_method(pyobj, "_get_flags"))
+    {
+        args = PyTuple_New(2);
+        PyTuple_SetItem(args, 0, PyLong_FromSize_t(index));
+        PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat));
+
+        pyret = run_python_method(pyobj, "_get_flags", args);
+
+        if (pyret != NULL)
+        {
+            ret = convert_to_buffer_line_flags(pyret, &result);
+
+            if (ret != 1)
+                result = BLF_NONE;
+
+            Py_DECREF(pyret);
+
+        }
+
+        Py_DECREF(args);
+
+    }
+
+    PyGILState_Release(gstate);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : generator = générateur à utiliser pour l'impression.         *
+*                line      = ligne de rendu à compléter.                      *
+*                index     = indice de cette même ligne dans le tampon global.*
+*                repeat    = indice d'utilisations successives du générateur. *
+*                content   = éventuel contenu binaire brut à imprimer.        *
+*                                                                             *
+*  Description : Imprime dans une ligne de rendu le contenu représenté.       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content)
+{
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *pyret;                        /* Bilan de consultation       */
+
+#define LINE_GENERATOR_PRINT_WRAPPER PYTHON_WRAPPER_DEF             \
+(                                                                   \
+    _print, "$self, line, index, repeat, content, /",               \
+    METH_VARARGS,                                                   \
+    "Abstract method used to generate content into a rendering"     \
+    " line, which is a provided pychrysalide.glibext.BufferLine"    \
+    " instance.\n"                                                  \
+    "\n"                                                            \
+    "The line index and the number of repetitions (only relevant"   \
+    " if the generator produces several lines) give indications"    \
+    " about the current rendering position.\n"                      \
+    "\n"                                                            \
+    "If set, the content is a pychrysalide.analysis.BinContent"     \
+    " instance providing access to the processed binary data."      \
+)
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(generator));
+
+    if (has_python_method(pyobj, "_print"))
+    {
+        args = PyTuple_New(4);
+        PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line)));
+        PyTuple_SetItem(args, 1, PyLong_FromSize_t(index));
+        PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat));
+        PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(content)));
+
+        pyret = run_python_method(pyobj, "_print", args);
+
+        Py_DECREF(args);
+
+        Py_XDECREF(pyret);
+
+    }
+
+    PyGILState_Release(gstate);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                           CONNEXION AVEC L'API DE PYTHON                           */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : self = classe représentant un générateur à manipuler.        *
 *                args = arguments fournis à l'appel.                          *
 *                                                                             *
 *  Description : Retrouve l'emplacement correspondant à une position donnée.  *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : Emplacement constitué.                                       *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
-#if 0
-static PyObject *py_line_generator_compute_addr(PyObject *self, PyObject *args)
+
+static PyObject *py_line_generator_compute_cursor(PyObject *self, PyObject *args)
 {
-    PyObject *result;                       /* Localisation à retourner    */
-    GLineGenerator *generator;              /* Version native              */
-    gint x;                                 /* Position géographique       */
+    PyObject *result;                       /* Propriétés à retourner      */
+    int x;                                  /* Position horizontale        */
     size_t index;                           /* Indice dans le tampon       */
     size_t repeat;                          /* Utilisations successives    */
     int ret;                                /* Bilan de lecture des args.  */
-    vmpa2t addr;                            /* Adresse visée par l'opérat° */
+    GLineGenerator *generator;              /* Version native              */
+    GLineCursor *cursor;                    /* Curseur nouveau obtenu      */
+
+#define LINE_GENERATOR_COMPUTE_CURSOR_METHOD PYTHON_METHOD_DEF      \
+(                                                                   \
+    compute_cursor, "$self, x, index, repeat, /",                   \
+    METH_VARARGS, py_line_generator,                                \
+    "Create a a new cursor for a given location inside displayed"   \
+    " lines.\n"                                                     \
+    "\n"                                                            \
+    "The position on the horizontal axis, the line index and the"   \
+    " number of repetitions (only relevant if the generator"        \
+    " produces several lines) give indications about the active"    \
+    " position.\n"                                                  \
+    "\n"                                                            \
+    "The result has to be a pychrysalide.glibext.LineCursor"        \
+    " instance."                                                    \
+)
+
+    ret = PyArg_ParseTuple(args, "inn", &x, &index, &repeat);
+    if (!ret) return NULL;
 
     generator = G_LINE_GENERATOR(pygobject_get(self));
 
-    ret = PyArg_ParseTuple(args, "ikk", &x, &index, &repeat);
-    if (!ret) return NULL;
+    cursor = g_line_generator_compute_cursor(generator, x, index, repeat);
 
-    g_line_generator_compute_addr(generator, x, &addr, index, repeat);
-
-    result = build_from_internal_vmpa(&addr);
+    if (cursor != NULL)
+    {
+        result = pygobject_new(G_OBJECT(cursor));
+        g_object_unref(G_OBJECT(cursor));
+    }
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
 
     return result;
 
 }
-#endif
 
 
 /******************************************************************************
@@ -137,30 +556,46 @@ static PyObject *py_line_generator_compute_addr(PyObject *self, PyObject *args)
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
-#if 0
-static PyObject *py_line_generator_contains_addr(PyObject *self, PyObject *args)
+
+static PyObject *py_line_generator_contain_cursor(PyObject *self, PyObject *args)
 {
-    GLineGenerator *generator;              /* Version native              */
-    PyObject *py_vmpa;                      /* Localisation version Python */
+    PyObject *result;                       /* Propriétés à retourner      */
     size_t index;                           /* Indice dans le tampon       */
     size_t repeat;                          /* Utilisations successives    */
+    GLineCursor *cursor;                    /* Curseur à venir situer      */
     int ret;                                /* Bilan de lecture des args.  */
-    vmpa2t *addr;                           /* Adresse visée par l'opérat° */
+    GLineGenerator *generator;              /* Version native              */
+    int status;                             /* Bilan d'une analyse         */
+
+#define LINE_GENERATOR_CONTAIN_CURSOR_METHOD PYTHON_METHOD_DEF      \
+(                                                                   \
+    contain_cursor, "$self, index, repeat, cursor, /",              \
+    METH_VARARGS, py_line_generator,                                \
+    "Check the position of a cursor in relation to rendering"       \
+    " lines.\n"                                                     \
+    "\n"                                                            \
+    "The line index and the number of repetitions (only relevant"   \
+    " if the generator produces several lines) give indications"    \
+    " about the active position. The cursor is a"                   \
+    " pychrysalide.glibext.LineCursor instance.\n"                  \
+    "\n"                                                            \
+    "The result has to be an integer less than, equal to, or"       \
+    " greater than zero if the cursor is, respectively, before,"    \
+    " inside or after the area covered by the generator."           \
+)
+
+    ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_line_cursor, &cursor);
+    if (!ret) return NULL;
 
     generator = G_LINE_GENERATOR(pygobject_get(self));
 
-    ret = PyArg_ParseTuple(args, "O!kk", get_python_vmpa_type(), &py_vmpa, &index, &repeat);
-    if (!ret) return NULL;
-
-    addr = get_internal_vmpa(py_vmpa);
-    if (addr == NULL) return NULL;
+    status = g_line_generator_contains_cursor(generator, index, repeat, cursor);
 
-    g_line_generator_contains_addr(generator, addr, index, repeat);
+    result = PyLong_FromLong(status);
 
-    Py_RETURN_NONE;
+    return result;
 
 }
-#endif
 
 
 /******************************************************************************
@@ -179,20 +614,34 @@ static PyObject *py_line_generator_contains_addr(PyObject *self, PyObject *args)
 static PyObject *py_line_generator_get_flags(PyObject *self, PyObject *args)
 {
     PyObject *result;                       /* Propriétés à retourner      */
-    GLineGenerator *generator;              /* Version native              */
     size_t index;                           /* Indice dans le tampon       */
     size_t repeat;                          /* Utilisations successives    */
     int ret;                                /* Bilan de lecture des args.  */
+    GLineGenerator *generator;              /* Version native              */
     BufferLineFlags flags;                  /* Propriétés courantes        */
 
-    generator = G_LINE_GENERATOR(pygobject_get(self));
-
-    ret = PyArg_ParseTuple(args, "kk", &index, &repeat);
+#define LINE_GENERATOR_GET_FLAGS_METHOD PYTHON_METHOD_DEF               \
+(                                                                       \
+    get_flags, "$self, index, repeat, /",                               \
+    METH_VARARGS, py_line_generator,                                    \
+    "Get the flags of a given position from the generator.\n"           \
+    "\n"                                                                \
+    "The line index and the number of repetitions (only relevant"       \
+    " if the generator produces several lines) give indications"        \
+    " about the active position.\n"                                     \
+    "\n"                                                                \
+    "The result is a pychrysalide.glibext.BufferLine.BufferLineFlags"   \
+    " value."                                                           \
+)
+
+    ret = PyArg_ParseTuple(args, "nn", &index, &repeat);
     if (!ret) return NULL;
 
+    generator = G_LINE_GENERATOR(pygobject_get(self));
+
     flags = g_line_generator_get_flags(generator, index, repeat);
 
-    result = Py_BuildValue("I", flags);
+    result = cast_with_constants_group_from_type(get_python_buffer_line_type(), "BufferLineFlags", flags);
 
     return result;
 
@@ -214,19 +663,33 @@ static PyObject *py_line_generator_get_flags(PyObject *self, PyObject *args)
 
 static PyObject *py_line_generator_print(PyObject *self, PyObject *args)
 {
-    GLineGenerator *generator;              /* Version native              */
     GBufferLine *line;                      /* Ligne de rendu à compléter  */
     size_t index;                           /* Indice dans le tampon       */
     size_t repeat;                          /* Utilisations successives    */
     GBinContent *content;                   /* Contenu binaire associé     */
+    GLineGenerator *generator;              /* Version native              */
     int ret;                                /* Bilan de lecture des args.  */
 
-    generator = G_LINE_GENERATOR(pygobject_get(self));
-
-    ret = PyArg_ParseTuple(args, "O&kkO&", convert_to_buffer_line, &line, &index,
+#define LINE_GENERATOR_PRINT_METHOD PYTHON_METHOD_DEF               \
+(                                                                   \
+    print, "$self, line, index, repeat, content, /",                \
+    METH_VARARGS, py_line_generator,                                \
+    "Produce output into a rendering line with optional content.\n" \
+    "\n"                                                            \
+    "The line index and the number of repetitions (only relevant"   \
+    " if the generator produces several lines) give indications"    \
+    " about the current rendering position.\n"                      \
+    "\n"                                                            \
+    "If set, the content is a pychrysalide.analysis.BinContent"     \
+    " instance providing access to the processed binary data."      \
+)
+
+    ret = PyArg_ParseTuple(args, "O&nnO&", convert_to_buffer_line, &line, &index,
                            &repeat, convert_to_binary_content, &content);
     if (!ret) return NULL;
 
+    generator = G_LINE_GENERATOR(pygobject_get(self));
+
     g_line_generator_print(generator, line, index, repeat, content);
 
     Py_RETURN_NONE;
@@ -234,78 +697,45 @@ static PyObject *py_line_generator_print(PyObject *self, PyObject *args)
 }
 
 
-
-
-
-
-
-
-
-
-
-
-
-#if 0
-
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : -                                                            *
+*  Paramètres  : self    = classe représentant un générateur à manipuler.     *
+*                closure = non utilisé ici.                                   *
 *                                                                             *
-*  Description : Fournit un accès à une définition de type à diffuser.        *
+*  Description : Indique le nombre de ligne prêtes à être générées.           *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : Nombre de lignes devant apparaître au final.                 *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static void python_line_generator_interface_init(GLineGeneratorIface *iface, PyTypeObject *type)
+static PyObject *py_line_generator_get_lines_count(PyObject *self, void *closure)
 {
-    GLineGeneratorIface *parent;            /* Défintion parente           */
-    size_t i;                               /* Boucle de parcours          */
-    PyObject *method;                       /* Méthode à associer          */
-
-    static const char *meth_names[] = {
-        "count_lines",
-        "compute_addr",
-        "contains_addr",
-        "get_flags",
-        "print"
-    };
-
-    parent = g_type_interface_peek_parent(iface);
-
-    for (i = 0; i < ARRAY_SIZE(meth_names); i++)
-    {
-        method = NULL;
-
-        if (type != NULL)
-            method = PyObject_GetAttrString((PyObject *)type, meth_names[i]);
-
-        if (method != NULL && PyObject_TypeCheck(method, &PyCFunction_Type) == 0)
-            /*iface->iface_method = _wrap_TestInterface__proxy_do_iface_method*/;
+    PyObject *result;                       /* Décompte à retourner        */
+    GLineGenerator *generator;              /* Version native              */
+    size_t count;                           /* Nombre de lignes présentes  */
 
-        else
-        {
-            PyErr_Clear();
+#define LINE_GENERATOR_LINES_COUNT_ATTRIB PYTHON_GET_DEF_FULL       \
+(                                                                   \
+    lines_count, py_line_generator,                                 \
+    "Quantity of lines produced by the generator.\n"                \
+    "\n"                                                            \
+    "This number may vary between calls, if a width has changed"    \
+    " for instance."                                                \
+)
 
-            if (parent != NULL)
-                /*iface->iface_method = parent->iface_method*/;
+    generator = G_LINE_GENERATOR(pygobject_get(self));
 
-        }
+    count = g_line_generator_count_lines(generator);
 
-        Py_XDECREF(method);
+    result = PyLong_FromSize_t(count);
 
-    }
+    return result;
 
 }
 
 
-#endif
-
-
-
-
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : -                                                            *
@@ -321,37 +751,20 @@ static void python_line_generator_interface_init(GLineGeneratorIface *iface, PyT
 PyTypeObject *get_python_line_generator_type(void)
 {
     static PyMethodDef py_line_generator_methods[] = {
-        {
-            "count_lines", py_line_generator_count_lines,
-            METH_NOARGS,
-            "count_lines($self, /)\n--\n\nCount the number of lines which can be displayed."
-        },
-#if 0
-        {
-            "compute_addr", py_line_generator_compute_addr,
-            METH_VARARGS,
-            "compute_addr($self, x, index, repeat, /)\n--\n\nReturn the position at a given location."
-        },
-        {
-            "contains_addr", py_line_generator_contains_addr,
-            METH_VARARGS,
-            "contains_addr($self, addr, index, repeat, /)\n--\n\nTell if the generator contains an address."
-        },
-#endif
-        {
-            "get_flags", py_line_generator_get_flags,
-            METH_VARARGS,
-            "get_flags($self, index, repeat, /)\n--\n\nGet the flags of a position from the generator."
-        },
-        {
-            "print", py_line_generator_print,
-            METH_VARARGS,
-            "print($self, line, index, repeat, content, /)\n--\n\nProduce output into a line from content."
-        },
+        LINE_GENERATOR_COUNT_LINES_WRAPPER,
+        LINE_GENERATOR_COMPUTE_CURSOR_WRAPPER,
+        LINE_GENERATOR_CONTAIN_CURSOR_WRAPPER,
+        LINE_GENERATOR_GET_FLAGS_WRAPPER,
+        LINE_GENERATOR_PRINT_WRAPPER,
+        LINE_GENERATOR_COMPUTE_CURSOR_METHOD,
+        LINE_GENERATOR_CONTAIN_CURSOR_METHOD,
+        LINE_GENERATOR_GET_FLAGS_METHOD,
+        LINE_GENERATOR_PRINT_METHOD,
         { NULL }
     };
 
     static PyGetSetDef py_line_generator_getseters[] = {
+        LINE_GENERATOR_LINES_COUNT_ATTRIB,
         { NULL }
     };
 
@@ -360,11 +773,11 @@ PyTypeObject *get_python_line_generator_type(void)
         PyVarObject_HEAD_INIT(NULL, 0)
 
         .tp_name        = "pychrysalide.glibext.LineGenerator",
-        //.tp_basicsize   = sizeof(PyGObject),
+        .tp_basicsize   = sizeof(PyObject),
 
         .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
 
-        .tp_doc         = "PyChrysalide line content generator",
+        .tp_doc         = LINE_GENERATOR_DOC,
 
         .tp_methods     = py_line_generator_methods,
         .tp_getset      = py_line_generator_getseters,
@@ -394,6 +807,14 @@ bool ensure_python_line_generator_is_registered(void)
     PyObject *module;                       /* Module à recompléter        */
     PyObject *dict;                         /* Dictionnaire du module      */
 
+    static GInterfaceInfo info = {          /* Paramètres d'inscription    */
+
+        .interface_init = (GInterfaceInitFunc)py_line_generator_interface_init,
+        .interface_finalize = NULL,
+        .interface_data = NULL,
+
+    };
+
     type = get_python_line_generator_type();
 
     if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
@@ -402,7 +823,7 @@ bool ensure_python_line_generator_is_registered(void)
 
         dict = PyModule_GetDict(module);
 
-        if (!register_interface_for_pygobject(dict, G_TYPE_LINE_GENERATOR, type))
+        if (!register_interface_for_pygobject_2(dict, G_TYPE_LINE_GENERATOR, type, &info))
             return false;
 
     }
@@ -410,3 +831,48 @@ bool ensure_python_line_generator_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 générateur de lignes.                  *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_line_generator(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_line_generator_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 line generator");
+            break;
+
+        case 1:
+            *((GLineGenerator **)dst) = G_LINE_GENERATOR(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/linegen.h b/plugins/pychrysalide/glibext/linegen.h
index 4502347..bfad885 100644
--- a/plugins/pychrysalide/glibext/linegen.h
+++ b/plugins/pychrysalide/glibext/linegen.h
@@ -37,6 +37,9 @@ PyTypeObject *get_python_line_generator_type(void);
 /* Prend en charge l'objet 'pychrysalide.glibext.LineGenerator'. */
 bool ensure_python_line_generator_is_registered(void);
 
+/* Tente de convertir en générateur de lignes. */
+int convert_to_line_generator(PyObject *, void *);
+
 
 
 #endif  /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H */
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index 47285f0..5e911ea 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -916,6 +916,51 @@ bool register_interface_for_pygobject(PyObject *dict, GType gtype, PyTypeObject
 *  Paramètres  : dict  = dictionnaire où conserver une référence au type créé.*
 *                gtype = type dans sa version GLib.                           *
 *                type  = type dans sa version Python.                         *
+*                                                                             *
+*  Description : Enregistre correctement une interface GObject pour Python.   *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool register_interface_for_pygobject_2(PyObject *dict, GType gtype, PyTypeObject *type, const GInterfaceInfo *info)
+{
+    bool result;                            /* Bilan à retourner           */
+    char *name;                             /* Désignation de la classe    */
+
+    assert(gtype != G_TYPE_INVALID);
+
+    name = strrchr(type->tp_name, '.');
+    assert(name != NULL);
+
+    name++;
+
+    pyg_register_interface(dict, name, gtype, type);
+
+    pyg_register_interface_info(gtype, info);
+
+    if (startswith(type->tp_name, "pychrysalide."))
+    {
+        define_auto_documentation(type);
+
+        result = include_python_type_into_features(dict, type);
+
+    }
+    else
+        result = true;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : dict  = dictionnaire où conserver une référence au type créé.*
+*                gtype = type dans sa version GLib.                           *
+*                type  = type dans sa version Python.                         *
 *                base  = type de base de l'objet.                             *
 *                                                                             *
 *  Description : Enregistre un type Python dérivant d'un type GLib dynamique. *
diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h
index 8ed9d9a..dbc9272 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -162,7 +162,10 @@ bool _register_class_for_pygobject(PyObject *, GType, PyTypeObject *, PyTypeObje
     _register_class_for_pygobject(dict, gtype, type, base, NULL)
 
 /* Enregistre correctement une interface GObject pour Python. */
-bool register_interface_for_pygobject(PyObject *, GType, PyTypeObject *);
+bool register_interface_for_pygobject(PyObject *, GType, PyTypeObject *) __attribute__ ((deprecated));
+
+/* Enregistre correctement une interface GObject pour Python. */
+bool register_interface_for_pygobject_2(PyObject *, GType, PyTypeObject *, const GInterfaceInfo *);
 
 /* Enregistre un type Python dérivant d'un type GLib dynamique. */
 bool register_class_for_dynamic_pygobject(GType, PyTypeObject *, PyTypeObject *);
diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am
index af4f876..4739fef 100644
--- a/src/glibext/Makefile.am
+++ b/src/glibext/Makefile.am
@@ -11,6 +11,7 @@ libglibext_la_SOURCES =					\
 	gbinarycursor.h gbinarycursor.c		\
 	gbinportion-int.h					\
 	gbinportion.h gbinportion.c			\
+	gbuffercache-int.h					\
 	gbuffercache.h gbuffercache.c		\
 	gbufferline.h gbufferline.c			\
 	gbufferview.h gbufferview.c			\
diff --git a/src/glibext/gbuffercache-int.h b/src/glibext/gbuffercache-int.h
new file mode 100644
index 0000000..6886fb0
--- /dev/null
+++ b/src/glibext/gbuffercache-int.h
@@ -0,0 +1,96 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * gbuffercache-int.h - définitions internes d'affichage à la demande d'un ensemble de lignes
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_GBUFFERCACHE_INT_H
+#define _GLIBEXT_GBUFFERCACHE_INT_H
+
+
+#include "gbuffercache.h"
+
+
+
+/* --------------------- FONCTIONS AUXILIAIRES DE MANIPULATIONS --------------------- */
+
+
+/* Informations rattachées à la génération d'une ligne */
+typedef struct _generator_link
+{
+    GLineGenerator *instance;               /* Fournisseur de contenu      */
+    size_t repeat;                          /* Compteur de successions     */
+
+} generator_link;
+
+/* Suivi interne de l'état d'une ligne */
+typedef struct _cache_info
+{
+    union
+    {
+        generator_link generator;           /* Générateur unique           */
+        generator_link *generators;         /* Liste de générateurs        */
+    };
+    size_t count;                           /* Taille de cette liste       */
+
+    GBufferLine *line;                      /* Ligne en place ou NULL      */
+
+    BufferLineFlags extra_flags;            /* Propriétés supplémentaires  */
+
+} cache_info;
+
+
+
+/* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */
+
+
+/* Tampon pour gestion de lignes optimisée (instance) */
+struct _GBufferCache
+{
+    GObject parent;                         /* A laisser en premier        */
+
+    GBinContent *content;                   /* Contenu binaire global      */
+
+    cache_info *lines;                      /* Liste des lignes intégrées  */
+    size_t count;                           /* Quantité en cache           */
+    size_t used;                            /* Quantité utilisée           */
+
+    GWidthTracker *tracker;                 /* Suivi des largeurs          */
+
+};
+
+/* Tampon pour gestion de lignes optimisée (classe) */
+struct _GBufferCacheClass
+{
+    GObjectClass parent;                    /* A laisser en premier        */
+
+    gint line_height;                       /* Hauteur maximale des lignes */
+    gint left_margin;                       /* Marge gauche + espace       */
+    gint text_pos;                          /* Début d'impression du code  */
+
+    /* Signaux */
+
+    void (* size_changed) (GBufferCache *, bool, size_t, size_t);
+
+};
+
+
+
+#endif  /* _GLIBEXT_GBUFFERCACHE_INT_H */
diff --git a/src/glibext/gbuffercache.c b/src/glibext/gbuffercache.c
index 08ca020..d918a5d 100644
--- a/src/glibext/gbuffercache.c
+++ b/src/glibext/gbuffercache.c
@@ -29,6 +29,7 @@
 #include <stdlib.h>
 
 
+#include "gbuffercache-int.h"
 #include "chrysamarshal.h"
 
 
@@ -36,31 +37,6 @@
 /* --------------------- FONCTIONS AUXILIAIRES DE MANIPULATIONS --------------------- */
 
 
-/* Informations rattachées à la génération d'une ligne */
-typedef struct _generator_link
-{
-    GLineGenerator *instance;               /* Fournisseur de contenu      */
-    size_t repeat;                          /* Compteur de successions     */
-
-} generator_link;
-
-/* Suivi interne de l'état d'une ligne */
-typedef struct _cache_info
-{
-    union
-    {
-        generator_link generator;           /* Générateur unique           */
-        generator_link *generators;         /* Liste de générateurs        */
-    };
-    size_t count;                           /* Taille de cette liste       */
-
-    GBufferLine *line;                      /* Ligne en place ou NULL      */
-
-    BufferLineFlags extra_flags;            /* Propriétés supplémentaires  */
-
-} cache_info;
-
-
 /* Gros verrou global pour alléger les structures... */
 G_LOCK_DEFINE_STATIC(_line_update);
 
@@ -97,37 +73,6 @@ static void reset_cache_info_line(cache_info *);
 /* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */
 
 
-/* Tampon pour gestion de lignes optimisée (instance) */
-struct _GBufferCache
-{
-    GObject parent;                         /* A laisser en premier        */
-
-    GBinContent *content;                   /* Contenu binaire global      */
-
-    cache_info *lines;                      /* Liste des lignes intégrées  */
-    size_t count;                           /* Quantité en cache           */
-    size_t used;                            /* Quantité utilisée           */
-
-    GWidthTracker *tracker;                 /* Suivi des largeurs          */
-
-};
-
-/* Tampon pour gestion de lignes optimisée (classe) */
-struct _GBufferCacheClass
-{
-    GObjectClass parent;                    /* A laisser en premier        */
-
-    gint line_height;                       /* Hauteur maximale des lignes */
-    gint left_margin;                       /* Marge gauche + espace       */
-    gint text_pos;                          /* Début d'impression du code  */
-
-    /* Signaux */
-
-    void (* size_changed) (GBufferCache *, bool, size_t, size_t);
-
-};
-
-
 /* Taille des allocations de masse */
 #define LINE_ALLOC_BULK 1000
 
@@ -388,7 +333,7 @@ static void get_cache_info_cursor(const cache_info *info, size_t index, gint x,
     else
         generator = &info->generators[0];
 
-    g_line_generator_compute_cursor(generator->instance, x, index, generator->repeat, cursor);
+    *cursor = g_line_generator_compute_cursor(generator->instance, x, index, generator->repeat);
 
 }
 
@@ -586,6 +531,12 @@ static void g_buffer_cache_class_init(GBufferCacheClass *class)
 
 static void g_buffer_cache_init(GBufferCache *cache)
 {
+    cache->content = NULL;
+
+    cache->lines = NULL;
+    cache->count = 0;
+    cache->used = 0;
+
     cache->tracker = g_width_tracker_new(cache);
 
 }
@@ -723,6 +674,32 @@ gint g_buffer_cache_get_line_height(const GBufferCache *cache)
 *                                                                             *
 *  Paramètres  : cache = tampon de lignes à consulter.                        *
 *                                                                             *
+*  Description : Indique l'éventuel contenu binaire associé au cache.         *
+*                                                                             *
+*  Retour      : Eventuel contenu renseigné ou NULL.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GBinContent *g_buffer_cache_get_content(const GBufferCache *cache)
+{
+    GBinContent *result;                    /* Contenu à retourner         */
+
+    result = cache->content;
+
+    if (result != NULL)
+        g_object_ref(G_OBJECT(result));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cache = tampon de lignes à consulter.                        *
+*                                                                             *
 *  Description : Fournit la taille réservée pour la marge gauche.             *
 *                                                                             *
 *  Retour      : Largeur en pixels.                                           *
@@ -895,14 +872,14 @@ void g_buffer_cache_insert_at(GBufferCache *cache, size_t index, GLineGenerator
 
     if (!before && !after)
     {
-        g_line_generator_compute_cursor(generator, 0, index, 0, &gen_cursor);
+        gen_cursor = g_line_generator_compute_cursor(generator, 0, index, 0);
 
         get_cache_info_cursor(&cache->lines[index], index, 0, &line_cursor);
 
         ret = g_line_cursor_compare(gen_cursor, line_cursor);
 
-        g_object_unref(G_OBJECT(gen_cursor));
         g_object_unref(G_OBJECT(line_cursor));
+        g_object_unref(G_OBJECT(gen_cursor));
 
         assert(ret == 0);
 
@@ -1264,8 +1241,6 @@ void g_buffer_cache_extend_with(GBufferCache *cache, size_t count, GLineGenerato
 
     cache->used = count;
 
-    g_object_unref(G_OBJECT(generator));
-
     if (added > 0)
     {
         g_width_tracker_update_added(cache->tracker, index, added);
@@ -1297,8 +1272,6 @@ void g_buffer_cache_truncate(GBufferCache *cache, size_t max)
     size_t j;                               /* Boucle de parcours #2       */
     size_t removed;                         /* Nombre de retraits effectués*/
 
-    assert(max <= cache->used);
-
     for (i = max; i < cache->used; i++)
     {
         info = &cache->lines[i];
@@ -1319,12 +1292,12 @@ void g_buffer_cache_truncate(GBufferCache *cache, size_t max)
 
     }
 
-    removed = cache->used - max;
+    if (max < cache->used)
+    {
+        removed = cache->used - max;
 
-    cache->used = max;
+        cache->used = max;
 
-    if (removed > 0)
-    {
         g_width_tracker_update_deleted(cache->tracker, max, max + removed - 1);
 
         g_signal_emit_by_name(cache, "size-changed", false, max, removed);
@@ -1378,6 +1351,8 @@ BufferLineFlags g_buffer_cache_get_line_flags(const GBufferCache *cache, size_t
     const generator_link *generator;        /* Générateur retenu           */
     size_t i;                               /* Boucle de parcours          */
 
+    // TODO : check lock
+
     assert(index < cache->used);
 
     info = &cache->lines[index];
@@ -1650,6 +1625,8 @@ size_t g_buffer_cache_look_for_flag(const GBufferCache *cache, size_t start, Buf
     GLineCursor *next;                      /* Localisation suivante       */
     int ret;                                /* Bilan de comparaison        */
 
+    // TODO : check lock
+
     assert(start < cache->used);
 
     result = start;
diff --git a/src/glibext/gbuffercache.h b/src/glibext/gbuffercache.h
index 58516b2..79158c4 100644
--- a/src/glibext/gbuffercache.h
+++ b/src/glibext/gbuffercache.h
@@ -39,12 +39,12 @@
 /* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */
 
 
-#define G_TYPE_BUFFER_CACHE             (g_buffer_cache_get_type())
-#define G_BUFFER_CACHE(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_CODE_BUFFER, GBufferCache))
-#define G_BUFFER_CACHE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_CODE_BUFFER, GBufferCacheClass))
-#define G_IS_BUFFER_CACHE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_CODE_BUFFER))
-#define G_IS_BUFFER_CACHE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_CODE_BUFFER))
-#define G_BUFFER_CACHE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_CODE_BUFFER, GBufferCacheClass))
+#define G_TYPE_BUFFER_CACHE            g_buffer_cache_get_type()
+#define G_BUFFER_CACHE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_BUFFER_CACHE, GBufferCache))
+#define G_BUFFER_CACHE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BUFFER_CACHE, GBufferCacheClass))
+#define G_IS_BUFFER_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_BUFFER_CACHE))
+#define G_IS_BUFFER_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BUFFER_CACHE))
+#define G_BUFFER_CACHE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BUFFER_CACHE, GBufferCacheClass))
 
 
 /* Tampon pour gestion de lignes optimisée (instance) */
@@ -60,6 +60,9 @@ GType g_buffer_cache_get_type(void);
 /* Crée un nouveau composant de tampon pour code désassemblé. */
 GBufferCache *g_buffer_cache_new(GBinContent *);
 
+/* Indique l'éventuel contenu binaire associé au cache. */
+GBinContent *g_buffer_cache_get_content(const GBufferCache *);
+
 /* Fournit la hauteur d'impression d'une ligne visualisée. */
 gint g_buffer_cache_get_line_height(const GBufferCache *);
 
diff --git a/src/glibext/gbufferline.c b/src/glibext/gbufferline.c
index 9b0a5a9..e3482aa 100644
--- a/src/glibext/gbufferline.c
+++ b/src/glibext/gbufferline.c
@@ -546,13 +546,16 @@ GObject *g_buffer_line_find_first_segment_creator(const GBufferLine *line, Buffe
 *                                                                             *
 ******************************************************************************/
 
-void g_buffer_line_append_text(GBufferLine *line, BufferLineColumn column, const char *text, size_t length, RenderingTagType type, GObject *creator)
+void g_buffer_line_append_text(GBufferLine *line, size_t column, const char *text, size_t length, RenderingTagType type, GObject *creator)
 {
     size_t index;                           /* Indice d'insertion          */
     content_origin *origin;                 /* Définition d'une origine    */
 
     assert(length > 0);
 
+    if (column == -1)
+        column = BLC_LAST_USED;
+
     if (column == BLC_MAIN)
         column = BLC_ASSEMBLY;//line->main_column;
 
diff --git a/src/glibext/gbufferline.h b/src/glibext/gbufferline.h
index e3d4894..11790fe 100644
--- a/src/glibext/gbufferline.h
+++ b/src/glibext/gbufferline.h
@@ -36,12 +36,12 @@
 
 
 
-#define G_TYPE_BUFFER_LINE                  (g_buffer_line_get_type())
-#define G_BUFFER_LINE(obj)                  (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_BUFFER_LINE, GBufferLine))
-#define G_BUFFER_LINE_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BUFFER_LINE, GBufferLineClass))
-#define G_IS_BUFFER_LINE(obj)               (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_BUFFER_LINE))
-#define G_IS_BUFFER_LINE_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BUFFER_LINE))
-#define G_BUFFER_LINE_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BUFFER_LINE, GBufferLineClass))
+#define G_TYPE_BUFFER_LINE            g_buffer_line_get_type()
+#define G_BUFFER_LINE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_BUFFER_LINE, GBufferLine))
+#define G_BUFFER_LINE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BUFFER_LINE, GBufferLineClass))
+#define G_IS_BUFFER_LINE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_BUFFER_LINE))
+#define G_IS_BUFFER_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BUFFER_LINE))
+#define G_BUFFER_LINE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BUFFER_LINE, GBufferLineClass))
 
 
 /* Représentation de fragments de texte en ligne (instance) */
@@ -124,7 +124,7 @@ void g_buffer_line_fill_content(GBufferLine *, const GBinContent *, const mrange
 GObject *g_buffer_line_find_first_segment_creator(const GBufferLine *, BufferLineColumn);
 
 /* Ajoute du texte à formater dans une ligne donnée. */
-void g_buffer_line_append_text(GBufferLine *, BufferLineColumn, const char *, size_t, RenderingTagType, GObject *);
+void g_buffer_line_append_text(GBufferLine *, size_t, const char *, size_t, RenderingTagType, GObject *);
 
 /* Remplace du texte dans une ligne donnée. */
 bool g_buffer_line_replace_text(GBufferLine *, const GObject *, const char *, size_t);
diff --git a/src/glibext/linegen.c b/src/glibext/linegen.c
index 67b348a..42915ee 100644
--- a/src/glibext/linegen.c
+++ b/src/glibext/linegen.c
@@ -90,18 +90,18 @@ size_t g_line_generator_count_lines(const GLineGenerator *generator)
 *                x         = position géographique sur la ligne concernée.    *
 *                index     = indice de cette même ligne dans le tampon global.*
 *                repeat    = indice d'utilisations successives du générateur. *
-*                cursor    = emplacement à constituer. [OUT]                  *
 *                                                                             *
 *  Description : Retrouve l'emplacement correspondant à une position donnée.  *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : Emplacement constitué.                                       *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-void g_line_generator_compute_cursor(const GLineGenerator *generator, gint x, size_t index, size_t repeat, GLineCursor **cursor)
+GLineCursor *g_line_generator_compute_cursor(const GLineGenerator *generator, gint x, size_t index, size_t repeat)
 {
+    GLineCursor *result;                    /* Emplacement à renvoyer      */
     GLineGeneratorIface *iface;             /* Interface utilisée          */
 
     iface = G_LINE_GENERATOR_GET_IFACE(generator);
@@ -111,7 +111,9 @@ void g_line_generator_compute_cursor(const GLineGenerator *generator, gint x, si
         assert(repeat < g_line_generator_count_lines(generator));
 #endif
 
-    iface->compute(generator, x, index, repeat, cursor);
+    iface->compute(generator, x, index, repeat, &result);
+
+    return result;
 
 }
 
@@ -133,6 +135,7 @@ void g_line_generator_compute_cursor(const GLineGenerator *generator, gint x, si
 
 int g_line_generator_contains_cursor(const GLineGenerator *generator, size_t index, size_t repeat, const GLineCursor *cursor)
 {
+    int result;                             /* Bilan d'analyse à retourner */
     GLineGeneratorIface *iface;             /* Interface utilisée          */
 
     iface = G_LINE_GENERATOR_GET_IFACE(generator);
@@ -142,7 +145,9 @@ int g_line_generator_contains_cursor(const GLineGenerator *generator, size_t ind
         assert(repeat < g_line_generator_count_lines(generator));
 #endif
 
-    return iface->contains(generator, index, repeat, cursor);
+    result = iface->contains(generator, index, repeat, cursor);
+
+    return result;
 
 }
 
@@ -163,6 +168,7 @@ int g_line_generator_contains_cursor(const GLineGenerator *generator, size_t ind
 
 BufferLineFlags g_line_generator_get_flags(const GLineGenerator *generator, size_t index, size_t repeat)
 {
+    BufferLineFlags result;                 /* Fanions à retourner         */
     GLineGeneratorIface *iface;             /* Interface utilisée          */
 
     iface = G_LINE_GENERATOR_GET_IFACE(generator);
@@ -172,7 +178,9 @@ BufferLineFlags g_line_generator_get_flags(const GLineGenerator *generator, size
         assert(repeat < g_line_generator_count_lines(generator));
 #endif
 
-    return iface->get_flags(generator, index, repeat);
+    result = iface->get_flags(generator, index, repeat);
+
+    return result;
 
 }
 
@@ -204,6 +212,6 @@ void g_line_generator_print(GLineGenerator *generator, GBufferLine *line, size_t
         assert(repeat < g_line_generator_count_lines(generator));
 #endif
 
-    return iface->print(generator, line, index, repeat, content);
+    iface->print(generator, line, index, repeat, content);
 
 }
diff --git a/src/glibext/linegen.h b/src/glibext/linegen.h
index cb2e1fd..967e95e 100644
--- a/src/glibext/linegen.h
+++ b/src/glibext/linegen.h
@@ -34,7 +34,7 @@
 
 
 
-#define G_TYPE_LINE_GENERATOR               (g_line_generator_get_type())
+#define G_TYPE_LINE_GENERATOR               g_line_generator_get_type()
 #define G_LINE_GENERATOR(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_LINE_GENERATOR, GLineGenerator))
 #define G_LINE_GENERATOR_CLASS(vtable)      (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_LINE_GENERATOR, GLineGeneratorIface))
 #define GTK_IS_LINE_GENERATOR(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_LINE_GENERATOR))
@@ -56,7 +56,7 @@ GType g_line_generator_get_type(void) G_GNUC_CONST;
 size_t g_line_generator_count_lines(const GLineGenerator *);
 
 /* Retrouve l'emplacement correspondant à une position donnée. */
-void g_line_generator_compute_cursor(const GLineGenerator *, gint, size_t, size_t, GLineCursor **);
+GLineCursor *g_line_generator_compute_cursor(const GLineGenerator *, gint, size_t, size_t);
 
 /* Détermine si le conteneur s'inscrit dans une plage donnée. */
 int g_line_generator_contains_cursor(const GLineGenerator *, size_t, size_t, const GLineCursor *);
diff --git a/src/gtkext/hexdisplay.c b/src/gtkext/hexdisplay.c
index 685fc81..0f0bd30 100644
--- a/src/gtkext/hexdisplay.c
+++ b/src/gtkext/hexdisplay.c
@@ -321,10 +321,7 @@ static void gtk_hex_display_populate_cache(GtkHexDisplay *display)
         g_buffer_cache_truncate(display->cache, needed);
 
     else if (needed > count)
-    {
-        g_object_ref(G_OBJECT(display->generator));
         g_buffer_cache_extend_with(display->cache, needed, G_LINE_GENERATOR(display->generator));
-    }
 
     if (needed != count)
         gtk_widget_queue_resize(GTK_WIDGET(display));
diff --git a/tests/glibext/buffercache.py b/tests/glibext/buffercache.py
new file mode 100644
index 0000000..30488f7
--- /dev/null
+++ b/tests/glibext/buffercache.py
@@ -0,0 +1,49 @@
+
+from chrysacase import ChrysalideTestCase
+from gi.repository import GObject
+from pychrysalide.analysis.contents import MemoryContent
+from pychrysalide.glibext import BufferCache
+from pychrysalide.glibext import BufferLine
+from pychrysalide.glibext import LineGenerator
+
+
+class CommentBuilder(GObject.Object, LineGenerator):
+    def _count_lines(self):
+        return 1
+    def _get_flags(self, index, repeat):
+        return BufferLine.BufferLineFlags.NONE
+    def _print(self, line, index, repeat, content):
+        line.append_text(0, '# Comment', BufferLine.RenderingTagType.PRINTABLE)
+
+
+class TestBufferCache(ChrysalideTestCase):
+    """TestCase for glibext.BufferCache*"""
+
+    def testCacheConstructor(self):
+        """Build all possible kinds of buffer caches."""
+
+        cache = BufferCache()
+        self.assertIsNotNone(cache)
+        self.assertIsNone(cache.content)
+
+        cache = BufferCache(None)
+        self.assertIsNotNone(cache)
+        self.assertIsNone(cache.content)
+
+        cnt = MemoryContent(b'\x00' * 8)
+
+        cache = BufferCache(cnt)
+        self.assertIsNotNone(cache)
+        self.assertIsNotNone(cache.content)
+
+
+    def testCacheRendering(self):
+        """Check a buffer cache simple content."""
+
+        cache = BufferCache()
+
+        cache.append(CommentBuilder(), BufferLine.BufferLineFlags.NONE)
+
+        cache.append(CommentBuilder())
+
+        self.assertEqual(2, cache.lines_count)
-- 
cgit v0.11.2-87-g4458