/* OpenIDA - Outil d'analyse de fichiers binaires * glance.c - panneau d'aperçu rapide * * Copyright (C) 2009-2012 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "glance.h" #include #include "panel-int.h" /* Panneau d'aperçu rapide (instance) */ struct _GGlancePanel { GPanelItem parent; /* A laisser en premier */ GtkViewPanel *view; /* Vue à représenter */ GtkRequisition req; /* Pleine taille de la source */ GtkScrolledWindow *support; /* Support défilant associé */ double scale; /* Ratio de réduction */ cairo_surface_t *cache; /* Cache grandeur nature */ GtkAllocation painting; /* Zone réservée pour l'aperçu */ GtkAllocation visible; /* Sous-partie visible */ double red; /* Conversion Gdk -> Cairo #1 */ double green; /* Conversion Gdk -> Cairo #2 */ double blue; /* Conversion Gdk -> Cairo #3 */ gdouble start_x; /* Abscisse du point de souris */ gdouble start_y; /* Ordonnée du point de souris */ bool valid; /* Point de départ visible ? */ gdouble ref_h; /* Position horizontale de ref.*/ gdouble ref_v; /* Position verticale de ref. */ }; /* Panneau d'aperçu rapide (classe) */ struct _GGlancePanelClass { GPanelItemClass parent; /* A laisser en premier */ }; /* Initialise la classe des panneaux d'aperçu rapide. */ static void g_glance_panel_class_init(GGlancePanelClass *); /* Initialise une instance de panneau d'aperçu rapide. */ static void g_glance_panel_init(GGlancePanel *); /* Définit une fois et au bon moment le fond de l'aperçu rapide. */ static void define_glance_bg(GtkWidget *, GGlancePanel *); /* Lance une actualisation du fait d'un changement de support. */ static void update_glance_panel_for_view(GGlancePanel *, GtkViewPanel *); /* Réagit à la préparation du défilement du support original. */ static void on_view_scroll_setup(GtkAdjustment *, GGlancePanel *); /* Réagit à un défilement du support original. */ static void on_view_scrolled(GtkAdjustment *, GGlancePanel *); /* Réagit à un changement de taille de l'espace de rendu. */ static void on_glance_resize(GtkWidget *, GdkRectangle *, GGlancePanel *); /* Calcule l'emplacement du rendu maniature et son échelle. */ static void compute_glance_scale(GGlancePanel *); /* Lance une actualisation du fait d'un changement de vue. */ static void update_glance_panel_for_view_content(GGlancePanel *, GtkViewPanel *); /* Met à jour l'affichage de l'aperçu rapide à présenter. */ static gboolean redraw_glance_area(GtkWidget *, GdkEventExpose *, GGlancePanel *); /* Assure la gestion des clics de souris sur l'aperçu. */ static gboolean on_button_press_over_glance(GtkWidget *, GdkEventButton *, GGlancePanel *); /* Assure la gestion du déplacement de la souris sur l'aperçu. */ static gboolean on_mouse_motion_over_glance(GtkWidget *, GdkEventMotion *, GGlancePanel *); /* Indique le type définit pour un panneau d'aperçu rapide. */ G_DEFINE_TYPE(GGlancePanel, g_glance_panel, G_TYPE_PANEL_ITEM); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des panneaux d'aperçu rapide. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_glance_panel_class_init(GGlancePanelClass *klass) { } /****************************************************************************** * * * Paramètres : panel = instance à initialiser. * * * * Description : Initialise une instance de panneau d'aperçu rapide. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_glance_panel_init(GGlancePanel *panel) { GEditorItem *base; /* Version basique d'instance */ GtkWidget *area; /* Surface de dessin réelle */ base = G_EDITOR_ITEM(panel); base->name = _("Glance"); area = gtk_drawing_area_new(); g_signal_connect(G_OBJECT(area), "realize", G_CALLBACK(define_glance_bg), panel); gtk_widget_show(area); base->widget = gtk_event_box_new(); gtk_container_add(GTK_CONTAINER(base->widget), area); gtk_widget_show(base->widget); base->update_view = (update_item_view_fc)update_glance_panel_for_view; base->update_content = (update_item_view_fc)update_glance_panel_for_view_content; gtk_widget_set_size_request(base->widget, 300, 300); g_signal_connect(G_OBJECT(area), "expose_event", G_CALLBACK(redraw_glance_area), panel); g_signal_connect(G_OBJECT(area), "size-allocate", G_CALLBACK(on_glance_resize), panel); g_signal_connect(G_OBJECT(base->widget), "button-press-event", G_CALLBACK(on_button_press_over_glance), panel); g_signal_connect(G_OBJECT(base->widget), "motion-notify-event", G_CALLBACK(on_mouse_motion_over_glance), panel); } /****************************************************************************** * * * Paramètres : ref = espace de référencement global. * * * * Description : Crée un panneau d'aperçu rapide. * * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GEditorItem *g_glance_panel_new(GObject *ref) { GEditorItem *result; /* Structure à retourner */ result = g_object_new(G_TYPE_GLANCE_PANEL, NULL); g_panel_item_init_ext(G_PANEL_ITEM(result), ref, PANEL_GLANCE_ID, _("Glance"), G_EDITOR_ITEM(result)->widget, "es"); return result; } /****************************************************************************** * * * Paramètres : widget = composant GTK en préparation. * * panel = informations liées au panneau associé. * * * * Description : Définit une fois et au bon moment le fond de l'aperçu rapide.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void define_glance_bg(GtkWidget *widget, GGlancePanel *panel) { gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &widget->style->mid[GTK_STATE_NORMAL]); panel->red = widget->style->mid[GTK_STATE_NORMAL].red / USHRT_MAX; panel->green = widget->style->mid[GTK_STATE_NORMAL].green / USHRT_MAX; panel->blue = widget->style->mid[GTK_STATE_NORMAL].blue / USHRT_MAX; } /****************************************************************************** * * * Paramètres : ref = espace de référencement global. * * * * Description : Construit et intègre un panneau d'affichage des symboles. * * * * Retour : Adresse du panneau mis en place. * * * * Remarques : - * * * ******************************************************************************/ GPanelItem *create_glance_panel(GObject *ref) { GEditorItem *result; /* Elément réactif à renvoyer */ result = g_glance_panel_new(ref); /* Enregistre correctement le tout */ register_editor_item(result); return G_PANEL_ITEM(result); } /****************************************************************************** * * * Paramètres : panel = panneau à actualiser. * * view = nouveau panneau d'affichage actif. * * * * Description : Lance une actualisation du fait d'un changement de support. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void update_glance_panel_for_view(GGlancePanel *panel, GtkViewPanel *view) { GtkWidget *parent; /* Support défilant de la vue */ GtkAdjustment *adj; /* Gestionnaire du défilement */ if (panel->view != NULL) { g_object_unref(panel->view); panel->view = NULL; g_object_unref(panel->support); panel->support = NULL; if (panel->cache != NULL) { cairo_surface_destroy(panel->cache); panel->cache = NULL; } } parent = gtk_widget_get_parent(GTK_WIDGET(view)); if (!GTK_IS_SCROLLED_WINDOW(parent)) return; panel->view = view; g_object_ref(panel->view); panel->support = GTK_SCROLLED_WINDOW(parent); g_object_ref(panel->support); adj = gtk_scrolled_window_get_hadjustment(panel->support); g_signal_connect(G_OBJECT(adj), "changed", G_CALLBACK(on_view_scroll_setup), panel); g_signal_connect(G_OBJECT(adj), "value-changed", G_CALLBACK(on_view_scrolled), panel); adj = gtk_scrolled_window_get_vadjustment(panel->support); g_signal_connect(G_OBJECT(adj), "changed", G_CALLBACK(on_view_scroll_setup), panel); g_signal_connect(G_OBJECT(adj), "value-changed", G_CALLBACK(on_view_scrolled), panel); } /****************************************************************************** * * * Paramètres : adj = contrôle du défilement modifié. * * panel = panneau de l'aperçu à actualiser. * * * * Description : Réagit à la préparation du défilement du support original. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_view_scroll_setup(GtkAdjustment *adj, GGlancePanel *panel) { GtkAdjustment *hadj; /* Gestionnaire du défilement */ GtkAdjustment *vadj; /* Gestionnaire du défilement */ hadj = gtk_scrolled_window_get_hadjustment(panel->support); vadj = gtk_scrolled_window_get_vadjustment(panel->support); if (hadj->page_size == 0 || vadj->page_size == 0) return; compute_glance_scale(panel); on_view_scrolled(adj, panel); gtk_widget_size_request(GTK_WIDGET(panel->view), &panel->req); panel->req.width += hadj->value; panel->req.height += vadj->value; update_glance_panel_for_view_content(panel, panel->view); } /****************************************************************************** * * * Paramètres : adj = contrôle du défilement modifié. * * panel = panneau de l'aperçu à actualiser. * * * * Description : Réagit à un défilement du support original. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_view_scrolled(GtkAdjustment *adj, GGlancePanel *panel) { GtkAdjustment *hadj; /* Gestionnaire du défilement */ GtkAdjustment *vadj; /* Gestionnaire du défilement */ hadj = gtk_scrolled_window_get_hadjustment(panel->support); vadj = gtk_scrolled_window_get_vadjustment(panel->support); if (hadj->page_size == 0 || vadj->page_size == 0) return; panel->visible.x = panel->painting.x + hadj->value * panel->scale; panel->visible.y = panel->painting.y + vadj->value * panel->scale; panel->visible.width = hadj->page_size * panel->scale; panel->visible.height = vadj->page_size * panel->scale; gtk_widget_queue_draw(G_EDITOR_ITEM(panel)->widget); } /****************************************************************************** * * * Paramètres : widget = composant GTK ayant changé de taille. * * alloc = nouvel espace mis à disposition. * * panel = panneau de l'aperçu à actualiser. * * * * Description : Réagit à un changement de taille de l'espace de rendu. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_glance_resize(GtkWidget *widget, GdkRectangle *allocation, GGlancePanel *panel) { if (panel->view != NULL) { on_view_scroll_setup(NULL, panel); update_glance_panel_for_view_content(panel, panel->view); } } /****************************************************************************** * * * Paramètres : panel = panneau à actualiser. * * * * Description : Calcule l'emplacement du rendu maniature et son échelle. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void compute_glance_scale(GGlancePanel *panel) { GtkAllocation available; /* Surface disponible totale */ gint border; /* Taille de la bordure */ double sx; /* Echelle sur l'axe X */ double sy; /* Echelle sur l'axe Y */ /* Superficies niveau GTK... */ gtk_widget_get_allocation(G_EDITOR_ITEM(panel)->widget, &available); border = MIN(2, G_EDITOR_ITEM(panel)->widget->style->xthickness); if (border > 0) { available.x = border; available.width -= 2 * border; } border = MIN(2, G_EDITOR_ITEM(panel)->widget->style->ythickness); if (border > 0) { available.y = border; available.height -= 2 * border; } /* Calcul des ratios et emplacements */ sx = (1.0 * available.width) / panel->req.width; sy = (1.0 * available.height) / panel->req.height; if (sx < sy) { panel->scale = sx; panel->painting.width = available.width; panel->painting.height = panel->req.height * panel->scale; } else { panel->scale = sy; panel->painting.width = panel->req.width * panel->scale; panel->painting.height = available.height; } panel->painting.x = available.x + (available.width - panel->painting.width) / 2; panel->painting.y = available.y + (available.height - panel->painting.height) / 2; } /****************************************************************************** * * * Paramètres : panel = panneau à actualiser. * * view = nouveau panneau d'affichage actif. * * * * Description : Lance une actualisation du fait d'un changement de vue. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void update_glance_panel_for_view_content(GGlancePanel *panel, GtkViewPanel *view) { cairo_t *cairo; /* Assistant pour le dessin */ /* Mise en place d'un cache adapté */ if (panel->cache != NULL) cairo_surface_destroy(panel->cache); panel->cache = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, panel->painting.width, panel->painting.height); cairo = cairo_create(panel->cache); cairo_scale(cairo, panel->scale, panel->scale); /* Dessin de l'aperçu représentatif */ gtk_view_panel_cache_glance(view, cairo); cairo_destroy(cairo); gtk_widget_queue_draw(G_EDITOR_ITEM(panel)->widget); } /****************************************************************************** * * * Paramètres : widget = composant GTK à redessiner. * * event = informations liées à l'événement. * * panel = informations liées au panneau associé. * * * * Description : Met à jour l'affichage de l'aperçu rapide à présenter. * * * * Retour : FALSE pour poursuivre la propagation de l'événement. * * * * Remarques : - * * * ******************************************************************************/ static gboolean redraw_glance_area(GtkWidget *widget, GdkEventExpose *event, GGlancePanel *panel) { GtkAllocation available; /* Surface disponible totale */ cairo_t *cairo; /* Gestionnaire de rendu */ /* Dessin de la bordure */ gtk_widget_get_allocation(widget, &available); gtk_paint_box(widget->style, event->window, widget->state, GTK_SHADOW_IN, &event->area, NULL, NULL, 0, 0, available.width, available.height); /* Dessin de l'aperçu */ if (panel->cache != NULL) { cairo = gdk_cairo_create(widget->window); cairo_rectangle(cairo, panel->visible.x, panel->visible.y, panel->visible.width, panel->visible.height); cairo_clip(cairo); cairo_set_source_rgb(cairo, 1.0, 1.0, 1.0); cairo_rectangle(cairo, panel->painting.x, panel->painting.y, panel->painting.width, panel->painting.height); cairo_fill(cairo); cairo_reset_clip(cairo); cairo_set_source_surface(cairo, panel->cache, panel->painting.x, panel->painting.y); cairo_paint(cairo); cairo_destroy(cairo); } return TRUE; } /****************************************************************************** * * * Paramètres : widget = composant GTK visé par l'opération. * * event = informations liées à l'événement. * * panel = informations liées au panneau associé. * * * * Description : Assure la gestion des clics de souris sur l'aperçu. * * * * Retour : FALSE pour poursuivre la propagation de l'événement. * * * * Remarques : - * * * ******************************************************************************/ static gboolean on_button_press_over_glance(GtkWidget *widget, GdkEventButton *event, GGlancePanel *panel) { GtkAdjustment *hadj; /* Gestionnaire du défilement */ GtkAdjustment *vadj; /* Gestionnaire du défilement */ if (panel->view != NULL && event->button == 1) { panel->start_x = event->x; panel->start_y = event->y; hadj = gtk_scrolled_window_get_hadjustment(panel->support); vadj = gtk_scrolled_window_get_vadjustment(panel->support); panel->ref_h = hadj->value; panel->ref_v = vadj->value; panel->valid = (panel->visible.x <= panel->start_x && panel->start_x < (panel->visible.x + panel->visible.width) && panel->visible.y <= panel->start_y && panel->start_y < (panel->visible.y + panel->visible.height)); } return FALSE; } /****************************************************************************** * * * Paramètres : widget = composant GTK visé par l'opération. * * event = informations liées à l'événement. * * panel = informations liées au panneau associé. * * * * Description : Assure la gestion du déplacement de la souris sur l'aperçu. * * * * Retour : FALSE pour poursuivre la propagation de l'événement. * * * * Remarques : - * * * ******************************************************************************/ static gboolean on_mouse_motion_over_glance(GtkWidget *widget, GdkEventMotion *event, GGlancePanel *panel) { gdouble diff_x; /* Evolution sur les abscisses */ gdouble diff_y; /* Evolution sur les ordonnées */ GtkAdjustment *hadj; /* Gestionnaire du défilement */ GtkAdjustment *vadj; /* Gestionnaire du défilement */ gdouble value; /* Nouvelle valeur bornée */ if (panel->view != NULL && event->state & GDK_BUTTON1_MASK && panel->valid) { diff_x = (event->x - panel->start_x) / panel->scale; diff_y = (event->y - panel->start_y) / panel->scale; hadj = gtk_scrolled_window_get_hadjustment(panel->support); vadj = gtk_scrolled_window_get_vadjustment(panel->support); value = CLAMP(panel->ref_h + diff_x, hadj->lower, hadj->upper - hadj->page_size); gtk_adjustment_set_value(hadj, value); value = CLAMP(panel->ref_v + diff_y, vadj->lower, vadj->upper - vadj->page_size); gtk_adjustment_set_value(vadj, value); } return FALSE; }