/* Chrysalide - Outil d'analyse de fichiers binaires
* gtkbufferview.c - affichage de tampons de lignes
*
* Copyright (C) 2010-2014 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide 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.
*
* Chrysalide 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 "../arch/target.h"
#include "../common/extstr.h"
#include "../core/params.h"
#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 *);
/* Supprime toutes les références externes. */
static void gtk_buffer_view_dispose(GtkBufferView *);
/* Procède à la libération totale de la mémoire. */
static void gtk_buffer_view_finalize(GtkBufferView *);
/* Intègre le focus dans le rendu du composant. */
static gboolean gtk_buffer_view_focus(GtkWidget *, GdkEventFocus *);
/* Assure la gestion des clics de souris sur le composant. */
static gboolean gtk_buffer_view_button_press(GtkWidget *, GdkEventButton *);
/* 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 *);
/* Prépare l'affichage d'une astuce. */
static gboolean gtk_buffer_view_query_tooltip(GtkWidget *, gint, gint, gboolean, GtkTooltip *);
/* Indique les dimensions de travail du composant d'affichage. */
static void gtk_buffer_view_compute_requested_size(GtkBufferView *, gint *, gint *);
/* Détermine la taille des bonds lors de défilements. */
static void gtk_buffer_view_compute_scroll_inc(GtkBufferView *, gint, GtkOrientation, gdouble *, gdouble *);
/* Réagit à un défilement chez une barre associée au composant. */
static void gtk_buffer_view_adjust_scroll_value(GtkBufferView *, GtkAdjustment *, GtkOrientation);
/* Indique la position courante du curseur. */
static const vmpa2t *gtk_buffer_view_get_caret_location(const GtkBufferView *);
/* Indique la position d'affichage d'une adresse donnée. */
static bool gtk_buffer_view_get_address_coordinates(const GtkBufferView *, const vmpa2t *, gint *, gint *, ScrollPositionTweak);
/* Fournit des éléments liés à la position courante dans la vue. */
static bool gtk_buffer_view_get_position(const GtkBufferView *, GBufferLine **, GObject **);
/* 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 ------------------------------ */
/* Déplace le curseur à un emplacement défini. */
static bool _gtk_buffer_view_move_caret_to(GtkBufferView *, gint, gint);
/* Déplace le curseur en effaçant son éventuelle position. */
static void gtk_buffer_view_relocate_caret(GtkBufferView *, const GdkRectangle *, const vmpa2t *);
/* 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_DISPLAY_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)
{
GObjectClass *object; /* Autre version de la classe */
GtkWidgetClass *widget_class; /* Classe version Widget */
GtkDisplayPanelClass *panel_class; /* Classe parente */
object = G_OBJECT_CLASS(class);
widget_class = GTK_WIDGET_CLASS(class);
panel_class = GTK_DISPLAY_PANEL_CLASS(class);
object->dispose = (GObjectFinalizeFunc/* ! */)gtk_buffer_view_dispose;
object->finalize = (GObjectFinalizeFunc)gtk_buffer_view_finalize;
widget_class->focus_in_event = gtk_buffer_view_focus;
widget_class->focus_out_event = gtk_buffer_view_focus;
widget_class->button_press_event = gtk_buffer_view_button_press;
widget_class->draw = gtk_buffer_view_draw;
widget_class->key_press_event = gtk_buffer_view_key_press;
widget_class->query_tooltip = gtk_buffer_view_query_tooltip;
panel_class->compute_size = (compute_requested_size_fc)gtk_buffer_view_compute_requested_size;
panel_class->compute_inc = (compute_scroll_inc_fc)gtk_buffer_view_compute_scroll_inc;
panel_class->adjust = (adjust_scroll_value_fc)gtk_buffer_view_adjust_scroll_value;
panel_class->get_caret_loc = (get_caret_location_fc)gtk_buffer_view_get_caret_location;
panel_class->get_coordinates = (get_addr_coordinates_fc)gtk_buffer_view_get_address_coordinates;
panel_class->get_position = (get_view_position_fc)gtk_buffer_view_get_position;
panel_class->move_caret_to = (move_caret_to_fc)_gtk_buffer_view_move_caret_to;
panel_class->cache_glance = (cache_glance_fc)gtk_buffer_view_cache_glance;
g_signal_new("reach-limit",
GTK_TYPE_BUFFER_VIEW,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GtkBufferViewClass, reach_limit),
NULL, NULL,
g_cclosure_marshal_VOID__ENUM,
G_TYPE_NONE, 1, GTK_TYPE_SCROLL_TYPE);
}
/******************************************************************************
* *
* 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)
{
GObject *object; /* Autre version de l'instance */
object = G_OBJECT(view);
g_object_set(object, "has-tooltip", TRUE, NULL);
view->caret.x = 10;
view->caret.y = 10;
view->caret.width = 100;
view->caret.height = 100;
}
/******************************************************************************
* *
* Paramètres : view = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_buffer_view_dispose(GtkBufferView *view)
{
G_OBJECT_CLASS(gtk_buffer_view_parent_class)->dispose(G_OBJECT(view));
}
/******************************************************************************
* *
* Paramètres : view = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_buffer_view_finalize(GtkBufferView *view)
{
G_OBJECT_CLASS(gtk_buffer_view_parent_class)->finalize(G_OBJECT(view));
}
/******************************************************************************
* *
* Paramètres : widget = composant GTK visé par l'opération. *
* event = informations liées à l'événement. *
* *
* 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, GdkEventFocus *event)
{
GtkBufferView *view; /* Autre version du composant */
gboolean has_focus; /* Etat courant */
view = GTK_BUFFER_VIEW(widget);
has_focus = event->in;
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 */
GBufferLine *line; /* Ligne à la position courante*/
view = GTK_BUFFER_VIEW(widget);
real_x = event->x;
real_y = event->y;
gtk_display_panel_compute_real_coord(GTK_DISPLAY_PANEL(view), &real_x, &real_y);
printf(" !mouse! :: (%g ; %g) -> (%d ; %d)\n",
event->x, event->y,
real_x, real_y);
if (real_x < view->left_margin)
{
line = g_buffer_view_find_line_at(view->buffer_view, real_y, NULL);
if (line == NULL) return FALSE;
/* TODO */
printf("Border Line :: %p\n", line);
}
else
_gtk_buffer_view_move_caret_to(view, real_x, real_y);
gtk_widget_grab_focus(widget);
return FALSE;
}
/******************************************************************************
* *
* 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 */
GtkDisplayPanel *panel; /* Autre version du composant */
GdkWindow *window; /* Fenêtre à redessiner */
cairo_region_t *region; /* Région visible à redessiner */
cairo_rectangle_int_t area; /* Surface correspondante */
GtkStyleContext *context; /* Contexte du thème actuel */
GdkRGBA color; /* Couleur de thème récupérée */
gint fake_x; /* Abscisse virtuelle */
gint fake_y; /* Ordonnée virtuelle */
bool sel_line; /* Souslignage de la sélection */
gint *selected; /* Ordonnée d'une sélection */
view = GTK_BUFFER_VIEW(widget);
panel = GTK_DISPLAY_PANEL(widget);
window = gtk_widget_get_window(widget);
cairo_save(cr);
gtk_cairo_transform_to_window(cr, widget, window);
region = gdk_window_get_clip_region(window);
cairo_region_get_extents(region, &area);
cairo_region_destroy(region);
context = gtk_widget_get_style_context(widget);
if (panel->show_border)
{
gtk_display_panel_define_border_path(panel, cr, 0, 0);
cairo_clip(cr);
}
/* Dessin de la marge gauche */
gtk_style_context_save(context);
gtk_style_context_add_class(context, GTK_STYLE_CLASS_TOOLBAR);
gtk_style_context_get_background_color(context, GTK_STATE_FLAG_ACTIVE, &color);
cairo_set_source_rgba(cr, color.red, color.green, color.blue, color.alpha);
cairo_rectangle(cr, 0, area.y, view->left_margin, area.height);
cairo_fill(cr);
gtk_style_context_restore(context);
/* Fond de la zone de texte */
gtk_style_context_save(context);
gtk_style_context_add_class(context, GTK_STYLE_CLASS_VIEW);
gtk_style_context_get_background_color(context, GTK_STATE_FLAG_ACTIVE, &color);
cairo_set_source_rgba(cr, color.red, color.green, color.blue, color.alpha * 0.7);
cairo_rectangle(cr, view->left_margin, area.y, area.width, area.height);
cairo_fill(cr);
gtk_style_context_restore(context);
/* Ligne de séparation */
gtk_style_context_save(context);
gtk_style_context_add_class(context, GTK_STYLE_CLASS_FRAME);
gtk_style_context_get_border_color(context, GTK_STATE_FLAG_ACTIVE, &color);
cairo_set_source_rgba(cr, color.red, color.green, color.blue, color.alpha);
cairo_set_line_width(cr, 1.0);
cairo_move_to(cr, view->left_margin + 0.5, area.y - 0.5);
cairo_line_to(cr, view->left_margin + 0.5, area.y + area.height + 0.5);
cairo_stroke(cr);
gtk_style_context_restore(context);
/* Eventuelle bordure globale */
if (panel->show_border)
gtk_display_panel_draw_border(panel, cr);
/* Impression du désassemblage */
if (view->buffer_view != NULL)
{
fake_x = 0;
fake_y = 0;
gtk_display_panel_compute_fake_coord(GTK_DISPLAY_PANEL(view), &fake_x, &fake_y);
g_generic_config_get_value(get_main_configuration(), MPK_SELECTION_LINE, &sel_line);
sel_line &= gtk_widget_has_focus(widget);
if (!sel_line || view->caret_addr == NULL)
selected = NULL;
else
selected = &view->caret.y;
g_buffer_view_draw(view->buffer_view, cr, fake_x, fake_y, &area, panel->display, selected);
}
/* Curseur clignotant ? */
if (gtk_widget_is_focus(widget))
{
view->show_caret = !view->show_caret;
gtk_buffer_view_refresh_caret(view);
}
cairo_restore(cr);
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 */
GtkBufferView *view; /* Autre version du composant */
GtkDisplayPanel *panel; /* Autre version du composant */
bool ctrl; /* Statut de la touche Contrôle*/
GdkScrollDirection dir; /* Direction du déplacement */
GdkRectangle area; /* Emplacement de curseur */
const vmpa2t *addr; /* Adresse du nouveau curseur */
result = FALSE;
view = GTK_BUFFER_VIEW(widget);
panel = GTK_DISPLAY_PANEL(widget);
switch (event->keyval)
{
case GDK_KEY_Left:
dir = GDK_SCROLL_LEFT;
result = TRUE;
break;
case GDK_KEY_Up:
dir = GDK_SCROLL_UP;
result = TRUE;
break;
case GDK_KEY_Right:
dir = GDK_SCROLL_RIGHT;
result = TRUE;
break;
case GDK_KEY_Down:
dir = GDK_SCROLL_DOWN;
result = TRUE;
break;
default:
break;
}
if (result)
{
area = view->caret;
ctrl = (event->state & GDK_CONTROL_MASK);
addr = g_buffer_view_move_caret(view->buffer_view, &area, ctrl, dir, panel->display);
if (addr != NULL)
{
gtk_buffer_view_relocate_caret(view, &area, addr);
_gtk_display_panel_scroll_to_address(panel, addr, SPT_RAW, false);
}
else
g_signal_emit_by_name(view, "reach-limit", dir);
//if (addr == NULL) return FALSE;
}
return result;
}
/******************************************************************************
* *
* Paramètres : widget = composant GTK visé par l'opération. *
* x = abscisse de la position du message. *
* y = ordonnée de la position du message. *
* keyboard = indique une demande suite à obtiention du focus. *
* tooltip = astuce à compléter. [OUT] *
* *
* Description : Prépare l'affichage d'une astuce. *
* *
* Retour : TRUE pour un affichage validé, FALSE sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static gboolean gtk_buffer_view_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard, GtkTooltip *tooltip)
{
gboolean result; /* Bilan à retourner */
GBinFormat *format; /* Format du fichier binaire */
GtkBufferView *view; /* Autre version du composant */
gint real_x; /* Abscisse absolue réelle */
gint real_y; /* Ordonnée absolue réelle */
GBufferLine *line; /* Ligne en cours de survol */
GObject *creator; /* Créateur à l'orgine du seg. */
virt_t virt; /* Adresse virtuelle */
vmpa2t addr; /* Adresse de destination */
GBinSymbol *target_sym; /* Symbole présent à l'adresse */
GBinSymbol *next_sym; /* Symbole suivant l'adresse */
GCodeBuffer *buffer; /* Tampon où lire les lignes */
const vmpa2t *stop_addr; /* Adresse associée, pour fin */
const mrange_t *lrange; /* Couverture d'une ligne */
size_t count; /* Nbre de lignes max à traiter*/
char *markup; /* Description à construire */
size_t i; /* Boucle de parcours */
size_t index; /* Indice d'une ligne imprimée */
char *text; /* Contenu à ajouter */
if (keyboard) return FALSE;
result = FALSE;
format = NULL;
view = GTK_BUFFER_VIEW(widget);
/* Récupération de la destination pointée */
real_x = x;
real_y = y;
gtk_display_panel_compute_real_coord(GTK_DISPLAY_PANEL(view), &real_x, &real_y);
line = g_buffer_view_find_line_and_creator_at(view->buffer_view,
&real_x, real_y, NULL,
GTK_DISPLAY_PANEL(view)->display, &creator);
if (line == NULL || creator == NULL) goto no_tooltip;
/**
* On fait le pari de reposer uniquement sur des adresses virtuelles !
* A changer dans un futur ?
*/
virt = VMPA_NO_VIRTUAL;
if (G_IS_TARGET_OPERAND(creator))
virt = g_target_operand_get_addr(G_TARGET_OPERAND(creator));
else if (G_IS_IMM_OPERAND(creator))
{
if (!g_imm_operand_to_virt_t(G_IMM_OPERAND(creator), &virt))
virt = VMPA_NO_VIRTUAL;
}
if (virt == VMPA_NO_VIRTUAL) goto no_tooltip;
init_vmpa(&addr, VMPA_NO_PHYSICAL, virt);
/* Construction du contenu textuel */
format = G_BIN_FORMAT(g_loaded_binary_get_format(GTK_DISPLAY_PANEL(view)->binary));
if (!g_binary_format_find_symbol_at(format, &addr, &target_sym))
goto no_tooltip;
g_object_unref(G_OBJECT(target_sym));
/* Construction du contenu textuel */
/**
* Dans le cas des vues de blocs basiques, il est impératif
* de chercher les lignes dans le tampon global, et non uniquement dans
* celui propre au bloc basique courant.
*/
buffer = g_loaded_binary_get_disassembled_buffer(GTK_DISPLAY_PANEL(view)->binary);
if (g_binary_format_find_next_symbol_at(format, &addr, &next_sym))
stop_addr = get_mrange_addr(g_binary_symbol_get_range(next_sym));
else
stop_addr = NULL; /* Pour GCC */
g_generic_config_get_value(get_main_configuration(), MPK_TOOLTIP_SIZE, &count);
markup = NULL;
for (i = 0, line = g_code_buffer_find_line_by_addr(buffer, &addr, BLF_NONE, &index);
i < count && line != NULL;
i++, line = g_code_buffer_find_line_by_index(buffer, index + i))
{
/* Si on commence à marcher sur les plates-bandes du symbole suivant... */
if (next_sym != NULL)
{
lrange = g_buffer_line_get_range(line);
if (mrange_contains_addr(lrange, stop_addr))
break;
}
text = g_buffer_line_get_text(line, BLC_ASSEMBLY_HEAD, BLC_COUNT, true);
if (markup != NULL)
markup = stradd(markup, "\n");
if (text != NULL)
markup = stradd(markup, text);
free(text);
}
if (next_sym != NULL)
g_object_unref(G_OBJECT(next_sym));
if (markup == NULL) goto no_tooltip;
/* Impression finale */
result = TRUE;
gtk_tooltip_set_markup(tooltip, markup);
free(markup);
no_tooltip:
if (creator != NULL)
g_object_unref(creator);
/*
FIXME : ref() !
if (line != NULL)
g_object_unref(G_OBJECT(line));
*/
if (format != NULL)
g_object_unref(G_OBJECT(format));
return result;
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à consulter. *
* width = largeur requise à renseigner ou NULL. [OUT] *
* height = hauteur requise à renseigner ou NULL. [OUT] *
* *
* Description : Indique les dimensions de travail du composant d'affichage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_buffer_view_compute_requested_size(GtkBufferView *view, gint *width, gint *height)
{
if (width != NULL)
{
if (view->buffer_view != NULL)
*width = g_buffer_view_get_width(view->buffer_view, GTK_DISPLAY_PANEL(view)->display);
else
*width = 0;
}
if (height != NULL)
{
if (view->buffer_view != NULL)
*height = g_buffer_view_get_height(view->buffer_view);
else
*height = 0;
}
}
/******************************************************************************
* *
* Paramètres : panel = composant GTK d'affichage à mettre à jour. *
* size = taille de l'espace dans la direction donnée. *
* orientation = indication sur le défilement à traiter. *
* step = valeur d'un petit pas de défilement. [OUT] *
* page = valeur d'un grand pas de défilement. [OUT] *
* *
* Description : Détermine la taille des bonds lors de défilements. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_buffer_view_compute_scroll_inc(GtkBufferView *view, gint size, GtkOrientation orientation, gdouble *step, gdouble *page)
{
if (orientation == GTK_ORIENTATION_VERTICAL)
{
*step = 17; // FIXME g_buffer_view_get_line_height(view->buffer_view);
*page = *step * 10;
}
else
GTK_DISPLAY_PANEL_CLASS(gtk_buffer_view_parent_class)->compute_inc(GTK_DISPLAY_PANEL(view),
size, orientation, step, page);
}
/******************************************************************************
* *
* Paramètres : view = panneau d'affichage concerné. *
* adj = défilement dont une valeur a changé. *
* orientation = indication sur le défilement à traiter. *
* *
* Description : Réagit à un défilement chez une barre associée au composant. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_buffer_view_adjust_scroll_value(GtkBufferView *view, GtkAdjustment *adj, GtkOrientation orientation)
{
GtkWidget *widget; /* Autre vision du composant */
widget = GTK_WIDGET(view);
if (gtk_widget_get_realized(widget))
gdk_window_invalidate_rect(gtk_widget_get_window(widget), NULL, false);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à manipuler. *
* *
* Description : Indique la position courante du curseur. *
* *
* Retour : Emplacement courant du curseur ou NULL si aucun. *
* *
* Remarques : - *
* *
******************************************************************************/
static const vmpa2t *gtk_buffer_view_get_caret_location(const GtkBufferView *view)
{
return view->caret_addr;
}
/******************************************************************************
* *
* 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] *
* tweak = adaptation finale à effectuer. *
* *
* 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, const vmpa2t *addr, gint *x, gint *y, ScrollPositionTweak tweak)
{
bool result; /* Bilan à remonter */
bool need_code; /* Recherche plus raffinée */
int height; /* Hauteur allouée */
need_code = (tweak == SPT_BOTTOM);
result = g_buffer_view_get_address_coordinates(view->buffer_view, addr, x, y, need_code);
if (result)
{
*x += g_buffer_view_get_margin(view->buffer_view, GTK_DISPLAY_PANEL(view)->display);
height = gtk_widget_get_allocated_height(GTK_WIDGET(view));
switch (tweak)
{
case SPT_RAW:
break;
case SPT_TOP:
break;
case SPT_CENTER:
*y -= (height / 2);
break;
case SPT_BOTTOM:
*y -= height;
*y += g_buffer_view_get_line_height(view->buffer_view);
break;
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à consulter. *
* line = ligne de tampon où se trouve le curseur. [OUT] *
* creator = instance à l'origine de la représentation. [OUT] *
* *
* Description : Fournit des éléments liés à la position courante dans la vue.*
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool gtk_buffer_view_get_position(const GtkBufferView *view, GBufferLine **line, GObject **creator)
{
GObject *obj; /* Elément à récupérer */
/* Si aucune position n'est définie... */
if (view->caret_addr == NULL)
return false;
*line = g_buffer_view_find_line_and_creator_at(view->buffer_view,
(gint []){ view->caret.x }, view->caret.y, NULL,
GTK_DISPLAY_PANEL(view)->display, &obj);
if (creator != NULL)
*creator = obj;
return (line != NULL);
}
/******************************************************************************
* *
* 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. *
* *
* Description : Prend acte de l'association d'un tampon de lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_buffer_view_attach_buffer(GtkBufferView *view, GBufferView *buffer)
{
gint width; /* Largeur de l'objet actuelle */
gint height; /* Hauteur de l'objet actuelle */
if (view->buffer_view != 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;
/* 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, GTK_DISPLAY_PANEL(view)->display);
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));
}
/******************************************************************************
* *
* 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)
{
/* TODO : ref... */
return view->buffer_view;
}
/* ---------------------------------------------------------------------------------- */
/* ANIMATION DU CURSEUR */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : view = composant GTK à manipuler. *
* x = abscisse proposée pour le nouvel emplacement. *
* y = ordonnée proposée pour le nouvel emplacement. *
* *
* Description : Déplace le curseur à un emplacement défini. *
* *
* Retour : true si un traitement a été effectué, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool _gtk_buffer_view_move_caret_to(GtkBufferView *view, gint x, gint y)
{
size_t index; /* Indice de ligne de tampon */
GBufferLine *line; /* Ligne à la position courante*/
GtkDisplayPanel *panel; /* Autre version du composant */
const vmpa2t *addr; /* Position mémoire associée */
GdkRectangle new; /* Nouvel emplacement calculé */
if (x < view->left_margin) return false;
line = g_buffer_view_find_line_at(view->buffer_view, y, &index);
if (line == NULL) return false;
panel = GTK_DISPLAY_PANEL(view);
addr = g_buffer_view_compute_caret_full(view->buffer_view, line, index, x, panel->display, &new);
if (addr != NULL)
gtk_buffer_view_relocate_caret(view, &new, addr);
return (addr != NULL);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à manipuler. *
* beginning = précise le coin où se retrouvera le curseur. *
* same_x = tente de conserver une même abscisse ou NULL ? *
* *
* Description : Déplace le curseur à un emplacement en extrémité. *
* *
* Retour : true si un traitement a été effectué, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool gtk_buffer_view_move_caret_to(GtkBufferView *view, bool beginning, gint *same_x)
{
bool result; /* Bilan à remonter */
gint x; /* Abscisse d'emplacement */
gint y; /* Ordonnée d'emplacement */
if (beginning)
{
x = same_x != NULL ? *same_x : view->left_margin * 2;
y = 0;
}
else
{
if (same_x != NULL) x = *same_x;
gtk_buffer_view_compute_requested_size(view, same_x != NULL ? NULL : &x, &y);
y--;
}
result = _gtk_buffer_view_move_caret_to(view, x, y);
return result;
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à manipuler. *
* area = emplacement pour le dessin d'un curseur. *
* addr = position dans la mémoire représentée du curseur. *
* *
* Description : Déplace le curseur en effaçant son éventuelle position. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_buffer_view_relocate_caret(GtkBufferView *view, const GdkRectangle *area, const vmpa2t *addr)
{
bool clear_old; /* Effacement chirurgical */
GdkRectangle old_area; /* Mémorisation de l'ancien */
bool need_redraw; /* Besoin de rafraîchissement ?*/
if (view->caret_addr != NULL)
{
clear_old = true;
old_area = view->caret;
}
else
clear_old = false;
view->caret = *area;
view->caret_addr = addr;
if (GTK_BUFFER_VIEW_GET_CLASS(view)->notify_caret != NULL)
need_redraw = GTK_BUFFER_VIEW_GET_CLASS(view)->notify_caret(view, area, addr);
else
need_redraw = false;
if (need_redraw)
gtk_widget_queue_draw(GTK_WIDGET(view));
else if (clear_old)
{
gtk_display_panel_compute_relative_coords(GTK_DISPLAY_PANEL(view), &old_area.x, &old_area.y);
gtk_widget_queue_draw_area(GTK_WIDGET(view), old_area.x, old_area.y,
old_area.width, old_area.height);
}
restart_caret_blinking(view);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à manipuler. *
* *
* Description : Redémarre l'affichage du curseur à l'emplacement courant. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void restart_caret_blinking(GtkBufferView *view)
{
if (view->caret_timer != 0)
{
g_source_remove(view->caret_timer);
view->caret_timer = 0;
}
if (view->caret_addr != NULL)
{
view->show_caret = false;
gtk_buffer_view_refresh_caret(view);
view->caret_timer = g_timeout_add_seconds(1, (GSourceFunc)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 */
GdkWindow *window; /* Fenêtre de support associée */
GdkRectangle area; /* Région adaptée à traiter */
cairo_t *cr; /* Contexte graphique */
GdkRGBA *color; /* Couleur du curseur */
widget = GTK_WIDGET(view);
window = gtk_widget_get_window(widget);
/**
* Si le composant n'est pas encore réalisé (ou caché, en cas de
* basculement entre les types de vues), gdk_cairo_create() ne va
* pas apprécier l'argument NULL. Donc on écourte l'opération.
*/
if (window == NULL)
{
view->show_caret = !view->show_caret;
return TRUE;
}
area = view->caret;
gtk_display_panel_compute_relative_coords(GTK_DISPLAY_PANEL(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;
}