/* Chrysalide - Outil d'analyse de fichiers binaires * gbufferline.c - représentation de fragments de texte en ligne * * Copyright (C) 2010-2014 Cyrille Bagard * * This file is part of Chrysalide. * * OpenIDA 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. * * OpenIDA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see . */ #include "gbufferline.h" #include #include #include /* Récupération du langage par défaut ; FIXME ? */ #include "chrysamarshal.h" #include "../common/extstr.h" #include "../gtkext/support.h" #include /* FIXME : à virer */ /* ---------------------------- REGROUPEMENT PAR COLONNE ---------------------------- */ /* Informations sur le contenu d'une colonne */ typedef struct _buffer_line_column { GBufferSegment **segments; size_t count; int max_width; /* Largeur max. de l'espace */ } buffer_line_column; /* Réinitialise une colonne de ligne. */ static void reset_column(buffer_line_column *); /* Fournit la quantité de pixels requise pour l'impression. */ static gint get_column_width(const buffer_line_column *); /* Ajoute un fragment de texte à une colonne de ligne. */ static void add_segment_to_column(buffer_line_column *, GBufferSegment *); /* Valide ou non la présence d'un segment dans une colonne. */ static bool column_has_segment(const buffer_line_column *, const GBufferSegment *, size_t *); #define get_first_segment(col) ((col)->count > 0 ? (col)->segments[0] : NULL) #define get_last_segment(col) ((col)->count > 0 ? (col)->segments[(col)->count - 1] : NULL) /* Donne le segment d'une colonne présent à une abscisse donnée. */ static GBufferSegment *get_segment_at(const buffer_line_column *, gint *, GdkScrollDirection, gint *); /* Fournit le segment voisin d'un autre segment identifié. */ static GBufferSegment *find_near_segment(const buffer_line_column *, GBufferSegment *, GdkScrollDirection); /* Imprime le contenu d'une colonne de ligne de texte. */ static void draw_segments_of_column(buffer_line_column *, cairo_t *, gint, gint, const segcnt_list *); /* Exporte la ligne de texte représentée. */ static void export_segments_of_column(buffer_line_column *, buffer_export_context *, BufferExportType, int); /* ---------------------------- GESTION DE LINE COMPLETE ---------------------------- */ /* 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 */ buffer_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 */ union { struct { gint max_widths[BLC_COUNT]; /* Taille cachée des colonnes */ gint merged_width; /* Largeur cumulée avant fusion*/ }; GBufferLine *manager; /* Représentante d'un groupe */ }; }; /* 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 (* 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 *); /* ---------------------------------------------------------------------------------- */ /* REGROUPEMENT PAR COLONNE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : column = colonne de ligne à mettre à jour. * * * * Description : Réinitialise une colonne de ligne. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void reset_column(buffer_line_column *column) { column->max_width = 0; } /****************************************************************************** * * * Paramètres : column = colonne de ligne à consulter. * * * * Description : Fournit la quantité de pixels requise pour l'impression. * * * * Retour : Largeur requise par la colonne, en pixel. * * * * Remarques : - * * * ******************************************************************************/ static gint get_column_width(const buffer_line_column *column) { return column->max_width; } /****************************************************************************** * * * Paramètres : column = colonne de ligne à venir compléter. * * segment = fragment de texte à ajouter à la colonne. * * * * Description : Ajoute un fragment de texte à une colonne de ligne. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void add_segment_to_column(buffer_line_column *column, GBufferSegment *segment) { /* FIXME : à remplacer */ column->segments = (GBufferSegment **)realloc(column->segments, ++column->count * sizeof(GBufferSegment *)); column->segments[column->count - 1] = segment; column->max_width += g_buffer_segment_get_width(segment); } /****************************************************************************** * * * Paramètres : column = colonne de ligne à venir consulter. * * segment = fragment de texte à trouver dans la colonne. * * index = indice du segment retrouvé ou NULL. [OUT] * * * * Description : Valide ou non la présence d'un segment dans une colonne. * * * * Retour : Statut de présence du segment indiqué. * * * * Remarques : - * * * ******************************************************************************/ static bool column_has_segment(const buffer_line_column *column, const GBufferSegment *segment, size_t *index) { size_t i; /* Boucle de parcours */ for (i = 0; i < column->count; i++) if (column->segments[i] == segment) { if (index != NULL) *index = i; break; } return (i < column->count); } /****************************************************************************** * * * Paramètres : column = colonne de ligne de texte à consulter. * * x = position de recherche, puis position locale. [OUT]* * dir = direction d'un éventuel déplacement en cours. * * consumed = distance pour arriver à la base du segment. [OUT] * * * * Description : Donne le segment d'une colonne présent à une abscisse donnée.* * * * Retour : Segment trouvé ou NULL si hors borne. * * * * Remarques : - * * * ******************************************************************************/ static GBufferSegment *get_segment_at(const buffer_line_column *column, gint *x, GdkScrollDirection dir, gint *consumed) { GBufferSegment *result; /* Trouvaille à retourner */ size_t i; /* Boucle de parcours */ gint width; /* Largeur à retirer */ bool included; /* Appartenance à une largeur ?*/ result = NULL; *consumed = 0; printf(" == gs@ == count = %d width = %d\n", column->count, get_column_width(column)); for (i = 0; i < column->count && result == NULL; i++) { width = g_buffer_segment_get_width(column->segments[i]); printf(" -s- |%d| -> x=%d w=%d\n", i, *x, width); /** * Soit une limite entre deux segments A et B : * * - dans le cas d'un déplacement vers la gauche, on part de cette limite * pour progresser à l'intérieur de A. Donc la limite fait partie de A. * * - dans le cas d'un déplacement vers la droite, on part de cette limite * pour progresser à l'intérieur de B. Donc la limite ne fait pas partie de A. */ if (dir == GDK_SCROLL_LEFT) included = (width >= *x); else included = (width > *x); if (included) result = column->segments[i]; else if ((i + 1) == column->count) { result = column->segments[i]; *x = width; } else { *x -= width; *consumed += width; } } printf(" == gs@ == > segment = %p\n", result); return result; } /****************************************************************************** * * * Paramètres : column = colonne de ligne de texte à consulter. * * target = segment dont un voisin est à retourner. * * dir = orientation des recherches. * * * * Description : Fournit le segment voisin d'un autre segment identifié. * * * * Retour : Segment trouvé ou NULL si hors borne ou inconnu. * * * * Remarques : - * * * ******************************************************************************/ static GBufferSegment *find_near_segment(const buffer_line_column *column, GBufferSegment *target, GdkScrollDirection dir) { GBufferSegment *result; /* Trouvaille à retourner */ size_t i; /* Boucle de parcours */ result = NULL; column_has_segment(column, target, &i); if (i < column->count) switch (dir) { case GDK_SCROLL_LEFT: if (i > 0) result = column->segments[i - 1]; break; case GDK_SCROLL_RIGHT: if ((i + 1) < column->count) result = column->segments[i + 1]; break; default: break; } return result; } /****************************************************************************** * * * Paramètres : column = colonne de ligne de texte à manipuler. * * cairo = contexte graphique à utiliser pour les pinceaux. * * x_init = abscisse du point d'impression de départ. * * y = ordonnée du point d'impression. * * list = liste de contenus à mettre en évidence. * * * * Description : Imprime le contenu d'une colonne de ligne de texte. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void draw_segments_of_column(buffer_line_column *column, cairo_t *cairo, gint x_init, gint y, const segcnt_list *list) { gint x; /* Abscisse d'impression */ size_t i; /* Boucle de parcours */ x = x_init; for (i = 0; i < column->count; i++) g_buffer_segment_draw(column->segments[i], cairo, &x, y, list); } /****************************************************************************** * * * Paramètres : column = colonne de ligne de texte à manipuler. * * ctx = éléments à disposition pour l'exportation. * * type = type d'exportation attendue. * * span = fusion de colonnes au sein des cellules ? * * * * Description : Exporte la ligne de texte représentée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void export_segments_of_column(buffer_line_column *column, buffer_export_context *ctx, BufferExportType type, int span) { size_t i; /* Boucle de parcours */ switch (type) { case BET_HTML: switch (span) { case 0: break; case 1: dprintf(ctx->fd, "\t\t"); break; default: if (span > 0) dprintf(ctx->fd, "\t\t", span); break; } break; default: break; } for (i = 0; i < column->count; i++) g_buffer_segment_export(column->segments[i], ctx, type); switch (type) { case BET_HTML: if (span < 0 || span == 1) dprintf(ctx->fd, "\n"); break; default: break; } } /* ---------------------------------------------------------------------------------- */ /* 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("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++) reset_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) { if (line->flags & BLF_WIDTH_MANAGER) g_object_unref(G_OBJECT(line->manager)); 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) { /* TODO : segments des colonnes... */ 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. * * 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_mrange(GBufferLine *line, MemoryDataSize psize, MemoryDataSize vsize) { size_t len; /* Taille de l'élément inséré */ VMPA_BUFFER(address); /* Adresse au format texte */ size_t i; /* Boucle de parcours */ /* Position physique */ if (has_phys_addr(get_mrange_addr(&line->range))) { mrange_phys_to_string(&line->range, psize, true, address, &len); for (i = 2; i < len; i++) if (address[i] != '0') break; if (i == len) g_buffer_line_insert_text(line, BLC_PHYSICAL, address, len, RTT_PHYS_ADDR_PAD); else { g_buffer_line_insert_text(line, BLC_PHYSICAL, address, 2, RTT_PHYS_ADDR); g_buffer_line_insert_text(line, BLC_PHYSICAL, &address[2], i - 2, RTT_PHYS_ADDR_PAD); g_buffer_line_insert_text(line, BLC_PHYSICAL, &address[i], len - i, RTT_PHYS_ADDR); } } /* Adresse virtuelle */ if (has_virt_addr(get_mrange_addr(&line->range))) { mrange_virt_to_string(&line->range, vsize, true, address, &len); for (i = 2; i < len; i++) if (address[i] != '0') break; if (i == len) g_buffer_line_insert_text(line, BLC_VIRTUAL, address, len, RTT_VIRT_ADDR_PAD); else { g_buffer_line_insert_text(line, BLC_VIRTUAL, address, 2, RTT_VIRT_ADDR); g_buffer_line_insert_text(line, BLC_VIRTUAL, &address[2], i - 2, RTT_VIRT_ADDR_PAD); g_buffer_line_insert_text(line, BLC_VIRTUAL, &address[i], len - i, RTT_VIRT_ADDR); } } } /****************************************************************************** * * * Paramètres : line = ligne à venir compléter. * * psize = taille souhaitée de l'impression des positions. * * vsize = taille souhaitée de l'impression des adresses. * * content = contenu binaire global à venir lire. * * full = la portion est assez courte pour être entière ? * * * * Description : Construit le tronc commun d'une ligne d'instruction. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_line_fill_for_instr(GBufferLine *line, MemoryDataSize psize, MemoryDataSize vsize, const GBinContent *content, bool full) { phys_t length; /* Taille de la couverture */ 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"; /* Adresse physique puis virtuelle */ g_buffer_line_fill_mrange(line, psize, vsize); /* Détermination du réceptacle */ length = get_mrange_length(&line->range); required = length * 3 + 3; 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(&line->range)); for (i = 0, iter = bin_code; i < length; i++, iter += ret) { if (!g_binary_content_read_u8(content, &pos, &byte)) { iter[0] = '?'; iter[1] = '?'; } else { iter[0] = charset[byte >> 4]; iter[1] = charset[byte & 0x0f]; } if ((i + 1) < length) { iter[2] = ' '; ret = 3; } else { if (full) { iter[2] = '\0'; ret = 2; } else { strcpy(iter + 2, "..."); ret = 5; } } } /* Conclusion */ g_buffer_line_insert_text(line, BLC_BINARY, bin_code, iter - bin_code, RTT_RAW_CODE); if (bin_code != static_buffer) free(bin_code); } /****************************************************************************** * * * Paramètres : line = ligne à venir compléter. * * index = index de la colonne visée par la procédure. * * segment = fragment de texte à ajouter à la colonne. * * * * Description : Ajoute un fragment de texte à une colonne de ligne. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_line_add_segment(GBufferLine *line, BufferLineColumn index, GBufferSegment *segment) { add_segment_to_column(&line->columns[index], segment); } /****************************************************************************** * * * Paramètres : line = ligne à venir consulter. * * max_widths = largeurs de colonne à respecter. * * display = règles d'affichage des colonnes modulables. * * 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 : - * * * ******************************************************************************/ GBufferSegment *g_buffer_line_get_segment_at(const GBufferLine *line, const gint max_widths[BLC_COUNT], const bool *display, gint *base, gint *offset, GdkScrollDirection dir, bool force) { GBufferSegment *result; /* Trouvaille à retourner */ BufferLineColumn last; /* Dernière colonne remplie */ gint last_base; /* Dernière abscisse associée */ BufferLineColumn i; /* Boucle de parcours */ gint width; /* Largeur d'une colonne donnée*/ gint limit; gint consumed; /* Distance vers le segment */ gint old_base; /* Somme de toutes les largeurs*/ result = NULL; *base = 0; last = BLC_COUNT; //sum = 0; printf("---------------\n"); /* On cible déjà la colonne idéale */ for (i = 0; i < BLC_COUNT; i++) { printf(" @ (%d) x=%d width=%d max=%d display ? %d\n", i, *offset, get_column_width(&line->columns[i]), max_widths[i], i < BLC_DISPLAY ? display[i] : true); if (i < BLC_DISPLAY && !display[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, max_widths); 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; } } } printf(" -- get segment at -- found index %u (max=%u)\n", i, BLC_COUNT); printf(" last seen = %u\n", last); if (i < BLC_COUNT) { printf(" -- width @ %u : %d\n", i, get_column_width(&line->columns[i])); 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_segment_at(&line->columns[i], offset, dir, &consumed); *base += consumed; } /* La position fournie tombe dans une colonne vide ! */ else { if (force || get_column_width(&line->columns[i]) == 0) { result = NULL; *offset = 0; old_base = *base; for (i++; i < BLC_COUNT && result == NULL; i++) { printf(" -- update to col %u -- x = %d\n", i, *offset); if ((i - 1) < line->merge_start) { width = g_buffer_line_compute_max_width(line, i - 1, max_widths); *base += (width + COL_MARGIN); } else *base += get_column_width(&line->columns[i - 1]); result = get_first_segment(&line->columns[i]); } printf(" -- final x = %d (result=%p)\n", *offset, result); if (result == NULL) { *base = old_base; goto use_right_border; } } } } else /* if (i == BLC_COUNT) */ { if (force && last != BLC_COUNT) { use_right_border: result = get_last_segment(&line->columns[last]); *base = last_base; *offset = get_column_width(&line->columns[last]); } else result = NULL; } return result; } /****************************************************************************** * * * Paramètres : line = ligne à venir consulter. * * target = segment dont un voisin est à retourner. * * max_widths = largeurs de colonne à respecter. * * display = règles d'affichage des colonnes modulables. * * dir = orientation des recherches. * * offset = décalage pour amener à l'extrémité nouvelle. [OUT] * * * * Description : Fournit le segment voisin d'un autre segment identifié. * * * * Retour : Segment trouvé dans la ligne ou NULL. * * * * Remarques : - * * * ******************************************************************************/ GBufferSegment *g_buffer_line_find_near_segment(const GBufferLine *line, GBufferSegment *target, const gint max_widths[BLC_COUNT], const bool *display, GdkScrollDirection dir, gint *offset) { GBufferSegment *result; /* Trouvaille à retourner */ BufferLineColumn i; /* Boucle de parcours #1 */ bool displayed; /* Confort de lecture */ BufferLineColumn k; /* Boucle de parcours #2 */ result = NULL; /* Recherche dans la colonne de départ */ for (i = 0; i < BLC_COUNT; i++) if (column_has_segment(&line->columns[i], target, NULL)) break; if (i == BLC_COUNT) return NULL; result = find_near_segment(&line->columns[i], target, dir); if (result != NULL) return result; /* Recherche dans la direction des colonnes voisines */ if (result == NULL) 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 == NULL; i--) { displayed = (i <= BLC_DISPLAY ? display[i - 1] : true); if (displayed) result = get_last_segment(&line->columns[i - 1]); } break; case GDK_SCROLL_RIGHT: /* Si on a atteint la dernière colonne sans trouver... */ /*if (i == BLC_COUNT) break;*/ /* On s'assure que la colonne suivante est visible et peuplée */ for (; (i + 1) < BLC_COUNT && result == NULL; i++) { displayed = ((i + 1) < BLC_DISPLAY ? display[i + 1] : true); if (displayed) result = get_first_segment(&line->columns[i + 1]); } break; default: break; } /* Calcul de la position finale */ if (result != NULL) { *offset = 0; for (k = 0; k < i; k++) { displayed = (k < BLC_DISPLAY ? display[k] : true); if (displayed) { *offset += g_buffer_line_compute_max_width(line, k, max_widths); 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 à 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. * * * * Description : Ajoute du texte à formater dans une ligne donnée. * * * * Retour : Portion de texte mis en place, voire NULL. * * * * Remarques : - * * * ******************************************************************************/ GBufferSegment *g_buffer_line_insert_text(GBufferLine *line, BufferLineColumn column, const char *text, size_t length, RenderingTagType type) { GBufferSegment *result; /* Portion de texte à renvoyer */ if (length == 0) return; if (column == BLC_MAIN) column = line->main_column; if (column == BLC_LAST_USED) column = line->last_used; else line->last_used = column; result = g_buffer_segment_new(type, text, length); g_buffer_line_add_segment(line, column, result); 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 #1 */ size_t j; /* Boucle de parcours #2 */ 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, " "); for (j = 0; j < line->columns[i].count; j++) { extra = g_buffer_segment_get_text(line->columns[i].segments[j], markup); if (result == NULL) result = extra; else { result = stradd(result, extra); free(extra); } } } return result; } /****************************************************************************** * * * Paramètres : line = ligne à venir consulter/compléter. * * manager = ligne de départ d'un groupe de ligne. * * * * Description : Retient les largeurs d'une ligne si maximales. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_line_update_max_widths(GBufferLine *line, GBufferLine *manager) { BufferLineColumn i; /* Boucle de parcours */ GBufferLine *old; /* Ancienne ligne associée */ gint merged_width; /* Largeur cumulée avant fusion*/ gint width; /* Largeur d'une colonne */ /* Réinitialisation ? */ if (line == manager) { assert(line->flags & BLF_WIDTH_MANAGER); for (i = 0; i < BLC_COUNT; i++) line->max_widths[i] = 0; line->merged_width = 0; } else { assert((line->flags & BLF_WIDTH_MANAGER) == 0); old = line->manager; g_object_ref(G_OBJECT(manager)); line->manager = manager; g_object_unref(G_OBJECT(old)); } /* Mises à jour */ merged_width = 0; for (i = 0; i < BLC_COUNT; i++) { width = get_column_width(&line->columns[i]); if (i < line->merge_start) manager->max_widths[i] = MAX(manager->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) manager->merged_width = MAX(manager->merged_width, merged_width); } /****************************************************************************** * * * Paramètres : line = ligne à venir consulter. * * max_widths = tableau résumant les largeurs maximales. [OUT]* * merged_width = largeur maximale en cas de fusion. |OUT] * * * * Description : Filtre des largeurs de lignes et ne garde que les maximales. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_line_apply_max_widths(GBufferLine *line, gint *max_widths, gint *merged_width) { GBufferLine *manager; /* Gestionnaire de groupe */ BufferLineColumn i; /* Boucle de parcours */ if (line->flags & BLF_WIDTH_MANAGER) manager = line; else manager = line->manager; for (i = 0; i < BLC_COUNT; i++) max_widths[i] = MAX(manager->max_widths[i], max_widths[i]); *merged_width = MAX(manager->merged_width, *merged_width); } /****************************************************************************** * * * Paramètres : line = ligne à venir consulter. * * index = indice de la colonne visée. * * max_widths = tableau résumant les largeurs maximales. * * * * 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 gint *max_widths) { gint result; /* Largeur à retourner */ assert(index < BLC_COUNT); if (index >= line->merge_start) result = get_column_width(&line->columns[index]); else { if (index < BLC_ASSEMBLY) result = max_widths[index]; else { if (line->flags & BLF_WIDTH_MANAGER) result = line->max_widths[index]; else result = line->manager->max_widths[index]; } } return result; } /****************************************************************************** * * * 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. * * cairo = contexte graphique à utiliser pour les pinceaux.* * max_widths = largeurs de colonne à respecter. * * x_init = abscisse du point d'impression de départ. * * y = ordonnée du point d'impression. * * display = règles d'affichage des colonnes modulables. * * 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 gint max_widths[BLC_COUNT], gint x_init, gint y, const bool *display, const segcnt_list *list) { GBufferLineClass *class; /* Stockage de briques de base */ gint x; /* Point de départ d'impression*/ BufferLineColumn 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); else if (line->flags & BLF_BOOKMARK) cairo_set_source_surface(cairo, class->bookmark_img, 5, y); cairo_paint(cairo); } x = x_init; for (i = 0; i < BLC_COUNT; i++) { if (i < BLC_DISPLAY && !display[i]) continue; draw_segments_of_column(&line->columns[i], cairo, x, y, list); if (i < line->merge_start) { max_width = g_buffer_line_compute_max_width(line, i, max_widths); x += max_width + COL_MARGIN; } } } /****************************************************************************** * * * 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_segments_of_column(). * * 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_segments_of_column(&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; } }