/* 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; }