/* 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 "support.h"
#include "../analysis/exporter.h"
#include "../common/dllist.h"
#define MARGIN_SPACE 4 /* TODO : delete me ! */
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 */
gint line_height; /* Hauteur maximale des lignes */
gint left_margin; /* Marge gauche + espace */
gint left_text; /* Début d'impression du code */
const exe_format *format; /* Format du contenu bianire */
};
struct _GtkBlockViewClass
{
GtkBinViewClass parent; /* A laisser en premier */
GdkPixbuf *entry_pix; /* Image du point d'entrée */
GdkPixbuf *breakpoint_pix; /* Image de point d'arrêt */
GdkPixbuf *stopped_pix; /* Image de point actif */
};
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;
gchar *filename; /* Fichier d'image à charger */
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;
/* Image de la marge gauche */
filename = find_pixmap_file("entry.png");
if (filename == NULL) klass->entry_pix = NULL;
else
{
klass->entry_pix = gdk_pixbuf_new_from_file(filename, NULL);
g_free(filename);
}
filename = find_pixmap_file("breakpoint.png");
if (filename == NULL) klass->breakpoint_pix = NULL;
else
{
klass->breakpoint_pix = gdk_pixbuf_new_from_file(filename, NULL);
g_free(filename);
}
filename = find_pixmap_file("stopped.png");
if (filename == NULL) klass->stopped_pix = NULL;
else
{
klass->stopped_pix = gdk_pixbuf_new_from_file(filename, NULL);
g_free(filename);
}
}
/******************************************************************************
* *
* 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 */
PangoFontDescription *font_desc;
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);
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->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));
}
}
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);
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 */
GtkTextIter iter; /* Point d'insertion */
GtkTextMark *mark; /* Marquage de ligne associée */
GRenderingLine *line; /* Ligne de rendu */
result = FALSE;
view = GTK_BLOCK_VIEW(widget);
gtk_text_layout_get_line_at_y(view->layout, &iter, event->y, NULL);
mark = gtk_text_iter_get_marks(&iter)->data;
line = g_object_get_data(G_OBJECT(mark), "line");
/* Clic dans la marge */
if (event->type == GDK_BUTTON_PRESS && event->x < view->left_margin)
{
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)
{
GtkBlockView *view;
//GtkWidget *widget; /* Autre version du composant */
GtkBinView *bview; /* Autre version du composant */
GtkStyle *style; /* Style associé au composant */
GtkBinViewClass *parent_class; /* Version pure du parent */
GtkTextIter iter; /* Point d'insertion */
GdkRectangle rect; /* Zone d'un point */
GtkTextMark *mark; /* Marquage de ligne associée */
GRenderingLine *line; /* Ligne de rendu */
RenderingLineFlag flags; /* Propriétés de la ligne */
GdkPixbuf *pixbuf; /* Données utiles au dessin */
//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 */
GList *child_exposes;
view = GTK_BLOCK_VIEW(widget);
widget = GTK_WIDGET(view);
bview = GTK_BIN_VIEW(widget);
gdk_window_begin_paint_region(GDK_DRAWABLE(widget->window), event->region);
gdk_gc_set_clip_region(bview->gc, event->region);
style = gtk_widget_get_style(GTK_WIDGET(view));
//gtk_widget_get_size_request(widget, &width, &height);
/* Dessin de la marge gauche */
gdk_gc_set_foreground(bview->gc, &style->mid[GTK_WIDGET_STATE(widget)]);
gdk_draw_rectangle(GDK_DRAWABLE(widget->window), bview->gc, TRUE,
0, event->area.y, view->left_margin, event->area.y + event->area.height);
gdk_gc_set_foreground(bview->gc, &style->dark[GTK_WIDGET_STATE(widget)]);
gdk_draw_line(GDK_DRAWABLE(widget->window), bview->gc,
view->left_margin, event->area.y,
view->left_margin, event->area.y + event->area.height);
/* Eventuelle bordure globale */
parent_class = GTK_BIN_VIEW_CLASS(g_type_class_peek_parent(GTK_BLOCK_VIEW_GET_CLASS(view)));
GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
/* Informations individuelles des lignes */
gtk_text_layout_get_line_at_y(view->layout, &iter, event->area.y, NULL);
gtk_text_layout_get_iter_location(view->layout, &iter, &rect);
while (rect.y < (event->area.y + event->area.height))
{
mark = gtk_text_iter_get_marks(&iter)->data;
line = g_object_get_data(G_OBJECT(mark), "line");
flags = g_rendering_line_get_flags(line);
if (flags & RLF_RUNNING_BP)
pixbuf = GTK_BLOCK_VIEW_GET_CLASS(view)->stopped_pix;
else if (flags & RLF_BREAK_POINT)
pixbuf = GTK_BLOCK_VIEW_GET_CLASS(view)->breakpoint_pix;
else pixbuf = NULL;
if (pixbuf != NULL)
gdk_draw_pixbuf(GDK_DRAWABLE(widget->window), bview->gc, pixbuf, 0, 0, 10, rect.y,
gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
GDK_RGB_DITHER_NORMAL, 0, 0);
/* Le point d'entrée prime */
if (flags & RLF_ENTRY_POINT)
{
pixbuf = GTK_BLOCK_VIEW_GET_CLASS(view)->entry_pix;
gdk_draw_pixbuf(GDK_DRAWABLE(widget->window), bview->gc, pixbuf, 0, 0, 10, rect.y,
gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
GDK_RGB_DITHER_NORMAL, 0, 0);
}
if (!gtk_text_layout_move_iter_to_next_line(view->layout, &iter)) break;
gtk_text_layout_get_iter_location(view->layout, &iter, &rect);
}
/* Impression du désassemblage */
#if 0
gdk_window_begin_paint_region(GDK_DRAWABLE(widget->window), event->region);
gdk_gc_set_clip_region(bview->gc, event->region);
gdk_gc_get_values(bview->gc, &values);
gdk_color_white(gtk_widget_get_colormap(widget), &white);
gdk_gc_set_foreground(bview->gc, &white);
gtk_widget_get_size_request(widget, &width, &height);
gdk_draw_rectangle(GDK_DRAWABLE(widget->window), bview->gc,
TRUE, 0, 0, width, height);
gdk_gc_set_foreground(bview->gc, &values.foreground);
/*
y = event->area.y;
iter = g_rendering_line_find_by_y(bview->lines, bview->last, &y);
y = event->area.y - y;
for ( ; iter != NULL && y < (event->area.y + event->area.height);
iter = g_rendering_line_get_next_iter(bview->lines, iter, bview->last))
{
g_rendering_line_draw(iter, GDK_DRAWABLE(widget->window), bview->gc,
MARGIN_SPACE, 2 * MARGIN_SPACE + GTK_BLOCK_VIEW(bview)->line_height,
y, GTK_BLOCK_VIEW(bview)->line_height, GTK_BLOCK_VIEW(bview)->rendering);
y += GTK_BLOCK_VIEW(bview)->line_height;
}
*/
gdk_window_end_paint(GDK_DRAWABLE(widget->window));
#endif
gtk_text_layout_set_screen_width (GTK_BLOCK_VIEW(bview)->layout,
MAX (1, 1000));
gtk_text_layout_draw(GTK_BLOCK_VIEW(bview)->layout, widget, GDK_DRAWABLE(widget->window),
NULL, view->left_text, 0, 0, 0, 5000, 50000, &child_exposes);
gdk_window_end_paint(GDK_DRAWABLE(widget->window));
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)
{
size_t lengths[SAR_COUNT]; /* Différentes tailles de zone */
GRenderingLine *iter; /* Boucle de parcours */
GtkTextIter pos; /* Point d'insertion */
GtkTextMark *mark; /* Marquage de ligne associée */
PangoTabArray *tabs; /* Tailles de tabulation */
GdkRectangle rect; /* Zone d'un point */
lengths[SAR_ADDRESS] = 0;
lengths[SAR_CODE] = 0;
lengths[SAR_INSTRUCTION] = 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_signal_connect(iter, "rendering-line-flags-changed",
G_CALLBACK(gtk_block_view_update_margin), view);
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);
mark = gtk_text_buffer_create_mark(view->buffer, NULL, &pos, TRUE);
g_object_set_data(G_OBJECT(mark), "line", iter);
g_content_exporter_add_to_gtk_buffer(G_CONTENT_EXPORTER(iter), view->rendering,
view->buffer, &pos, lengths);
}
gtk_text_layout_move_iter_visually(view->layout, &pos, -1);
gtk_text_layout_get_iter_location(view->layout, &pos, &rect);
/* Taille des tabulations */
if (lengths[SAR_ADDRESS] > 0 && lengths[SAR_CODE] > 0)
tabs = pango_tab_array_new_with_positions(3, TRUE,
PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3),
PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3 + lengths[SAR_CODE] + 3),
PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3 + lengths[SAR_CODE] + 3 + lengths[SAR_INSTRUCTION] + 4));
else if (lengths[SAR_ADDRESS] > 0 && lengths[SAR_CODE] == 0)
tabs = pango_tab_array_new_with_positions(2, TRUE,
PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3),
PANGO_TAB_LEFT, rect.width * (lengths[SAR_ADDRESS] + 3 + lengths[SAR_INSTRUCTION] + 4));
else if (lengths[SAR_ADDRESS] == 0 && lengths[SAR_CODE] > 0)
tabs = pango_tab_array_new_with_positions(2, TRUE,
PANGO_TAB_LEFT, rect.width * (lengths[SAR_CODE] + 3),
PANGO_TAB_LEFT, rect.width * (lengths[SAR_CODE] + 3 + lengths[SAR_INSTRUCTION] + 4));
else
tabs = pango_tab_array_new_with_positions(1, TRUE,
PANGO_TAB_LEFT, rect.width * (lengths[SAR_INSTRUCTION] + 4));
if (view->layout->default_style->tabs)
pango_tab_array_free(view->layout->default_style->tabs);
view->layout->default_style->tabs = tabs;
gtk_text_layout_default_style_changed(view->layout);
/* Taille des marges */
view->line_height = rect.height;
view->left_margin = 2 * rect.height;
view->left_text = -2.5 * rect.height;
/* Validation finale */
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)
{
gint width; /* Largeur de l'objet actuelle */
gint height; /* Hauteur de l'objet actuelle */
gtk_text_layout_get_size(view->layout, &width, &height);
width += -view->left_text + 1;
height += 1;
gtk_widget_set_size_request(GTK_WIDGET(view), width, 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;
}