summaryrefslogtreecommitdiff
path: root/src/glibext/linesegment.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/glibext/linesegment.c')
-rw-r--r--src/glibext/linesegment.c1155
1 files changed, 1155 insertions, 0 deletions
diff --git a/src/glibext/linesegment.c b/src/glibext/linesegment.c
new file mode 100644
index 0000000..e233b00
--- /dev/null
+++ b/src/glibext/linesegment.c
@@ -0,0 +1,1155 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * linesegment.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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "linesegment.h"
+
+
+#include <assert.h>
+#include <limits.h>
+#include <malloc.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include "../common/extstr.h"
+#include "../common/fnv1a.h"
+#include "../gtkext/support.h"
+
+
+
+/* ------------------------ NATURE 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)
+
+
+/* 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;
+
+typedef struct _segment_rendering
+{
+ 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 */
+
+} segment_rendering;
+
+
+/* Configuration globale des rendus */
+static segment_rendering _seg_params;
+
+
+
+/* ----------------------- ISOLATION DE CONTENUS PARTAGEABLES ----------------------- */
+
+
+/* Fragment de caractères aux propriétés potentiellement partagées */
+struct _line_segment
+{
+ 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é */
+
+};
+
+
+/* Conservation de toutes les créations partagées */
+static GHashTable *_segcnt_htable;
+
+
+/* Fournit l'empreinte d'un contenu pour segments. */
+static guint get_line_segment_hash(const line_segment *);
+
+/* Détermine si deux contenus pour segments sont identiques. */
+static bool is_line_segment_equal(const line_segment *, const line_segment *);
+
+/* Détermine si deux contenus pour segments sont identiques. */
+static line_segment *get_shared_segment_content(const line_segment *);
+
+/* Abandonne un contenu pour segments. */
+static void release_shared_segment_content(line_segment *);
+
+
+
+/* -------------------- 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 */
+
+};
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* NATURE POUR UN FRAGMENT DE TEXTE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Procède à l'initialisation des paramètres de rendu de texte. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool load_segment_rendering_parameters(void)
+{
+ 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 */
+
+ /* 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 = &_seg_params.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);
+ _seg_params.x_advances[CAIRO_FONT_INDEX(s, w)] = extents.x_advance;
+
+ }
+
+ g_free(filename);
+
+ /* Fond d'impression */
+
+ _seg_params.selection_bg.has_color = true;
+ _seg_params.selection_bg.color.red = 0.5;
+ _seg_params.selection_bg.color.green = 0.5;
+ _seg_params.selection_bg.color.blue = 0.5;
+ _seg_params.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], &_seg_params.patterns[i]);
+
+ /* Nettoyages finaux... */
+
+ gtk_widget_path_free(path);
+ g_object_unref(context);
+
+ return true;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* 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_line_segment_hash,
+ (GEqualFunc)is_line_segment_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_line_segment_hash(const line_segment *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_line_segment_equal(const line_segment *content, const line_segment *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 line_segment *get_shared_segment_content(const line_segment *content)
+{
+ line_segment *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(line_segment) + strlen(content->text) + 1;
+
+ result = (line_segment *)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_segment_content(line_segment *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 */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* 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 : Elément créé ou recyclé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+line_segment *get_new_line_segment(RenderingTagType type, const char *text, size_t length)
+{
+ line_segment *result; /* Elément à retourner */
+ char atmp[sizeof(line_segment) + 128]; /* Allocation static facile */
+ line_segment *content; /* Contenu à mettre en place ? */
+
+ if (length < (sizeof(atmp) - sizeof(line_segment)))
+ content = (line_segment *)atmp;
+ else
+ content = (line_segment *)malloc(sizeof(line_segment) + length + 1);
+
+ content->pattern = &_seg_params.patterns[type];
+
+ content->hash = fnv_64a_hash(text);
+
+ memcpy(content->text, text, length);
+ content->text[length] = '\0';
+
+ result = get_shared_segment_content(content);
+
+ if (content != (line_segment *)atmp)
+ free(content);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : segment = fragment de texte à libérer de la mémoire. *
+* *
+* Description : Retire une utilisation à un fragment de texte. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void release_line_segment(line_segment *segment)
+{
+ release_shared_segment_content(segment);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : segment = fragment de texte à consulter. *
+* *
+* Description : Indique le type de rendu associé à un segment de ligne. *
+* *
+* Retour : Identifiant de type de rendu. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+RenderingTagType get_line_segment_type(const line_segment *segment)
+{
+ RenderingTagType result; /* Résultat à renvoyer */
+
+ result = (RenderingTagType)(segment->pattern - _seg_params.patterns);
+
+ 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 *get_line_segment_text(const line_segment *segment, bool markup)
+{
+ char *result; /* Description à renvoyer */
+ char color[7]; /* Couleur hexadécimale */
+ char *valid;
+
+ /* Résolution du cas simple */
+ if (!markup)
+ return strdup(segment->text);
+
+ result = strdup("<span ");
+
+ /* Couleur */
+
+ result = stradd(result, "foreground=\"#");
+
+ snprintf(color, sizeof(color), "%02hhx%02hhx%02hhx",
+ (unsigned char)(segment->pattern->foreground.color.red * 255),
+ (unsigned char)(segment->pattern->foreground.color.green * 255),
+ (unsigned char)(segment->pattern->foreground.color.blue * 255));
+
+ result = stradd(result, color);
+
+ result = stradd(result, "\"");
+
+ /* Style */
+
+ result = stradd(result, "style=\"");
+
+ switch (segment->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 (segment->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(segment->text);
+ valid = strrpl(valid, "<", "&lt;");
+ valid = strrpl(valid, "&", "&amp;");
+
+ result = stradd(result, valid);
+
+ free(valid);
+
+ result = stradd(result, "</span>");
+
+ 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 get_line_segment_width(const line_segment *segment)
+{
+ gint result; /* Largeur à retourner */
+ cairo_font_slant_t slant; /* Style d'impression */
+ cairo_font_weight_t weight; /* Poids de la police */
+
+ slant = segment->pattern->slant;
+ weight = segment->pattern->weight;
+
+ result = _seg_params.x_advances[CAIRO_FONT_INDEX(slant, weight)] * strlen(segment->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 get_caret_position_from_line_segment(const line_segment *segment, gint x)
+{
+ gint result; /* Position à retourner */
+ gint width; /* Largeur du segment */
+ gint char_width; /* Largeur de police fixe */
+
+ width = get_line_segment_width(segment);
+
+ if (x <= 0)
+ result = 0;
+
+ else if (x >= width)
+ result = width;
+
+ else
+ {
+ char_width = width / strlen(segment->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 move_caret_on_line_segment(const line_segment *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 = get_line_segment_width(segment);
+ char_width = width / strlen(segment->text);
+
+ if (dir == GDK_SCROLL_LEFT)
+ {
+ 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)
+ {
+ if (*x == width) goto gbsmc_done;
+
+ if (ctrl) *x = width;
+ else *x = MIN(width, *x + char_width);
+
+ result = true;
+
+ }
+
+ gbsmc_done:
+
+ 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 draw_line_segment(const line_segment *segment, cairo_t *cr, gint *x, gint y, const segcnt_list *list)
+{
+ bool selected; /* Marquer une sélection ? */
+ gint width; /* Largeur du segment */
+ cairo_operator_t old; /* Sauvegarde avant changement */
+ const rendering_color_t *used_fg; /* Couleur d'impression utile */
+
+ selected = selection_list_has_segment_content(list, segment);
+
+ width = get_line_segment_width(segment);
+
+ /* Fond du texte */
+ if (selected)
+ {
+ cairo_set_source_rgba(cr,
+ _seg_params.selection_bg.color.red,
+ _seg_params.selection_bg.color.green,
+ _seg_params.selection_bg.color.blue,
+ _seg_params.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 */
+
+ if (selected)
+ used_fg = &segment->pattern->inverted;
+ else
+ used_fg = &segment->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", segment->pattern->slant, segment->pattern->weight);
+ cairo_set_font_size(cr, 13);
+
+ cairo_move_to(cr, *x, y + 17 - 3); /* 3 = font extents.descent */
+
+ cairo_show_text(cr, segment->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 export_line_segment_style(buffer_export_context *ctx, BufferExportType type)
+{
+ size_t i; /* Boucle de parcours */
+ const rendering_pattern_t *pattern; /* Modèle à transcrire */
+
+ for (i = 0; i < RTT_COUNT; i++)
+ {
+ pattern = &_seg_params.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;
+
+ }
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* 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 export_line_segment(const line_segment *segment, buffer_export_context *ctx, BufferExportType type)
+{
+ size_t index; /* Indice du modèle de rendu */
+
+ switch (type)
+ {
+ case BET_HTML:
+ index = (segment->pattern - _seg_params.patterns);
+ dprintf(ctx->fd, "<SPAN class=\"%s\">", _segment_names[index]);
+ break;
+ default:
+ break;
+ }
+
+ dprintf(ctx->fd, "%s", segment->text);
+
+ switch (type)
+ {
+ case BET_HTML:
+ dprintf(ctx->fd, "</SPAN>");
+ 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 line_segment *segment)
+{
+ bool result; /* Bilan à retourner */
+ size_t i; /* Boucle de parcours */
+
+ static const char white_list[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ result = false;
+
+ for (i = 0; i < (sizeof(white_list) - 1) && !result; i++)
+ result = (strchr(segment->text, white_list[i]) != NULL);
+
+ if (result)
+ {
+ list->hashes = (fnv64_t *)realloc(list->hashes, ++list->count * sizeof(fnv64_t));
+
+ list->hashes[list->count - 1] = segment->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 line_segment *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->hash) == 0);
+
+ return result;
+
+}