/* 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
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
#include
#include "gtkbinview-int.h"
#include "../analysis/exporter.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 ? */
GtkTextBuffer *buffer; /* Code sous forme de texte */
GtkTextLayout *_layout; /* Disposition du texte */
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 */
};
static void
gtk_text_view2_set_attributes_from_style (GtkTextAttributes *values,
GtkStyle *style)
{
PangoFontDescription *font_desc;
values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
values->appearance.fg_color = style->text[GTK_STATE_NORMAL];
if (values->font)
pango_font_description_free (values->font);
font_desc = pango_font_description_from_string ("mono 10");
values->font = pango_font_description_copy (/*style->*/font_desc);
pango_font_description_free (font_desc);
}
/* 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 *, GdkEventExpose *);
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 */
PangoTabArray *tabs;
gint current_y;
PangoFontDescription *font_desc;
GList *child_exposes;
PangoContext *ltr_context, *rtl_context;
GtkTextAttributes *style;
static bool done = false;
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;
view->buffer = gtk_text_buffer_new(get_gtk_tag_table());
view->_layout = gtk_text_layout_new();
gtk_text_layout_set_buffer(view->_layout, view->buffer);
if (!done || 1)
{
done = true;
gtk_text_layout_set_cursor_visible(GTK_BLOCK_VIEW(view)->_layout, FALSE);
gtk_text_layout_set_overwrite_mode(GTK_BLOCK_VIEW(view)->_layout, FALSE);
ltr_context = gtk_widget_create_pango_context(view);
pango_context_set_base_dir(ltr_context, PANGO_DIRECTION_LTR);
rtl_context = gtk_widget_create_pango_context(view);
pango_context_set_base_dir(rtl_context, PANGO_DIRECTION_RTL);
gtk_text_layout_set_contexts(GTK_BLOCK_VIEW(view)->_layout, ltr_context, rtl_context);
tabs = pango_tab_array_new_with_positions(5, TRUE,
PANGO_TAB_LEFT, 120,
PANGO_TAB_LEFT, 290,
PANGO_TAB_LEFT, 50,
PANGO_TAB_LEFT, 50,
PANGO_TAB_LEFT, 50);
style = gtk_text_attributes_new ();
gtk_widget_ensure_style(view);
font_desc = pango_font_description_from_string ("mono 10");
gtk_widget_modify_font (view, font_desc);
pango_font_description_free (font_desc);
gtk_text_view2_set_attributes_from_style(style, GTK_WIDGET(view)->style);
/*
style->pixels_above_lines = 5;
style->pixels_below_lines = 5;
style->pixels_inside_wrap = 10;
style->left_margin = 10;
style->right_margin = 10;
style->indent = 10;*/
style->left_margin = 10;
style->right_margin = 10;
style->tabs = tabs;
style->wrap_mode = GTK_WRAP_WORD;
style->justification = GTK_JUSTIFY_LEFT;
style->direction = gtk_widget_get_direction(view);
gtk_text_layout_set_default_style(GTK_BLOCK_VIEW(view)->_layout, style);
gtk_text_attributes_unref(style);
//gtk_text_layout_get_size (GTK_BLOCK_VIEW(view)->_layout, &width, &height);
gtk_text_layout_set_screen_width (GTK_BLOCK_VIEW(view)->_layout,
MAX (1, 1000));
}
gtk_text_buffer_set_text(view->buffer, "\t|\t|\t|\t|\t|\n", -1);
}
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 */
PangoFontDescription *font_desc;
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);
font_desc = pango_font_description_from_string("mono 10");
gtk_widget_modify_font(widget, font_desc);
pango_font_description_free(font_desc);
}
static gboolean gtk_block_view_button_press(GtkWidget *widget, GdkEventButton *event)
{
gboolean result; /* Décision à retourner */
GtkBlockView *view; /* Composant GTK réel */
int 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);
}
}
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)
{
/* TODO : à améliorer ! */
gtk_widget_queue_draw(GTK_WIDGET(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)
{
GtkBinView *view; /* Autre version 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 */
PangoFontDescription *font_desc;
GList *child_exposes;
view = GTK_BIN_VIEW(widget);
#if 0
gdk_window_begin_paint_region(GDK_DRAWABLE(widget->window), event->region);
gdk_gc_set_clip_region(view->gc, event->region);
gdk_gc_get_values(view->gc, &values);
gdk_color_white(gtk_widget_get_colormap(widget), &white);
gdk_gc_set_foreground(view->gc, &white);
gtk_widget_get_size_request(widget, &width, &height);
gdk_draw_rectangle(GDK_DRAWABLE(widget->window), view->gc,
TRUE, 0, 0, width, height);
gdk_gc_set_foreground(view->gc, &values.foreground);
/*
y = event->area.y;
iter = g_rendering_line_find_by_y(view->lines, view->last, &y);
y = event->area.y - y;
for ( ; iter != NULL && y < (event->area.y + event->area.height);
iter = g_rendering_line_get_next_iter(view->lines, iter, view->last))
{
g_rendering_line_draw(iter, GDK_DRAWABLE(widget->window), view->gc,
MARGIN_SPACE, 2 * MARGIN_SPACE + GTK_BLOCK_VIEW(view)->line_height,
y, GTK_BLOCK_VIEW(view)->line_height, GTK_BLOCK_VIEW(view)->rendering);
y += GTK_BLOCK_VIEW(view)->line_height;
}
*/
gdk_window_end_paint(GDK_DRAWABLE(widget->window));
#endif
gtk_text_layout_set_screen_width (GTK_BLOCK_VIEW(view)->_layout,
MAX (1, 1000));
//gtk_text_buffer_set_text(GTK_BLOCK_VIEW(view)->buffer, "Hello, this some text\n", -1);
font_desc = pango_font_description_from_string ("mono 10");
gtk_widget_modify_font (view, font_desc);
pango_font_description_free (font_desc);
gtk_text_layout_draw(GTK_BLOCK_VIEW(view)->_layout, widget, GDK_DRAWABLE(widget->window),
NULL, 0, 0, 0, 0, 5000, 50000, &child_exposes);
return TRUE;
}
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 */
GtkTextIter pos; /* Point d'insertion */
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);
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))
{
if (iter != GTK_BIN_VIEW(view)->lines)
{
gtk_text_buffer_get_end_iter(view->buffer, &pos);
gtk_text_buffer_insert_with_tags(view->buffer, &pos, "\n", 1, NULL);
}
gtk_text_buffer_get_end_iter(view->buffer, &pos);
g_content_exporter_add_to_gtk_buffer(G_CONTENT_EXPORTER(iter), view->rendering,
view->buffer, &pos);
}
gtk_text_layout_validate(GTK_BLOCK_VIEW(view)->_layout, 100000);
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 */
gtk_text_layout_get_size(view->_layout, &width, &height);
gtk_widget_set_size_request(GTK_WIDGET(view),
width + 2 * MARGIN_SPACE + 20/*view->line_height*/,
height);
printf("req size :: (%d ; %d)\n", width, height);
/*
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;
}