diff options
Diffstat (limited to 'src/glibext/bufferline.c')
-rw-r--r-- | src/glibext/bufferline.c | 1558 |
1 files changed, 1558 insertions, 0 deletions
diff --git a/src/glibext/bufferline.c b/src/glibext/bufferline.c new file mode 100644 index 0000000..b9413e2 --- /dev/null +++ b/src/glibext/bufferline.c @@ -0,0 +1,1558 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * bufferline.c - représentation de fragments de texte en ligne + * + * Copyright (C) 2010-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 "bufferline.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include "chrysamarshal.h" +#include "linecolumn.h" +#include "../common/extstr.h" +#include "../core/paths.h" + + + +/* ---------------------------- GESTION DE LINE COMPLETE ---------------------------- */ + + +/* Mémorisation des origines de texte */ +typedef struct _content_origin +{ + col_coord_t coord; /* Localisation d'attachement */ + + GObject *creator; /* Origine de la création */ + +} content_origin; + +/* Représentation de fragments de texte en ligne (instance) */ +struct _GBufferLine +{ + GObject parent; /* A laisser en premier */ + + mrange_t range; /* Couverture geographique */ + BufferLineColumn main_column; /* Colonne principale */ + + line_column columns[BLC_COUNT]; /* Répartition du texte */ + BufferLineColumn merge_start; /* Début de la zone globale */ + BufferLineColumn last_used; /* Dernière colonne utilisée */ + + BufferLineFlags flags; /* Drapeaux particuliers */ + + content_origin *origins; /* Mémorisation des origines */ + size_t ocount; /* Nombre de ces mémorisations */ + + union + { + struct + { + gint max_widths[BLC_COUNT]; /* Taille cachée des colonnes */ + gint merged_width; /* Largeur cumulée avant fusion*/ + }; + }; + +}; + +/* Représentation de fragments de texte en ligne (classe) */ +struct _GBufferLineClass +{ + GObjectClass parent; /* A laisser en premier */ + + cairo_surface_t *entrypoint_img; /* Image pour les entrées */ + cairo_surface_t *bookmark_img; /* Image pour les signets */ + + /* Signaux */ + + void (* content_changed) (GBufferLine *, line_segment *); + + void (* flip_flag) (GBufferLine *, BufferLineFlags, BufferLineFlags); + +}; + + +/* Procède à l'initialisation d'une classe de représentation. */ +static void g_buffer_line_class_init(GBufferLineClass *); + +/* Procède à l'initialisation d'une représentation de fragments. */ +static void g_buffer_line_init(GBufferLine *); + +/* Supprime toutes les références externes. */ +static void g_buffer_line_dispose(GBufferLine *); + +/* Procède à la libération totale de la mémoire. */ +static void g_buffer_line_finalize(GBufferLine *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION DE LINE COMPLETE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Détermine le type de la représentation de fragments de texte en ligne. */ +G_DEFINE_TYPE(GBufferLine, g_buffer_line, G_TYPE_OBJECT); + + + +/****************************************************************************** +* * +* Paramètres : class = classe de composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation d'une classe de représentation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_buffer_line_class_init(GBufferLineClass *class) +{ + GObjectClass *object; /* Autre version de la classe */ + gchar *filename; /* Chemin d'accès à utiliser */ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_buffer_line_dispose; + object->finalize = (GObjectFinalizeFunc)g_buffer_line_finalize; + + filename = find_pixmap_file("entrypoint.png"); + assert(filename != NULL); + + class->entrypoint_img = cairo_image_surface_create_from_png(filename); + + g_free(filename); + + filename = find_pixmap_file("bookmark.png"); + assert(filename != NULL); + + class->bookmark_img = cairo_image_surface_create_from_png(filename); + + g_free(filename); + + g_signal_new("content-changed", + G_TYPE_BUFFER_LINE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBufferLineClass, content_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + g_signal_new("flip-flag", + G_TYPE_BUFFER_LINE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBufferLineClass, flip_flag), + NULL, NULL, + g_cclosure_user_marshal_VOID__ENUM_ENUM, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + +} + + +/****************************************************************************** +* * +* Paramètres : line = composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation d'une représentation de fragments.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_buffer_line_init(GBufferLine *line) +{ + BufferLineColumn i; /* Boucle de parcours */ + + for (i = 0; i < BLC_COUNT; i++) + init_line_column(&line->columns[i]); + + line->merge_start = BLC_COUNT; + line->last_used = BLC_COUNT; + +} + + +/****************************************************************************** +* * +* Paramètres : line = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_buffer_line_dispose(GBufferLine *line) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < line->ocount; i++) + g_object_unref(G_OBJECT(line->origins[i].creator)); + + G_OBJECT_CLASS(g_buffer_line_parent_class)->dispose(G_OBJECT(line)); + +} + + +/****************************************************************************** +* * +* Paramètres : line = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_buffer_line_finalize(GBufferLine *line) +{ + BufferLineColumn i; /* Boucle de parcours */ + + for (i = 0; i < BLC_COUNT; i++) + reset_line_column(&line->columns[i]); + + if (line->origins != NULL) + free(line->origins); + + G_OBJECT_CLASS(g_buffer_line_parent_class)->finalize(G_OBJECT(line)); + +} + + +/****************************************************************************** +* * +* Paramètres : range = emplacement où va se situer la ligne. * +* main = colonne à référencer comme étant la principale. * +* * +* Description : Crée une nouvelle représentation de fragments de texte. * +* * +* Retour : Composant GTK créé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBufferLine *g_buffer_line_new(const mrange_t *range, BufferLineColumn main) +{ + GBufferLine *result; /* Composant à retourner */ + + result = g_object_new(G_TYPE_BUFFER_LINE, NULL); + + copy_mrange(&result->range, range); + result->main_column = main; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* * +* Description : Indique la zone mémoire où se situe la ligne. * +* * +* Retour : Emplacement mémoire virtuel ou physique. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const mrange_t *g_buffer_line_get_range(const GBufferLine *line) +{ + return &line->range; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir compléter. * +* size = taille souhaitée de l'impression des positions. * +* addr = localisation physique à venir représenter. * +* * +* Description : Construit le tronc commun d'une ligne autour de sa position. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_fill_phys(GBufferLine *line, MemoryDataSize size, const vmpa2t *addr) +{ + VMPA_BUFFER(position); /* Emplacement au format texte */ + size_t len; /* Taille de l'élément inséré */ + size_t i; /* Boucle de parcours #1 */ + + vmpa2_phys_to_string(addr, size, position, &len); + + for (i = 2; i < len; i++) + if (position[i] != '0') break; + + if (i == len) + i = len - 1; + + if (i > 0) + g_buffer_line_append_text(line, BLC_PHYSICAL, position, i, RTT_PHYS_ADDR_PAD, NULL); + + g_buffer_line_append_text(line, BLC_PHYSICAL, &position[i], len - i, RTT_PHYS_ADDR, NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir compléter. * +* size = taille souhaitée de l'impression des positions. * +* addr = localisation virtuelle à venir représenter. * +* * +* Description : Construit le tronc commun d'une ligne autour de sa position. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_fill_virt(GBufferLine *line, MemoryDataSize size, const vmpa2t *addr) +{ + VMPA_BUFFER(position); /* Emplacement au format texte */ + size_t len; /* Taille de l'élément inséré */ + size_t i; /* Boucle de parcours #1 */ + + vmpa2_virt_to_string(addr, size, position, &len); + + if (has_virt_addr(addr)) + { + for (i = 2; i < len; i++) + if (position[i] != '0') break; + + if (i == len) + i = len - 1; + + if (i > 0) + g_buffer_line_append_text(line, BLC_VIRTUAL, position, i, RTT_VIRT_ADDR_PAD, NULL); + + g_buffer_line_append_text(line, BLC_VIRTUAL, &position[i], len - i, RTT_VIRT_ADDR, NULL); + + } + + else + g_buffer_line_append_text(line, BLC_VIRTUAL, position, len, RTT_VIRT_ADDR_PAD, NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir compléter. * +* addr = localisation à afficher. * +* psize = taille souhaitée de l'impression des positions. * +* vsize = taille souhaitée de l'impression des adresses. * +* * +* Description : Construit le tronc commun d'une ligne autour de sa position. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_fill_vmpa(GBufferLine *line, const vmpa2t *addr, MemoryDataSize psize, MemoryDataSize vsize) +{ + g_buffer_line_fill_phys(line, psize, addr); + + g_buffer_line_fill_virt(line, vsize, addr); + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir compléter. * +* content = contenu binaire global à venir lire. * +* range = localisation des données à venir lire et présenter.* +* max = taille maximale de la portion binaire en octets. * +* * +* Description : Construit le tronc commun d'une ligne autour de son contenu. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_fill_content(GBufferLine *line, const GBinContent *content, const mrange_t *range, phys_t max) +{ + phys_t length; /* Taille de la couverture */ + bool truncated; /* Indique si le code est coupé*/ + size_t required; /* Taille de traitement requise*/ + char static_buffer[64]; /* Petit tampon local rapide */ + char *bin_code; /* Tampon utilisé pour le code */ + vmpa2t pos; /* Boucle de parcours #1 */ + phys_t i; /* Boucle de parcours #2 */ + char *iter; /* Boucle de parcours #3 */ + int ret; /* Progression dans l'écriture */ + uint8_t byte; /* Octet à représenter */ + + static const char *charset = "0123456789abcdef"; + + /* Détermination du réceptacle */ + + length = get_mrange_length(range); + + truncated = (max != VMPA_NO_PHYSICAL && length > max); + + if (truncated) + { + length = max; + required = length * 3 + 4 /* "..." */ + 1; + } + else + required = length * 3 + 1; + + if (required <= sizeof(static_buffer)) + bin_code = static_buffer; + else + bin_code = (char *)calloc(required, sizeof(char)); + + /* Code brut */ + + copy_vmpa(&pos, get_mrange_addr(range)); + + for (i = 0, iter = bin_code; i < length; i++, iter += ret) + { + if (i == 0) + ret = 0; + else + { + iter[0] = ' '; + ret = 1; + } + + if (!g_binary_content_read_u8(content, &pos, &byte)) + { + iter[ret + 0] = '?'; + iter[ret + 1] = '?'; + } + else + { + iter[ret + 0] = charset[byte >> 4]; + iter[ret + 1] = charset[byte & 0x0f]; + } + + ret += 2; + + } + + if (truncated) + { + strcpy(iter, "..."); + iter += 3; + } + else + *iter = '\0'; + + /* Conclusion */ + + g_buffer_line_append_text(line, BLC_BINARY, bin_code, iter - bin_code, RTT_RAW_CODE, NULL); + + if (bin_code != static_buffer) + free(bin_code); + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* column = indice de la colonne visée par les recherches. * +* * +* Description : Recherche le premier créateur enregistré dans des segments. * +* * +* Retour : Créateur trouvé à déréférencer par la suite ou NULL si échec.* +* * +* Remarques : - * +* * +******************************************************************************/ + +GObject *g_buffer_line_find_first_segment_creator(const GBufferLine *line, BufferLineColumn column) +{ + GObject *result; /* Trouvaille à retourner */ + size_t i; /* Boucle de parcours */ + + assert(column < BLC_COUNT); + + result = NULL; + + for (i = 0; i < line->ocount && result == NULL; i++) + { + if (line->origins[i].coord.column == column) + result = line->origins[i].creator; + } + + if (result != NULL) + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir compléter. * +* column = colonne de la ligne visée par l'insertion. * +* text = texte à insérer dans l'existant. * +* length = taille du texte à traiter. * +* type = type de décorateur à utiliser. * +* creator = instance GLib quelconque à associer. * +* * +* Description : Ajoute du texte à formater dans une ligne donnée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +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; + + if (column == BLC_LAST_USED) + column = line->last_used; + else + line->last_used = column; + + index = append_text_to_line_column(&line->columns[column], text, length, type); + + if (creator != NULL) + { + line->origins = (content_origin *)realloc(line->origins, ++line->ocount * sizeof(content_origin)); + + origin = &line->origins[line->ocount - 1]; + + origin->coord.column = column; + origin->coord.index = index; + + origin->creator = creator; + g_object_ref(G_OBJECT(creator)); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir compléter. * +* creator = instance GLib quelconque identifiant un segment. * +* text = texte à insérer dans l'existant. * +* length = taille du texte à traiter. * +* * +* Description : Remplace du texte dans une ligne donnée. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_buffer_line_replace_text(GBufferLine *line, const GObject *creator, const char *text, size_t length) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + const col_coord_t *coord; /* Emplacement du contenu visé */ + + result = false; + + for (i = 0; i < line->ocount && !result; i++) + { + if (line->origins[i].creator == creator) + { + coord = &line->origins[i].coord; + + replace_text_in_line_column(&line->columns[coord->column], coord->index, text, length); + + g_signal_emit_by_name(line, "content-changed", NULL); + + result = true; + + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* first = première colonne à parcourir. * +* end = colonne de fin de parcours. * +* * +* Description : Indique si du texte est présent dans une ligne de tampon. * +* * +* Retour : true pour indiquer la présence de texte, false pour du vide. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_buffer_line_has_text(const GBufferLine *line, BufferLineColumn first, BufferLineColumn end) +{ + bool result; /* Bilan à retourner */ + BufferLineColumn i; /* Boucle de parcours */ + + result = false; + + assert(first < end); + + for (i = first; i < end && !result; i++) + result = (line->columns[i].count > 0); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* first = première colonne à parcourir. * +* end = colonne de fin de parcours. * +* markup = indique si le texte doit être décoré ou non. * +* * +* Description : Donne le texte représenté par une ligne de tampon. * +* * +* Retour : Texte à libérer de la mémoire après usage. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_buffer_line_get_text(const GBufferLine *line, BufferLineColumn first, BufferLineColumn end, bool markup) +{ + char *result; /* Construction à retourner */ + BufferLineColumn i; /* Boucle de parcours */ + char *extra; /* Contenu à intégrer au texte */ + + result = NULL; + + assert(first < end); + + for (i = first; i < end; i++) + { + if (i > first && result != NULL) + result = stradd(result, " "); + + extra = get_line_column_text(&line->columns[i], markup); + + /* Si la colonne était vide, suivante ! */ + if (extra == NULL) continue; + + if (result == NULL) + result = extra; + + else + { + result = stradd(result, extra); + free(extra); + } + + } + + return result; + +} + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir modifier. * +* first = première colonne à parcourir. * +* end = colonne de fin de parcours. * +* * +* Description : Supprime du texte représenté par une ligne de tampon. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_delete_text(GBufferLine *line, BufferLineColumn first, BufferLineColumn end) +{ + BufferLineColumn i; /* Boucle de parcours #1 */ + + assert(first < end); + + for (i = first; i < end; i++) + reset_line_column(&line->columns[i]); + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* * +* Description : Fournit la colonne à partir de laquelle une fusion opère. * +* * +* Retour : Début de la première (et unique) zone globale. * +* * +* Remarques : - * +* * +******************************************************************************/ + +BufferLineColumn g_buffer_line_get_merge_start(const GBufferLine *line) +{ + return line->merge_start; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir compléter. * +* start = début de la première (et unique) zone globale. * +* * +* Description : Définit la colonne à partir de laquelle la fusion opère. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_start_merge_at(GBufferLine *line, BufferLineColumn start) +{ + if (start == BLC_LAST_USED) + line->merge_start = line->last_used; + else + line->merge_start = start; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir compléter. * +* flag = propriété à intégrer. * +* * +* Description : Ajoute une propriété particulière à une ligne donnée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_add_flag(GBufferLine *line, BufferLineFlags flag) +{ + if ((line->flags & flag) == 0) + { + g_signal_emit_by_name(line, "flip-flag", line->flags, flag); + + line->flags |= flag; + + } + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* * +* Description : Renseigne sur les propriétés particulières liées à une ligne.* +* * +* Retour : Propriétés intégrées. * +* * +* Remarques : - * +* * +******************************************************************************/ + +BufferLineFlags g_buffer_line_get_flags(const GBufferLine *line) +{ + return line->flags; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir compléter. * +* flag = propriété à supprimer. * +* * +* Description : Retire une propriété particulière à une ligne donnée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_remove_flag(GBufferLine *line, BufferLineFlags flag) +{ + if ((line->flags & flag) != 0) + { + g_signal_emit_by_name(line, "flip-flag", line->flags, flag); + + line->flags &= ~flag; + + } + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne de texte à manipuler. * +* ctx = éléments à disposition pour l'exportation. * +* type = type d'exportation attendue. * +* display = règles d'affichage des colonnes modulables. * +* * +* Description : Exporte la ligne de texte représentée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_export(GBufferLine *line, buffer_export_context *ctx, BufferExportType type, const bool *display) +{ + BufferLineColumn i; /* Boucle de parcours */ + int col_span; /* Fusion de colonnes ? */ + + switch (type) + { + case BET_HTML: + dprintf(ctx->fd, "\t<TR>\n"); + break; + default: + break; + } + + for (i = 0; i < BLC_COUNT; i++) + { + if (i < BLC_DISPLAY && !display[i]) continue; + + switch (type) + { + case BET_TEXT: + if (i > 0) dprintf(ctx->fd, "%s", ctx->sep); + break; + default: + break; + } + + /** + * Pour la signification des différentes valeurs assignées, + * se référer au code de export_line_column_segments(). + * + * En gros : + * - 1 = rien de spécial. + * - >1 = il s'agit de la première cellule fusionnée de la ligne. + * - 0 = fusion déjà faite, on ne peut que rajouter du contenu dedans. + * - <1 = il s'agit de la dernière cellule fusionnée de la ligne. + * + * On considère qu'une fusion ne peut pas se réaliser sur la dernière + * cellule uniquement (ce qui a du sens : c'est inutile). + */ + + if (i < line->merge_start) + col_span = 1; + + else if (i == line->merge_start) + col_span = BLC_COUNT - i; + + else + col_span = ((i + 1) == BLC_COUNT ? -1 : 0); + + export_line_column_segments(&line->columns[i], ctx, type, col_span); + + } + + switch (type) + { + case BET_TEXT: + dprintf(ctx->fd, "\n"); + break; + case BET_HTML: + dprintf(ctx->fd, "</TR>\n"); + break; + default: + break; + } + +} + + + +/*----------------------------------------------------------------------------------- */ +/* MANIPULATION DES LARGEURS REQUISES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* summary = largeurs maximales à faire évoluer. * +* * +* Description : Fait remonter les largeurs requises par une ligne donnée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_collect_widths(GBufferLine *line, line_width_summary *summary) +{ + gint merged_width; /* Largeur cumulée avant fusion*/ + BufferLineColumn i; /* Boucle de parcours */ + gint width; /* Largeur d'une colonne */ + + merged_width = 0; + + for (i = 0; i < BLC_COUNT; i++) + { + width = get_column_width(&line->columns[i]); + + if (i < line->merge_start) + summary->max_widths[i] = MAX(summary->max_widths[i], width); + + if (i >= BLC_DISPLAY) + { + merged_width += width; + + if (i < line->merge_start && (i + 1) < BLC_COUNT) + merged_width += COL_MARGIN; + + } + + } + + if (line->merge_start != BLC_COUNT) + summary->merged_width = MAX(summary->merged_width, merged_width); + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* index = indice de la colonne visée. * +* summary = résumé des largeurs maximales. * +* offsets = décalages supplémentaires à appliquer. * +* * +* Description : Fournit la largeur d'une colonne finalement appliquée. * +* * +* Retour : Largeur globale ou spécifique, selon l'indice communiqué. * +* * +* Remarques : - * +* * +******************************************************************************/ + +gint g_buffer_line_compute_max_width(const GBufferLine *line, BufferLineColumn index, const line_width_summary *summary, const line_width_summary *offsets) +{ + gint result; /* Largeur à retourner */ + + assert(index < BLC_COUNT); + + if (index >= line->merge_start) + result = get_column_width(&line->columns[index]); + + else + result = summary->max_widths[index]; + + if (result < offsets->max_widths[index]) + result = offsets->max_widths[index]; + + return result; + +} + + + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* coord = coordonnées interne du segment à retrouver. * +* * +* Description : Fournit le segment présent à une position donnée. * +* * +* Retour : Segment trouvé ou NULL si hors borne. * +* * +* Remarques : - * +* * +******************************************************************************/ + +line_segment *g_buffer_line_get_segment_from_coord(const GBufferLine *line, const col_coord_t *coord) +{ + line_segment *result; /* Trouvaille à retourner */ + + if (coord->column < BLC_COUNT) + result = get_line_column_content_from_index(&line->columns[coord->column], coord->index); + else + result = NULL; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* summary = résumé des largeurs maximales. * +* options = règles d'affichage des colonnes modulables. * +* offsets = décalages supplémentaires à appliquer. * +* base = position jusqu'au segment trouvé. [OUT] * +* offset = position à la colonne visée. [OUT] * +* dir = direction d'un éventuel déplacement en cours. * +* force = accepte les segments en bordure au pire. * +* coord = cordonnées à usage interne à renseigner. [OUT] * +* * +* Description : Fournit les coordonnées correspondant à une abscisse donnée. * +* * +* Retour : true si des coordonnées valides ont été renseignées. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_buffer_line_get_coord_at(const GBufferLine *line, const line_width_summary *summary, const GDisplayOptions *options, const line_width_summary *offsets, gint *base, gint *offset, GdkScrollDirection dir, bool force, col_coord_t *coord) +{ + bool result; /* Bilan à retourner */ + BufferLineColumn last; /* Dernière colonne remplie */ + gint last_base; /* Dernière abscisse associée */ + size_t count; /* Qté de colonnes en option */ + size_t i; /* Boucle de parcours */ + gint width; /* Largeur d'une colonne donnée*/ + gint limit; /* Limite d'appartenance */ + gint consumed; /* Distance vers le segment */ + gint old_base; /* Somme de toutes les largeurs*/ + + result = false; + + *base = 0; + + last = BLC_COUNT; + last_base = 0; + + /* On cible déjà la colonne idéale */ + + count = g_display_options_count(options); + + for (i = 0; i < BLC_COUNT; i++) + { + if (i < count) + { + if (!g_display_options_get(options, i)) + continue; + } + + /* Mémorisation de la dernière colonne contenant quelque chose... */ + if (get_column_width(&line->columns[i]) > 0) + { + last = i; + last_base = *base; + } + + if (i < line->merge_start) + { + width = g_buffer_line_compute_max_width(line, i, summary, offsets); + + /* Si la colonne n'est absolument pas visible, on ne s'arrête pas dessus ! */ + if (width == 0) continue; + + if ((i + 1) < BLC_COUNT) limit = width + COL_MARGIN / 2; + else limit = width; + + if (*offset <= limit) break; + else + { + *offset -= width + COL_MARGIN; + *base += width + COL_MARGIN; + } + + } + else + { + width = get_column_width(&line->columns[i]); + + if (*offset <= width) break; + else + { + *offset -= width; + *base += width; + } + + } + + + } + + /* Si l'abscisse fournie tombe encore dans une colonne... */ + + if (i < BLC_COUNT) + { + /* Il y a bien du contenu dans cette colonne */ + + if (get_column_width(&line->columns[i]) > 0) + { + /** + * Si la position était au milieu d'une marge, la sélection a pu pousser + * jusqu'à la colonne suivante, plus proche. + * Relativment à la base de cette dernière, la position est donc devenue négative. + */ + if (*offset < 0) *offset = 0; + + result = get_line_column_content_index_at(&line->columns[i], offset, dir, &consumed, &coord->index); + + if (result) + { + coord->column = i; + + *base += consumed; + + } + + } + + /* La position fournie tombe dans une colonne vide ! */ + + else + { + if (force || get_column_width(&line->columns[i]) == 0) + { + result = false; + *offset = 0; + + old_base = *base; + + for (i++; i < BLC_COUNT && !result; i++) + { + if ((i - 1) < line->merge_start) + { + width = g_buffer_line_compute_max_width(line, i - 1, summary, offsets); + + if (width > 0) + *base += (width + COL_MARGIN); + + } + else + *base += get_column_width(&line->columns[i - 1]); + + result = get_line_column_first_content_index(&line->columns[i], &coord->index); + + if (result) + coord->column = i; + + } + + if (!result) + { + *base = old_base; + goto use_right_border; + } + + } + + } + + } + + else /* if (i == BLC_COUNT) */ + { + if (force) + { + use_right_border: + + if (last != BLC_COUNT) + { + result = get_line_column_last_content_index(&line->columns[last], &coord->index); + + if (result) + { + coord->column = last; + + *base = last_base; + *offset = get_column_width(&line->columns[last]); + + } + + } + + /* Il n'y a rien sur la ligne ! */ + else + { + result = true; + + *base = 0; + *offset = 0; + + coord->column = BLC_COUNT; + coord->index = -1; + + } + + } + else + result = false; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* summary = résumé des largeurs maximales. * +* options = règles d'affichage des colonnes modulables. * +* offsets = décalages supplémentaires à appliquer. * +* base = position jusqu'au segment trouvé. [OUT] * +* offset = position à la colonne visée. [OUT] * +* dir = direction d'un éventuel déplacement en cours. * +* force = accepte les segments en bordure au pire. * +* * +* Description : Donne le segment présent à une abscisse donnée. * +* * +* Retour : Segment trouvé ou NULL si hors borne. * +* * +* Remarques : - * +* * +******************************************************************************/ + +line_segment *g_buffer_line_get_segment_at(const GBufferLine *line, const line_width_summary *summary, const GDisplayOptions *options, const line_width_summary *offsets, gint *base, gint *offset, GdkScrollDirection dir, bool force) +{ + line_segment *result; /* Trouvaille à retourner */ + col_coord_t coord; /* Emplacement du contenu visé */ + bool status; /* Bilan de la localisation */ + + status = g_buffer_line_get_coord_at(line, summary, options, offsets, base, offset, dir, force, &coord); + + if (status) + result = g_buffer_line_get_segment_from_coord(line, &coord); + else + result = NULL; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* summary = résumé des largeurs maximales. * +* options = règles d'affichage des colonnes modulables. * +* offsets = décalages supplémentaires à appliquer. * +* base = position jusqu'au segment trouvé. [OUT] * +* offset = position à la colonne visée. [OUT] * +* dir = direction d'un éventuel déplacement en cours. * +* force = accepte les segments en bordure au pire. * +* * +* Description : Donne le créateur présent à une abscisse donnée. * +* * +* Retour : Créateur trouvé ou NULL si hors borne. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GObject *g_buffer_line_get_creator_at(const GBufferLine *line, const line_width_summary *summary, const GDisplayOptions *options, const line_width_summary *offsets, gint *base, gint *offset, GdkScrollDirection dir, bool force) +{ + GObject *result; /* Trouvaille à retourner */ + col_coord_t target; /* Emplacement du contenu visé */ + bool status; /* Bilan de la localisation */ + size_t i; /* Boucle de parcours */ + const col_coord_t *coord; /* Emplacement du contenu visé */ + + result = NULL; + + status = g_buffer_line_get_coord_at(line, summary, options, offsets, base, offset, dir, force, &target); + + if (status) + { + for (i = 0; i < line->ocount && result == NULL; i++) + { + coord = &line->origins[i].coord; + + if (coord->column == target.column && coord->index == target.index) + result = line->origins[i].creator; + + } + + if (result != NULL) + g_object_ref(G_OBJECT(result)); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne à venir consulter. * +* coord = cordonnées à consulter puis renseigner. [OUT] * +* summary = résumé des largeurs maximales. * +* options = règles d'affichage des colonnes modulables. * +* offsets = décalages supplémentaires à appliquer. * +* dir = orientation des recherches. * +* offset = décalage pour amener à l'extrémité nouvelle. [OUT] * +* * +* Description : Fournit des coordonnées voisines selon une direction donnée. * +* * +* Retour : true si des coordonnées valides ont été renseignées. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_buffer_line_find_near_coord(const GBufferLine *line, col_coord_t *coord, const line_width_summary *summary, const GDisplayOptions *options, const line_width_summary *offsets, GdkScrollDirection dir, gint *offset) +{ + bool result; /* Bilan à retourner */ + size_t count; /* Qté de colonnes en option */ + size_t i; /* Boucle de parcours #1 */ + bool displayed; /* Confort de lecture */ + size_t k; /* Boucle de parcours #2 */ + gint width; /* Largeur d'une colonne donnée*/ + + result = false; + + /* Recherche dans la colonne de départ */ + + i = coord->column; + + if (i == BLC_COUNT) return false; + + result = find_near_segment(&line->columns[i], &coord->index, dir); + + /* Recherche dans la direction des colonnes voisines */ + + count = g_display_options_count(options); + + if (!result) + switch (dir) + { + case GDK_SCROLL_LEFT: + + /* Si on a atteint la première colonne sans trouver... */ + if (i == 0) break; + + /* On s'assure que la colonne précédente est visible et peuplée */ + for (; i > BLC_FIRST && !result; i--) + { + displayed = (i <= count ? g_display_options_get(options, i - 1) : true); + + if (displayed) + { + result = get_line_column_first_content_index(&line->columns[i - 1], &coord->index); + + if (result) + coord->column = i - 1; + + } + + } + + break; + + case GDK_SCROLL_RIGHT: + + /* On s'assure que la colonne suivante est visible et peuplée */ + for (; (i + 1) < BLC_COUNT && !result; i++) + { + displayed = ((i + 1) < count ? g_display_options_get(options, i + 1) : true); + + if (displayed) + { + result = get_line_column_first_content_index(&line->columns[i + 1], &coord->index); + + if (result) + coord->column = i + 1; + + } + + } + + break; + + default: + break; + + } + + /* Calcul de la position finale */ + + if (result) + { + *offset = 0; + + for (k = 0; k < i; k++) + { + displayed = (k < count ? g_display_options_get(options, k) : true); + + if (displayed) + { + width = g_buffer_line_compute_max_width(line, k, summary, offsets); + + if (width > 0) + { + *offset += width; + if (k < line->merge_start) *offset += COL_MARGIN; + } + + } + + } + + switch (dir) + { + case GDK_SCROLL_LEFT: + *offset += get_column_width(&line->columns[i]); + break; + + case GDK_SCROLL_RIGHT: + /**offset += 0;*/ + break; + + default: + break; + + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : line = ligne de texte à manipuler. * +* cairo = contexte graphique à utiliser pour les pinceaux. * +* summary = résumé des largeurs maximales. * +* x_init = abscisse du point d'impression de départ. * +* y = ordonnée du point d'impression. * +* options = règles d'affichage des colonnes modulables. * +* offsets = décalages supplémentaires à appliquer. * +* list = liste de contenus à mettre en évidence. * +* * +* Description : Imprime la ligne de texte représentée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_draw(GBufferLine *line, cairo_t *cairo, const line_width_summary *summary, gint x_init, gint y, const GDisplayOptions *options, const line_width_summary *offsets, const segcnt_list *list) +{ + GBufferLineClass *class; /* Stockage de briques de base */ + bool has_src_surface; /* Note une présence définie */ + gint x; /* Point de départ d'impression*/ + size_t count; /* Qté de colonnes en option */ + size_t i; /* Boucle de parcours */ + gint max_width; /* Largeur maximale de colonne */ + + if (line->flags != BLF_NONE && line->flags != BLF_HAS_CODE) + { + class = G_BUFFER_LINE_GET_CLASS(line); + + if (line->flags & BLF_ENTRYPOINT) + { + cairo_set_source_surface(cairo, class->entrypoint_img, 5, y); + has_src_surface = true; + } + else if (line->flags & BLF_BOOKMARK) + { + cairo_set_source_surface(cairo, class->bookmark_img, 5, y); + has_src_surface = true; + } + else + has_src_surface = false; + + if (has_src_surface) + cairo_paint(cairo); + + } + + x = x_init; + + count = g_display_options_count(options); + + for (i = 0; i < BLC_COUNT; i++) + { + if (i < count) + { + if (!g_display_options_get(options, i)) + continue; + } + + draw_line_column_segments(&line->columns[i], cairo, x, y, list); + + if (i < line->merge_start) + { + max_width = g_buffer_line_compute_max_width(line, i, summary, offsets); + + if (max_width > 0) + x += max_width + COL_MARGIN; + + } + + } + +} |