/* Chrysalide - Outil d'analyse de fichiers binaires * hexview.c - affichage de contenus de binaire * * Copyright (C) 2016-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 Chrysalide. If not, see . */ #include "hexview.h" #include #include #include "area.h" #include "hexview-int.h" #include "../glibext/options/hex.h" /* ------------------------- BASES D'UN COMPOSANT GRAPHIQUE ------------------------- */ /* Procède à l'initialisation de l'afficheur générique. */ static void gtk_hex_view_class_init(GtkHexViewClass *); /* Procède à l'initialisation de l'afficheur générique. */ static void gtk_hex_view_init(GtkHexView *); /* Supprime toutes les références externes. */ static void gtk_hex_view_dispose(GtkHexView *); /* Procède à la libération totale de la mémoire. */ static void gtk_hex_view_finalize(GtkHexView *); /* Procède à l'actualisation de l'affichage d'un sous-composant. */ static void gtk_hex_view_dispatch_sub_snapshot(GtkWidget *, GtkSnapshot *, GtkWidget *); /* Adapte le cache de lignes hexadécimales à la taille courante. */ static void gtk_hex_view_populate_cache(GtkHexView *); void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot, GtkWidget *parent); /* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ /* Prend acte de la taille allouée au composant d'affichage. */ static void gtk_hex_view_size_allocate(GtkWidget *, int, int, int); /* Fournit le mode de calcul pour déterminer la taille. */ static GtkSizeRequestMode gtk_hex_view_get_request_mode(GtkWidget *); /* Fournit les mesures mainimale et idéale du composant. */ static void gtk_hex_view_measure(GtkWidget *, GtkOrientation, int, int *, int *, int *, int *); /* ---------------------------------------------------------------------------------- */ /* BASES D'UN COMPOSANT GRAPHIQUE */ /* ---------------------------------------------------------------------------------- */ /* Détermine le type du composant d'affichage générique. */ G_DEFINE_TYPE(GtkHexView, gtk_hex_view, GTK_TYPE_BUFFER_VIEW); /****************************************************************************** * * * Paramètres : class = classe GTK à initialiser. * * * * Description : Procède à l'initialisation de l'afficheur générique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_hex_view_class_init(GtkHexViewClass *class) { GObjectClass *object; /* Plus haut niveau équivalent */ GtkWidgetClass *widget; /* Classe de haut niveau */ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)gtk_hex_view_dispose; object->finalize = (GObjectFinalizeFunc)gtk_hex_view_finalize; widget = GTK_WIDGET_CLASS(class); gtk_widget_class_set_css_name(widget, "GtkHexView"); g_type_ensure(GTK_TYPE_COMPOSING_AREA); gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/hexview.ui"); gtk_widget_class_bind_template_child(widget, GtkHexView, offsets); gtk_widget_class_bind_template_child(widget, GtkHexView, hex); gtk_widget_class_bind_template_child(widget, GtkHexView, ascii); widget->size_allocate = gtk_hex_view_size_allocate; widget->get_request_mode = gtk_hex_view_get_request_mode; widget->measure = gtk_hex_view_measure; } /****************************************************************************** * * * Paramètres : view = composant GTK à initialiser. * * * * Description : Procède à l'initialisation de l'afficheur générique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_hex_view_init(GtkHexView *view) { GtkContentView *base; /* Base d'instance supérieure */ /* Niveau supérieur */ base = GTK_CONTENT_VIEW(view); base->options = G_DISPLAY_OPTIONS(g_hex_options_new()); base->sub_children = view->children; base->sub_count = _CHILDREN_COUNT; /* Instance courante */ gtk_widget_init_template(GTK_WIDGET(view)); g_raw_scan_cache_register_snapshot(GTK_COMPOSING_AREA(view->offsets), gtk_hex_view_dispatch_sub_snapshot, GTK_WIDGET(view)); g_raw_scan_cache_register_snapshot(GTK_COMPOSING_AREA(view->hex), gtk_hex_view_dispatch_sub_snapshot, GTK_WIDGET(view)); g_raw_scan_cache_register_snapshot(GTK_COMPOSING_AREA(view->ascii), gtk_hex_view_dispatch_sub_snapshot, GTK_WIDGET(view)); view->generator = NULL; } /****************************************************************************** * * * Paramètres : view = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_hex_view_dispose(GtkHexView *view) { gtk_widget_dispose_template(GTK_WIDGET(view), GTK_TYPE_HEX_VIEW); g_clear_object(&view->generator); G_OBJECT_CLASS(gtk_hex_view_parent_class)->dispose(G_OBJECT(view)); } /****************************************************************************** * * * Paramètres : view = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_hex_view_finalize(GtkHexView *view) { G_OBJECT_CLASS(gtk_hex_view_parent_class)->finalize(G_OBJECT(view)); } /****************************************************************************** * * * Paramètres : content = contenu binaire à exposer de façon brute. * * * * Description : Crée un composant d'affichage d'octets bruts et imprimables. * * * * Retour : Centralisateur mis en place pour un composant GTK donné. * * * * Remarques : - * * * ******************************************************************************/ GtkHexView *gtk_hex_view_new(GBinContent *content) { GtkHexView *result; /* Nouvelle instance à renvoyer*/ result = g_object_new(GTK_TYPE_HEX_VIEW, NULL); if (!gtk_hex_view_create(result, content)) g_clear_object(&result); return result; } /****************************************************************************** * * * Paramètres : view = composant d'affichage à initialiser pleinement. * * content = contenu binaire à exposer de façon brute. * * * * Description : Met en place un nouveau composant d'affichage d'octets bruts.* * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool gtk_hex_view_create(GtkHexView *view, GBinContent *content) { bool result; /* Bilan à retourner */ GtkBufferView *parent; /* Version parente du composant*/ GBufferCache *cache; /* Tampon à représenter */ result = true; assert(g_display_options_count(GTK_CONTENT_VIEW(view)->options) == 1); parent = GTK_BUFFER_VIEW(view); cache = g_buffer_cache_new(1, 2); parent->view = g_buffer_view_new(cache, parent->style); unref_object(cache); view->generator = g_hex_generator_new(content); return result; } /****************************************************************************** * * * Paramètres : widget = composant GTK à redessiner. * * snapshot = gestionnaire de noeuds de rendu à solliciter. * * parent = composant GTK parent et cadre de l'appel. * * * * Description : Procède à l'actualisation de l'affichage d'un sous-composant.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_hex_view_dispatch_sub_snapshot(GtkWidget *widget, GtkSnapshot *snapshot, GtkWidget *parent) { GtkHexView *view; /* Version spécialisée */ size_t column; /* Indice de colonne à traiter */ int width; /* Largeur à disposition */ int height; /* Hauteur à disposition */ cairo_t *cr; /* Pinceau pour les dessins */ view = GTK_HEX_VIEW(parent); if (widget == view->offsets) column = HCO_OFFSET; else if (widget == view->hex) column = HCO_COUNT + 0; else { assert(widget == view->ascii); column = HCO_COUNT + 1; } width = gtk_widget_get_width(widget); height = gtk_widget_get_height(widget); cr = gtk_snapshot_append_cairo(snapshot, &GRAPHENE_RECT_INIT(0, 0, width, height)); g_buffer_view_draw(GTK_BUFFER_VIEW(parent)->view, cr, column, GTK_BUFFER_VIEW(parent)->virt_top, height); cairo_destroy(cr); } /****************************************************************************** * * * Paramètres : view = composant GTK à mettre à jour. * * * * Description : Adapte le cache de lignes hexadécimales à la taille courante.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_hex_view_populate_cache(GtkHexView *view) { GBinContent *content; /* Contenu binaire affiché */ phys_t full; /* Taille totale à représenter */ phys_t line; /* Taille représentée par ligne*/ size_t needed; /* Nombre de lignes nécessaires*/ GBufferCache *cache; /* Tampon à représenter */ size_t count; /* Nombre actuel de lignes */ /* Détermination du besoin */ content = g_hex_generator_get_content(view->generator); full = g_binary_content_compute_size(content); unref_object(content); line = g_hex_generator_get_bytes_per_line(view->generator); needed = full / line; if (full % line > 0) needed++; /* Adaptation du tampon interne ? */ cache = g_buffer_view_get_cache(GTK_BUFFER_VIEW(view)->view); g_buffer_cache_wlock(cache); count = g_buffer_cache_count_lines(cache); if (needed < count) g_buffer_cache_truncate(cache, needed); else if (needed > count) g_buffer_cache_extend_with(cache, needed, G_TOKEN_GENERATOR(view->generator)); g_buffer_cache_wunlock(cache); unref_object(cache); /* Mise à jour de l'affichage ? */ if (needed != count) gtk_widget_queue_resize(GTK_WIDGET(view)); } void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot, GtkWidget *parent) { GdkRGBA red, green, yellow, blue; float w, h; gdk_rgba_parse (&red, "red"); gdk_rgba_parse (&green, "green"); gdk_rgba_parse (&yellow, "yellow"); gdk_rgba_parse (&blue, "blue"); w = gtk_widget_get_width (widget) / 2.0; h = gtk_widget_get_height (widget) / 2.0; h /= 2.0; gtk_snapshot_append_color (snapshot, &red, &GRAPHENE_RECT_INIT(0, 0, w, h)); gtk_snapshot_append_color (snapshot, &green, &GRAPHENE_RECT_INIT(w, 0, w, h)); gtk_snapshot_append_color (snapshot, &yellow, &GRAPHENE_RECT_INIT(0, h, w, h)); gtk_snapshot_append_color (snapshot, &blue, &GRAPHENE_RECT_INIT(w, h, w, h)); } /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : widget = composant GTK à examiner. * * width = largeur affectée au composant graphique. * * height = hauteur affectée au composant graphique. * * baseline = ligne de base affectée au composant graphique. * * * * Description : Prend acte de la taille allouée au composant d'affichage. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_hex_view_size_allocate(GtkWidget *widget, int width, int height, int baseline) { GtkHexView *view; /* Version spécialisée */ int *min_widths; /* Tailles minimales imposées */ int *final_widths; /* Tailles finales retenues */ size_t i; /* Boucle de parcours */ int available; /* Largeur disponible */ bool changed; /* Détection de variation */ GWidthTracker *tracker; /* Collecteur de largeurs */ view = GTK_HEX_VIEW(widget); min_widths = alloca(_CHILDREN_COUNT * sizeof(int)); final_widths = alloca(_CHILDREN_COUNT * sizeof(int)); for (i = 0; i < _CHILDREN_COUNT; i++) gtk_widget_measure(view->children[i], GTK_ORIENTATION_HORIZONTAL, -1, &min_widths[i], NULL, NULL, NULL); /* Passe 1 : tentative sans défilement vertical */ available = width; for (i = 0; i < _CHILDREN_COUNT; i++) available -= min_widths[i]; changed = g_hex_generator_allocate(view->generator, GTK_CONTENT_VIEW(view)->options, GTK_BUFFER_VIEW(view)->style, available, final_widths); /* Application des largeurs calculées */ if (changed) { gtk_hex_view_populate_cache(view); tracker = g_buffer_view_get_tracker(GTK_BUFFER_VIEW(view)->view); for (i = 0; i < _CHILDREN_COUNT; i++) { final_widths[i] += min_widths[i]; g_width_tracker_set_column_min_width(tracker, i, final_widths[i]); } unref_object(tracker); } /* Mise à jour des éléments plus internes */ GTK_WIDGET_CLASS(gtk_hex_view_parent_class)->size_allocate(widget, width, height, baseline); } /****************************************************************************** * * * Paramètres : widget = composant GTK à consulter. * * * * Description : Fournit le mode de calcul pour déterminer la taille. * * * * Retour : Mode de calcul adapté au composant graphique. * * * * Remarques : - * * * ******************************************************************************/ static GtkSizeRequestMode gtk_hex_view_get_request_mode(GtkWidget *widget) { GtkSizeRequestMode result; /* Configuration à remonter */ result = GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; return result; } /****************************************************************************** * * * Paramètres : widget = composant GTK à examiner. * * orientation = direction à observer pour les calculs. * * for_size = taille de la direction opposée. * * minimum = taille minimale pour le composant. [OUT] * * natural = taille idéale pour le composant. [OUT] * * min_baseline = ligne de base minimale. [OUT] * * nat_baseline = ligne de base idéale. [OUT] * * * * Description : Fournit les mesures mainimale et idéale du composant. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_hex_view_measure(GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, int *natural, int *min_baseline, int *nat_baseline) { bool processed; /* Calcul de hauteur effectué */ GtkHexView *view; /* Version spécialisée */ int requested; /* Taille requise à priori */ size_t i; /* Boucle de parcours */ int min; /* Valeur minimale locale */ int nat; /* Valeur idéale locale */ processed = false; /* Demande de hauteur minimale / idéale */ if (orientation == GTK_ORIENTATION_VERTICAL && for_size != -1) { view = GTK_HEX_VIEW(widget); requested = 0; for (i = 0; i < _CHILDREN_COUNT; i++) { gtk_widget_measure(view->children[i], GTK_ORIENTATION_HORIZONTAL, -1, &min, NULL, NULL, NULL); requested += min; } for_size -= requested; if (for_size > 0) { requested = g_hex_generator_mesure_height_for_width(view->generator, GTK_CONTENT_VIEW(view)->options, GTK_BUFFER_VIEW(view)->style, for_size); if (minimum != NULL) *minimum = 0; if (natural != NULL) *natural = requested; for (i = 0; i < _CHILDREN_COUNT; i++) { gtk_widget_measure(view->children[i], GTK_ORIENTATION_VERTICAL, -1, &min, &nat, NULL, NULL); if (minimum != NULL && min > *minimum) *minimum = min; if (natural != NULL && nat > *natural) *natural = nat; } processed = true; } } if (!processed) GTK_WIDGET_CLASS(gtk_hex_view_parent_class)->measure(widget, orientation, for_size, minimum, natural, min_baseline, nat_baseline); }