/* Chrysalide - Outil d'analyse de fichiers binaires * gbuffersegment.c - concentration d'un fragment de caractères aux propriétés communes * * 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 "gbuffersegment.h" #include #include #include #include #include #include #include "../common/extstr.h" #include "../common/fnv1a.h" #include "../gtkext/gtkblockview.h" #include "../gtkext/support.h" /* ----------------------- ISOLATION DE CONTENUS PARTAGEABLES ----------------------- */ /* Propriétés de rendu */ typedef struct _rendering_color_t { GdkRGBA color; /* Couleur de rendu */ bool has_color; /* Définition en place ? */ } rendering_color_t; typedef struct _rendering_pattern_t { rendering_color_t foreground; /* Couleur d'impression */ rendering_color_t inverted; /* Couleur inversée pour sél. */ cairo_font_slant_t slant; /* Style d'impression */ cairo_font_weight_t weight; /* Poids de la police */ } rendering_pattern_t; /* Contenu brut pour segment, potentiellement commun */ typedef struct _seg_content { unsigned int ref_count; /* Compteur de références */ rendering_pattern_t *pattern; /* Propriétés du rendu */ fnv64_t hash; /* Empreinte pour comparaisons */ char text[0]; /* Texte brut conservé */ } seg_content; /* Conservation de toutes les créations partagées */ static GHashTable *_segcnt_htable; /* Fournit l'empreinte d'un contenu pour segments. */ static guint get_seg_content_hash(const seg_content *); /* Détermine si deux contenus pour segments sont identiques. */ static bool is_seg_content_equal(const seg_content *, const seg_content *); /* Détermine si deux contenus pour segments sont identiques. */ static seg_content *get_shared_content(const seg_content *); /* Abandonne un contenu pour segments. */ static void release_shared_content(seg_content *); /* -------------------- NATURE DE BASE POUR UN FRAGMENT DE TEXTE -------------------- */ /* Nom des éléments CSS */ #define SEGMENT_NAME(s) "segment-" s static const char *_segment_names[RTT_COUNT] = { [RTT_RAW] = SEGMENT_NAME("raw"), [RTT_COMMENT] = SEGMENT_NAME("comment"), [RTT_INDICATION] = SEGMENT_NAME("indication"), [RTT_PHYS_ADDR_PAD] = SEGMENT_NAME("phys-addr-padding"), [RTT_PHYS_ADDR] = SEGMENT_NAME("phys-addr"), [RTT_VIRT_ADDR_PAD] = SEGMENT_NAME("virt-addr-padding"), [RTT_VIRT_ADDR] = SEGMENT_NAME("virt-addr"), [RTT_RAW_CODE] = SEGMENT_NAME("raw-code"), [RTT_LABEL] = SEGMENT_NAME("label"), [RTT_INSTRUCTION] = SEGMENT_NAME("instruction"), [RTT_IMMEDIATE] = SEGMENT_NAME("immediate"), [RTT_REGISTER] = SEGMENT_NAME("register"), [RTT_PUNCT] = SEGMENT_NAME("punct"), [RTT_HOOK] = SEGMENT_NAME("hooks"), [RTT_SIGNS] = SEGMENT_NAME("signs"), [RTT_LTGT] = SEGMENT_NAME("ltgt"), [RTT_SECTION] = SEGMENT_NAME("section"), [RTT_SEGMENT] = SEGMENT_NAME("segment"), [RTT_STRING] = SEGMENT_NAME("string"), [RTT_VAR_NAME] = SEGMENT_NAME("var-name"), [RTT_KEY_WORD] = SEGMENT_NAME("keyword"), [RTT_ERROR] = SEGMENT_NAME("error"), }; /* Compléments à Cairo */ #define CAIRO_FONT_SLANT_COUNT 3 #define CAIRO_FONT_WEIGHT_COUNT 2 #define CAIRO_FONTS_COUNT (CAIRO_FONT_SLANT_COUNT * CAIRO_FONT_WEIGHT_COUNT) #define CAIRO_FONT_INDEX(s, w) ((s) + (w) * CAIRO_FONT_WEIGHT_COUNT) /* Fragment de caractères aux propriétés communes (instance) */ struct _GBufferSegment { GObject parent; /* A laisser en premier */ seg_content *content; /* Contenu, partagé ou non */ }; /* Fragment de caractères aux propriétés communes (classe) */ struct _GBufferSegmentClass { GObjectClass parent; /* A laisser en premier */ rendering_color_t selection_bg; /* Fond d'impression */ cairo_t *font_ctxts[CAIRO_FONTS_COUNT]; /* Contextes de police */ double x_advances[CAIRO_FONTS_COUNT]; /* Largeurs par caractère */ rendering_pattern_t patterns[RTT_COUNT];/* Modèles d'impression */ /* Signaux */ void (* content_changed) (GBufferSegment *); }; /* Procède à l'initialisation d'une classe de fragment de texte. */ static void g_buffer_segment_class_init(GBufferSegmentClass *); /* Procède à l'initialisation d'un fragment de texte. */ static void g_buffer_segment_init(GBufferSegment *); /* Supprime toutes les références externes. */ static void g_buffer_segment_dispose(GBufferSegment *); /* Procède à la libération totale de la mémoire. */ static void g_buffer_segment_finalize(GBufferSegment *); /* Met à jour le contenu d'un fragment de texte. */ void g_buffer_segment_set_text(GBufferSegment *, rendering_pattern_t *, const char *, size_t); /* -------------------- GESTION OPTIMALE D'UNE LISTE DE CONTENUS -------------------- */ /* Liste identifiant un ensemble de segments */ struct _segcnt_list { fnv64_t *hashes; /* Empreinte pour comparaisons */ size_t count; /* Nommbre de ces empreintes */ }; /* ---------------------------------------------------------------------------------- */ /* ISOLATION DE CONTENUS PARTAGEABLES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : - * * * * Description : Initialise la table mémorisant les contenus pour segments. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool init_segment_content_hash_table(void) { _segcnt_htable = g_hash_table_new_full((GHashFunc)get_seg_content_hash, (GEqualFunc)is_seg_content_equal, free, NULL); return (_segcnt_htable != NULL); } /****************************************************************************** * * * Paramètres : - * * * * Description : Organise la sortie de la table des contenus pour segments. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void exit_segment_content_hash_table(void) { assert(g_hash_table_size(_segcnt_htable) == 0); g_hash_table_unref(_segcnt_htable); } /****************************************************************************** * * * Paramètres : content = contenu pour segment à consulter. * * * * Description : Fournit l'empreinte d'un contenu pour segments. * * * * Retour : Empreinte de lu contenu représenté. * * * * Remarques : - * * * ******************************************************************************/ static guint get_seg_content_hash(const seg_content *content) { return content->hash; } /****************************************************************************** * * * Paramètres : content = premier contenu pour segment à analyser. * * other = second contenu pour segment à analyser. * * * * Description : Détermine si deux contenus pour segments sont identiques. * * * * Retour : Bilan de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ static bool is_seg_content_equal(const seg_content *content, const seg_content *other) { bool result; /* Résultat à retourner */ result = (content->pattern == other->pattern); result &= (cmp_fnv_64a(content->hash, other->hash) == 0); result &= (strcmp(content->text, other->text) == 0); return result; } /****************************************************************************** * * * Paramètres : content = premier contenu pour segment à analyser. * * other = second contenu pour segment à analyser. * * * * Description : Détermine si deux contenus pour segments sont identiques. * * * * Retour : Bilan de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ static seg_content *get_shared_content(const seg_content *content) { seg_content *result; /* Contenu partagé à renvoyer */ gboolean found; /* Le contenu existe déjà ? */ size_t allocated; /* Besoin complet en mémoire */ #ifndef NDEBUG gboolean created; /* Validation de mise en place */ #endif /** * On considère qu'il n'y a pas besoin de mutex ici, puisque toutes les * opérations se réalisent à priori dans le seul thread principal pour l'affichage. */ found = g_hash_table_lookup_extended(_segcnt_htable, content, (gpointer *)&result, NULL); if (!found) { allocated = sizeof(seg_content) + strlen(content->text) + 1; result = (seg_content *)malloc(allocated); memcpy(result, content, allocated); result->ref_count = 1; #ifndef NDEBUG created = g_hash_table_insert(_segcnt_htable, result, result); assert(created); #else g_hash_table_insert(_segcnt_htable, result, result); #endif } else { assert(result->ref_count < UINT_MAX); result->ref_count++; } return result; } /****************************************************************************** * * * Paramètres : content = contenu pour segments à délaisser. * * * * Description : Abandonne un contenu pour segments. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void release_shared_content(seg_content *content) { #ifndef NDEBUG gboolean deleted; /* Validation de suppression */ #endif /** * On considère qu'il n'y a pas besoin de mutex ici, puisque toutes les * opérations se réalisent à priori dans le seul thread principal pour l'affichage. */ if (--content->ref_count == 0) { #ifndef NDEBUG deleted = g_hash_table_remove(_segcnt_htable, content); assert(deleted); #else g_hash_table_remove(_segcnt_htable, content); #endif } } /* ---------------------------------------------------------------------------------- */ /* NATURE DE BASE POUR UN FRAGMENT DE TEXTE */ /* ---------------------------------------------------------------------------------- */ /* Détermine le type du fragment de caractères aux propriétés communes. */ G_DEFINE_TYPE(GBufferSegment, g_buffer_segment, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : class = classe de composant GTK à initialiser. * * * * Description : Procède à l'initialisation d'une classe de fragment de texte.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_segment_class_init(GBufferSegmentClass *class) { GObjectClass *object; /* Autre version de la classe */ GtkStyleContext *context; /* Contexte pour les styles */ GtkWidgetPath *path; /* Chemin d'accès aux thèmes */ gchar *filename; /* Accès à une image 1x1 */ cairo_font_slant_t s; /* Boucle de parcours #1 */ cairo_font_weight_t w; /* Boucle de parcours #2 */ cairo_t **cr; /* Contexte à créer */ cairo_surface_t *surface; /* Surface pour dessin Cairo */ cairo_text_extents_t extents; /* Couverture des caractères */ RenderingTagType i; /* Boucle de parcours */ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)g_buffer_segment_dispose; object->finalize = (GObjectFinalizeFunc)g_buffer_segment_finalize; /* Création d'un contexte d'accès */ path = gtk_widget_path_new(); gtk_widget_path_append_type(path, G_TYPE_OBJECT); context = gtk_style_context_new(); gtk_style_context_set_path(context, path); gtk_style_context_set_screen(context, gdk_screen_get_default()); /* Contextes pour les mesures initiales */ filename = find_pixmap_file("nil.png"); if (filename == NULL) abort(); for (s = CAIRO_FONT_SLANT_NORMAL; s < CAIRO_FONT_SLANT_COUNT; s++) for (w = CAIRO_FONT_WEIGHT_NORMAL; w < CAIRO_FONT_WEIGHT_COUNT; w++) { cr = &class->font_ctxts[CAIRO_FONT_INDEX(s, w)]; surface = cairo_image_surface_create_from_png(filename); *cr = cairo_create(surface); cairo_surface_destroy(surface); cairo_select_font_face(*cr, "mono", s, w); cairo_set_font_size(*cr, 13); cairo_text_extents(*cr, "A", &extents); class->x_advances[CAIRO_FONT_INDEX(s, w)] = extents.x_advance; } g_free(filename); /* Fond d'impression */ class->selection_bg.has_color = true; class->selection_bg.color.red = 0.5; class->selection_bg.color.green = 0.5; class->selection_bg.color.blue = 0.5; class->selection_bg.color.alpha = 1.0; /* Chargement des définitions utiles */ void define_rendering_pattern(GtkStyleContext *ctx, const char *name, rendering_pattern_t *pattern) { GdkRGBA *tmp_color; /* Description d'une couleur */ PangoFontDescription *font_desc; /* Description d'une police */ gtk_style_context_save(ctx); gtk_style_context_add_class(context, name); gtk_style_context_get(ctx, GTK_STATE_NORMAL, GTK_STYLE_PROPERTY_COLOR, &tmp_color, NULL); pattern->foreground.has_color = true; pattern->foreground.color = *tmp_color; pattern->inverted.has_color = true; pattern->inverted.color.red = 1.0 - tmp_color->red; pattern->inverted.color.green = 1.0 - tmp_color->green; pattern->inverted.color.blue = 1.0 - tmp_color->blue; pattern->inverted.color.alpha = tmp_color->alpha; gdk_rgba_free(tmp_color); gtk_style_context_get(context, GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_FONT, &font_desc, NULL); switch (pango_font_description_get_style(font_desc)) { case PANGO_STYLE_NORMAL: pattern->slant = CAIRO_FONT_SLANT_NORMAL; break; case PANGO_STYLE_ITALIC: pattern->slant = CAIRO_FONT_SLANT_ITALIC; break; case PANGO_STYLE_OBLIQUE: pattern->slant = CAIRO_FONT_SLANT_OBLIQUE; break; } switch (pango_font_description_get_weight(font_desc)) { case PANGO_WEIGHT_THIN: case PANGO_WEIGHT_ULTRALIGHT: case PANGO_WEIGHT_LIGHT: case PANGO_WEIGHT_SEMILIGHT: case PANGO_WEIGHT_BOOK: case PANGO_WEIGHT_NORMAL: case PANGO_WEIGHT_MEDIUM: pattern->weight = CAIRO_FONT_WEIGHT_NORMAL; break; case PANGO_WEIGHT_SEMIBOLD: case PANGO_WEIGHT_BOLD: case PANGO_WEIGHT_ULTRABOLD: case PANGO_WEIGHT_HEAVY: case PANGO_WEIGHT_ULTRAHEAVY: pattern->weight = CAIRO_FONT_WEIGHT_BOLD; break; } pango_font_description_free(font_desc); gtk_style_context_restore(context); } for (i = 0; i < RTT_COUNT; i++) define_rendering_pattern(context, _segment_names[i], &class->patterns[i]); /* Nettoyages finaux... */ gtk_widget_path_free(path); g_object_unref(context); /* Signaux */ g_signal_new("content-changed", G_TYPE_BUFFER_SEGMENT, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GBufferSegmentClass, content_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /****************************************************************************** * * * Paramètres : segment = composant GTK à initialiser. * * * * Description : Procède à l'initialisation d'un fragment de texte. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_segment_init(GBufferSegment *segment) { } /****************************************************************************** * * * Paramètres : segment = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_segment_dispose(GBufferSegment *segment) { release_shared_content(segment->content); G_OBJECT_CLASS(g_buffer_segment_parent_class)->dispose(G_OBJECT(segment)); } /****************************************************************************** * * * Paramètres : segment = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_segment_finalize(GBufferSegment *segment) { G_OBJECT_CLASS(g_buffer_segment_parent_class)->finalize(G_OBJECT(segment)); } /****************************************************************************** * * * Paramètres : type = propriétés de la zone de texte. * * text = chaîne de caractères à traiter. * * length = quantité de ces caractères. * * * * Description : Crée un nouveau fragment de texte avec des propriétés. * * * * Retour : Composant GTK créé. * * * * Remarques : - * * * ******************************************************************************/ GBufferSegment *g_buffer_segment_new(RenderingTagType type, const char *text, size_t length) { GBufferSegment *result; /* Composant à retourner */ GBufferSegmentClass *class; /* Stockage de styles préparés */ result = g_object_new(G_TYPE_BUFFER_SEGMENT, NULL); class = G_BUFFER_SEGMENT_GET_CLASS(result); g_buffer_segment_set_text(result, &class->patterns[type], text, length); return result; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à mettre à jour. * * pattern = paramètres d'impression du texte. * * text = chaîne de caractères à traiter. * * length = quantité de ces caractères. * * * * Description : Met à jour le contenu d'un fragment de texte. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_segment_set_text(GBufferSegment *segment, rendering_pattern_t *pattern, const char *text, size_t length) { char atmp[sizeof(seg_content) + 128]; /* Allocation static facile */ seg_content *content; /* Contenu à mettre en place ? */ if (length < (sizeof(atmp) - sizeof(seg_content))) content = (seg_content *)atmp; else content = (seg_content *)malloc(sizeof(seg_content) + length + 1); content->pattern = pattern; content->hash = fnv_64a_hash(text); memcpy(content->text, text, length); content->text[length] = '\0'; segment->content = get_shared_content(content); if (content != (seg_content *)atmp) free(content); } /****************************************************************************** * * * Paramètres : segment = fragment de texte à mettre à jour. * * text = chaîne de caractères à traiter. * * length = quantité de ces caractères. * * * * Description : Met à jour le contenu d'un fragment de texte. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_segment_update_text(GBufferSegment *segment, const char *text, size_t length) { rendering_pattern_t *pattern; /* Conservation des paramètres */ /* Destruction */ pattern = segment->content->pattern; release_shared_content(segment->content); /* Création */ g_buffer_segment_set_text(segment, pattern, text, length); g_signal_emit_by_name(segment, "content-changed"); } /****************************************************************************** * * * Paramètres : segment = fragment de texte à consulter. * * ref = segment de référence servant à la comparaison. * * * * Description : Indique si les textes de deux segments sont identiques. * * * * Retour : Bilan de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ bool g_buffer_segment_compare(const GBufferSegment *segment, const GBufferSegment *ref) { bool result; /* Bilan à retourner */ result = is_seg_content_equal(segment->content, ref->content); return result; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à consulter. * * markup = indique si le texte doit être décoré ou non. * * * * Description : Fournit le texte brut conservé dans le segment. * * * * Retour : Texte conservé en interne. * * * * Remarques : - * * * ******************************************************************************/ char *g_buffer_segment_get_text(const GBufferSegment *segment, bool markup) { char *result; /* Description à renvoyer */ const seg_content *content; /* Accès au contenu */ char color[7]; /* Couleur hexadécimale */ char *valid; content = segment->content; /* Résolution du cas simple */ if (!markup) return strdup(content->text); result = strdup("pattern->foreground.color.red * 255), (unsigned char)(content->pattern->foreground.color.green * 255), (unsigned char)(content->pattern->foreground.color.blue * 255)); result = stradd(result, color); result = stradd(result, "\""); /* Style */ result = stradd(result, "style=\""); switch (content->pattern->slant) { case CAIRO_FONT_SLANT_NORMAL: result = stradd(result, "normal"); break; case CAIRO_FONT_SLANT_ITALIC: result = stradd(result, "italic"); break; case CAIRO_FONT_SLANT_OBLIQUE: result = stradd(result, "oblique"); break; } result = stradd(result, "\""); /* Epaisseur */ result = stradd(result, "weight=\""); switch (content->pattern->weight) { case CAIRO_FONT_WEIGHT_NORMAL: result = stradd(result, "normal"); break; case CAIRO_FONT_WEIGHT_BOLD: result = stradd(result, "bold"); break; } result = stradd(result, "\""); /* Conclusion */ result = stradd(result, ">"); valid = strdup(content->text); valid = strrpl(valid, "<", "<"); valid = strrpl(valid, "&", "&"); result = stradd(result, valid); free(valid); result = stradd(result, ""); return result; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à consulter. * * * * Description : Fournit la quantité de pixels requise pour l'impression. * * * * Retour : Largeur requise par la colonne, en pixel. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_segment_get_width(const GBufferSegment *segment) { gint result; /* Largeur à retourner */ GBufferSegmentClass *class; /* Classe associée à l'instance*/ const seg_content *content; /* Accès au contenu */ cairo_font_slant_t slant; /* Style d'impression */ cairo_font_weight_t weight; /* Poids de la police */ class = G_BUFFER_SEGMENT_GET_CLASS(segment); content = segment->content; slant = content->pattern->slant; weight = content->pattern->weight; result = class->x_advances[CAIRO_FONT_INDEX(slant, weight)] * strlen(content->text); return result; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à consulter. * * x = position horizontale au niveau du segment. * * * * Description : Fournit la position idéale pour un marqueur. * * * * Retour : Position dans le segment donné. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_segment_get_caret_position(const GBufferSegment *segment, gint x) { gint result; /* Position à retourner */ gint width; /* Largeur du segment */ gint char_width; /* Largeur de police fixe */ width = g_buffer_segment_get_width(segment); if (x <= 0) result = 0; else if (x >= width) result = width; else { char_width = width / strlen(segment->content->text); result = (x / char_width) * char_width; if ((x % char_width) > (char_width / 2)) result += char_width; } return result; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à manipuler. * * x = position du curseur à faire évoluer. [OUT] * * ctrl = indique la demande d'un parcours rapide. * * dir = direction du parcours. * * * * Description : Déplace le curseur au sein d'un segment de tampon. * * * * Retour : true si un déplacement a été effectué, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_buffer_segment_move_caret(const GBufferSegment *segment, gint *x, bool ctrl, GdkScrollDirection dir) { bool result; /* Bilan d'opération à renvoyer*/ gint width; /* Largeur du segment */ gint char_width; /* Largeur de police fixe */ result = false; width = g_buffer_segment_get_width(segment); char_width = width / strlen(segment->content->text); if (dir == GDK_SCROLL_LEFT) { printf(">>>>> left ::: x=%d width=%d char=%d\n", *x, width, char_width); if (*x > width) *x = width + char_width; if (*x == 0) goto gbsmc_done; if (ctrl) *x = 0; else *x = MAX(0, *x - char_width); result = true; } else if (dir == GDK_SCROLL_RIGHT) { printf(">>>>> right ::: x=%d width=%d char=%d\n", *x, width, char_width); if (*x == width) goto gbsmc_done; if (ctrl) *x = width; else *x = MIN(width, *x + char_width); result = true; } gbsmc_done: printf(">>>>> result ::: %d\n", result); return result; } /****************************************************************************** * * * Paramètres : segment = fragment de texte à manipuler. * * cr = contexte graphique à utiliser pour les pinceaux. * * x = abscisse du point d'impression (à maj). [OUT] * * y = ordonnée du point d'impression. * * list = liste de contenus à mettre en évidence. * * * * Description : Imprime le fragment de texte représenté. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_segment_draw(GBufferSegment *segment, cairo_t *cr, gint *x, gint y, const segcnt_list *list) { bool selected; /* Marquer une sélection ? */ gint width; /* Largeur du segment */ GBufferSegmentClass *class; /* Accès aux infos globales */ cairo_operator_t old; /* Sauvegarde avant changement */ const seg_content *content; /* Accès au contenu */ const rendering_color_t *used_fg; /* Couleur d'impression utile */ selected = selection_list_has_segment_content(list, segment); width = g_buffer_segment_get_width(segment); /* Fond du texte */ if (selected) { class = G_BUFFER_SEGMENT_GET_CLASS(segment); cairo_set_source_rgba(cr, class->selection_bg.color.red, class->selection_bg.color.green, class->selection_bg.color.blue, class->selection_bg.color.alpha); cairo_rectangle(cr, *x, y, width, 17); old = cairo_get_operator(cr); cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE); cairo_fill(cr); cairo_set_operator(cr, old); } /* Couleur d'impression */ content = segment->content; if (selected) used_fg = &content->pattern->inverted; else used_fg = &content->pattern->foreground; if (used_fg->has_color) cairo_set_source_rgba(cr, used_fg->color.red, used_fg->color.green, used_fg->color.blue, used_fg->color.alpha); else cairo_set_source_rgb(cr, 0, 0, 0); /* Impression du texte */ cairo_select_font_face(cr, "mono", content->pattern->slant, content->pattern->weight); cairo_set_font_size(cr, 13); cairo_move_to(cr, *x, y + 17 - 3); /* 3 = font extents.descent */ cairo_show_text(cr, content->text); *x += width; } /****************************************************************************** * * * Paramètres : ctx = éléments à disposition pour l'exportation. * * type = type d'exportation attendue. * * * * Description : Exporte tous les styles utilisés par des segments. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_segment_export_style(buffer_export_context *ctx, BufferExportType type) { GBufferSegment *dummy; /* Segment servant de sujet */ GBufferSegmentClass *class; /* Classe des segments */ size_t i; /* Boucle de parcours */ const rendering_pattern_t *pattern; /* Modèle à transcrire */ dummy = g_object_new(G_TYPE_BUFFER_SEGMENT, NULL); class = G_BUFFER_SEGMENT_GET_CLASS(dummy); for (i = 0; i < RTT_COUNT; i++) { pattern = &class->patterns[i]; switch (type) { case BET_HTML: dprintf(ctx->fd, ".%s {\n", _segment_names[i]); if (pattern->foreground.has_color) dprintf(ctx->fd, "\tcolor: #%02hhx%02hhx%02hhx;\n", (unsigned char)(pattern->foreground.color.red * 255), (unsigned char)(pattern->foreground.color.green * 255), (unsigned char)(pattern->foreground.color.blue * 255)); switch (pattern->slant) { case CAIRO_FONT_SLANT_ITALIC: dprintf(ctx->fd, "\tfont-style: italic;\n"); break; case CAIRO_FONT_SLANT_OBLIQUE: dprintf(ctx->fd, "\tfont-style: oblique;\n"); break; default: dprintf(ctx->fd, "\tfont-style: normal;\n"); break; } switch (pattern->weight) { case CAIRO_FONT_WEIGHT_BOLD: dprintf(ctx->fd, "\tfont-weight: bold;\n"); break; default: dprintf(ctx->fd, "\tfont-weight: normal;\n"); break; } dprintf(ctx->fd, "}\n"); break; default: break; } } g_object_unref(G_OBJECT(dummy)); } /****************************************************************************** * * * Paramètres : segment = fragment de texte à manipuler. * * ctx = éléments à disposition pour l'exportation. * * type = type d'exportation attendue. * * * * Description : Exporte le fragment de texte représenté. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_segment_export(const GBufferSegment *segment, buffer_export_context *ctx, BufferExportType type) { const seg_content *content; /* Accès au contenu */ GBufferSegmentClass *class; /* Classe des segments */ size_t index; /* Indice du modèle de rendu */ content = segment->content; switch (type) { case BET_HTML: class = G_BUFFER_SEGMENT_GET_CLASS(segment); index = (content->pattern - class->patterns); dprintf(ctx->fd, "", _segment_names[index]); break; default: break; } dprintf(ctx->fd, "%s", content->text); switch (type) { case BET_HTML: dprintf(ctx->fd, ""); break; default: break; } } /* ---------------------------------------------------------------------------------- */ /* GESTION OPTIMALE D'UNE LISTE DE CONTENUS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : - * * * * Description : Initilise une liste de contenus de segments. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ segcnt_list *init_segment_content_list(void) { segcnt_list *result; /* Structure à retourner */ result = (segcnt_list *)calloc(1, sizeof(segcnt_list)); return result; } /****************************************************************************** * * * Paramètres : list = ensemble de références de contenus à traiter. * * * * Description : Libère la mémoire occupée par une liste de contenus. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void exit_segment_content_list(segcnt_list *list) { reset_segment_content_list(list); free(list); } /****************************************************************************** * * * Paramètres : list = ensemble de références de contenus à manipuler. * * * * Description : Vide, si besoin est, une liste de contenus de segments. * * * * Retour : true si des éléments ont été purgés, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool reset_segment_content_list(segcnt_list *list) { bool result; /* Bilan d'action à renvoyer */ result = (list->count > 0); if (list->hashes != NULL) { free(list->hashes); list->hashes = NULL; } list->count = 0; return result; } /****************************************************************************** * * * Paramètres : list = ensemble de références de contenus à manipuler. * * segment = fragment de texte à conservr. * * * * Description : Marque le contenu d'un segment comme remarquable. * * * * Retour : true si la liste a été complétée, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool add_segment_content_to_selection_list(segcnt_list *list, const GBufferSegment *segment) { bool result; /* Bilan à retourner */ const seg_content *content; /* Accès au contenu */ size_t i; /* Boucle de parcours */ static const char white_list[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; result = false; content = segment->content; for (i = 0; i < (sizeof(white_list) - 1) && !result; i++) result = (strchr(content->text, white_list[i]) != NULL); if (result) { list->hashes = (fnv64_t *)realloc(list->hashes, ++list->count * sizeof(fnv64_t)); list->hashes[list->count - 1] = content->hash; } return result; } /****************************************************************************** * * * Paramètres : list = ensemble de références de contenus à consulter. * * segment = fragment de texte à comparer. * * * * Description : Indique si le contenu d'un segment est notable ou non. * * * * Retour : true si le segment a un contenu présent dans la sélection. * * * * Remarques : - * * * ******************************************************************************/ bool selection_list_has_segment_content(const segcnt_list *list, const GBufferSegment *segment) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ result = false; for (i = 0; i < list->count && !result; i++) result = (cmp_fnv_64a(list->hashes[i], segment->content->hash) == 0); return result; }