/* Chrysalide - Outil d'analyse de fichiers binaires * tokenstyle.c - centralisation des paramètres de rendu de bribes textuelles * * Copyright (C) 2018-2024 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Chrysalide is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "tokenstyle.h" #include #include #include "tokenstyle-int.h" #include "../core/logs.h" #include "../common/extstr.h" /* -------------------------- DEFINITION D'UN NOUVEL OBJET -------------------------- */ /* Procède à l'initialisation des gestionnaires de paramètres. */ static void g_token_style_class_init(GTokenStyleClass *); /* Procède à l'initialisation d'un gestionnaire de paramètres. */ static void g_token_style_init(GTokenStyle *); /* Supprime toutes les références externes. */ static void g_token_style_dispose(GTokenStyle *); /* Procède à la libération totale de la mémoire. */ static void g_token_style_finalize(GTokenStyle *); /* ------------------------ RECEPTACLES DE SIGNAUX CONNECTES ------------------------ */ /* Nom des éléments CSS */ #define SEGMENT_NAME(s) "token-" s static const char *_token_names[TRT_COUNT] = { [TRT_NONE] = SEGMENT_NAME("none"), [TRT_RAW_PRINTABLE] = SEGMENT_NAME("raw-printable"), [TRT_RAW_NOT_PRINTABLE] = SEGMENT_NAME("raw-not-printable"), [TRT_RAW_FULL] = SEGMENT_NAME("raw-full"), [TRT_RAW_NULL] = SEGMENT_NAME("raw-null"), [TRT_CHR_PRINTABLE] = SEGMENT_NAME("chr-printable"), [TRT_CHR_NOT_PRINTABLE] = SEGMENT_NAME("chr-not-printable"), [TRT_COMMENT] = SEGMENT_NAME("comment"), [TRT_INDICATION] = SEGMENT_NAME("indication"), [TRT_PHYS_ADDR_PAD] = SEGMENT_NAME("phys-addr-padding"), [TRT_PHYS_ADDR] = SEGMENT_NAME("phys-addr"), [TRT_VIRT_ADDR_PAD] = SEGMENT_NAME("virt-addr-padding"), [TRT_VIRT_ADDR] = SEGMENT_NAME("virt-addr"), [TRT_RAW_CODE] = SEGMENT_NAME("raw-code"), [TRT_RAW_CODE_NULL] = SEGMENT_NAME("raw-code-null"), [TRT_LABEL] = SEGMENT_NAME("label"), [TRT_INSTRUCTION] = SEGMENT_NAME("instruction"), [TRT_IMMEDIATE] = SEGMENT_NAME("immediate"), [TRT_REGISTER] = SEGMENT_NAME("register"), [TRT_PUNCT] = SEGMENT_NAME("punct"), [TRT_HOOK] = SEGMENT_NAME("hooks"), [TRT_SIGNS] = SEGMENT_NAME("signs"), [TRT_LTGT] = SEGMENT_NAME("ltgt"), [TRT_SECTION] = SEGMENT_NAME("section"), [TRT_SEGMENT] = SEGMENT_NAME("segment"), [TRT_STRING] = SEGMENT_NAME("string"), [TRT_VAR_NAME] = SEGMENT_NAME("var-name"), [TRT_KEY_WORD] = SEGMENT_NAME("keyword"), [TRT_ERROR] = SEGMENT_NAME("error"), }; /* Réagit à un changement de racine pour le composant suivi. */ static void on_root_change(GtkWidget *, GParamSpec *, GTokenStyle *); /* Charge toutes les indications liées aux polices. */ static bool g_token_style_retrieve_font(GTokenStyle *); /* Détermine les tailles d'impression possibles pour les rendus. */ static bool g_token_style_retrieve_advances(GTokenStyle *); /* Charge les définitions complémentaires spécifiques. */ static bool g_token_style_retrieve_rendering_properties(GTokenStyle *, TokenRenderingTag ); /* ---------------------------------------------------------------------------------- */ /* DEFINITION D'UN NOUVEL OBJET */ /* ---------------------------------------------------------------------------------- */ /* Détermine le type du composant d'affichage générique. */ G_DEFINE_TYPE(GTokenStyle, g_token_style, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : class = classe des objets à initialiser. * * * * Description : Procède à l'initialisation des gestionnaires de paramètres. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_token_style_class_init(GTokenStyleClass *class) { GObjectClass *object; /* Plus haut niveau équivalent */ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)g_token_style_dispose; object->finalize = (GObjectFinalizeFunc)g_token_style_finalize; } /****************************************************************************** * * * Paramètres : style = centralisation de paramètres de rendu à initialiser. * * * * Description : Procède à l'initialisation d'un gestionnaire de paramètres. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_token_style_init(GTokenStyle *style) { style->target = NULL; style->loaded = false; style->font_family = NULL; style->font_size = 0; } /****************************************************************************** * * * Paramètres : style = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_token_style_dispose(GTokenStyle *style) { g_clear_object(&style->target); G_OBJECT_CLASS(g_token_style_parent_class)->dispose(G_OBJECT(style)); } /****************************************************************************** * * * Paramètres : style = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_token_style_finalize(GTokenStyle *style) { if (style->font_family != NULL) free(style->font_family); G_OBJECT_CLASS(g_token_style_parent_class)->finalize(G_OBJECT(style)); } /****************************************************************************** * * * Paramètres : target = composant GTK à dédier au futur gestionnaire. * * * * Description : Crée un gestionnaire de style pour le rendu des lignes. * * * * Retour : Centralisateur mis en place pour un composant GTK donné. * * * * Remarques : - * * * ******************************************************************************/ GTokenStyle *g_token_style_new(GtkWidget *target) { GTokenStyle *result; /* Nouvelle instance à renvoyer*/ result = g_object_new(G_TYPE_TOKEN_STYLE, NULL); if (!g_token_style_create(result, target)) g_clear_object(&result); return result; } /****************************************************************************** * * * Paramètres : style = gestionnaire à initialiser. * * target = composant GTK à dédier au gestionnaire préparé. * * * * Description : Met en place un nouveau gestionnaire de rendu des lignes. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_token_style_create(GTokenStyle *style, GtkWidget *target) { bool result; /* Bilan à retourner */ result = true; style->target = target; ref_object(target); /** * Si le composant est déjà affiché, l'actualisation des paramètres est * forcée ! Dans le cas contraire, la surveillance des changements * déclenchera l'opération. */ if (gtk_widget_get_root(target) != NULL) on_root_change(target, NULL, style); g_signal_connect(target, "notify::root", G_CALLBACK(on_root_change), style); return result; } /****************************************************************************** * * * Paramètres : style = gestionnaire de paramètres de rendu à consulter. * * * * Description : Fournit la quantité de pixels requise pour une ligne. * * * * Retour : Hauteur de chaque ligne de rendu, en pixels. * * * * Remarques : - * * * ******************************************************************************/ int g_token_style_get_line_height(const GTokenStyle *style) { int result; /* Hauteur à retourner */ result = style->font_extents.height; return result; } /****************************************************************************** * * * Paramètres : style = gestionnaire de paramètres de rendu à consulter. * * tag = type de rendu sollicité. * * length = nombre de caractères à mesurer. * * * * Description : Fournit la quantité de pixels requise pour l'impression. * * * * Retour : Largeur requise par la colonne, en pixels. * * * * Remarques : - * * * ******************************************************************************/ int g_token_style_measure_width(const GTokenStyle *style, TokenRenderingTag tag, size_t length) { int result; /* Largeur à retourner */ const rendering_property_t *prop; /* Motif de rendu visé */ size_t index; /* Indice de config. adaptée */ prop = &style->properties[tag]; index = CAIRO_FONT_INDEX(prop->slant, prop->weight); result = style->x_advances[index] * length; return result; } /****************************************************************************** * * * Paramètres : style = gestionnaire de paramètres de rendu à consulter. * * tag = type de rendu sollicité. * * cr = contexte graphique à utiliser pour les pinceaux. * * x = abscisse du point d'impression (à maj). [OUT] * * y = ordonnée du point d'impression. * * text = texte UTF-8 à faire appraître. * * length = taille de ce texte à imprimer. * * * * Description : Imprime le fragment de texte transmis. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_token_style_draw_text(const GTokenStyle *style, TokenRenderingTag tag, cairo_t *cr, int *x, int y, const char *text, size_t length) { bool selected; /* Marquer une sélection ? */ gint width; /* Largeur du segment */ cairo_operator_t old; /* Sauvegarde avant changement */ const rendering_property_t *prop; /* Motif de rendu visé */ /* Cas particulier d'une tabulation */ if (length == 1 && (text[0] == '\t' || text[0] == ' ')) { width = g_token_style_measure_width(style, tag, text[0] == '\t' ? TAB_SIZE : 1); goto small_sep; } selected = false;//selection_list_has_segment_content(list, segment); width = g_token_style_measure_width(style, tag, length); /* Fond du texte */ if (selected) { #if 0 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); #endif } /* Couleur d'impression */ prop = &style->properties[tag]; if (selected) cairo_set_source_rgba(cr, prop->inverted.red, prop->inverted.green, prop->inverted.blue, prop->inverted.alpha); else cairo_set_source_rgba(cr, prop->foreground.red, prop->foreground.green, prop->foreground.blue, prop->foreground.alpha); /* Impression du texte */ cairo_select_font_face(cr, style->font_family, prop->slant, prop->weight); cairo_set_font_size(cr, style->font_size); cairo_move_to(cr, *x, y + style->font_extents.ascent); cairo_show_text(cr, text); small_sep: *x += width; } /****************************************************************************** * * * Paramètres : style = gestionnaire de paramètres de rendu à consulter. * * tag = type de rendu sollicité. * * text = texte à encadrer par des balises Pango. * * * * Description : Enjolive du texte selon les paramètres d'un élément de thème.* * * * Retour : Chaîne de caractère à libérer après usage. * * * * Remarques : - * * * ******************************************************************************/ char *g_token_style_build_markup(const GTokenStyle *style, TokenRenderingTag tag, const char *text) { char *result; /* Construction à retourner */ const rendering_property_t *prop; /* Motif de rendu visé */ char color[10]; /* Définition hexa de couleur */ result = strdup(text); prop = &style->properties[tag]; snprintf(color, sizeof(color), "#%02hhx%02hhx%02hhx%02hhx", (unsigned char)(255 * prop->foreground.red), (unsigned char)(255 * prop->foreground.green), (unsigned char)(255 * prop->foreground.blue), (unsigned char)(255 * prop->foreground.alpha)); result = strprep(result, "\">"); result = strprep(result, color); result = strprep(result, ""); if (prop->slant == CAIRO_FONT_SLANT_ITALIC || prop->slant == CAIRO_FONT_SLANT_OBLIQUE) { result = strprep(result, ""); result = stradd(result, ""); } if (prop->weight == CAIRO_FONT_WEIGHT_BOLD) { result = strprep(result, ""); result = stradd(result, ""); } return result; } /****************************************************************************** * * * Paramètres : style = gestionnaire de paramètres de rendu à consulter. * * base = base de texte à compléter. * * tag = type de rendu sollicité. * * text = texte à encadrer par des balises Pango. * * * * Description : Ajoute du texte enjolivé selon un élément de thème. * * * * Retour : Chaîne de caractère à libérer après usage. * * * * Remarques : - * * * ******************************************************************************/ char *g_token_style_append_markup(const GTokenStyle *style, char *base, TokenRenderingTag tag, const char *text) { char *result; /* Construction à retourner */ char *tmp; /* Stockage temporaire */ tmp = g_token_style_build_markup(style, tag, text); result = stradd(base, tmp); free(tmp); return result; } /****************************************************************************** * * * Paramètres : style = gestionnaire de paramètres de rendu à consulter. * * max = valeur maximale atteignable. * * virt = indique une position virtuelle et non physique. * * * * Description : Détermine une taille de localisation, physique ou virtuelle. * * * * Retour : Largeur minimale à observer en pixels. * * * * Remarques : - * * * ******************************************************************************/ int g_token_style_compute_location_width(const GTokenStyle *style, uint64_t max, bool virt) { int result; /* Taille à retourner */ double n; /* Nombre de chiffres hexa */ double c; /* Valeur ajustée */ TokenRenderingTag tag; /* Type de représentation */ /** * Les situations suivantes ont été évaluées : * * max | n | c * ----------------------------------- * 0 | 0 | 0 * 1 | 0,25 | 1 * f | 1 | 1 * 10 | 1,02187 | 2 * 11 | 1,04248 | 2 * ff | 2 | 2 * 100 | 2,00141 | 3 * ffffffff | 8 | 8 * ffffffffffffffff | 16 | 16 */ n = log1p(max) / log(16); c = ceil(n); tag = virt ? TRT_VIRT_ADDR : TRT_PHYS_ADDR; result = g_token_style_measure_width(style, tag, c); return result; } /* ---------------------------------------------------------------------------------- */ /* RECEPTACLES DE SIGNAUX CONNECTES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : widget = composant graphique de GTK changeant d'affichage. * * pspec = détails de la propriété ayant évolué. * * style = concentration de paramètres de rendu à actualiser. * * * * Description : Réagit à un changement de racine pour le composant suivi. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_root_change(GtkWidget *widget, GParamSpec *pspec, GTokenStyle *style) { GtkCssProvider *css; /* Feuille de style maison */ GdkDisplay *display; /* Zone d'affichage courante */ TokenRenderingTag i; /* Boucle de parcours */ /** * Si on fermeture n'est pas en court... */ if (gtk_widget_get_root(widget) != NULL) { /* Chargement des définitions dédiées */ css = gtk_css_provider_new(); gtk_css_provider_load_from_resource(css, "/re/chrysalide/framework/glibext/tokenstyle.css"); display = gtk_widget_get_display(widget); gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); /* Relecture des paramètres */ gtk_widget_add_css_class(widget, "token-generic"); style->loaded = g_token_style_retrieve_font(style); if (style->loaded) style->loaded = g_token_style_retrieve_advances(style); for (i = 0; i < TRT_COUNT && style->loaded; i++) { gtk_widget_add_css_class(widget, _token_names[i]); style->loaded = g_token_style_retrieve_rendering_properties(style, i); gtk_widget_remove_css_class(widget, _token_names[i]); } gtk_widget_remove_css_class(widget, "token-generic"); /* Déchargement des définitions prises en compte */ gtk_style_context_remove_provider_for_display(display, GTK_STYLE_PROVIDER(css)); unref_object(css); } } /****************************************************************************** * * * Paramètres : style = gestionnaire de paramètres de rendu à actualiser. * * * * Description : Charge toutes les indications liées aux polices. * * * * Retour : Bilan de l'opération : true pour un chargement sans encombre.* * * * Remarques : - * * * ******************************************************************************/ static bool g_token_style_retrieve_font(GTokenStyle *style) { bool result; /* Bilan à faire remonter */ PangoContext *context; /* Contexte Pango du composant */ PangoFontDescription *desc; /* Détails de la police liée */ const char *family; /* Eventuelle désignation */ bool is_mono; /* Propriété attendue de chasse*/ PangoFontFamily **pango_families; /* Liste des familles connues */ int pango_families_count; /* Taille de cette liste */ int k; /* Boucle de parcours */ const char *other; /* Autre nom à comparer */ result = true; /* Réinitialisation */ if (style->font_family != NULL) { free(style->font_family); style->font_family = NULL; } style->font_size = 0; /* Chargement */ context = gtk_widget_create_pango_context(style->target); desc = pango_context_get_font_description(context); if (desc != NULL) { family = pango_font_description_get_family(desc); if (family != NULL) { is_mono = false; pango_context_list_families(context, &pango_families, &pango_families_count); for (k = 0; k < pango_families_count && !is_mono; k++) { if (!pango_font_family_is_monospace(pango_families[k])) continue; other = pango_font_family_get_name(pango_families[k]); is_mono = (strcmp(family, other) == 0); } g_free(pango_families); if (is_mono) style->font_family = strdup(family); else log_variadic_message(LMT_WARNING, _("Selected font '%s' does not seem to be a monospace font; skipping it..."), family); } style->font_size = pango_font_description_get_size(desc); if (pango_font_description_get_size_is_absolute(desc)) style->font_size /= PANGO_SCALE; } /* Application de valeurs par défaut au besoin */ if (style->font_family == NULL) { style->font_family = strdup(DEFAULT_FONT_FAMILY); log_variadic_message(LMT_WARNING, _("No font family defined; switching to '%s' as default"), DEFAULT_FONT_FAMILY); } if (style->font_size == 0) { style->font_size = DEFAULT_FONT_SIZE; log_variadic_message(LMT_WARNING, _("No font size defined; switching to '%s' as default"), DEFAULT_FONT_SIZE); } log_variadic_message(LMT_INFO, _("Using '%s' as font family for buffer rendering (size: %d)"), style->font_family, style->font_size); unref_object(context); return result; } /****************************************************************************** * * * Paramètres : style = gestionnaire de paramètres de rendu à actualiser. * * * * Description : Détermine les tailles d'impression possibles pour les rendus.* * * * Retour : Bilan de l'opération : true pour un chargement sans encombre.* * * * Remarques : - * * * ******************************************************************************/ static bool g_token_style_retrieve_advances(GTokenStyle *style) { bool result; /* Bilan à faire remonter */ cairo_font_slant_t s; /* Boucle de parcours #1 */ cairo_font_weight_t w; /* Boucle de parcours #2 */ cairo_t *cr; /* Contexte à usage unique */ cairo_surface_t *surface; /* Surface pour dessin Cairo */ cairo_text_extents_t extents; /* Couverture des caractères */ result = true; for (s = CAIRO_FONT_SLANT_NORMAL; s < CAIRO_FONT_SLANT_COUNT; s++) for (w = CAIRO_FONT_WEIGHT_NORMAL; w < CAIRO_FONT_WEIGHT_COUNT; w++) { surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 100, 100); cr = cairo_create(surface); cairo_select_font_face(cr, style->font_family, s, w); cairo_set_font_size(cr, style->font_size); if (s == CAIRO_FONT_SLANT_NORMAL && w == CAIRO_FONT_WEIGHT_NORMAL) { cairo_font_extents(cr, &style->font_extents); } cairo_text_extents(cr, "A", &extents); style->x_advances[CAIRO_FONT_INDEX(s, w)] = extents.x_advance; cairo_destroy(cr); cairo_surface_destroy(surface); } return result; } /****************************************************************************** * * * Paramètres : style = gestionnaire de paramètres de rendu à actualiser. * * tag = désignation du style de rendu à traiter. * * * * Description : Charge les définitions complémentaires spécifiques. * * * * Retour : Bilan de l'opération : true pour un chargement sans encombre.* * * * Remarques : - * * * ******************************************************************************/ static bool g_token_style_retrieve_rendering_properties(GTokenStyle *style, TokenRenderingTag tag) { bool result; /* Bilan à faire remonter */ rendering_property_t *prop; /* Motif de rendu visé */ GtkStyleContext *s_context; /* Contexte voué à disparaître */ PangoContext *context; /* Contexte Pango du composant */ PangoFontDescription *desc; /* Détails de la police liée */ result = true; prop = &style->properties[tag]; /* Couleur d'impression */ /** * En attendant la disponibilité de GTK 4.10 et l'appel * gtk_widget_get_color(widget, &color); */ s_context = gtk_widget_get_style_context(style->target); gtk_style_context_get_color(s_context, &prop->foreground); /* Couleur inversée */ prop->inverted.red = 1.0 - prop->foreground.red; prop->inverted.green = 1.0 - prop->foreground.green; prop->inverted.blue = 1.0 - prop->foreground.blue; prop->inverted.alpha = prop->foreground.alpha; /* Style d'impression */ context = gtk_widget_create_pango_context(style->target); desc = pango_context_get_font_description(context); if (desc == NULL) { prop->slant = CAIRO_FONT_SLANT_NORMAL; prop->weight = CAIRO_FONT_WEIGHT_NORMAL; } else { switch (pango_font_description_get_style(desc)) { case PANGO_STYLE_NORMAL: prop->slant = CAIRO_FONT_SLANT_NORMAL; break; case PANGO_STYLE_ITALIC: prop->slant = CAIRO_FONT_SLANT_ITALIC; break; case PANGO_STYLE_OBLIQUE: prop->slant = CAIRO_FONT_SLANT_OBLIQUE; break; } switch (pango_font_description_get_weight(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: prop->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: prop->weight = CAIRO_FONT_WEIGHT_BOLD; break; } } unref_object(context); return result; }