/* OpenIDA - Outil d'analyse de fichiers binaires * gtkbufferview.c - affichage de tampons de lignes * * Copyright (C) 2010-2013 Cyrille Bagard * * This file is part of OpenIDA. * * OpenIDA is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * OpenIDA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see . */ #include "gtkbufferview-int.h" #include #include "../glibext/chrysamarshal.h" /* -------------------------- INTERACTION DIRECTE AVEC GTK -------------------------- */ /* Procède à l'initialisation de l'afficheur de tampons. */ static void gtk_buffer_view_class_init(GtkBufferViewClass *); /* Procède à l'initialisation de l'afficheur de tampons. */ static void gtk_buffer_view_init(GtkBufferView *); /* Intègre le focus dans le rendu du composant. */ static gboolean gtk_buffer_view_focus(GtkWidget *, GtkDirectionType); /* Assure la gestion des clics de souris sur le composant. */ static gboolean gtk_buffer_view_button_press(GtkWidget *, GdkEventButton *); /* Fournit la hauteur de composant requise pour un plein rendu. */ static void gtk_buffer_view_get_preferred_height(GtkWidget *, gint *, gint *); /* Fournit la largeur de composant requise pour un plein rendu. */ static void gtk_buffer_view_get_preferred_width(GtkWidget *, gint *, gint *); /* S'adapte à la surface concédée par le composant parent. */ static void gtk_buffer_view_size_allocate(GtkWidget *, GtkAllocation *); /* Met à jour l'affichage de la visualisation de code buffer. */ static gboolean gtk_buffer_view_draw(GtkWidget *, cairo_t *); /* Prend en compte une frappe de touche sur le composant. */ static gboolean gtk_buffer_view_key_press(GtkWidget *, GdkEventKey *); /* Indique la position d'affichage d'une adresse donnée. */ static bool gtk_buffer_view_get_address_coordinates(const GtkBufferView *, vmpa_t, gint *, gint *); /* Réagit à un défilement quelconque. */ static void gtk_buffer_view_scroll(GtkBufferView *); /* Place en cache un rendu destiné à l'aperçu graphique rapide. */ static void gtk_buffer_view_cache_glance(GtkBufferView *, cairo_t *, const GtkAllocation *, double); /* ------------------------------ ANIMATION DU CURSEUR ------------------------------ */ /* Redémarre l'affichage du curseur à l'emplacement courant. */ static void restart_caret_blinking(GtkBufferView *); /* Bascule et relance l'affichage du curseur. */ static gboolean gtk_buffer_view_refresh_caret(GtkBufferView *); /* ---------------------------------------------------------------------------------- */ /* INTERACTION DIRECTE AVEC GTK */ /* ---------------------------------------------------------------------------------- */ /* Détermine le type du composant d'affichage de tampon de lignes. */ G_DEFINE_TYPE(GtkBufferView, gtk_buffer_view, GTK_TYPE_VIEW_PANEL) /****************************************************************************** * * * Paramètres : class = classe GTK à initialiser. * * * * Description : Procède à l'initialisation de l'afficheur de tampons. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_buffer_view_class_init(GtkBufferViewClass *class) { GtkWidgetClass *widget_class; /* Classe version Widget */ widget_class = GTK_WIDGET_CLASS(class); widget_class->focus = gtk_buffer_view_focus; widget_class->button_press_event = gtk_buffer_view_button_press; widget_class->get_preferred_height = gtk_buffer_view_get_preferred_height; widget_class->get_preferred_width = gtk_buffer_view_get_preferred_width; widget_class->size_allocate = gtk_buffer_view_size_allocate; widget_class->draw = gtk_buffer_view_draw; widget_class->key_press_event = gtk_buffer_view_key_press; g_signal_new("caret-moved", GTK_TYPE_BUFFER_VIEW, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GtkBufferViewClass, caret_moved), NULL, NULL, g_cclosure_user_marshal_VOID__UINT64, G_TYPE_NONE, 1, G_TYPE_UINT64); } /****************************************************************************** * * * Paramètres : view = composant GTK à initialiser. * * * * Description : Procède à l'initialisation de l'afficheur de tampons. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_buffer_view_init(GtkBufferView *view) { GtkViewPanel *viewpanel; /* Instance parente */ viewpanel = GTK_VIEW_PANEL(view); viewpanel->get_coordinates = (get_addr_coordinates_fc)gtk_buffer_view_get_address_coordinates; viewpanel->scroll = (scroll_fc)gtk_buffer_view_scroll; viewpanel->cache_glance = (cache_glance_fc)gtk_buffer_view_cache_glance; view->caret.x = 10; view->caret.y = 10; view->caret.width = 100; view->caret.height = 100; } /****************************************************************************** * * * Paramètres : widget = composant GTK visé par l'opération. * * dir = sens de l'opération : perte ou gain de focus. * * * * Description : Intègre le focus dans le rendu du composant. * * * * Retour : FALSE pour poursuivre la propagation de l'événement. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_buffer_view_focus(GtkWidget *widget, GtkDirectionType direction) { GtkBufferView *view; /* Autre version du composant */ gboolean has_focus; /* Etat courant */ view = GTK_BUFFER_VIEW(widget); has_focus = gtk_widget_is_focus(widget); if (has_focus) restart_caret_blinking(view); else if (view->caret_timer != 0) { g_source_remove(view->caret_timer); view->caret_timer = 0; view->show_caret = true; gtk_buffer_view_refresh_caret(view); } return TRUE; } /****************************************************************************** * * * Paramètres : widget = composant GTK visé par l'opération. * * event = informations liées à l'événement. * * * * 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_buffer_view_button_press(GtkWidget *widget, GdkEventButton *event) { GtkBufferView *view; /* Autre version du composant */ gint real_x; /* Abscisse absolue réelle */ gint real_y; /* Ordonnée absolue réelle */ size_t index; /* Indice de ligne de tampon */ GBufferLine *line; /* Ligne à la position courante*/ vmpa_t addr; /* Position mémoire associée */ GdkRectangle new; /* Nouvel emplacement calculé */ view = GTK_BUFFER_VIEW(widget); gtk_widget_grab_focus(widget); real_x = event->x; real_y = event->y; gtk_buffer_view_compute_real_coord(view, &real_x, &real_y); printf(" !mouse! :: (%g ; %g) -> (%d ; %d)\n", event->x, event->y, real_x, real_y); line = g_buffer_view_find_line_at(view->buffer_view, real_y, &index); if (line == NULL) return FALSE; if (real_x < view->left_margin) { printf("Border Line :: %p\n", line); } else { addr = g_buffer_view_compute_caret(view->buffer_view, line, index, real_x, &new); if (addr == VMPA_INVALID) return FALSE; gtk_buffer_view_compute_relative_coords(view, &view->caret.x, &view->caret.y); printf(" mouse --old-- :: (%d ; %d)\n", view->caret.x, view->caret.y); printf(" mouse --new-- :: (%d ; %d)\n", new.x, new.y); gtk_widget_queue_draw_area(GTK_WIDGET(view), view->caret.x, view->caret.y, view->caret.width, view->caret.height); view->caret = new; view->caret_addr = addr; restart_caret_blinking(view); } return FALSE; } /****************************************************************************** * * * Paramètres : view = composant GTK à consulter. * * event = informations liées à l'événement. * * * * Description : Transcrit les coordonnées à l'écran en coordonnées absolues. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_buffer_view_compute_fake_coord(GtkBufferView *view, gint *x, gint *y) { if (GTK_VIEW_PANEL(view)->hadjustment != NULL) *x -= gtk_adjustment_get_value(GTK_VIEW_PANEL(view)->hadjustment); if (GTK_VIEW_PANEL(view)->vadjustment != NULL) *y += gtk_adjustment_get_value(GTK_VIEW_PANEL(view)->vadjustment); } /****************************************************************************** * * * Paramètres : view = composant GTK à consulter. * * event = informations liées à l'événement. * * * * Description : Transcrit les coordonnées absolues en coordonnées à l'écran. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_buffer_view_compute_real_coord(GtkBufferView *view, gint *x, gint *y) { if (x != NULL && GTK_VIEW_PANEL(view)->hadjustment != NULL) *x += gtk_adjustment_get_value(GTK_VIEW_PANEL(view)->hadjustment); if (y != NULL && GTK_VIEW_PANEL(view)->vadjustment != NULL) *y += gtk_adjustment_get_value(GTK_VIEW_PANEL(view)->vadjustment); } /****************************************************************************** * * * Paramètres : widget = composant GTK à consulter. * * minimal = taille minimale. [OUT] * * natural = taille idéale. [OUT] * * * * Description : Fournit la hauteur de composant requise pour un plein rendu. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_buffer_view_get_preferred_height(GtkWidget *widget, gint *minimal, gint *natural) { GtkBufferView *view; /* Autre version du composant */ view = GTK_BUFFER_VIEW(widget); if (view->buffer_view != NULL) *minimal = g_buffer_view_get_height(view->buffer_view); else *minimal = 0; *natural = *minimal; } /****************************************************************************** * * * Paramètres : widget = composant GTK à consulter. * * minimal = taille minimale. [OUT] * * natural = taille idéale. [OUT] * * * * Description : Fournit la largeur de composant requise pour un plein rendu. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_buffer_view_get_preferred_width(GtkWidget *widget, gint *minimal, gint *natural) { GtkBufferView *view; /* Autre version du composant */ view = GTK_BUFFER_VIEW(widget); if (view->buffer_view != NULL) *minimal = g_buffer_view_get_width(view->buffer_view, *GTK_VIEW_PANEL(view)->display_addr, *GTK_VIEW_PANEL(view)->display_code); else *minimal = 0; *natural = *minimal; } /****************************************************************************** * * * Paramètres : view = 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_buffer_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation) { GtkViewPanel *panel; /* Autre version du composant */ GtkBufferView *view; /* Encore une autre version */ gint width; /* Largeur de l'objet actuelle */ gint height; /* Hauteur de l'objet actuelle */ GtkAllocation valloc; /* Surface utilisable */ gboolean changed; /* Changement de valeur ? */ /* Mise à jour GTK */ gtk_widget_set_allocation(widget, allocation); if (gtk_widget_get_realized(widget)) gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, allocation->height); panel = GTK_VIEW_PANEL(widget); if (panel->hadjustment == NULL || panel->vadjustment == NULL) return; view = GTK_BUFFER_VIEW(widget); width = g_buffer_view_get_width(view->buffer_view, *panel->display_addr, *panel->display_code); height = g_buffer_view_get_height(view->buffer_view); gtk_view_panel_compute_allocation(panel, &valloc); /* Défilement horizontal */ gtk_adjustment_set_page_size(panel->hadjustment, valloc.width); gtk_adjustment_set_step_increment(panel->hadjustment, valloc.width * 0.1); gtk_adjustment_set_page_increment(panel->hadjustment, valloc.width * 0.9); gtk_adjustment_set_upper(panel->hadjustment, MAX(width, valloc.width)); gtk_view_panel_reclamp_adjustment(panel->hadjustment, &changed); gtk_adjustment_changed(panel->hadjustment); if (changed) gtk_adjustment_value_changed(panel->hadjustment); /* Défilement vertical */ gtk_adjustment_set_page_size(panel->vadjustment, valloc.height); gtk_adjustment_set_step_increment(panel->vadjustment, view->line_height); gtk_adjustment_set_page_increment(panel->vadjustment, view->line_height * 10.0); gtk_adjustment_set_upper(panel->vadjustment, MAX(height, valloc.height)); gtk_view_panel_reclamp_adjustment(panel->vadjustment, &changed); gtk_adjustment_changed(panel->vadjustment); if (changed) gtk_adjustment_value_changed(panel->vadjustment); } /****************************************************************************** * * * Paramètres : widget = composant GTK à redessiner. * * cr = contexte graphique associé à l'événement. * * * * Description : Met à jour l'affichage de la visualisation de code buffer. * * * * Retour : FALSE pour poursuivre la propagation de l'événement. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_buffer_view_draw(GtkWidget *widget, cairo_t *cr) { GtkBufferView *view; /* Autre version du composant */ GtkViewPanel *pview; /* Autre version du composant */ cairo_region_t *region; /* Région visible à redessiner */ cairo_rectangle_int_t area; /* Surface correspondante */ gint fake_x; /* Abscisse virtuelle */ gint fake_y; /* Ordonnée virtuelle */ GtkStateFlags state; /* Etat du composant */ GdkRGBA *color; /* Couleur du curseur */ view = GTK_BUFFER_VIEW(widget); widget = GTK_WIDGET(view); pview = GTK_VIEW_PANEL(widget); region = gdk_window_get_visible_region(gtk_widget_get_window(widget)); cairo_region_get_extents(region, &area); cairo_region_destroy(region); fake_x = 0; fake_y = 0; gtk_buffer_view_compute_fake_coord(view, &fake_x, &fake_y); do { GtkStyleContext *context; context = gtk_widget_get_style_context(widget); gtk_render_background(context, cr, 0, 0, 250, 250); printf("Passage!\n"); } while (0); /* Dessin de la marge gauche */ state = gtk_widget_get_state_flags(widget); gtk_style_context_get(gtk_widget_get_style_context(widget), state, GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &color, NULL); cairo_rectangle(cr, fake_x, area.y, view->left_margin, area.y + area.height); cairo_fill(cr); gtk_style_context_get(gtk_widget_get_style_context(widget), state, GTK_STYLE_PROPERTY_BORDER_COLOR, &color, NULL); cairo_move_to(cr, fake_x + view->left_margin, area.y); cairo_line_to(cr, fake_x + view->left_margin, area.y + area.height); cairo_fill(cr); /* Eventuelle bordure globale */ GTK_WIDGET_CLASS(gtk_buffer_view_parent_class)->draw(widget, cr); /* Impression du désassemblage */ if (view->buffer_view != NULL) g_buffer_view_draw(view->buffer_view, cr, fake_x, fake_y, &area, *pview->display_addr, *pview->display_code); return TRUE; } /****************************************************************************** * * * Paramètres : widget = composant visé par l'opération. * * event = informations liées à l'événement. * * * * Description : Prend en compte une frappe de touche sur le composant. * * * * Retour : FALSE pour poursuivre la propagation de l'événement. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_buffer_view_key_press(GtkWidget *widget, GdkEventKey *event) { gboolean result; /* Suites à renvoyer */ bool ctrl; /* Statut de la touche Contrôle*/ result = FALSE; ctrl = (event->state & GDK_CONTROL_MASK); switch (event->keyval) { case GDK_KEY_Left: result = TRUE; break; case GDK_KEY_Up: result = TRUE; break; case GDK_KEY_Right: result = TRUE; break; case GDK_KEY_Down: result = TRUE; break; } printf("ctrl ? %d -- keyval = %d -->> %d\n", ctrl, event->keyval, result); return result; } /****************************************************************************** * * * Paramètres : view = 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] * * * * 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_buffer_view_get_address_coordinates(const GtkBufferView *view, vmpa_t addr, gint *x, gint *y) { return g_buffer_view_get_address_coordinates(view->buffer_view, addr, x, y); } /****************************************************************************** * * * Paramètres : view = composant GTK à mettre à jour. * * * * Description : Réagit à un défilement quelconque. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_buffer_view_scroll(GtkBufferView *view) { gtk_widget_queue_draw(GTK_WIDGET(view)); } /****************************************************************************** * * * Paramètres : view = composant GTK à manipuler. * * cairo = 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_buffer_view_cache_glance(GtkBufferView *view, cairo_t *cairo, const GtkAllocation *area, double scale) { cairo_set_line_width(cairo, 1); cairo_set_source_rgb(cairo, 0.4, 0.4, 0.4); cairo_rectangle(cairo, area->x + 0.5, area->y + 0.5, area->width - 1, area->height - 1); cairo_stroke(cairo); } /****************************************************************************** * * * Paramètres : view = composant GTK à mettre à jour. * * buffer = tampon de lignes à encadrer. * * addr = indique si les positions doivent être affichées. * * code = indique si le code binaire doit être affiché. * * * * Description : Prend acte de l'association d'un tampon de lignes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_buffer_view_attach_buffer(GtkBufferView *view, GBufferView *buffer, bool *addr, bool *code) { gint width; /* Largeur de l'objet actuelle */ gint height; /* Hauteur de l'objet actuelle */ if (view->buffer != NULL) { g_object_unref(G_OBJECT(view->buffer)); g_object_unref(G_OBJECT(view->buffer_view)); } view->buffer = g_buffer_view_get_buffer(buffer); g_object_ref(G_OBJECT(view->buffer)); view->buffer_view = buffer; //gdk_threads_enter(); /* Taille des marges */ view->line_height = g_buffer_view_get_line_height(view->buffer_view); view->left_margin = 2 * view->line_height; view->left_text = -2.5 * view->line_height; /* Validation finale */ width = g_buffer_view_get_width(view->buffer_view, *addr, *code); height = g_buffer_view_get_height(view->buffer_view); width += -view->left_text + 1; height += 1; //gtk_widget_set_size_request(GTK_WIDGET(view), width, height); gtk_widget_queue_draw(GTK_WIDGET(view)); //gdk_flush (); //gdk_threads_leave(); } /****************************************************************************** * * * Paramètres : view = composant GTK à consulter. * * * * Description : Fournit la vue associée au tampon de lignes courant. * * * * Retour : Vue mise en place. * * * * Remarques : - * * * ******************************************************************************/ GBufferView *gtk_buffer_view_get_buffer(const GtkBufferView *view) { return view->buffer_view; } /* ---------------------------------------------------------------------------------- */ /* CONVERSIONS DE COORDONNEES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : view = composant GTK à consulter. * * x = abscisse à ajuster. [OUT] * * x = ordonnée à ajuster. [OUT] * * * * Description : Transcrit les coordonnées absolues en coordonnées à l'écran. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_buffer_view_compute_relative_coords(GtkBufferView *view, gint *x, gint *y) { if (x != NULL && GTK_VIEW_PANEL(view)->hadjustment != NULL) *x -= gtk_adjustment_get_value(GTK_VIEW_PANEL(view)->hadjustment); if (y != NULL && GTK_VIEW_PANEL(view)->vadjustment != NULL) *y -= gtk_adjustment_get_value(GTK_VIEW_PANEL(view)->vadjustment); } /* ---------------------------------------------------------------------------------- */ /* ANIMATION DU CURSEUR */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : view = composant GTK à manipuler. * * * * Description : Redémarre l'affichage du curseur à l'emplacement courant. * * * * Retour : TRUE pour poursuivre les basculements automatiques. * * * * Remarques : - * * * ******************************************************************************/ static void restart_caret_blinking(GtkBufferView *view) { if (view->caret_addr == VMPA_INVALID) return; if (view->caret_timer != 0) g_source_remove(view->caret_timer); view->caret_timer = g_timeout_add_seconds(1, (GSourceFunc)gtk_buffer_view_refresh_caret, view); view->show_caret = false; gtk_buffer_view_refresh_caret(view); g_signal_emit_by_name(view, "caret-moved", view->caret_addr); } /****************************************************************************** * * * Paramètres : view = composant GTK à manipuler. * * * * Description : Bascule et relance l'affichage du curseur. * * * * Retour : TRUE pour poursuivre les basculements automatiques. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_buffer_view_refresh_caret(GtkBufferView *view) { GtkWidget *widget; /* Autre version du composant */ GdkRectangle area; /* Région adaptée à traiter */ cairo_t *cr; /* Contexte graphique */ GdkRGBA *color; /* Couleur du curseur */ widget = GTK_WIDGET(view); area = view->caret; gtk_buffer_view_compute_relative_coords(view, &area.x, &area.y); /* Réinitialisation de la surface */ if (view->show_caret) { view->show_caret = false; gtk_widget_queue_draw_area(widget, area.x, area.y, area.width, area.height); } /* Dessin */ else { view->show_caret = true; cr = gdk_cairo_create(gtk_widget_get_window(widget)); gtk_style_context_get(gtk_widget_get_style_context(widget), gtk_widget_get_state_flags(widget), GTK_STYLE_PROPERTY_COLOR, &color, NULL); cairo_set_source_rgb(cr, color->red, color->green, color->blue); cairo_rectangle(cr, area.x, area.y, area.width, area.height); cairo_fill(cr); cairo_destroy(cr); } return TRUE; }