/* OpenIDA - Outil d'analyse de fichiers binaires
* gtkblockview.c - affichage d'un fragment de code d'assemblage
*
* Copyright (C) 2008 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 "gtkblockview.h"
#include
#include
#include "gtkbinview-int.h"
#include "../common/dllist.h"
#define MARGIN_SPACE 4
struct _GtkBlockView
{
GtkBinView parent; /* A laisser en premier */
MainRendering rendering; /* Support final des lignes */
bool show_vaddress; /* Affichage des adresses ? */
bool show_code; /* Affichage du code brut ? */
PangoLayout *layout; /* Moteur de rendu du code ASM */
int line_height; /* Hauteur maximale des lignes */
const exe_format *format; /* Format du contenu bianire */
};
struct _GtkBlockViewClass
{
GtkBinViewClass parent; /* A laisser en premier */
};
/* Procède à l'initialisation de l'afficheur d'un bloc binaire. */
static void gtk_block_view_init(GtkBlockView *);
/* Encadre la construction graphique initiale de la visualisation. */
static void gtk_block_view_realize(GtkWidget *);
/* Définit les lignes du bloc de représentation. */
static void gtk_block_view_set_rendering_lines(GtkBlockView *, GRenderingLine *, GRenderingLine *);
/* Redessine l'affichage suite une mise à jour dans la marge. */
void gtk_block_view_update_margin(GRenderingLine *, GtkBlockView *);
/* Réclame une nouvelle taille adaptée au contenu présent. */
void gtk_block_view_recompute_size_request(GtkBlockView *);
/* Indique la position d'affichage d'une adresse donnée. */
static bool gtk_block_view_get_address_coordinates(const GtkBlockView *, vmpa_t, gint *, gint *);
static void gtk_block_view_class_init(GtkBlockViewClass *klass);
static void gtk_block_view_init(GtkBlockView *cpu);
static void gtk_block_view_size_request(GtkWidget *widget,
GtkRequisition *requisition);
static void gtk_block_view_size_allocate(GtkWidget *widget,
GtkAllocation *allocation);
static gboolean gtk_block_view_button_press(GtkWidget *, GdkEventButton *event);
/* Met à jour l'affichage de la vue sous forme de bloc. */
static gboolean gtk_block_view_expose(GtkWidget *widget, GdkEventExpose *event);
static void gtk_block_view_paint(GtkBlockView *view);
static void gtk_block_view_destroy(GtkObject *object);
/* Détermine le type du composant d'affichage en block. */
G_DEFINE_TYPE(GtkBlockView, gtk_block_view, GTK_TYPE_BIN_VIEW)
/******************************************************************************
* *
* Paramètres : rendering = support effectif des lignes au final. *
* *
* Description : Crée un nouveau composant pour l'affichage en block. *
* *
* Retour : Composant GTK créé. *
* *
* Remarques : - *
* *
******************************************************************************/
GtkWidget * gtk_block_view_new(MainRendering rendering)
{
GtkBlockView *result;
result = gtk_type_new(gtk_block_view_get_type());
result->rendering = rendering;
return GTK_WIDGET(result);
}
static void gtk_block_view_class_init(GtkBlockViewClass *klass)
{
GtkWidgetClass *widget_class; /* Classe version Widget */
GtkObjectClass *object_class;
widget_class = (GtkWidgetClass *) klass;
object_class = (GtkObjectClass *) klass;
widget_class->button_press_event = gtk_block_view_button_press;
widget_class->realize = gtk_block_view_realize;
widget_class->size_request = gtk_block_view_size_request;
widget_class->size_allocate = gtk_block_view_size_allocate;
widget_class->expose_event = gtk_block_view_expose;
object_class->destroy = gtk_block_view_destroy;
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation de l'afficheur d'un bloc binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_block_view_init(GtkBlockView *view)
{
GtkBinView *binview; /* Instance parente */
binview = GTK_BIN_VIEW(view);
binview->set_lines = (set_rendering_lines_fc)gtk_block_view_set_rendering_lines;
binview->get_coordinates = (get_addr_coordinates_fc)gtk_block_view_get_address_coordinates;
}
static void
gtk_block_view_size_request(GtkWidget *widget,
GtkRequisition *requisition)
{
g_return_if_fail(widget != NULL);
g_return_if_fail(GTK_IS_BLOCK_VIEW(widget));
g_return_if_fail(requisition != NULL);
requisition->width = 80;
requisition->height = 100;
}
static void
gtk_block_view_size_allocate(GtkWidget *widget,
GtkAllocation *allocation)
{
g_return_if_fail(widget != NULL);
g_return_if_fail(GTK_IS_BLOCK_VIEW(widget));
g_return_if_fail(allocation != NULL);
widget->allocation = *allocation;
if (GTK_WIDGET_REALIZED(widget)) {
gdk_window_move_resize(
widget->window,
allocation->x, allocation->y,
allocation->width, allocation->height
);
}
}
/******************************************************************************
* *
* Paramètres : widget = composant GTK à redessiner. *
* *
* Description : Encadre la construction graphique initiale de la visualisat°.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_block_view_realize(GtkWidget *widget)
{
GtkBinViewClass *parent_class; /* Version pure du parent */
parent_class = GTK_BIN_VIEW_CLASS(g_type_class_peek_parent(GTK_BLOCK_VIEW_GET_CLASS(widget)));
GTK_WIDGET_CLASS(parent_class)->realize(widget);
GTK_BLOCK_VIEW(widget)->layout = gtk_widget_create_pango_layout(widget, NULL);
}
static gboolean gtk_block_view_button_press(GtkWidget *widget, GdkEventButton *event)
{
gboolean result; /* Décision à retourner */
GtkBlockView *view; /* Composant GTK réel */
gdouble y; /* Position à manipuler */
GRenderingLine *line; /* Ligne de rendu visée */
result = FALSE;
view = GTK_BLOCK_VIEW(widget);
y = event->y;
line = g_rendering_line_find_by_y(GTK_BIN_VIEW(view)->lines, GTK_BIN_VIEW(view)->last, &y);
if (line != NULL)
{
/* Clic dans la marge */
if (event->type == GDK_BUTTON_PRESS && event->x < (2 * MARGIN_SPACE + view->line_height))
{
result = TRUE;
g_rendering_line_toggle_flag(line, RLF_BREAK_POINT);
}
}
if (result)
{
/* TODO: regions */
gtk_block_view_paint(view);
}
return result;
}
/******************************************************************************
* *
* Paramètres : line = ligne dont un drapeau a évolué. *
* view = composant GTK à mettre à jour. *
* *
* Description : Redessine l'affichage suite une mise à jour dans la marge. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_block_view_update_margin(GRenderingLine *line, GtkBlockView *view)
{
gtk_block_view_paint(view);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à redessiner. *
* event = informations liées à l'événement. *
* *
* Description : Met à jour l'affichage de la vue sous forme de bloc. *
* *
* Retour : FALSE pour poursuivre la propagation de l'événement. *
* *
* Remarques : - *
* *
******************************************************************************/
static gboolean gtk_block_view_expose(GtkWidget *widget, GdkEventExpose *event)
{
GtkBinViewClass *parent_class; /* Version pure du parent */
g_return_val_if_fail(widget != NULL, FALSE);
g_return_val_if_fail(GTK_IS_BLOCK_VIEW(widget), FALSE);
g_return_val_if_fail(event != NULL, FALSE);
gtk_block_view_paint(GTK_BLOCK_VIEW(widget));
/*
gdk_gc_set_clip_region (GdkGC *gc,
const GdkRegion *region);
gdk_window_invalidate_region (GdkWindow *window,
const GdkRegion *region,
gboolean invalidate_children);
gdk_window_begin_paint_region (GdkWindow *window,
const GdkRegion *region);
void gdk_window_end_paint (GdkWindow *window);
*/
parent_class = GTK_BIN_VIEW_CLASS(g_type_class_peek_parent(GTK_BLOCK_VIEW_GET_CLASS(widget)));
GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
return TRUE;
}
static void
gtk_block_view_paint(GtkBlockView *view)
{
GtkWidget *widget; /* Version GTK du composant */
GdkGCValues values; /* Propriétés du contexte */
GdkColor white; /* Couleur du fond */
int width; /* Largeur de l'élément */
int height; /* Hauteur de l'élément */
int y; /* Ordonnée du haut d'une ligne*/
GRenderingLine *iter; /* Boucle de parcours */
widget = GTK_WIDGET(view);
gdk_gc_get_values(GTK_BIN_VIEW(view)->gc, &values);
gdk_color_white(gtk_widget_get_colormap(widget), &white);
gdk_gc_set_foreground(GTK_BIN_VIEW(view)->gc, &white);
gtk_widget_get_size_request(widget, &width, &height);
gdk_draw_rectangle(GDK_DRAWABLE(widget->window), GTK_BIN_VIEW(widget)->gc,
TRUE, 0, 0, width, height);
gdk_gc_set_foreground(GTK_BIN_VIEW(view)->gc, &values.foreground);
gdk_draw_layout(GDK_DRAWABLE(widget->window), GTK_BIN_VIEW(view)->gc,
2 * MARGIN_SPACE + view->line_height, 0,
view->layout);
y = 0;
for (iter = GTK_BIN_VIEW(view)->lines;
iter != NULL;
iter = g_rendering_line_get_next_iter(GTK_BIN_VIEW(view)->lines, iter, GTK_BIN_VIEW(view)->last))
{
g_rendering_line_draw(iter, GDK_DRAWABLE(widget->window), GTK_BIN_VIEW(view)->gc,
MARGIN_SPACE, 2 * MARGIN_SPACE + view->line_height,
y, view->line_height, view->rendering);
y += view->line_height;
}
}
static void
gtk_block_view_destroy(GtkObject *object)
{
GtkBlockView *cpu;
GtkBlockViewClass *klass;
g_return_if_fail(object != NULL);
g_return_if_fail(GTK_IS_BLOCK_VIEW(object));
cpu = GTK_BLOCK_VIEW(object);
klass = gtk_type_class(gtk_widget_get_type());
if (GTK_OBJECT_CLASS(klass)->destroy) {
(* GTK_OBJECT_CLASS(klass)->destroy) (object);
}
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à mettre à jour. *
* show = état de l'affichage auquel parvenir. *
* *
* Description : Choisit d'afficher les adresses virtuelles ou non. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_block_view_show_vaddress(GtkBlockView *view, gboolean show)
{
view->show_vaddress = show;
//gtk_block_view_build_content(view);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à mettre à jour. *
* show = état de l'affichage auquel parvenir. *
* *
* Description : Choisit d'afficher le code brut ou non. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_block_view_show_code(GtkBlockView *view, gboolean show)
{
view->show_code = show;
//gtk_block_view_build_content(view);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à mettre à jour. *
* format = format du binaire affiché. *
* *
* Description : Définit le format auquel le contenu est lié. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_block_view_set_format(GtkBlockView *view, const exe_format *format)
{
view->format = format;
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à mettre à jour. *
* lines = informations à intégrer. *
* last = dernière ligne à intégrer ou NULL pour toutes. *
* *
* Description : Définit les lignes du bloc de représentation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_block_view_set_rendering_lines(GtkBlockView *view, GRenderingLine *lines, GRenderingLine *last)
{
GRenderingLine *iter; /* Boucle de parcours */
for (iter = GTK_BIN_VIEW(view)->lines;
iter != NULL;
iter = g_rendering_line_get_next_iter(GTK_BIN_VIEW(view)->lines, iter, GTK_BIN_VIEW(view)->last))
{
g_signal_connect(iter, "rendering-line-flags-changed",
G_CALLBACK(gtk_block_view_update_margin), view);
}
g_rendering_line_update_bin_len(GTK_BIN_VIEW(view)->lines,
GTK_BIN_VIEW(view)->last, view->rendering);
gtk_block_view_recompute_size_request(view);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à mettre à jour. *
* *
* Description : Réclame une nouvelle taille adaptée au contenu présent. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_block_view_recompute_size_request(GtkBlockView *view)
{
int width; /* Largeur de l'objet actuelle */
int height; /* Hauteur de l'objet actuelle */
g_rendering_line_get_size(GTK_BIN_VIEW(view)->lines,
GTK_BIN_VIEW(view)->last,
view->rendering,
&width, &height, &view->line_height);
gtk_widget_set_size_request(GTK_WIDGET(view),
width + 2 * MARGIN_SPACE + view->line_height,
height);
}
/******************************************************************************
* *
* 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_block_view_get_address_coordinates(const GtkBlockView *view, vmpa_t addr, gint *x, gint *y)
{
bool result; /* Bilan à retourner */
GRenderingLine *iter; /* Boucle de parcours */
result = false;
*x = 0;
*y = 0;
for (iter = GTK_BIN_VIEW(view)->lines;
iter != NULL && !result;
iter = g_rendering_line_get_next_iter(GTK_BIN_VIEW(view)->lines, iter, GTK_BIN_VIEW(view)->last))
{
if (get_rendering_line_address(iter) == addr) result = true;
else *y += view->line_height;
}
return result;
}