diff options
Diffstat (limited to 'src/glibext/buffercache.c')
-rw-r--r-- | src/glibext/buffercache.c | 1725 |
1 files changed, 1725 insertions, 0 deletions
diff --git a/src/glibext/buffercache.c b/src/glibext/buffercache.c new file mode 100644 index 0000000..16d96dc --- /dev/null +++ b/src/glibext/buffercache.c @@ -0,0 +1,1725 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * buffercache.c - affichage à la demande d'un ensemble de lignes + * + * Copyright (C) 2016-2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "buffercache.h" + + +#include <assert.h> +#include <malloc.h> +#include <stdlib.h> + + +#include "buffercache-int.h" +#include "chrysamarshal.h" + + + +/* --------------------- FONCTIONS AUXILIAIRES DE MANIPULATIONS --------------------- */ + + +/* 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 *, BufferLineFlags); + +/* 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_cursor(const cache_info *, size_t, gint, GLineCursor **); + +/* 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, const GBinContent *); + +/* Force la réinitialisation d'une éventuelle ligne cachée. */ +static void _reset_cache_info_line_unlocked(cache_info *); + +/* Force la réinitialisation d'une éventuelle ligne cachée. */ +static void reset_cache_info_line(cache_info *); + + + +/* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */ + + +/* 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. * +* flags = propriétés supplémentaires à associer à la ligne.* +* * +* Description : Ajoute un générateur aux informations sur une ligne. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void extend_cache_info(cache_info *info, GLineGenerator *generator, BufferLineFlags flags) +{ + 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); + + /** + * On peut rajouter des indications, mais, en cas de retrait d'un générateur, + * on ne saura pas forcément lesquelles retirer puisque qu'on ne trace pas + * leur origine. + * + * On considère donc que seul le premier générateur (le principal) a le + * droit de poser des fanions. + */ + + assert(flags == BLF_NONE); + +} + + +/****************************************************************************** +* * +* 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)); + + g_object_unref(G_OBJECT(generator)); + + 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. * +* cursor = emplacement à constituer. [OUT] * +* * +* Description : Retrouve l'emplacement correspondant à une position de ligne.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void get_cache_info_cursor(const cache_info *info, size_t index, gint x, GLineCursor **cursor) +{ + const generator_link *generator; /* Générateur retenu */ + + if (info->count == 1) + generator = &info->generator; + else + generator = &info->generators[0]; + + *cursor = g_line_generator_compute_cursor(generator->instance, x, 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); + + _reset_cache_info_line_unlocked(info); + + G_UNLOCK(_line_update); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations sur une ligne à venir manipuler. * +* index = indice de la ligne à constituer. * +* content = éventuel contenu binaire brut à imprimer. * +* * +* 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, const GBinContent *content) +{ + 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(UNUSED_MRANGE_PTR, 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, content); + + else + for (i = 0; i < info->count; i++) + g_line_generator_print(info->generators[i].instance, result, index, + info->generators[i].repeat, content); + + 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_unlocked(cache_info *info) +{ + if (info->line != NULL) + { + g_object_remove_toggle_ref(G_OBJECT(info->line), (GToggleNotify)on_line_ref_toggle, info); + + info->line = NULL; + + } + +} + + +/****************************************************************************** +* * +* 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); + + _reset_cache_info_line_unlocked(info); + + 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->content = NULL; + + cache->lines = NULL; + cache->count = 0; + cache->used = 0; + + 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 direct à une ligne */ + size_t j; /* Boucle de parcours #2 */ + + g_clear_object(&cache->content); + + for (i = 0; i < cache->used; i++) + { + info = &cache->lines[i]; + + if (info->count == 1) + g_clear_object(&info->generator.instance); + + else + for (j = 0; j < info->count; j++) + g_clear_object(&info->generators[j].instance); + + g_clear_object(&info->line); + + } + + g_clear_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 direct à 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 : content = éventuel contenu binaire brut à référencer. * +* * +* Description : Crée un nouveau composant de tampon pour code désassemblé. * +* * +* Retour : Composant GLib créé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBufferCache *g_buffer_cache_new(GBinContent *content) +{ + GBufferCache *result; /* Composant à retourner */ + + result = g_object_new(G_TYPE_BUFFER_CACHE, NULL); + + if (content != NULL) + { + result->content = content; + g_object_ref(G_OBJECT(content)); + } + + 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 : 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. * +* * +* 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 : - * +* * +******************************************************************************/ + +GWidthTracker *g_buffer_cache_get_width_tracker(const GBufferCache *cache) +{ + GWidthTracker *result; /* Instance à retourner * */ + + result = cache->tracker; + + g_object_ref(G_OBJECT(result)); + + return result; + +} + + + + + +/****************************************************************************** +* * +* 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 direct à 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. * +* flags = propriétés supplémentaires à associer à la ligne.* +* 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, BufferLineFlags flags, bool before, bool after) +{ +#ifndef NDEBUG + GLineCursor *gen_cursor; /* Position du générateur */ + GLineCursor *line_cursor; /* Position de la ligne */ + int ret; /* Bilan de comparaison */ +#endif + size_t needed; /* Emplacements nécessaires */ + size_t i; /* Boucle de parcours */ + + assert(index < cache->used); + + assert(!(before && after)); + +#ifndef NDEBUG + + if (!before && !after) + { + 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(line_cursor)); + g_object_unref(G_OBJECT(gen_cursor)); + + assert(ret == 0); + + } + +#endif + + /* Cas particulier d'ajout en fin de cache... */ + if (after && (index + 1) == cache->used) + { + g_buffer_cache_append(cache, generator, flags); + 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)); + } + } + + else if (needed > 1) + { + if ((cache->used + needed - 1) >= cache->count) + { + cache->count += needed - 1 + LINE_ALLOC_BULK; + cache->lines = (cache_info *)realloc(cache->lines, cache->count * sizeof(cache_info)); + } + } + + /* Insertion du générateur */ + + if (after) + index++; + + if (before || after) + { + memmove(&cache->lines[index + needed], &cache->lines[index], (cache->used - index) * sizeof(cache_info)); + + for (i = 0; i < needed; i++) + init_cache_info(&cache->lines[index + i], generator, i, flags); + + 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, flags); + + g_width_tracker_update(cache->tracker, index); + + if (needed > 1) + { + /* On déborde sur les lignes suivantes, donc on crée de l'espace ! */ + + memmove(&cache->lines[index + 1], + &cache->lines[index + 1 + needed - 1], (cache->used - index - 1) * sizeof(cache_info)); + + for (i = 1; i < needed; i++) + init_cache_info(&cache->lines[index + i], generator, i, BLF_NONE); + + cache->used += needed - 1; + + g_width_tracker_update_added(cache->tracker, index + 1, needed - 1); + + } + + g_signal_emit_by_name(cache, "size-changed", true, index, needed - 1); + + } + + gbcia_done: + + ; + +} + + +/****************************************************************************** +* * +* Paramètres : cache = instance GLib à modifier. * +* index = point de suppression. * +* * +* Description : Retire une ligne du tampon. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_cache_delete_at(GBufferCache *cache, size_t index) +{ + cache_info *info; /* Accès direct à une ligne */ + + assert(index < cache->used); + + info = &cache->lines[index]; + + release_cache_info(info); + + if ((index + 1) < cache->used) + memmove(&cache->lines[index], &cache->lines[index + 1], + (cache->used - index - 1) * sizeof(cache_info)); + + cache->used--; + + g_width_tracker_update_deleted(cache->tracker, index, index); + + g_signal_emit_by_name(cache, "size-changed", false, index, 1); + +} + + +/****************************************************************************** +* * +* 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 direct à 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 direct à 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_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 direct à 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; + + 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 direct à une ligne */ + size_t j; /* Boucle de parcours #2 */ + size_t removed; /* Nombre de retraits effectués*/ + + 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); + + } + + if (max < cache->used) + { + removed = cache->used - max; + + cache->used = max; + + 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. * +* cursor = emplacement à constituer. [OUT] * +* * +* Description : Retrouve l'emplacement correspondant à une position de ligne.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_cache_get_line_cursor(const GBufferCache *cache, size_t index, gint x, GLineCursor **cursor) +{ + assert(index < cache->used); + + get_cache_info_cursor(&cache->lines[index], index, x, cursor); + +} + + +/****************************************************************************** +* * +* 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 direct à une ligne */ + 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]; + + 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, cache->content); + 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, cache->content); + + 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. * +* options = règles d'affichage des colonnes modulables. * +* offsets = décalages supplémentaires à appliquer. * +* 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 GDisplayOptions *options, const line_width_summary *offsets, 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 direct à 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, cache->content); + + g_buffer_line_draw(line, cr, &summary, class->text_pos, y, options, offsets, list); + + g_object_unref(G_OBJECT(line)); + + y += class->line_height; + + } + +} + + +/****************************************************************************** +* * +* Paramètres : cache = tampon de lignes à consulter. * +* cursor = emplacement à 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_cursor(const GBufferCache *cache, const GLineCursor *cursor, 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 GLineCursor *c, 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_contain_cursor(generator->instance, + i - cache->lines, generator->repeat, c); + + } + + found = (cache_info *)bsearch(cursor, &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(cursor, found) != 0) + break; + + } + + else + for (; result < end; result++) + { + found = &cache->lines[result + 1]; + + if (find_containing_generator(cursor, found) != 0) + break; + + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : cache = tampon de lignes à consulter. * +* cursor = emplacement à 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_cursor(const GBufferCache *cache, const GLineCursor *cursor, bool first) +{ + size_t result; /* Indice à retourner */ + + if (cache->used == 0) + result = 0; + else + result = _g_buffer_cache_find_index_by_cursor(cache, cursor, 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 */ + GLineCursor *init; /* Localisation de départ */ + size_t i; /* Boucle de parcours */ + GLineCursor *next; /* Localisation suivante */ + int ret; /* Bilan de comparaison */ + + // TODO : check lock + + assert(start < cache->used); + + result = start; + + get_cache_info_cursor(&cache->lines[start], start, 0, &init); + + for (i = start + 1; i < cache->used; i++) + { + get_cache_info_cursor(&cache->lines[i], i, 0, &next); + + ret = g_line_cursor_compare(init, next); + + g_object_unref(G_OBJECT(next)); + + if (ret != 0) + break; + + if ((g_buffer_cache_get_line_flags(cache, i) & flag) != 0) + { + result = i; + break; + } + + } + + g_object_unref(G_OBJECT(init)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : cache = tampon de lignes à consulter. * +* cursor = emplacement à 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_cursor_coordinates(const GBufferCache *cache, const GLineCursor *cursor, 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_cursor(cache, cursor, 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_contain_cursor(generator->instance, index + 1, generator->repeat, cursor)) + break; + + *y += lheight; + + } + + } + + return result; + +} |