summaryrefslogtreecommitdiff
path: root/src/gtkext/gtkgraphdisplay.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gtkext/gtkgraphdisplay.c')
-rw-r--r--src/gtkext/gtkgraphdisplay.c1226
1 files changed, 1226 insertions, 0 deletions
diff --git a/src/gtkext/gtkgraphdisplay.c b/src/gtkext/gtkgraphdisplay.c
new file mode 100644
index 0000000..1d4f831
--- /dev/null
+++ b/src/gtkext/gtkgraphdisplay.c
@@ -0,0 +1,1226 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * gtkgraphdisplay.c - affichage de morceaux de code sous forme graphique
+ *
+ * Copyright (C) 2009-2016 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "gtkgraphdisplay.h"
+
+
+#include <assert.h>
+
+
+#include "gtkblockdisplay.h"
+#include "gtkbufferdisplay.h"
+#include "gtkdisplaypanel-int.h"
+#include "graph/cluster.h"
+#include "../analysis/blocks/flow.h"
+#include "../format/format.h"
+#include "../gui/editem.h"
+
+
+
+/* Composant d'affichage sous forme graphique (instance) */
+struct _GtkGraphDisplay
+{
+ GtkDisplayPanel parent; /* A laisser en premier */
+ GtkWidget *support; /* Support des vues en bloc */
+ GtkWidget *extender; /* Force la taille du support */
+
+ GBinRoutine *routine; /* Routine en cours d'affichage*/
+
+ segcnt_list *highlighted; /* Segments mis en évidence */
+
+ GGraphCluster *cluster; /* Disposition en graphique */
+
+ GGraphEdge **edges; /* Liens entre les noeuds */
+ size_t edges_count; /* Quantité de ces liens */
+
+ gdouble start_x; /* Abscisse du point de souris */
+ gdouble start_y; /* Ordonnée du point de souris */
+ bool big_enough; /* Capacités de déplacement ? */
+ gdouble ref_h; /* Position horizontale de ref.*/
+ gdouble ref_v; /* Position verticale de ref. */
+
+};
+
+/* Composant d'affichage sous forme graphique (classe) */
+struct _GtkGraphDisplayClass
+{
+ GtkDisplayPanelClass parent; /* A laisser en premier */
+
+};
+
+
+/* Profondeur de l'ombre */
+#define SHADOW_SIZE 4
+
+/* Marges en bordure de graphique */
+#define GRAPH_MARGIN 23
+
+
+/* Initialise la classe générique des graphiques de code. */
+static void gtk_graph_display_class_init(GtkGraphDisplayClass *);
+
+/* Initialise une instance d'afficheur de code en graphique. */
+static void gtk_graph_display_init(GtkGraphDisplay *);
+
+/* Supprime toutes les références externes. */
+static void gtk_graph_display_dispose(GtkGraphDisplay *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_graph_display_finalize(GtkGraphDisplay *);
+
+/* S'adapte à la surface concédée par le composant parent. */
+static void gtk_graph_display_size_allocate(GtkWidget *, GtkAllocation *);
+
+/* Centre si possible le contenu du panneau d'affichage. */
+static void gtk_graph_display_update_support_margins(GtkGraphDisplay *, const GtkAllocation *);
+
+/* Indique les dimensions de travail du composant d'affichage. */
+static void gtk_graph_display_compute_requested_size(GtkGraphDisplay *, gint *, gint *);
+
+/* Réagit à un défilement chez une barre associée au composant. */
+static void gtk_graph_display_adjust_scroll_value(GtkGraphDisplay *, GtkAdjustment *, GtkOrientation);
+
+/* Met à jour l'affichage de la vue sous forme graphique. */
+static gboolean gtk_graph_display_draw(GtkWidget *, cairo_t *, GtkGraphDisplay *);
+
+/* Assure la gestion des clics de souris sur le composant. */
+static gboolean gtk_graph_display_button_press(GtkWidget *, GdkEventButton *, GtkGraphDisplay *);
+
+/* Assure la gestion des clics de souris sur le composant. */
+static gboolean gtk_graph_display_button_release(GtkWidget *, GdkEventButton *, GtkGraphDisplay *);
+
+/* Assure la suivi des déplacements de souris sur le composant. */
+static gboolean gtk_graph_display_motion_notify(GtkWidget *, GdkEventMotion *, GtkGraphDisplay *);
+
+/* Réagit à la sélection externe d'une adresse. */
+static void gtk_graph_display_define_main_address(GtkGraphDisplay *, const vmpa2t *);
+
+/* Indique la position courante du curseur. */
+static const vmpa2t *gtk_graph_display_get_caret_location(const GtkGraphDisplay *);
+
+/* Indique la position d'affichage d'une adresse donnée. */
+static bool gtk_graph_display_get_address_coordinates(const GtkGraphDisplay *, const vmpa2t *addr, gint *x, gint *y, ScrollPositionTweak tweak);
+
+/* Déplace le curseur à un emplacement défini. */
+static bool gtk_graph_display_move_caret_to(GtkGraphDisplay *, gint, gint);
+
+/* Place en cache un rendu destiné à l'aperçu graphique rapide. */
+static void gtk_graph_display_cache_glance(GtkGraphDisplay *, cairo_t *, const GtkAllocation *, double);
+
+/* Supprime tout contenu de l'afficheur de code en graphique. */
+static void gtk_graph_display_reset(GtkGraphDisplay *);
+
+/* Notifie un changement de surbrillance au sein d'un noeud. */
+static void gtk_graph_display_changed_highlights(GtkBlockDisplay *, GtkGraphDisplay *);
+
+/* Notifie une incapacité de déplacement au sein d'un noeud. */
+static void gtk_graph_display_reach_caret_limit(GtkBufferDisplay *, GdkScrollDirection, GtkGraphDisplay *);
+
+
+
+/* Détermine le type du composant d'affichage en graphique. */
+G_DEFINE_TYPE(GtkGraphDisplay, gtk_graph_display, GTK_TYPE_DISPLAY_PANEL)
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Initialise la classe générique des graphiques de code. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_class_init(GtkGraphDisplayClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GtkWidgetClass *widget_class; /* Classe de haut niveau */
+ GtkDisplayPanelClass *panel_class; /* Classe parente */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)gtk_graph_display_dispose;
+ object->finalize = (GObjectFinalizeFunc)gtk_graph_display_finalize;
+
+ widget_class = GTK_WIDGET_CLASS(class);
+
+ widget_class->size_allocate = gtk_graph_display_size_allocate;
+
+ panel_class = GTK_DISPLAY_PANEL_CLASS(class);
+
+ panel_class->compute_size = (compute_requested_size_fc)gtk_graph_display_compute_requested_size;
+ panel_class->adjust = (adjust_scroll_value_fc)gtk_graph_display_adjust_scroll_value;
+ panel_class->define = (define_address_fc)gtk_graph_display_define_main_address;
+
+ panel_class->get_caret_loc = (get_caret_location_fc)gtk_graph_display_get_caret_location;
+ panel_class->get_coordinates = (get_addr_coordinates_fc)gtk_graph_display_get_address_coordinates;
+ panel_class->move_caret_to = (move_caret_to_fc)gtk_graph_display_move_caret_to;
+ panel_class->cache_glance = (cache_glance_fc)gtk_graph_display_cache_glance;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = instance GTK à initialiser. *
+* *
+* Description : Initialise une instance d'afficheur de code en graphique. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_init(GtkGraphDisplay *display)
+{
+ display->support = gtk_fixed_new();
+ gtk_widget_set_has_window(display->support, TRUE);
+ gtk_widget_set_can_focus(display->support, TRUE);
+
+ g_signal_connect(G_OBJECT(display->support), "draw",
+ G_CALLBACK(gtk_graph_display_draw), display);
+
+ g_signal_connect(G_OBJECT(display->support), "button-press-event",
+ G_CALLBACK(gtk_graph_display_button_press), display);
+ g_signal_connect(G_OBJECT(display->support), "button-release-event",
+ G_CALLBACK(gtk_graph_display_button_release), display);
+ g_signal_connect(G_OBJECT(display->support), "motion-notify-event",
+ G_CALLBACK(gtk_graph_display_motion_notify), display);
+
+ gtk_widget_add_events(display->support,
+ GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+
+ gtk_widget_show(display->support);
+
+ gtk_fixed_put(GTK_FIXED(display), display->support, 0, 0);
+
+ display->extender = gtk_fixed_new();
+
+ gtk_widget_set_margin_end(display->extender, 1);
+ gtk_widget_set_margin_top(display->extender, 1);
+
+ gtk_widget_show(display->extender);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_dispose(GtkGraphDisplay *display)
+{
+ g_object_unref(G_OBJECT(display->extender));
+
+ gtk_graph_display_reset(display);
+
+ G_OBJECT_CLASS(gtk_graph_display_parent_class)->dispose(G_OBJECT(display));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_finalize(GtkGraphDisplay *display)
+{
+ G_OBJECT_CLASS(gtk_graph_display_parent_class)->finalize(G_OBJECT(display));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : widget = composant GTK à mettre à jour. *
+* allocation = étendue accordée à la vue. *
+* *
+* Description : S'adapte à la surface concédée par le composant parent. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+ GtkGraphDisplay *display; /* Autre version du composant */
+
+ GTK_WIDGET_CLASS(gtk_graph_display_parent_class)->size_allocate(widget, allocation);
+
+ display = GTK_GRAPH_DISPLAY(widget);
+
+ gtk_graph_display_update_support_margins(display, allocation);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = panneau dont le contenu est à déplacer. *
+* allocation = étendue accordée à la vue. *
+* *
+* Description : Centre si possible le contenu du panneau d'affichage. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_update_support_margins(GtkGraphDisplay *display, const GtkAllocation *allocation)
+{
+ gint width; /* Largeur totale du support */
+ gint height; /* Hauteur totale du support */
+ gint start; /* Bordure horizontale */
+ gint top; /* Bordure verticale */
+
+ gtk_graph_display_compute_requested_size(display, &width, &height);
+
+ if (width > allocation->width)
+ start = 0;
+ else
+ start = (allocation->width - width) / 2;
+
+ if (height > allocation->height)
+ top = 0;
+ else
+ top = (allocation->height - height) / 2;
+
+ gtk_widget_set_margin_start(display->support, start);
+ gtk_widget_set_margin_top(display->support, top);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = composant GTK à consulter. *
+* width = largeur requise à renseigner ou NULL. [OUT] *
+* height = hauteur requise à renseigner ou NULL. [OUT] *
+* *
+* Description : Indique les dimensions de travail du composant d'affichage. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_compute_requested_size(GtkGraphDisplay *display, gint *width, gint *height)
+{
+ GtkAllocation needed; /* Taille requise */
+
+ if (display->cluster != NULL)
+ {
+ g_graph_cluster_compute_needed_alloc(display->cluster, &needed);
+ assert(needed.x == 0 && needed.y == 0);
+
+ needed.width += 2 * GRAPH_MARGIN;
+ needed.height += 2 * GRAPH_MARGIN;
+
+ }
+ else
+ {
+ needed.width = 0;
+ needed.height = 0;
+ }
+
+ if (width != NULL) *width = needed.width;
+ if (height != NULL) *height = needed.height;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = panneau d'affichage concerné. *
+* adj = défilement dont une valeur a changé. *
+* orientation = indication sur le défilement à traiter. *
+* *
+* Description : Réagit à un défilement chez une barre associée au composant. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_adjust_scroll_value(GtkGraphDisplay *display, GtkAdjustment *adj, GtkOrientation orientation)
+{
+ gint fake_x; /* Abscisse virtuelle */
+ gint fake_y; /* Ordonnée virtuelle */
+
+ fake_x = 0;
+ fake_y = 0;
+ gtk_display_panel_compute_fake_coord(GTK_DISPLAY_PANEL(display), &fake_x, &fake_y);
+
+ gtk_fixed_move(GTK_FIXED(display), display->support, fake_x, -fake_y);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : widget = composant GTK à redessiner. *
+* cr = contexte graphique associé à l'événement. *
+* display = support maître à consulter. *
+* *
+* Description : Met à jour l'affichage de la vue sous forme graphique. *
+* *
+* Retour : FALSE pour poursuivre la propagation de l'événement. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static gboolean gtk_graph_display_draw(GtkWidget *widget, cairo_t *cr, GtkGraphDisplay *display)
+{
+ size_t i; /* Boucle de parcours */
+
+ void draw_shadow(GtkWidget *child, gpointer unused)
+ {
+ GtkAllocation alloc; /* Emplacement de l'enfant */
+ gint j; /* Boucle de parcours */
+ cairo_pattern_t *pattern; /* Zones d'application */
+
+ /* On évite l'extenseur de support... */
+ if (!GTK_IS_DISPLAY_PANEL(child))
+ return;
+
+ gtk_widget_get_allocation(child, &alloc);
+
+ for (j = 1; j < SHADOW_SIZE; j++)
+ {
+ cairo_push_group(cr);
+
+ gtk_display_panel_define_border_path(GTK_DISPLAY_PANEL(child), cr, alloc.x + j, alloc.y + j);
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
+ cairo_fill(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ gtk_display_panel_define_border_path(GTK_DISPLAY_PANEL(child), cr, alloc.x, alloc.y);
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
+ cairo_fill(cr);
+
+ pattern = cairo_pop_group(cr);
+
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.3);
+ cairo_mask(cr, pattern);
+ cairo_fill(cr);
+
+ cairo_pattern_destroy(pattern);
+
+ }
+
+ }
+
+ gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)draw_shadow, NULL);
+
+ for (i = 0; i < display->edges_count; i++)
+ g_graph_edge_draw(display->edges[i], cr, true);
+
+ return FALSE;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : widget = composant GTK visé par l'opération. *
+* event = informations liées à l'événement. *
+* display = support maître à consulter. *
+* *
+* Description : Assure la gestion des clics de souris sur le composant. *
+* *
+* Retour : FALSE pour poursuivre la propagation de l'événement. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static gboolean gtk_graph_display_button_press(GtkWidget *widget, GdkEventButton *event, GtkGraphDisplay *display)
+{
+ gboolean result; /* Poursuite à faire suivre */
+ GtkScrolledWindow *support; /* Support défilant associé */
+ GtkAdjustment *hadj; /* Gestionnaire du défilement */
+ GtkAdjustment *vadj; /* Gestionnaire du défilement */
+ GdkCursor *cursor; /* Pointeur pour la surface */
+
+ result = FALSE;
+
+ if (event->button == 1)
+ {
+ support = GTK_SCROLLED_WINDOW(gtk_widget_get_parent(GTK_WIDGET(display)));
+
+ hadj = gtk_scrolled_window_get_hadjustment(support);
+ vadj = gtk_scrolled_window_get_vadjustment(support);
+
+ display->big_enough = (gtk_adjustment_get_upper(hadj) > gtk_adjustment_get_page_size(hadj)
+ || gtk_adjustment_get_upper(vadj) > gtk_adjustment_get_page_size(vadj));
+
+ if (display->big_enough)
+ {
+ display->start_x = event->x_root;
+ display->start_y = event->y_root;
+
+ display->ref_h = gtk_adjustment_get_value(hadj);
+ display->ref_v = gtk_adjustment_get_value(vadj);
+
+ cursor = gdk_cursor_new(GDK_FLEUR);
+ gdk_window_set_cursor(gtk_widget_get_window(widget), cursor);
+ g_object_unref(G_OBJECT(cursor));
+
+ result = TRUE;
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : widget = composant GTK visé par l'opération. *
+* event = informations liées à l'événement. *
+* display = support maître à consulter. *
+* *
+* Description : Assure la gestion des clics de souris sur le composant. *
+* *
+* Retour : FALSE pour poursuivre la propagation de l'événement. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static gboolean gtk_graph_display_button_release(GtkWidget *widget, GdkEventButton *event, GtkGraphDisplay *display)
+{
+ if (event->button == 1 && display->big_enough)
+ gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
+
+ return FALSE;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : widget = composant GTK visé par l'opération. *
+* event = informations liées à l'événement. *
+* display = support maître à consulter. *
+* *
+* Description : Assure la suivi des déplacements de souris sur le composant. *
+* *
+* Retour : FALSE pour poursuivre la propagation de l'événement. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static gboolean gtk_graph_display_motion_notify(GtkWidget *widget, GdkEventMotion *event, GtkGraphDisplay *display)
+{
+ gdouble diff_x; /* Evolution sur les abscisses */
+ gdouble diff_y; /* Evolution sur les ordonnées */
+ GtkScrolledWindow *support; /* Support défilant associé */
+ GtkAdjustment *hadj; /* Gestionnaire du défilement */
+ GtkAdjustment *vadj; /* Gestionnaire du défilement */
+ gdouble value; /* Nouvelle valeur bornée */
+
+ if (event->state & GDK_BUTTON1_MASK && display->big_enough)
+ {
+ diff_x = display->start_x - event->x_root;
+ diff_y = display->start_y - event->y_root;
+
+ support = GTK_SCROLLED_WINDOW(gtk_widget_get_parent(GTK_WIDGET(display)));
+
+ hadj = gtk_scrolled_window_get_hadjustment(support);
+ vadj = gtk_scrolled_window_get_vadjustment(support);
+
+ value = CLAMP(display->ref_h + diff_x, gtk_adjustment_get_lower(hadj),
+ gtk_adjustment_get_upper(hadj) - gtk_adjustment_get_page_size(hadj));
+ gtk_adjustment_set_value(hadj, value);
+
+ value = CLAMP(display->ref_v + diff_y, gtk_adjustment_get_lower(vadj),
+ gtk_adjustment_get_upper(vadj) - gtk_adjustment_get_page_size(vadj));
+ gtk_adjustment_set_value(vadj, value);
+
+ }
+
+ return FALSE;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = composant GTK à mettre à jour. *
+* addr = adresse sélectionnée de manière externe. *
+* *
+* Description : Réagit à la sélection externe d'une adresse. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_define_main_address(GtkGraphDisplay *display, const vmpa2t *addr)
+{
+ bool need_update; /* Mise à jour du contenu ? */
+ const mrange_t *range; /* Couverture courante */
+ GExeFormat *format; /* Type de fichier chargé */
+ GBinRoutine **routines; /* Liste des routines trouvées */
+ size_t routines_count; /* Nombre de ces routines */
+ size_t i; /* Boucle de parcours */
+ gint right; /* Abscisse du coin droit */
+ gint bottom; /* Ordonnée du coin inférieur */
+ GtkAllocation allocation; /* Espace alloué au panneau */
+
+ if (display->routine == NULL)
+ need_update = true;
+ else
+ {
+ range = g_binary_routine_get_range(display->routine);
+ need_update = !mrange_contains_addr(range, addr);
+ }
+
+ if (need_update)
+ {
+ gtk_graph_display_reset(display);
+
+ format = g_loaded_binary_get_format(GTK_DISPLAY_PANEL(display)->binary);
+ routines = g_binary_format_get_routines(G_BIN_FORMAT(format), &routines_count);
+
+ for (i = 0; i < routines_count; i++)
+ {
+ range = g_binary_routine_get_range(routines[i]);
+
+ if (mrange_contains_addr(range, addr))
+ {
+ display->routine = routines[i];
+ g_object_ref(G_OBJECT(display->routine));
+
+ display->highlighted = init_segment_content_list();
+
+ /*
+ display->children = gtk_graph_display_load_nodes(display, GTK_DISPLAY_PANEL(display)->binary,
+ routines[i]);
+
+ display->allocs = (GtkAllocation *)calloc(display->children_count,
+ sizeof(GtkAllocation));
+
+ display->layout = g_graph_layout_new(g_binary_routine_get_basic_blocks(display->routine),
+ display->children, display->children_count);
+
+ g_graph_layout_place(display->layout, display);
+ */
+
+ do
+ {
+
+ GBlockList *list;
+
+ list = g_binary_routine_get_basic_blocks(routines[i]);
+
+#if 0
+ display->cluster = g_graph_cluster_new(GTK_DISPLAY_PANEL(display)->binary,
+ list, 0/* FIXME */, display->highlighted);
+#endif
+
+
+ display->cluster = bootstrap_graph_cluster(GTK_DISPLAY_PANEL(display)->binary,
+ list, display->highlighted);
+
+
+
+ g_graph_cluster_place(display->cluster, display);
+
+ /**
+ * Comme la taille du support ne peut pas être forcée et
+ * étendue pour comprendre les ombres, on place un composant
+ * minuscule à l'extrémité de ce support.
+ */
+
+ gtk_graph_display_compute_requested_size(display, &right, &bottom);
+
+ g_object_ref(G_OBJECT(display->extender));
+ gtk_fixed_put(GTK_FIXED(display->support), display->extender, right - 1, bottom - 1);
+
+ /**
+ * Si possible, on centre le contenu obtenu.
+ */
+
+ gtk_widget_get_allocation(GTK_WIDGET(display), &allocation);
+
+ gtk_graph_display_update_support_margins(display, &allocation);
+
+
+
+ }
+ while (0);
+
+
+
+ break;
+
+ }
+
+ }
+
+ change_editor_items_current_view_content(GTK_DISPLAY_PANEL(display));
+
+ g_object_unref(G_OBJECT(format));
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = composant GTK à manipuler. *
+* *
+* Description : Indique la position courante du curseur. *
+* *
+* Retour : Emplacement courant du curseur ou NULL si aucun. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static const vmpa2t *gtk_graph_display_get_caret_location(const GtkGraphDisplay *display)
+{
+ return NULL; /* FIXME */
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = composant GTK à consulter. *
+* addr = adresse à présenter à l'écran. *
+* x = position horizontale au sein du composant. [OUT] *
+* y = position verticale au sein du composant. [OUT] *
+* tweak = adaptation finale à effectuer. *
+* *
+* Description : Indique la position d'affichage d'une adresse donnée. *
+* *
+* Retour : true si l'adresse fait partie du composant, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool gtk_graph_display_get_address_coordinates(const GtkGraphDisplay *display, const vmpa2t *addr, gint *x, gint *y, ScrollPositionTweak tweak)
+{
+ /* TODO */
+
+ return false;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = composant GTK à manipuler. *
+* x = abscisse proposée pour le nouvel emplacement. *
+* y = ordonnée proposée pour le nouvel emplacement. *
+* *
+* Description : Déplace le curseur à un emplacement défini. *
+* *
+* Retour : true si un traitement a été effectué, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool gtk_graph_display_move_caret_to(GtkGraphDisplay *display, gint x, gint y)
+{
+ bool result; /* Bilan à retourner */
+
+ result = false;
+
+ void move_caret_to_sub_block(GtkWidget *child, gpointer unused)
+ {
+ GtkAllocation alloc; /* Emplacement réservé */
+ GtkDisplayPanel *panel; /* Autre vision d'enfance */
+ gint sub_x; /* Abscisse relative à l'enfant*/
+ gint sub_y; /* Ordonnée relative à l'enfant*/
+
+ if (result)
+ return;
+
+ if (!GTK_IS_BUFFER_DISPLAY(child))
+ return;
+
+ gtk_widget_get_allocation(child, &alloc);
+
+ if (x < alloc.x || x >= (alloc.x + alloc.width)) return;
+ if (y < alloc.y || y >= (alloc.y + alloc.height)) return;
+
+ panel = GTK_DISPLAY_PANEL(child);
+
+ sub_x = x - alloc.x;
+ sub_y = y - alloc.y;
+
+ result = GTK_DISPLAY_PANEL_GET_CLASS(panel)->move_caret_to(panel, sub_x, sub_y);
+
+ }
+
+ gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)move_caret_to_sub_block, NULL);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = composant GTK à manipuler. *
+* cr = assistant pour la création de rendus. *
+* area = taille de la surface réduite à disposition. *
+* scale = échelle vis à vis de la taille réelle. *
+* *
+* Description : Place en cache un rendu destiné à l'aperçu graphique rapide. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_cache_glance(GtkGraphDisplay *display, cairo_t *cr, const GtkAllocation *area, double scale)
+{
+ size_t i; /* Boucle de parcours */
+
+ void draw_child_glance(GtkWidget *child, gpointer unused)
+ {
+ GtkAllocation sub_area; /* Emplacement réservé */
+
+ if (!GTK_IS_BUFFER_DISPLAY(child))
+ return;
+
+ gtk_widget_get_allocation(child, &sub_area);
+
+ sub_area.x *= scale;
+ sub_area.y *= scale;
+ sub_area.width = sub_area.width * scale + 1;
+ sub_area.height = sub_area.height * scale + 1;
+
+ gtk_display_panel_cache_glance(GTK_DISPLAY_PANEL(child), cr, &sub_area, scale);
+
+ }
+
+ gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)draw_child_glance, NULL);
+
+ cairo_scale(cr, scale, scale);
+
+ for (i = 0; i < display->edges_count; i++)
+ g_graph_edge_draw(display->edges[i], cr, false);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Crée un nouveau composant pour l'affichage en graphique. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GtkWidget *gtk_graph_display_new(void)
+{
+ return g_object_new(GTK_TYPE_GRAPH_DISPLAY, NULL);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = composant GTK à mettre à jour. *
+* widget = composant GTK à insérer. *
+* x = abscisse du point d'insertion. *
+* y = ordonnée du point d'insertion. *
+* *
+* Description : Place une vue sous forme de bloc dans le graphique. *
+* *
+* Retour : Plutôt que de redéfinir *toutes* les méthodes de *
+* GtkContainer, on étend ! *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void gtk_graph_display_put(GtkGraphDisplay *display, GtkWidget *widget, const GtkAllocation *alloc)
+{
+ g_signal_connect(widget, "reach-limit", G_CALLBACK(gtk_graph_display_reach_caret_limit), display);
+ g_signal_connect(widget, "highlight-changed", G_CALLBACK(gtk_graph_display_changed_highlights), display);
+
+ gtk_fixed_put(GTK_FIXED(display->support), widget, GRAPH_MARGIN + alloc->x, GRAPH_MARGIN + alloc->y);
+
+}
+
+
+
+/******************************************************************************
+* *
+* Paramètres : display = composant GTK à mettre à jour. *
+* edge = lien entre noeuds à conserver. *
+* *
+* Description : Intègre un lien entre blocs graphiques dans l'afficheur. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void gtk_graph_display_add_edge(GtkGraphDisplay *display, GGraphEdge *edge)
+{
+ g_graph_edge_offset(edge, GRAPH_MARGIN, GRAPH_MARGIN);
+
+ display->edges = (GGraphEdge **)realloc(display->edges,
+ ++display->edges_count * sizeof(GGraphEdge *));
+
+ display->edges[display->edges_count - 1] = edge;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : display = instance GTK à réinitialiser. *
+* *
+* Description : Supprime tout contenu de l'afficheur de code en graphique. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_reset(GtkGraphDisplay *display)
+{
+ size_t i; /* Boucle de parcours */
+
+
+ void detach_all_blocks(GtkWidget *widget, GtkContainer *container)
+ {
+
+
+
+ gtk_container_remove(container, widget);
+
+
+ }
+
+
+ gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)detach_all_blocks, display->support);
+
+
+
+ if (display->routine != NULL)
+ {
+ g_object_unref(G_OBJECT(display->routine));
+ display->routine = NULL;
+ }
+
+ if (display->highlighted != NULL)
+ exit_segment_content_list(display->highlighted);
+
+ if (display->cluster != NULL)
+ {
+ g_object_unref(G_OBJECT(display->cluster));
+ display->cluster = NULL;
+ }
+
+ for (i = 0; i < display->edges_count; i++)
+ g_object_unref(G_OBJECT(display->edges[i]));
+
+ if (display->edges_count > 0)
+ {
+ free(display->edges);
+ display->edges = NULL;
+
+ display->edges_count = 0;
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : node = composant d'affichage impliqué dans la procédure. *
+* display = support graphique de tous les noeuds. *
+* *
+* Description : Notifie un changement de surbrillance au sein d'un noeud. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_changed_highlights(GtkBlockDisplay *node, GtkGraphDisplay *display)
+{
+ void refresh_highlights(GtkWidget *child, gpointer unused)
+ {
+ if (!GTK_IS_BUFFER_DISPLAY(child))
+ return;
+
+ if (child != GTK_WIDGET(node))
+ gtk_widget_queue_draw(child);
+
+ }
+
+ gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)refresh_highlights, NULL);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : node = composant d'affichage impliqué dans la procédure. *
+* dir = direction du déplacement souhaité et impossible. *
+* display = support graphique de tous les noeuds. *
+* *
+* Description : Notifie une incapacité de déplacement au sein d'un noeud. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_graph_display_reach_caret_limit(GtkBufferDisplay *node, GdkScrollDirection dir, GtkGraphDisplay *display)
+{
+ GBufferView *view; /* Vue d'un tampon global */
+ vmpa2t first; /* Début d'un groupe de lignes */
+ vmpa2t last; /* Fin d'un groupe de lignes */
+ const mrange_t *range; /* Couverture courante */
+ GArchProcessor *proc; /* Processeur pour instructions*/
+ vmpa2t iaddr; /* Position de l'instructin */
+ instr_iter_t *iter; /* Boucle de parcours */
+ GArchInstruction *instr; /* Instruction à venir visiter */
+#ifndef NDEBUG
+ bool is_return; /* Est-ce une instruc. finale ?*/
+#endif
+ GtkBufferDisplay *target; /* Bloc suivant pour le focus */
+
+ /* Détermination de l'instruction à cibler */
+
+ view = gtk_buffer_display_get_view(node);
+ g_buffer_view_get_restrictions(view, &first, &last);
+ g_object_unref(G_OBJECT(view));
+
+ range = g_binary_routine_get_range(display->routine);
+
+ proc = g_loaded_binary_get_processor(GTK_DISPLAY_PANEL(display)->binary);
+
+ init_vmpa(&iaddr, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL);
+
+#ifndef NDEBUG
+ is_return = false;
+#endif
+
+ switch (dir)
+ {
+ case GDK_SCROLL_LEFT:
+ case GDK_SCROLL_UP:
+
+ if (cmp_vmpa(get_mrange_addr(range), &first) != 0)
+ {
+ iter = g_arch_processor_get_iter_from_address(proc, &first);
+
+ if (iter != NULL)
+ {
+ instr = get_instruction_iterator_prev(iter);
+
+ if (instr != NULL)
+ {
+ /* TODO : boucler si !HAS_CODE */
+
+ if (mrange_contains_addr(range, &iaddr))
+ copy_vmpa(&iaddr, get_mrange_addr(g_arch_instruction_get_range(instr)));
+
+ g_object_unref(G_OBJECT(instr));
+
+ }
+
+ delete_instruction_iterator(iter);
+
+ }
+
+ }
+
+ break;
+
+ case GDK_SCROLL_RIGHT:
+ case GDK_SCROLL_DOWN:
+
+ iter = g_arch_processor_get_iter_from_address(proc, &last);
+
+ if (iter != NULL)
+ {
+#ifndef NDEBUG
+ instr = get_instruction_iterator_current(iter);
+ if (instr != NULL)
+ {
+ is_return = (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT);
+ g_object_unref(G_OBJECT(instr));
+ }
+#endif
+
+ instr = get_instruction_iterator_next(iter);
+
+ if (instr != NULL)
+ {
+ /* TODO : boucler si !HAS_CODE */
+
+ copy_vmpa(&iaddr, get_mrange_addr(g_arch_instruction_get_range(instr)));
+
+ if (!mrange_contains_addr(range, &iaddr))
+ init_vmpa(&iaddr, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL);
+
+ g_object_unref(G_OBJECT(instr));
+
+ }
+
+ delete_instruction_iterator(iter);
+
+ }
+
+ break;
+
+ case GDK_SCROLL_SMOOTH:
+ assert(false); /* Argument jamais généré */
+ break;
+
+ }
+
+ g_object_unref(G_OBJECT(proc));
+
+ /* Recherche du bloc parent */
+
+ if (is_invalid_vmpa(&iaddr))
+ return;
+
+ target = NULL;
+
+ void find_target_block(GtkWidget *child, gpointer unused)
+ {
+ GtkBufferDisplay *test; /* Candidat potentiel à tester */
+
+ if (!GTK_IS_BUFFER_DISPLAY(child))
+ return;
+
+ test = GTK_BUFFER_DISPLAY(child);
+
+ view = gtk_buffer_display_get_view(test);
+ g_buffer_view_get_restrictions(view, &first, &last);
+ g_object_unref(G_OBJECT(view));
+
+ if (cmp_vmpa(&first, &iaddr) <= 0 && cmp_vmpa(&iaddr, &last) <= 0)
+ {
+ assert(target == NULL);
+ assert(node != test);
+ target = test;
+ return;
+ }
+
+ }
+
+ gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)find_target_block, NULL);
+
+ assert(target != NULL || is_return);
+
+ /* Affichage du nouveau curseur */
+
+ /**
+ * Il se peut qu'aucune adresse suivante ne soit disponible : c'est typiquement
+ * le cas sous ARM, avec les valeurs brutes référencées dans le code. Ces valeurs
+ * sont incluses dans la surface couverte par la routine concernée, mais ne sont
+ * pas intégrées dans les blocs basiques associés.
+ */
+
+ if (target == NULL)
+ return;
+
+ gtk_widget_grab_focus(GTK_WIDGET(target));
+
+ switch (dir)
+ {
+ case GDK_SCROLL_UP:
+ case GDK_SCROLL_LEFT:
+ gtk_buffer_display_move_caret_to(target, false, NULL);
+ break;
+
+ break;
+
+ case GDK_SCROLL_RIGHT:
+ case GDK_SCROLL_DOWN:
+ gtk_buffer_display_move_caret_to(target, true, NULL);
+ break;
+
+ break;
+
+ case GDK_SCROLL_SMOOTH:
+ assert(false); /* Argument jamais généré */
+ break;
+
+ }
+
+ /* TODO : scrolling... */
+
+}