summaryrefslogtreecommitdiff
path: root/src/glibext/gbuffercache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/glibext/gbuffercache.c')
-rw-r--r--src/glibext/gbuffercache.c1625
1 files changed, 1625 insertions, 0 deletions
diff --git a/src/glibext/gbuffercache.c b/src/glibext/gbuffercache.c
new file mode 100644
index 0000000..57a9487
--- /dev/null
+++ b/src/glibext/gbuffercache.c
@@ -0,0 +1,1625 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * gbuffercache.c - affichage à la demande d'un ensemble de lignes
+ *
+ * Copyright (C) 2016 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "gbuffercache.h"
+
+
+#include <assert.h>
+#include <malloc.h>
+#include <stdlib.h>
+
+
+#include "chrysamarshal.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;
+
+
+/* Gros verrou global pour alléger les structures... */
+G_LOCK_DEFINE_STATIC(_line_update);
+
+
+/* Met en place un nouvel ensemble d'information sur une ligne. */
+static void init_cache_info(cache_info *, GLineGenerator *, size_t, BufferLineFlags);
+
+/* Libère la mémoire occupée par des informations sur une ligne. */
+static void release_cache_info(cache_info *);
+
+/* Ajoute un générateur aux informations sur une ligne. */
+static void extend_cache_info(cache_info *, GLineGenerator *);
+
+/* Retire un générateur aux informations d'une ligne. */
+static void remove_from_cache_info(cache_info *, GLineGenerator *);
+
+/* Retrouve l'emplacement correspondant à une position de ligne. */
+static void get_cache_info_addr(const cache_info *, size_t, gint, vmpa2t *);
+
+/* Suivit les variations du compteur de références d'une ligne. */
+static void on_line_ref_toggle(cache_info *, GBufferLine *, gboolean);
+
+/* Fournit la ligne de tampon correspondant aux générateurs. */
+static GBufferLine *get_cache_info_line(cache_info *, size_t);
+
+/* Force la réinitialisation d'une éventuelle ligne cachée. */
+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 */
+
+ 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
+
+
+/* Procède à l'initialisation d'une classe de tampon de lignes. */
+static void g_buffer_cache_class_init(GBufferCacheClass *);
+
+/* Procède à l'initialisation d'un tampon de gestion de lignes. */
+static void g_buffer_cache_init(GBufferCache *);
+
+/* Supprime toutes les références externes. */
+static void g_buffer_cache_dispose(GBufferCache *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_buffer_cache_finalize(GBufferCache *);
+
+/* Calcule l'indice d'apparition d'un générateur dans le tampon. */
+static size_t g_buffer_cache_compute_repetition(GBufferCache *, size_t, GLineGenerator *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* FONCTIONS AUXILIAIRES DE MANIPULATIONS */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations concernant une ligne à constituer. *
+* generator = générateur à associer à toutes les lignes. *
+* repeat = compteur de répétition entre les lignes. *
+* flags = propriétés supplémentaires à associer à la ligne.*
+* *
+* Description : Met en place un nouvel ensemble d'information sur une ligne. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void init_cache_info(cache_info *info, GLineGenerator *generator, size_t repeat, BufferLineFlags flags)
+{
+ info->generator.instance = generator;
+ info->generator.repeat = repeat;
+
+ g_object_ref(G_OBJECT(generator));
+
+ info->count = 1;
+
+ info->line = NULL;
+
+ info->extra_flags = flags;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations concernant une ligne à constituer. *
+* *
+* Description : Libère la mémoire occupée par des informations sur une ligne.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void release_cache_info(cache_info *info)
+{
+ size_t i; /* Boucle de parcours */
+
+ if (info->count == 1)
+ g_object_unref(G_OBJECT(info->generator.instance));
+
+ else
+ for (i = 0; i < info->count; i++)
+ g_object_unref(G_OBJECT(info->generators[i].instance));
+
+ reset_cache_info_line(info);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations concernant une ligne à actualiser. *
+* generator = générateur à associer à toutes les lignes. *
+* *
+* Description : Ajoute un générateur aux informations sur une ligne. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void extend_cache_info(cache_info *info, GLineGenerator *generator)
+{
+ generator_link first; /* Générateur déjà en place */
+ generator_link *new; /* Nouveau générateur placé */
+
+ if (info->count == 1)
+ {
+ first = info->generator;
+
+ info->generators = (generator_link *)calloc(2, sizeof(generator_link));
+
+ info->generators[0] = first;
+ info->count = 2;
+
+ new = &info->generators[1];
+
+ }
+ else
+ {
+ info->generators = (generator_link *)realloc(info->generators,
+ ++info->count * sizeof(generator_link));
+
+ new = &info->generators[info->count - 1];
+
+ }
+
+ new->instance = generator;
+ new->repeat = 0;
+
+ g_object_ref(G_OBJECT(generator));
+
+ reset_cache_info_line(info);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations concernant une ligne à actualiser. *
+* generator = générateur à dissocier de toutes les lignes. *
+* *
+* Description : Retire un générateur aux informations d'une ligne. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void remove_from_cache_info(cache_info *info, GLineGenerator *generator)
+{
+ generator_link *link; /* Accès simplifié */
+ size_t i; /* Boucle de parcours */
+ generator_link *old; /* Mémorisation avant opérat° */
+
+ if (info->count == 1)
+ {
+ link = &info->generator;
+
+ assert(link->instance == generator);
+
+ g_object_unref(G_OBJECT(generator));
+
+ info->count = 0;
+
+ }
+
+ else
+ {
+ for (i = 0; i < info->count; i++)
+ {
+ link = &info->generators[i];
+
+ if (link->instance == generator)
+ {
+ if ((i + 1) < info->count)
+ memmove(&info->generators[i], &info->generators[i + 1],
+ (info->count - i - 1) * sizeof(generator_link));
+
+ if (info->count == 2)
+ {
+ old = info->generators;
+
+ info->count = 1;
+ info->generator = info->generators[0];
+
+ free(old);
+
+ }
+ else
+ info->generators = (generator_link *)realloc(info->generators,
+ --info->count * sizeof(generator_link));
+
+ break;
+
+ }
+
+ }
+
+#ifndef NDEBUG
+
+ /**
+ * Attention : si l'élément était en dernière position,
+ * l'indice de parcours est désormais égal au nombre de générateurs présents !
+ */
+ assert(i <= info->count);
+
+ for ( ; i < info->count; i++)
+ {
+ link = &info->generators[i];
+
+ assert(link->instance != generator);
+
+ }
+
+#endif
+
+ }
+
+ reset_cache_info_line(info);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations sur une ligne à venir consulter. *
+* index = indice de la ligne visée par la consultation. *
+* x = position géographique sur la ligne concernée. *
+* addr = adresse à renseigner. [OUT] *
+* *
+* Description : Retrouve l'emplacement correspondant à une position de ligne.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void get_cache_info_addr(const cache_info *info, size_t index, gint x, vmpa2t *addr)
+{
+ const generator_link *generator; /* Générateur retenu */
+
+ if (info->count == 1)
+ generator = &info->generator;
+ else
+ generator = &info->generators[0];
+
+ g_line_generator_compute_addr(generator->instance, x, addr, index, generator->repeat);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations sur une ligne à venir manipuler. *
+* line = tampon de lignes à venir supprimer au besoin. *
+* last = indication sur la valeur du compteur de références. *
+* *
+* Description : Suivit les variations du compteur de références d'une ligne. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void on_line_ref_toggle(cache_info *info, GBufferLine *line, gboolean last)
+{
+ if (last)
+ {
+ G_LOCK(_line_update);
+
+ assert(info->line != NULL);
+
+ info->line = NULL;
+
+ G_UNLOCK(_line_update);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations sur une ligne à venir manipuler. *
+* index = indice de la ligne à constituer. *
+* *
+* Description : Fournit la ligne de tampon correspondant aux générateurs. *
+* *
+* Retour : Ligne déjà en place ou créée pour le besoin. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GBufferLine *get_cache_info_line(cache_info *info, size_t index)
+{
+ GBufferLine *result; /* Construction à retourner */
+ size_t i; /* Boucle de parcours */
+
+ G_LOCK(_line_update);
+
+ result = info->line;
+
+ if (result == NULL)
+ {
+ result = g_buffer_line_new((mrange_t []){ { { 0 }, 0 } }, 0/* !! */);
+
+ g_object_add_toggle_ref(G_OBJECT(result), (GToggleNotify)on_line_ref_toggle, info);
+
+ if (info->count == 1)
+ g_line_generator_print(info->generator.instance, result, index, info->generator.repeat);
+
+ else
+ for (i = 0; i < info->count; i++)
+ g_line_generator_print(info->generators[i].instance, result, index, info->generators[i].repeat);
+
+ info->line = result;
+
+ }
+
+ else
+ g_object_ref(G_OBJECT(result));
+
+ G_UNLOCK(_line_update);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations sur une ligne à venir manipuler. *
+* *
+* Description : Force la réinitialisation d'une éventuelle ligne cachée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void reset_cache_info_line(cache_info *info)
+{
+ G_LOCK(_line_update);
+
+ if (info->line != NULL)
+ {
+ g_object_remove_toggle_ref(G_OBJECT(info->line), (GToggleNotify)on_line_ref_toggle, info);
+
+ g_object_unref(G_OBJECT(info->line));
+
+ info->line = NULL;
+
+ }
+
+ G_UNLOCK(_line_update);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* TAMPON POUR CODE DESASSEMBLE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Détermine le type du composant de tampon pour gestion de lignes optimisée. */
+G_DEFINE_TYPE(GBufferCache, g_buffer_cache, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe de composant GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation d'une classe de tampon de lignes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_buffer_cache_class_init(GBufferCacheClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_buffer_cache_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_buffer_cache_finalize;
+
+ class->line_height = 17;
+ class->left_margin = 2 * class->line_height;
+ class->text_pos = 2.5 * class->line_height;
+
+ /* Signaux */
+
+ g_signal_new("size-changed",
+ G_TYPE_BUFFER_CACHE,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(GBufferCacheClass, size_changed),
+ NULL, NULL,
+ g_cclosure_user_marshal_VOID__BOOLEAN_ULONG_ULONG,
+ G_TYPE_NONE, 3, G_TYPE_BOOLEAN, G_TYPE_ULONG, G_TYPE_ULONG);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = composant GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation d'un tampon de gestion de lignes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_buffer_cache_init(GBufferCache *cache)
+{
+ cache->tracker = g_width_tracker_new(cache);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_buffer_cache_dispose(GBufferCache *cache)
+{
+ size_t i; /* Boucle de parcours #1 */
+ cache_info *info; /* Accès directe à une ligne */
+ size_t j; /* Boucle de parcours #2 */
+
+ for (i = 0; i < cache->used; i++)
+ {
+ info = &cache->lines[i];
+
+ if (info->count == 1)
+ g_object_unref(G_OBJECT(info->generator.instance));
+
+ else
+ for (j = 0; j < info->count; j++)
+ g_object_unref(G_OBJECT(info->generators[j].instance));
+
+ if (info->line)
+ g_object_unref(G_OBJECT(info->line));
+
+ }
+
+ g_object_unref(G_OBJECT(cache->tracker));
+
+ G_OBJECT_CLASS(g_buffer_cache_parent_class)->dispose(G_OBJECT(cache));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_buffer_cache_finalize(GBufferCache *cache)
+{
+ size_t i; /* Boucle de parcours */
+ cache_info *info; /* Accès directe à une ligne */
+
+ for (i = 0; i < cache->used; i++)
+ {
+ info = &cache->lines[i];
+
+ if (info->count > 1)
+ free(info->generators);
+
+ }
+
+ if (cache->lines != NULL)
+ free(cache->lines);
+
+ G_OBJECT_CLASS(g_buffer_cache_parent_class)->finalize(G_OBJECT(cache));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Crée un nouveau composant de tampon pour code désassemblé. *
+* *
+* Retour : Composant GLib créé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GBufferCache *g_buffer_cache_new(void)
+{
+ GBufferCache *result; /* Composant à retourner */
+
+ result = g_object_new(G_TYPE_BUFFER_CACHE, NULL);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à consulter. *
+* *
+* Description : Fournit la hauteur d'impression d'une ligne visualisée. *
+* *
+* Retour : Hauteur de ligne en pixels. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+gint g_buffer_cache_get_line_height(const GBufferCache *cache)
+{
+ GBufferCacheClass *class; /* Classe des tampons */
+
+ class = G_BUFFER_CACHE_GET_CLASS(cache);
+
+ return class->line_height;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à consulter. *
+* *
+* Description : Fournit la taille réservée pour la marge gauche. *
+* *
+* Retour : Largeur en pixels. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+gint g_buffer_cache_get_left_margin(const GBufferCache *cache)
+{
+ GBufferCacheClass *class; /* Classe des tampons */
+
+ class = G_BUFFER_CACHE_GET_CLASS(cache);
+
+ return class->left_margin;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à consulter. *
+* *
+* Description : Fournit la position de départ pour l'impression de texte. *
+* *
+* Retour : Position en pixels. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+gint g_buffer_cache_get_text_position(const GBufferCache *cache)
+{
+ GBufferCacheClass *class; /* Classe des tampons */
+
+ class = G_BUFFER_CACHE_GET_CLASS(cache);
+
+ return class->text_pos;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = instance GLib à consulter. *
+* *
+* Description : Compte le nombre de lignes rassemblées dans un tampon. *
+* *
+* Retour : Nombre de lignes constituant le tampon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+size_t g_buffer_cache_count_lines(const GBufferCache *cache)
+{
+ return cache->used;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = composant GLib à consulter. *
+* *
+* Description : Fournit un lien vers la structure de suivi de largeurs. *
+* *
+* Retour : Gestionnaire de largeurs de lignes. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const GWidthTracker *g_buffer_cache_get_width_tracker(const GBufferCache *cache)
+{
+ return cache->tracker;
+
+}
+
+
+
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = instance GLib à consulter. *
+* index = indice de la ligne où se trouve le générateur. *
+* generator = générateur associé à au moins une ligne. *
+* *
+* Description : Calcule l'indice d'apparition d'un générateur dans le tampon.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static size_t g_buffer_cache_compute_repetition(GBufferCache *cache, size_t index, GLineGenerator *generator)
+{
+ size_t result; /* Compteur à retourner */
+ cache_info *info; /* Accès directe à une ligne */
+ size_t i; /* Boucle de parcours */
+
+ result = 0;
+
+ if (index > 0)
+ {
+ info = &cache->lines[index - 1];
+
+ if (info->count == 1)
+ {
+ if (info->generator.instance == generator)
+ result = info->generator.repeat + 1;
+
+ }
+
+ else
+ for (i = 0; i < info->count; i++)
+ if (info->generators[i].instance == generator)
+ {
+ result = info->generators[i].repeat + 1;
+ break;
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = instance GLib à modifier. *
+* index = point d'insertion, puis de sauvegarde. *
+* generator = générateur à insérer dans les lignes. *
+* before = précise l'emplacement final des nouvelles lignes.*
+* after = précise l'emplacement final des nouvelles lignes.*
+* *
+* Description : Insère un générateur dans des lignes à une position donnée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_buffer_cache_insert_at(GBufferCache *cache, size_t index, GLineGenerator *generator, bool before, bool after)
+{
+#ifndef NDEBUG
+ vmpa2t gen_addr; /* Position du générateur */
+ vmpa2t line_addr; /* Position de la ligne */
+#endif
+ size_t needed; /* Emplacements nécessaires */
+ size_t i; /* Boucle de parcours */
+
+ assert(index < cache->used);
+
+ assert(!(before && after));
+
+#ifndef NDEBUG
+
+ g_line_generator_compute_addr(generator, 0, &gen_addr, index, 0);
+
+ get_cache_info_addr(&cache->lines[index], index, 0, &line_addr);
+
+ ///////////////////////////////////////
+ if (cmp_vmpa(&gen_addr, &line_addr) != 0) return;
+
+ assert(cmp_vmpa(&gen_addr, &line_addr) == 0);
+
+#endif
+
+ /* Cas particulier d'ajout en fin de cache... */
+ if (after && (index + 1) == cache->used)
+ {
+ g_buffer_cache_append(cache, generator, BLF_NONE);
+ goto gbcia_done;
+ }
+
+ /* Adaptation de l'espace */
+
+ needed = g_line_generator_count_lines(generator);
+
+ if (before || after)
+ {
+ if ((cache->used + needed) >= cache->count)
+ {
+ cache->count += needed + LINE_ALLOC_BULK;
+ cache->lines = (cache_info *)realloc(cache->lines, cache->count * sizeof(cache_info));
+ }
+ }
+#ifndef NDEBUG
+ else
+ assert(needed == 1);
+#endif
+
+ /* Insertion du générateur */
+
+ if (after)
+ index++;
+
+ if (before || after)
+ {
+ memmove(&cache->lines[index], &cache->lines[index + needed], (cache->used - index) * sizeof(cache_info));
+
+ for (i = 0; i < needed; i++)
+ init_cache_info(&cache->lines[index + i], generator, i, BLF_NONE);
+
+ cache->used += needed;
+
+ g_width_tracker_update_added(cache->tracker, index, needed);
+
+ g_signal_emit_by_name(cache, "size-changed", true, index, needed);
+
+ }
+
+ else
+ {
+ extend_cache_info(&cache->lines[index], generator);
+
+ g_width_tracker_update(cache->tracker, index);
+
+ g_signal_emit_by_name(cache, "size-changed", true, index, 1);
+
+ }
+
+ gbcia_done:
+
+ ;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = instance GLib à modifier. *
+* index = point d'insertion, puis de sauvegarde. *
+* type = type de générateurs à retirer des lignes visées. *
+* before = précise l'emplacement final de l'élément visé. *
+* after = précise l'emplacement final de l'élément visé. *
+* *
+* Description : Retire un type de générateur de lignes. *
+* *
+* Retour : Générateur éventuellement trouvé ou NULL si aucun. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GLineGenerator *g_buffer_cache_delete_type_at(GBufferCache *cache, size_t index, GType type, bool before, bool after)
+{
+ GLineGenerator *result; /* Prédécesseur à retourner */
+ cache_info *info; /* Accès directe à une ligne */
+ generator_link *link; /* Accès simplifié */
+ size_t i; /* Boucle de parcours */
+ size_t count; /* Emplacements occupés */
+ size_t delete; /* Indice de suppression */
+
+ assert(index < cache->used);
+
+ assert(!(before && after));
+
+ result = NULL;
+
+ /* Recherche d'un générateur correspondant */
+
+ if (before)
+ info = &cache->lines[index - 1];
+ else if (after)
+ info = &cache->lines[index + 1];
+ else
+ info = &cache->lines[index];
+
+ if (info->count == 1)
+ {
+ link = &info->generator;
+
+ if (G_OBJECT_TYPE(link->instance) == type)
+ result = link->instance;
+
+ }
+
+ else
+ for (i = 0; i < info->count && result == NULL; i++)
+ {
+ link = &info->generators[i];
+
+ if (G_OBJECT_TYPE(link->instance) == type)
+ result = link->instance;
+
+ }
+
+ /* Retrait de l'instance trouvée */
+
+ if (result != NULL)
+ {
+ count = g_line_generator_count_lines(result);
+
+#ifndef NDEBUG
+ if (!before && !after)
+ assert(count == 1);
+#endif
+
+ g_object_ref(G_OBJECT(result));
+
+ /* Suppression de l'élément */
+
+ for (i = 0; i < count; i++)
+ {
+ if (before)
+ info = &cache->lines[index - 1 - i];
+ else if (after)
+ info = &cache->lines[index + 1 + i];
+ else
+ info = &cache->lines[index];
+
+ remove_from_cache_info(info, result);
+
+ }
+
+ /* Suppression des lignes associées */
+
+ for (i = 0; i < count; i++)
+ {
+ if (before)
+ delete = index - 1;
+ else if (after)
+ delete = index + 1;
+ else
+ delete = index;
+
+ info = &cache->lines[delete];
+
+ if (info->count == 0)
+ {
+ release_cache_info(info);
+
+ if ((delete + 1) < cache->used)
+ memmove(&cache->lines[delete], &cache->lines[delete + 1],
+ (cache->used - delete - 1) * sizeof(cache_info));
+
+ cache->used--;
+
+ g_width_tracker_update_deleted(cache->tracker, delete, delete);
+
+ g_signal_emit_by_name(cache, "size-changed", false, delete, 1);
+
+ }
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = instance GLib à modifier. *
+* generator = générateur à associer à toutes les lignes. *
+* flags = propriétés supplémentaires à associer à la ligne.*
+* *
+* Description : Ajoute en fin de tampon un générateur de lignes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_buffer_cache_append(GBufferCache *cache, GLineGenerator *generator, BufferLineFlags flags)
+{
+ size_t count; /* Nombre de lignes générées */
+ size_t index; /* Point d'insertion */
+ size_t i; /* Boucle de parcours */
+ cache_info *info; /* Accès directe à une ligne */
+
+ count = g_line_generator_count_lines(generator);
+
+ assert(count > 0);
+
+ assert((flags != BLF_NONE && count == 1) || flags == BLF_NONE);
+
+ if ((cache->used + count) > cache->count)
+ {
+ cache->count += count + LINE_ALLOC_BULK;
+ cache->lines = (cache_info *)realloc(cache->lines, cache->count * sizeof(cache_info));
+ }
+
+ index = cache->used;
+
+ for (i = 0; i < count; i++)
+ {
+ info = &cache->lines[index + i];
+
+ info->generator.instance = generator;
+ info->generator.repeat = g_buffer_cache_compute_repetition(cache, index + i, generator);
+
+ g_object_ref(G_OBJECT(generator));
+
+ info->count = 1;
+
+ info->line = NULL;
+
+ info->extra_flags = flags;
+
+ }
+
+ cache->used += count;
+
+ g_object_unref(G_OBJECT(generator));
+
+ g_width_tracker_update_added(cache->tracker, index, count);
+
+ g_signal_emit_by_name(cache, "size-changed", true, index, count);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = instance GLib à modifier. *
+* count = quantité totale de lignes à avoir à disposition. *
+* generator = générateur à associer à toutes les lignes. *
+* *
+* Description : Etend un tampon avec un générateur de lignes unique. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_buffer_cache_extend_with(GBufferCache *cache, size_t count, GLineGenerator *generator)
+{
+ size_t index; /* Point d'insertion */
+ size_t i; /* Boucle de parcours */
+ cache_info *info; /* Accès directe à une ligne */
+ size_t added; /* Nombre d'ajouts effectués */
+
+ assert(count >= cache->used);
+
+ if (count > cache->count)
+ {
+ cache->lines = (cache_info *)realloc(cache->lines, count * sizeof(cache_info));
+ cache->count = count;
+ }
+
+ index = cache->used;
+
+ for (i = index; i < count; i++)
+ {
+ info = &cache->lines[i];
+
+ info->generator.instance = generator;
+ info->generator.repeat = g_buffer_cache_compute_repetition(cache, i, generator);
+
+ g_object_ref(G_OBJECT(generator));
+
+ info->count = 1;
+
+ info->line = NULL;
+
+ }
+
+ added = count - cache->used;
+
+ cache->used = count;
+
+ g_object_unref(G_OBJECT(generator));
+
+ if (added > 0)
+ {
+ g_width_tracker_update_added(cache->tracker, index, added);
+
+ g_signal_emit_by_name(cache, "size-changed", true, index, added);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = instance GLib à modifier. *
+* max = nombre maximal de lignes à conserver. *
+* *
+* Description : Réduit le tampon à une quantité de lignes précise. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_buffer_cache_truncate(GBufferCache *cache, size_t max)
+{
+ size_t i; /* Boucle de parcours #1 */
+ cache_info *info; /* Accès directe à une ligne */
+ 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];
+
+ if (info->count == 1)
+ g_object_unref(G_OBJECT(info->generator.instance));
+
+ else
+ {
+ for (j = 0; j < info->count; j++)
+ g_object_unref(G_OBJECT(info->generators[j].instance));
+
+ free(info->generators);
+
+ }
+
+ reset_cache_info_line(info);
+
+ }
+
+ removed = cache->used - max;
+
+ cache->used = max - 1;
+
+ 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);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à venir consulter. *
+* index = indice de la ligne visée par la consultation. *
+* x = position géographique sur la ligne concernée. *
+* addr = adresse à renseigner. [OUT] *
+* *
+* Description : Retrouve l'emplacement correspondant à une position de ligne.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_buffer_cache_get_line_addr(const GBufferCache *cache, size_t index, gint x, vmpa2t *addr)
+{
+ assert(index < cache->used);
+
+ get_cache_info_addr(&cache->lines[index], index, x, addr);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à venir consulter. *
+* index = indice de la ligne visée par la consultation. *
+* *
+* Description : Détermine l'ensemble des propriétés attachées à une ligne. *
+* *
+* Retour : Somme de toutes les propriétés enregistrées. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+BufferLineFlags g_buffer_cache_get_line_flags(const GBufferCache *cache, size_t index)
+{
+ BufferLineFlags result; /* Somme à renvoyer */
+ cache_info *info; /* Accès directe à une ligne */
+ const generator_link *generator; /* Générateur retenu */
+ size_t i; /* Boucle de parcours */
+
+ assert(index < cache->used);
+
+ info = &cache->lines[index];
+
+ result = info->extra_flags;
+
+ if (info->count == 1)
+ {
+ generator = &info->generator;
+ result |= g_line_generator_get_flags(generator->instance, index, generator->repeat);
+ }
+
+ else
+ for (i = 0; i < info->count; i++)
+ {
+ generator = &info->generators[i];
+ result |= g_line_generator_get_flags(generator->instance, index, generator->repeat);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à consulter. *
+* index = indice de la ligne recherchée. *
+* *
+* Description : Retrouve une ligne au sein d'un tampon avec un indice. *
+* *
+* Retour : Line retrouvée ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GBufferLine *g_buffer_cache_find_line_by_index(const GBufferCache *cache, size_t index)
+{
+ GBufferLine *result; /* Ligne trouvée à retourner */
+
+ if (index < cache->used)
+ result = get_cache_info_line(&cache->lines[index], index);
+ else
+ result = NULL;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à venir consulter. *
+* index = indice de la ligne à mesurer. *
+* summary = largeurs maximales à faire évoluer. *
+* *
+* Description : Fait remonter les largeurs requises par une ligne donnée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_buffer_cache_collect_widths(GBufferCache *cache, size_t index, line_width_summary *summary)
+{
+ GBufferLine *line; /* Ligne éphémère à mesurer */
+
+ line = get_cache_info_line(&cache->lines[index], index);
+
+ g_buffer_line_collect_widths(line, summary);
+
+ g_object_unref(G_OBJECT(line));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = visualisation à représenter. *
+* cr = contexte graphique dédié à la procédure. *
+* first = première ligne à dessiner. *
+* last = dernière ligne à dessiner. *
+* area = position et surface à traiter. *
+* display = règles d'affichage des colonnes modulables. *
+* selected = ordonnée d'une ligne sélectionnée ou NULL. *
+* list = liste de contenus à mettre en évidence. *
+* *
+* Description : Imprime une partie choisie du tampon contenant des lignes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_buffer_cache_draw(const GBufferCache *cache, cairo_t *cr, size_t first, size_t last, const cairo_rectangle_int_t *area, const bool *display, const gint *selected, const segcnt_list *list)
+{
+ GBufferCacheClass *class; /* Classe des tampons */
+ gint y; /* Point de départ en ordonnée */
+ bool wait_selection; /* Sélection déjà passée ? */
+ size_t i; /* Boucle de parcours */
+ cache_info *info; /* Accès directe à une ligne */
+ line_width_summary summary; /* Résumé concis des largeurs */
+ GBufferLine *line; /* Ligne à venir dessiner */
+
+ class = G_BUFFER_CACHE_GET_CLASS(cache);
+
+ y = 0;
+
+ wait_selection = true;
+
+ if (cache->used > 0)
+ for (i = first; i <= last; i++)
+ {
+ /* Si sélection, on sousligne la ligne concernée */
+ if (wait_selection && selected != NULL && *selected == y)
+ {
+ cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.05);
+
+ cairo_rectangle(cr, area->x, y, area->width, class->line_height);
+ cairo_fill(cr);
+
+ wait_selection = false;
+
+ }
+
+ info = &cache->lines[i];
+
+ if (i == first || (g_buffer_cache_get_line_flags(cache, i) & BLF_WIDTH_MANAGER))
+ g_width_tracker_get_local_width_summary(cache->tracker, i, &summary);
+
+ line = get_cache_info_line(info, i);
+
+ g_buffer_line_draw(line, cr, &summary, class->text_pos, y, display, list);
+
+ g_object_unref(G_OBJECT(line));
+
+ y += class->line_height;
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à consulter. *
+* addr = adresse à retrouver dans le tampon. *
+* first = indique si on l'arrête à la première ou la dernière. *
+* start = borne inférieure des recherches (incluse). *
+* end = borne supérieure des recherches (incluse). *
+* *
+* Description : Indique l'indice correspondant à une adresse donnée. *
+* *
+* Retour : Indice des infos à l'adresse demandée, ou nombre de lignes. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+size_t _g_buffer_cache_find_index_by_addr(const GBufferCache *cache, const vmpa2t *addr, bool first, size_t start, size_t end)
+{
+ size_t result; /* Indice à retourner */
+ cache_info *found; /* Eventuel élément trouvé */
+
+ int find_containing_generator(const vmpa2t *a, const cache_info *i)
+ {
+ const generator_link *generator; /* Générateur retenu */
+
+ if (i->count == 1)
+ generator = &i->generator;
+ else
+ generator = &i->generators[0];
+
+ return g_line_generator_contains_addr(generator->instance, addr,
+ i - cache->lines, generator->repeat);
+
+ }
+
+ found = (cache_info *)bsearch(addr, &cache->lines[start], end - start + 1,
+ sizeof(cache_info), (__compar_fn_t)find_containing_generator);
+
+ if (found == NULL)
+ result = cache->used;
+
+ else
+ {
+ result = (found - cache->lines);
+ assert(start <= result && result <= end);
+
+ /* On s'assure d'un arrêt sur la bonne ligne */
+
+ if (first)
+ for (; result > start; result--)
+ {
+ found = &cache->lines[result - 1];
+
+ if (find_containing_generator(addr, found) != 0)
+ break;
+
+ }
+
+ else
+ for (; result < end; result++)
+ {
+ found = &cache->lines[result + 1];
+
+ if (find_containing_generator(addr, found) != 0)
+ break;
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à consulter. *
+* addr = adresse à retrouver dans le tampon. *
+* first = indique si on l'arrête à la première ou la dernière. *
+* *
+* Description : Indique l'indice correspondant à une adresse donnée. *
+* *
+* Retour : Indice des infos à l'adresse demandée, ou nombre de lignes. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+size_t g_buffer_cache_find_index_by_addr(const GBufferCache *cache, const vmpa2t *addr, bool first)
+{
+ size_t result; /* Indice à retourner */
+
+ if (cache->used == 0)
+ result = 0;
+ else
+ result = _g_buffer_cache_find_index_by_addr(cache, addr, first, 0, cache->used - 1);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à consulter. *
+* start = point de départ du parcours. *
+* flag = propriétés à retrouver si possible. *
+* *
+* Description : Avance autant que possible vers une ligne idéale. *
+* *
+* Retour : Indice de la ligne recherchée, si elle existe. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+size_t g_buffer_cache_look_for_flag(const GBufferCache *cache, size_t start, BufferLineFlags flag)
+{
+ size_t result; /* Indice de ligne à retourner */
+ vmpa2t start_addr; /* Localisation de départ */
+ size_t i; /* Boucle de parcours */
+ vmpa2t addr; /* Localisation suivante */
+
+ assert(start < cache->used);
+
+ result = start;
+
+ get_cache_info_addr(&cache->lines[start], start, 0, &start_addr);
+
+ for (i = start + 1; i < cache->used; i++)
+ {
+ get_cache_info_addr(&cache->lines[i], i, 0, &addr);
+
+ if (cmp_vmpa(&start_addr, &addr) != 0)
+ break;
+
+ if ((g_buffer_cache_get_line_flags(cache, i) & flag) != 0)
+ {
+ result = i;
+ break;
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cache = tampon de lignes à consulter. *
+* addr = adresse à présenter à l'écran. *
+* first = borne inférieure des recherches (incluse). *
+* last = borne supérieure des recherches (incluse). *
+* code = s'arrête si possible à une ligne avec code. *
+* x = position horizontale au sein du composant. [OUT] *
+* y = position verticale au sein du composant. [OUT] *
+* *
+* Description : Indique la position d'affichage d'une adresse donnée. *
+* *
+* Retour : true si l'adresse fait partie du composant, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_buffer_cache_get_address_coordinates(const GBufferCache *cache, const vmpa2t *addr, size_t first, size_t last, bool code, gint *x, gint *y)
+{
+ bool result; /* Bilan à retourner */
+ size_t index; /* Indice de correspondance */
+ gint lheight; /* Hauteur d'une ligne */
+ const cache_info *info; /* Infos sur une ligne donnée */
+ const generator_link *generator; /* Générateur retenu */
+
+ index = _g_buffer_cache_find_index_by_addr(cache, addr, true, first, last);
+
+ result = (index < cache->used);
+
+ if (result)
+ {
+ lheight = G_BUFFER_CACHE_GET_CLASS(cache)->line_height;
+
+ *x = 0;
+ *y = (index - first) * G_BUFFER_CACHE_GET_CLASS(cache)->line_height;
+
+ for (; code && index <= last; index++)
+ {
+ if (g_buffer_cache_get_line_flags(cache, index) & BLF_HAS_CODE)
+ break;
+
+ if (index == last)
+ break;
+
+ info = &cache->lines[index + 1];
+
+ if (info->count == 1)
+ generator = &info->generator;
+ else
+ generator = &info->generators[0];
+
+ if (!g_line_generator_contains_addr(generator->instance, addr, index + 1, generator->repeat))
+ break;
+
+ *y += lheight;
+
+ }
+
+ }
+
+ return result;
+
+}