/* Chrysalide - Outil d'analyse de fichiers binaires * gbufferline.c - représentation de fragments de texte en ligne * * Copyright (C) 2010-2017 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 . */ #include "gbufferline.h" #include #include #include #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_PHYS_ADDR_PAD, NULL); g_buffer_line_append_text(line, BLC_VIRTUAL, &position[i], len - i, RTT_PHYS_ADDR, NULL); } else g_buffer_line_append_text(line, BLC_VIRTUAL, position, len, RTT_PHYS_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 : Portion de texte mis en place, voire NULL. * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_line_append_text(GBufferLine *line, BufferLineColumn 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 == 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\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, "\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 : - * * * ******************************************************************************/ const 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 = 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. * * 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 : - * * * ******************************************************************************/ const 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) { const 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; } } }