From ff1ce15f6c4b3516d7a34b09dd99abb32a0bd671 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 21 Mar 2019 00:04:32 +0100
Subject: Introduced zoom in graph view.

---
 configure.ac                     |   2 +-
 src/glibext/gbufferview.c        |   5 +-
 src/glibext/gbufferview.h        |   2 +-
 src/gtkext/gtkbufferdisplay.c    |  18 ++++-
 src/gtkext/gtkdisplaypanel-int.h |  13 +++-
 src/gtkext/gtkdisplaypanel.c     | 137 +++++++++++++++++++++++++++++++++------
 src/gtkext/gtkdisplaypanel.h     |   6 ++
 src/gtkext/gtkgraphdisplay.c     | 129 ++++++++++++++++++++++++++++++++----
 src/gui/menus/menubar.c          |   2 +-
 src/gui/menus/view.c             |  93 +++++++++++++++++++++++++-
 src/gui/menus/view.h             |   2 +-
 11 files changed, 361 insertions(+), 48 deletions(-)

diff --git a/configure.ac b/configure.ac
index 1f50fd0..a2d8b5e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -365,7 +365,7 @@ AC_SUBST(LIBPYGOBJECT_LIBS)
 
 AC_CONFIG_FILES([stamp-h po/Makefile.in], [echo timestamp > stamp-h])
 
-AC_CONFIG_COMMANDS([marshal], [echo -e "VOID:UINT64\nVOID:INT,UINT64,INT\nVOID:OBJECT,OBJECT\nVOID:ENUM,OBJECT\nVOID:ENUM,ENUM\nVOID:BOOLEAN,UINT64\nVOID:BOOLEAN,ULONG,ULONG\nVOID:INT,INT\nVOID:OBJECT,BOOLEAN\nVOID:ULONG,BOOLEAN" > src/glibext/chrysamarshal.list])
+AC_CONFIG_COMMANDS([marshal], [echo -e "VOID:UINT64\nVOID:INT,UINT64,INT\nVOID:OBJECT,OBJECT\nVOID:ENUM,OBJECT\nVOID:ENUM,ENUM\nVOID:BOOLEAN,UINT64\nVOID:BOOLEAN,ULONG,ULONG\nVOID:INT,INT\nVOID:OBJECT,BOOLEAN\nVOID:ULONG,BOOLEAN\nVOID:DOUBLE,DOUBLE" > src/glibext/chrysamarshal.list])
 
 AC_CONFIG_FILES([Makefile
                  doc/Makefile
diff --git a/src/glibext/gbufferview.c b/src/glibext/gbufferview.c
index c5021f2..db8da28 100644
--- a/src/glibext/gbufferview.c
+++ b/src/glibext/gbufferview.c
@@ -1100,6 +1100,7 @@ bool g_buffer_view_highlight_segments(GBufferView *view, gint x, gint y, const G
 *                options  = règles d'affichage des colonnes modulables.       *
 *                offsets  = décalages supplémentaires à appliquer.            *
 *                selected = ordonnée d'une ligne sélectionnée ou NULL.        *
+*                scale    = échelle appliquée à la surface de rendu.          *
 *                export   = indique si la vue est en cours d'exportation.     *
 *                                                                             *
 *  Description : Imprime la visualisation du tampon de lignes quelconques.    *
@@ -1110,7 +1111,7 @@ bool g_buffer_view_highlight_segments(GBufferView *view, gint x, gint y, const G
 *                                                                             *
 ******************************************************************************/
 
-void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint virt_y, const cairo_rectangle_int_t *area, const GDisplayOptions *options, const line_width_summary *offsets, gint *selected, bool export)
+void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint virt_y, const cairo_rectangle_int_t *area, const GDisplayOptions *options, const line_width_summary *offsets, gint *selected, double scale, bool export)
 {
     gint line_height;                       /* Hauteur d'une ligne         */
     gint cr_y;                              /* Ordonnée pour le dessin     */
@@ -1118,7 +1119,7 @@ void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint virt_y, const
     size_t last;                            /* Dernière ligne visée        */
     segcnt_list *highlighted;               /* Segments mis en évidence    */
 
-    line_height = g_buffer_cache_get_line_height(view->cache);
+    line_height = g_buffer_cache_get_line_height(view->cache) * scale;
 
     /* Indice et point de départ */
 
diff --git a/src/glibext/gbufferview.h b/src/glibext/gbufferview.h
index 9d40cbd..c6cc5aa 100644
--- a/src/glibext/gbufferview.h
+++ b/src/glibext/gbufferview.h
@@ -98,7 +98,7 @@ bool g_buffer_view_unhighlight_segments(GBufferView *);
 bool g_buffer_view_highlight_segments(GBufferView *, gint, gint, const GDisplayOptions *, const line_width_summary *);
 
 /* Imprime la visualisation du tampon de lignes quelconques. */
-void g_buffer_view_draw(const GBufferView *, cairo_t *, gint, const cairo_rectangle_int_t *, const GDisplayOptions *, const line_width_summary *, gint *, bool);
+void g_buffer_view_draw(const GBufferView *, cairo_t *, gint, const cairo_rectangle_int_t *, const GDisplayOptions *, const line_width_summary *, gint *, double, bool);
 
 
 
diff --git a/src/gtkext/gtkbufferdisplay.c b/src/gtkext/gtkbufferdisplay.c
index b9784de..b7af1d6 100644
--- a/src/gtkext/gtkbufferdisplay.c
+++ b/src/gtkext/gtkbufferdisplay.c
@@ -423,8 +423,14 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr)
 
     if (parent->show_border)
     {
-        gtk_display_panel_define_border_path(parent, cr, 0, 0);
+        gtk_widget_get_allocation(widget, &allocation);
+
+        allocation.x = 0;
+        allocation.y = 0;
+
+        gtk_display_panel_define_border_path(parent, cr, &allocation);
         cairo_clip(cr);
+
     }
 
     /* Décalage pour le défilement horizontal */
@@ -441,7 +447,7 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr)
 
     cache = g_buffer_view_get_cache(display->view);
 
-    left_margin = g_buffer_cache_get_left_margin(cache);
+    left_margin = g_buffer_cache_get_left_margin(cache) * parent->scale;
 
     g_object_unref(G_OBJECT(cache));
 
@@ -494,6 +500,10 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr)
 
     /* Impression du désassemblage */
 
+    cairo_save(cr);
+
+    cairo_scale(cr, parent->scale, parent->scale);
+
     if (display->view != NULL)
     {
         g_generic_config_get_value(get_main_configuration(), MPK_SELECTION_LINE, &sel_line);
@@ -511,7 +521,7 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr)
         virt_y += area.y;
 
         g_buffer_view_draw(display->view, cr, virt_y, &area, parent->options, &display->offsets,
-                           selected, parent->export);
+                           selected, parent->scale, parent->export);
 
     }
 
@@ -528,6 +538,8 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr)
 
     GTK_WIDGET_CLASS(gtk_buffer_display_parent_class)->draw(widget, cr);
 
+    cairo_restore(cr);
+
     return FALSE;
 
 }
diff --git a/src/gtkext/gtkdisplaypanel-int.h b/src/gtkext/gtkdisplaypanel-int.h
index 7be5616..17fd6f0 100644
--- a/src/gtkext/gtkdisplaypanel-int.h
+++ b/src/gtkext/gtkdisplaypanel-int.h
@@ -71,6 +71,9 @@ typedef GLineCursor * (* get_cursor_fc) (const GtkDisplayPanel *);
 /* Place en cache un rendu destiné à l'aperçu graphique rapide. */
 typedef void (* cache_glance_fc) (GtkDisplayPanel *, cairo_t *, const GtkAllocation *, double);
 
+/* Spécifie l'échelle à appliquer à l'affichage du composant. */
+typedef void (* apply_scale_fc) (GtkDisplayPanel *, double, double);
+
 /* Marque ou non le composant pour une exportation prochaine. */
 typedef void (* prepare_export_fc) (GtkDisplayPanel *, bool);
 
@@ -86,6 +89,8 @@ struct _GtkDisplayPanel
     GtkScrollablePolicy hscroll_policy;     /* Politique horizontale       */           
     GtkScrollablePolicy vscroll_policy;     /* Politique verticale         */           
 
+    double scale;                           /* Echelle de l'affichage      */
+
     bool show_border;                       /* Affichage d'une bordure ?   */
     unsigned int view_index;                /* Indice du type de contenu   */
     GDisplayOptions *options;               /* Affichage des colonnes ?    */
@@ -112,8 +117,14 @@ struct _GtkDisplayPanelClass
     get_cursor_fc get_cursor;               /* Fourniture d'une position   */
     cache_glance_fc cache_glance;           /* Cache de la mignature       */
 
+    apply_scale_fc scale;                   /* Mise à jour de l'échelle    */
+
     prepare_export_fc prepare_export;       /* Préparation d'exportation   */
 
+    /* Signaux */
+
+    void (* scaled) (GtkDisplayPanel *, double, double);
+
 };
 
 /* Propriétés propres au composant d'affichage */
@@ -129,7 +140,7 @@ typedef enum _ViewPanelProps
 
 
 /* Définit un chemin décrivant la bordure autour du panneau. */
-void gtk_display_panel_define_border_path(GtkDisplayPanel *, cairo_t *, gint, gint);
+void gtk_display_panel_define_border_path(GtkDisplayPanel *, cairo_t *, const GtkAllocation *);
 
 /* Dessine si besoin est une bordure autour du composant. */
 void gtk_display_panel_draw_border(GtkDisplayPanel *, cairo_t *);
diff --git a/src/gtkext/gtkdisplaypanel.c b/src/gtkext/gtkdisplaypanel.c
index b32adfa..76c18ec 100644
--- a/src/gtkext/gtkdisplaypanel.c
+++ b/src/gtkext/gtkdisplaypanel.c
@@ -28,6 +28,7 @@
 
 
 #include "gtkdisplaypanel-int.h"
+#include "../glibext/chrysamarshal.h"
 #include "../glibext/gbinarycursor.h"    // REMME
 #include "../glibext/gloadedpanel-int.h"
 
@@ -161,6 +162,16 @@ static void gtk_display_panel_class_init(GtkDisplayPanelClass *class)
 
     panel->compute_inc = gtk_display_panel_compute_scroll_inc;
 
+    /* Signaux */
+
+    g_signal_new("scaled",
+                 GTK_TYPE_DISPLAY_PANEL,
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET(GtkDisplayPanelClass, scaled),
+                 NULL, NULL,
+                 g_cclosure_user_marshal_VOID__DOUBLE_DOUBLE,
+                 G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
 }
 
 
@@ -181,6 +192,8 @@ static void gtk_display_panel_init(GtkDisplayPanel *panel)
     gtk_widget_set_has_window(GTK_WIDGET(panel), TRUE);
     gtk_widget_set_can_focus(GTK_WIDGET(panel), TRUE);
 
+    panel->scale = 1.0;
+
     panel->export = false;
 
 }
@@ -460,9 +473,14 @@ static void gtk_display_panel_size_allocate(GtkWidget *widget, GtkAllocation *al
 
 static void gtk_display_panel_get_preferred_height(GtkWidget *widget, gint *minimum, gint *natural)
 {
+    GtkDisplayPanel *panel;                 /* Autre version du composant  */
     gint req;                               /* Dimension requise           */
 
-    GTK_DISPLAY_PANEL_GET_CLASS(widget)->compute_size(GTK_DISPLAY_PANEL(widget), NULL, &req);
+    panel = GTK_DISPLAY_PANEL(widget);
+
+    GTK_DISPLAY_PANEL_GET_CLASS(widget)->compute_size(panel, NULL, &req);
+
+    req *= panel->scale;
 
     if (minimum != NULL) *minimum = req;
     if (natural != NULL) *natural = req;
@@ -486,9 +504,14 @@ static void gtk_display_panel_get_preferred_height(GtkWidget *widget, gint *mini
 
 static void gtk_display_panel_get_preferred_width(GtkWidget *widget, gint *minimum, gint *natural)
 {
+    GtkDisplayPanel *panel;                 /* Autre version du composant  */
     gint req;                               /* Dimension requise           */
 
-    GTK_DISPLAY_PANEL_GET_CLASS(widget)->compute_size(GTK_DISPLAY_PANEL(widget), &req, NULL);
+    panel = GTK_DISPLAY_PANEL(widget);
+
+    GTK_DISPLAY_PANEL_GET_CLASS(widget)->compute_size(panel, &req, NULL);
+
+    req *= panel->scale;
 
     if (minimum != NULL) *minimum = req;
     if (natural != NULL) *natural = req;
@@ -687,7 +710,7 @@ static void gtk_display_panel_update_adjustment(GtkDisplayPanel *panel, GtkOrien
     {
         adj = panel->hadjustment;
 
-        GTK_DISPLAY_PANEL_GET_CLASS(panel)->compute_size(panel, &req, NULL);
+        gtk_widget_get_preferred_width(GTK_WIDGET(panel), &req, NULL);
         allocated = allocation.width;
 
     }
@@ -695,7 +718,7 @@ static void gtk_display_panel_update_adjustment(GtkDisplayPanel *panel, GtkOrien
     {
         adj = panel->vadjustment;
 
-        GTK_DISPLAY_PANEL_GET_CLASS(panel)->compute_size(panel, NULL, &req);
+        gtk_widget_get_preferred_height(GTK_WIDGET(panel), &req, NULL);
         allocated = allocation.height;
 
     }
@@ -739,6 +762,73 @@ static void gtk_display_panel_adjustment_value_changed(GtkAdjustment *adj, GtkDi
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : panel = composant GTK à mettre à jour.                       *
+*                                                                             *
+*  Description : Indique l'échelle appliquée à l'affichage du composant.      *
+*                                                                             *
+*  Retour      : Echelle appliquée à l'affichage.                             *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+double gtk_display_panel_get_scale(const GtkDisplayPanel *panel)
+{
+    double result;                          /* Echelle à retourner         */
+
+    result = panel->scale;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = composant GTK à mettre à jour.                       *
+*                scale = échelle appliquée à l'affichage.                     *
+*                                                                             *
+*  Description : Spécifie l'échelle à appliquer à l'affichage du composant.   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void gtk_display_panel_set_scale(GtkDisplayPanel *panel, double scale)
+{
+    double old_scale;                       /* Echelle précédente          */
+    GtkDisplayPanelClass *class;            /* Classe associée au composant*/
+
+    if (scale > 1.0)
+        scale = 1.0;
+
+    else if (scale < 0.01)
+        scale = 0.01;
+
+    if (panel->scale != scale)
+    {
+        old_scale = panel->scale;
+
+        panel->scale = scale;
+
+        class = GTK_DISPLAY_PANEL_GET_CLASS(panel);
+
+        if (class->scale != NULL)
+            class->scale(panel, old_scale, scale);
+
+        gtk_widget_queue_resize(GTK_WIDGET(panel));
+
+        g_signal_emit_by_name(panel, "scaled", old_scale, scale);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = composant GTK à mettre à jour.                       *
 *                show  = état de l'affichage auquel parvenir.                 *
 *                                                                             *
 *  Description : Définit si une bordure est à afficher.                       *
@@ -785,9 +875,9 @@ void gtk_display_panel_prepare_export(GtkDisplayPanel *panel, bool export)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : panel  = composant GTK à venir consulter.                    *
-*                cr     = contexte graphique associé à l'événement.           *
-*                offset = décalage éventuel à appliquer.                      *
+*  Paramètres  : panel = composant GTK à venir consulter.                     *
+*                cr    = contexte graphique associé à l'événement.            *
+*                area  = surface à considérer.                                *
 *                                                                             *
 *  Description : Définit un chemin décrivant la bordure autour du panneau.    *
 *                                                                             *
@@ -797,35 +887,32 @@ void gtk_display_panel_prepare_export(GtkDisplayPanel *panel, bool export)
 *                                                                             *
 ******************************************************************************/
 
-void gtk_display_panel_define_border_path(GtkDisplayPanel *panel, cairo_t *cr, gint off_x, gint off_y)
+void gtk_display_panel_define_border_path(GtkDisplayPanel *panel, cairo_t *cr, const GtkAllocation *area)
 {
-    GtkRequisition req;                     /* Taille allouée à l'élément  */
     double degrees;                         /* Conversion en degrés        */
 
-    gtk_widget_get_preferred_size(GTK_WIDGET(panel), NULL, &req);
-
     degrees = M_PI / 180.0;
 
     cairo_new_sub_path(cr);
 
     cairo_arc(cr,
-              off_x + req.width - BORDER_CORNER_RADIUS - 0.5,
-              off_y + BORDER_CORNER_RADIUS + 0.5,
+              area->x + area->width - BORDER_CORNER_RADIUS - 0.5,
+              area->y + BORDER_CORNER_RADIUS + 0.5,
               BORDER_CORNER_RADIUS, -90 * degrees, 0 * degrees);
 
     cairo_arc(cr,
-              off_x + req.width - BORDER_CORNER_RADIUS - 0.5,
-              off_y + req.height - BORDER_CORNER_RADIUS - 0.5,
+              area->x + area->width - BORDER_CORNER_RADIUS - 0.5,
+              area->y + area->height - BORDER_CORNER_RADIUS - 0.5,
               BORDER_CORNER_RADIUS, 0 * degrees, 90 * degrees);
 
     cairo_arc(cr,
-              off_x + BORDER_CORNER_RADIUS + 0.5,
-              off_y + req.height - BORDER_CORNER_RADIUS - 0.5,
+              area->x + BORDER_CORNER_RADIUS + 0.5,
+              area->y + area->height - BORDER_CORNER_RADIUS - 0.5,
               BORDER_CORNER_RADIUS, 90 * degrees, 180 * degrees);
 
     cairo_arc(cr,
-              off_x + BORDER_CORNER_RADIUS + 0.5,
-              off_y + BORDER_CORNER_RADIUS + 0.5,
+              area->x + BORDER_CORNER_RADIUS + 0.5,
+              area->y + BORDER_CORNER_RADIUS + 0.5,
               BORDER_CORNER_RADIUS, 180 * degrees, 270 * degrees);
 
     cairo_close_path(cr);
@@ -849,9 +936,10 @@ void gtk_display_panel_define_border_path(GtkDisplayPanel *panel, cairo_t *cr, g
 void gtk_display_panel_draw_border(GtkDisplayPanel *panel, cairo_t *cr)
 {
     GtkWidget *widget;                      /* Autre version du composant  */
-    GtkRequisition req;                     /* Taille allouée à l'élément  */
     GtkStyleContext *context;               /* Contexte du thème actuel    */
     GdkRGBA color;                          /* Couleur de thème récupérée  */
+    GtkRequisition req;                     /* Taille allouée à l'élément  */
+    GtkAllocation area;                     /* Emplacement à considérer    */
 
     if (panel->show_border)
     {
@@ -873,7 +961,14 @@ void gtk_display_panel_draw_border(GtkDisplayPanel *panel, cairo_t *cr)
 
         cairo_set_line_width(cr, 1.0);
 
-        gtk_display_panel_define_border_path(panel, cr, 0, 0);
+        gtk_widget_get_preferred_size(GTK_WIDGET(panel), NULL, &req);
+
+        area.x = 0;
+        area.y = 0;
+        area.width = req.width;
+        area.height = req.height;
+
+        gtk_display_panel_define_border_path(panel, cr, &area);
         cairo_stroke(cr);
 
         gtk_style_context_restore(context);
diff --git a/src/gtkext/gtkdisplaypanel.h b/src/gtkext/gtkdisplaypanel.h
index 937d41d..9f58b87 100644
--- a/src/gtkext/gtkdisplaypanel.h
+++ b/src/gtkext/gtkdisplaypanel.h
@@ -52,6 +52,12 @@ typedef struct _GtkDisplayPanelClass GtkDisplayPanelClass;
 /* Détermine le type du composant d'affichage générique. */
 GType gtk_display_panel_get_type(void);
 
+/* Indique l'échelle appliquée à l'affichage du composant. */
+double gtk_display_panel_get_scale(const GtkDisplayPanel *);
+
+/* Spécifie l'échelle à appliquer à l'affichage du composant. */
+void gtk_display_panel_set_scale(GtkDisplayPanel *, double);
+
 /* Définit si une bordure est à afficher. */
 void gtk_display_panel_show_border(GtkDisplayPanel *, bool);
 
diff --git a/src/gtkext/gtkgraphdisplay.c b/src/gtkext/gtkgraphdisplay.c
index 97bb4d3..aeecc0f 100644
--- a/src/gtkext/gtkgraphdisplay.c
+++ b/src/gtkext/gtkgraphdisplay.c
@@ -146,6 +146,9 @@ static GLineCursor *gtk_graph_display_get_cursor(const GtkGraphDisplay *);
 /* Place en cache un rendu destiné à l'aperçu graphique rapide. */
 static void gtk_graph_display_cache_glance(GtkGraphDisplay *, cairo_t *, const GtkAllocation *, double);
 
+/* Spécifie l'échelle à appliquer à l'affichage du composant. */
+static void gtk_graph_display_apply_scale(GtkGraphDisplay *, double, double);
+
 /* Marque ou non le composant pour une exportation prochaine. */
 static void gtk_graph_display_prepare_export(GtkGraphDisplay *, bool);
 
@@ -205,6 +208,8 @@ static void gtk_graph_display_class_init(GtkGraphDisplayClass *class)
     panel_class->get_cursor = (get_cursor_fc)gtk_graph_display_get_cursor;
     panel_class->cache_glance = (cache_glance_fc)gtk_graph_display_cache_glance;
 
+    panel_class->scale = (apply_scale_fc)gtk_graph_display_apply_scale;
+
     panel_class->prepare_export = (prepare_export_fc)gtk_graph_display_prepare_export;
 
 }
@@ -470,16 +475,23 @@ static void gtk_graph_display_adjust_scroll_value(GtkGraphDisplay *display, GtkA
 
 static gboolean gtk_graph_display_draw(GtkWidget *widget, cairo_t *cr, GtkGraphDisplay *display)
 {
+    GtkDisplayPanel *parent;                /* Autre version du composant  */
     cairo_surface_t *pat_image;             /* Fond du futur pinceau       */
     cairo_t *pat_cr;                        /* Pinceau pour le pinceau     */
     cairo_pattern_t *pattern;               /* Patron de remplissage       */
     double degrees;                         /* Conversion en degrés        */
     size_t i;                               /* Boucle de parcours          */
 
+    parent = GTK_DISPLAY_PANEL(display);
+
     /* Eventuel fond pour la zone de compression */
 
     if (display->may_collapsing && !GTK_DISPLAY_PANEL(display)->export)
     {
+        cairo_save(cr);
+
+        cairo_scale(cr, parent->scale, parent->scale);
+
         /* Préparation du pinceau */
 
         pat_image = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
@@ -555,13 +567,21 @@ static gboolean gtk_graph_display_draw(GtkWidget *widget, cairo_t *cr, GtkGraphD
 
         cairo_surface_destroy(pat_image);
 
+        cairo_restore(cr);
+
     }
 
     /* Dessin des ombres */
 
+    cairo_save(cr);
+
+    cairo_scale(cr, parent->scale, parent->scale);
+
     void draw_shadow(GtkWidget *child, gpointer unused)
     {
+        GGraphCluster *cluster;             /* Cluster correspondant       */
         GtkAllocation alloc;                /* Emplacement de l'enfant     */
+        GtkAllocation area;                 /* Emplacement à considérer    */
         gint j;                             /* Boucle de parcours          */
         cairo_pattern_t *pattern;           /* Zones d'application         */
 
@@ -569,19 +589,30 @@ static gboolean gtk_graph_display_draw(GtkWidget *widget, cairo_t *cr, GtkGraphD
         if (!GTK_IS_DISPLAY_PANEL(child))
             return;
 
-        gtk_widget_get_allocation(child, &alloc);
+        cluster = g_graph_cluster_find_by_widget(display->cluster, child);
+        assert(cluster != NULL);
+
+        g_graph_cluster_get_allocation(cluster, &alloc);
+
+        alloc.x += GRAPH_MARGIN;
+        alloc.y += GRAPH_MARGIN;
+
+        area = alloc;
 
         for (j = 1; j < SHADOW_SIZE; j++)
         {
             cairo_push_group(cr);
 
-            gtk_display_panel_define_border_path(GTK_DISPLAY_PANEL(child), cr, alloc.x + j, alloc.y + j);
+            area.x = alloc.x + j;
+            area.y = alloc.y + j;
+
+            gtk_display_panel_define_border_path(GTK_DISPLAY_PANEL(child), cr, &area);
             cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
             cairo_fill(cr);
 
             cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
 
-            gtk_display_panel_define_border_path(GTK_DISPLAY_PANEL(child), cr, alloc.x, alloc.y);
+            gtk_display_panel_define_border_path(GTK_DISPLAY_PANEL(child), cr, &alloc);
             cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
             cairo_fill(cr);
 
@@ -602,6 +633,8 @@ static gboolean gtk_graph_display_draw(GtkWidget *widget, cairo_t *cr, GtkGraphD
     for (i = 0; i < display->edges_count; i++)
         g_graph_edge_draw(display->edges[i], cr, true, display->hl_edge_index == i);
 
+    cairo_restore(cr);
+
     return FALSE;
 
 }
@@ -710,6 +743,7 @@ static gboolean gtk_graph_display_motion_notify(GtkWidget *widget, GdkEventMotio
     GtkAdjustment *hadj;                    /* Gestionnaire du défilement  */
     GtkAdjustment *vadj;                    /* Gestionnaire du défilement  */
     gdouble value;                          /* Nouvelle valeur bornée      */
+    double scale;                           /* Echelle appliquée au rendu  */
     size_t i;                               /* Boucle de parcours          */
 
     /* Déplacement du graphique ? */
@@ -739,8 +773,10 @@ static gboolean gtk_graph_display_motion_notify(GtkWidget *widget, GdkEventMotio
     /* Survol d'un lien ? */
     else
     {
+        scale = GTK_DISPLAY_PANEL(display)->scale;
+
         for (i = 0; i < display->edges_count; i++)
-            if (g_graph_edge_detect_at(display->edges[i], event->x, event->y))
+            if (g_graph_edge_detect_at(display->edges[i], event->x / scale, event->y / scale))
             {
                 display->hl_edge_index = i;
                 break;
@@ -1131,21 +1167,28 @@ static GLineCursor *gtk_graph_display_get_cursor(const GtkGraphDisplay *display)
 
 static void gtk_graph_display_cache_glance(GtkGraphDisplay *display, cairo_t *cr, const GtkAllocation *area, double scale)
 {
+    GtkDisplayPanel *parent;                /* Autre version du composant  */
     size_t i;                               /* Boucle de parcours          */
 
+    parent = GTK_DISPLAY_PANEL(display);
+
+    cairo_scale(cr, scale * parent->scale, scale * parent->scale);
+
     void draw_child_glance(GtkWidget *child, gpointer unused)
     {
+        GGraphCluster *cluster;             /* Cluster correspondant       */
         GtkAllocation sub_area;             /* Emplacement réservé         */
 
         if (!GTK_IS_BUFFER_DISPLAY(child))
             return;
 
-        gtk_widget_get_allocation(child, &sub_area);
+        cluster = g_graph_cluster_find_by_widget(display->cluster, child);
+        assert(cluster != NULL);
 
-        sub_area.x *= scale;
-        sub_area.y *= scale;
-        sub_area.width = sub_area.width * scale + 1;
-        sub_area.height = sub_area.height * scale + 1;
+        g_graph_cluster_get_allocation(cluster, &sub_area);
+
+        sub_area.x += GRAPH_MARGIN;
+        sub_area.y += GRAPH_MARGIN;
 
         g_loaded_panel_cache_glance(G_LOADED_PANEL(child), cr, &sub_area, scale);
 
@@ -1153,8 +1196,6 @@ static void gtk_graph_display_cache_glance(GtkGraphDisplay *display, cairo_t *cr
 
     gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)draw_child_glance, NULL);
 
-    cairo_scale(cr, scale, scale);
-
     for (i = 0; i < display->edges_count; i++)
         g_graph_edge_draw(display->edges[i], cr, false, false);
 
@@ -1164,6 +1205,68 @@ static void gtk_graph_display_cache_glance(GtkGraphDisplay *display, cairo_t *cr
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : display = composant GTK à mettre à jour.                     *
+*                old     = ancienne échelle appliquée.                        *
+*                new     = nouvelle échelle à appliquer.                      *
+*                                                                             *
+*  Description : Spécifie l'échelle à appliquer à l'affichage du composant.   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void gtk_graph_display_apply_scale(GtkGraphDisplay *display, double old, double new)
+{
+    GtkDisplayPanel *parent;                /* Autre version du composant  */
+    gint right;                             /* Abscisse du coin droit      */
+    gint bottom;                            /* Ordonnée du coin inférieur  */
+
+    /* Traitement des blocs */
+
+    void apply_child_scale(GtkWidget *child, gpointer unused)
+    {
+        GGraphCluster *cluster;             /* Cluster correspondant       */
+        GtkAllocation sub_area;             /* Emplacement réservé         */
+        gint x;                             /* Abscisse du point d'arrivée */
+        gint y;                             /* Ordonnée du point d'arrivée */
+
+        if (!GTK_IS_BUFFER_DISPLAY(child))
+            return;
+
+        cluster = g_graph_cluster_find_by_widget(display->cluster, child);
+        assert(cluster != NULL);
+
+        g_graph_cluster_get_allocation(cluster, &sub_area);
+
+        x = (GRAPH_MARGIN + sub_area.x) * new;
+        y = (GRAPH_MARGIN + sub_area.y) * new;
+
+        gtk_fixed_move(GTK_FIXED(display->support), child, x, y);
+
+        gtk_display_panel_set_scale(GTK_DISPLAY_PANEL(child), new);
+
+    }
+
+    gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)apply_child_scale, NULL);
+
+    /* Calcul du nouvel espace nécessaire */
+
+    parent = GTK_DISPLAY_PANEL(display);
+
+    gtk_graph_display_compute_requested_size(display, &right, &bottom);
+
+    right *= parent->scale;
+    bottom *= parent->scale;
+
+    gtk_fixed_move(GTK_FIXED(display->support), display->extender, right - 1, bottom - 1);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : display = composant GTK à mettre à jour.                     *
 *                export  = préparation d'une exportation complète du rendu ?  *
 *                                                                             *
 *  Description : Marque ou non le composant pour une exportation prochaine.   *
@@ -1238,8 +1341,7 @@ GtkWidget *gtk_graph_display_get_support(GtkGraphDisplay *display)
 *                                                                             *
 *  Paramètres  : display = composant GTK à mettre à jour.                     *
 *                widget  = composant GTK à insérer.                           *
-*                x       = abscisse du point d'insertion.                     *
-*                y       = ordonnée du point d'insertion.                     *
+*                alloc   = position du point d'insertion.                     *
 *                                                                             *
 *  Description : Place une vue sous forme de bloc dans le graphique.          *
 *                                                                             *
@@ -1261,7 +1363,6 @@ void gtk_graph_display_put(GtkGraphDisplay *display, GtkWidget *widget, const Gt
 }
 
 
-
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : display = composant GTK à mettre à jour.                     *
diff --git a/src/gui/menus/menubar.c b/src/gui/menus/menubar.c
index aa89a13..d386362 100644
--- a/src/gui/menus/menubar.c
+++ b/src/gui/menus/menubar.c
@@ -309,7 +309,7 @@ static void change_menubar_current_view(GMenuBar *bar, GLoadedPanel *old, GLoade
 
     rebuild_menu_view_for_view(bar->view, new);
 
-    update_access_for_view_in_menu_view(G_EDITOR_ITEM(bar)->ref, new);
+    update_access_for_view_in_menu_view(new);
 
     update_access_for_view_in_menu_binary(new);
 
diff --git a/src/gui/menus/view.c b/src/gui/menus/view.c
index 255f1d9..018cfbf 100644
--- a/src/gui/menus/view.c
+++ b/src/gui/menus/view.c
@@ -40,6 +40,7 @@
 #include "../core/panels.h"
 #include "../../analysis/loaded.h"
 #include "../../gtkext/easygtk.h"
+#include "../../gtkext/gtkdisplaypanel.h"
 #include "../../gtkext/gtkgraphdisplay.h"
 
 
@@ -65,6 +66,9 @@ static void handle_loaded_panel_first_allocation(GtkWidget *, GdkRectangle *, GL
 /* Effectue la bascule d'un panneau de chargement à un autre. */
 static void change_current_view_support(unsigned int);
 
+/* Réagit avec le menu "Affichage -> Zoom *". */
+static void mcb_view_zoom(GtkCheckMenuItem *, gpointer );
+
 /* Réagit avec le menu "Affichage -> (colonne xxx)". */
 static void mcb_view_display_column(GtkCheckMenuItem *, gpointer);
 
@@ -126,6 +130,28 @@ GtkWidget *build_menu_view(GObject *ref, GMenuBar *bar)
     /* Séparation */
 
     submenuitem = qck_create_menu_separator();
+    gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
+
+    /* Zooms */
+
+    submenuitem = qck_create_menu_item(ref, "mnu_view_zoom_in", _("Zoom in"),
+                                       G_CALLBACK(mcb_view_zoom), GINT_TO_POINTER(0));
+    add_accelerator_to_widget(submenuitem, "<Ctrl>plus");
+    gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
+
+    submenuitem = qck_create_menu_item(ref, "mnu_view_zoom_out", _("Zoom out"),
+                                       G_CALLBACK(mcb_view_zoom), GINT_TO_POINTER(1));
+    add_accelerator_to_widget(submenuitem, "<Ctrl>minus");
+    gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
+
+    submenuitem = qck_create_menu_item(ref, "mnu_view_zoom_reset", _("Reset zoom"),
+                                       G_CALLBACK(mcb_view_zoom), GINT_TO_POINTER(2));
+    add_accelerator_to_widget(submenuitem, "<Ctrl>0");
+    gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
+
+    /* Séparation */
+
+    submenuitem = qck_create_menu_separator();
     g_object_set_data(ref, "mnu_view_start_options", submenuitem);
     gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
 
@@ -399,8 +425,7 @@ void rebuild_menu_view_for_view(GtkWidget *widget, GLoadedPanel *new)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : ref   = espace de référencements à consulter.                *
-*                panel = panneau d'affichage actif ou NULL si aucun.          *
+*  Paramètres  : new = nouvelle vue du contenu chargé analysé.                *
 *                                                                             *
 *  Description : Met à jour les accès du menu "Affichage" selon le contenu.   *
 *                                                                             *
@@ -410,12 +435,31 @@ void rebuild_menu_view_for_view(GtkWidget *widget, GLoadedPanel *new)
 *                                                                             *
 ******************************************************************************/
 
-void update_access_for_view_in_menu_view(GObject *ref, GLoadedPanel *panel)
+void update_access_for_view_in_menu_view(GLoadedPanel *new)
 {
+    GObject *ref;                           /* Espace de référencements    */
+    gboolean access;                        /* Accès à déterminer          */
+    GtkWidget *item;                        /* Elément de menu à traiter   */
+
+    ref = get_global_ref();
+
     /* Bascules */
 
     update_switch_access_in_menu_view();
 
+    /* Zooms */
+
+    access = GTK_IS_GRAPH_DISPLAY(new);
+
+    item = GTK_WIDGET(g_object_get_data(ref, "mnu_view_zoom_in"));
+    gtk_widget_set_sensitive(item, access);
+
+    item = GTK_WIDGET(g_object_get_data(ref, "mnu_view_zoom_out"));
+    gtk_widget_set_sensitive(item, access);
+
+    item = GTK_WIDGET(g_object_get_data(ref, "mnu_view_zoom_reset"));
+    gtk_widget_set_sensitive(item, access);
+
 }
 
 
@@ -843,6 +887,49 @@ static void change_current_view_support(unsigned int wanted)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : menuitem = élément de menu sélectionné.                      *
+*                data     = données indiquant la nature du zoom.              *
+*                                                                             *
+*  Description : Réagit avec le menu "Affichage -> Zoom *".                   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void mcb_view_zoom(GtkCheckMenuItem *menuitem, gpointer data)
+{
+    GtkDisplayPanel *panel;                 /* Afficheur effectif de code  */
+    double scale;                           /* Echelle à appliquer         */
+
+    panel = GTK_DISPLAY_PANEL(get_current_view());
+
+    scale = gtk_display_panel_get_scale(panel);
+
+    switch (GPOINTER_TO_INT(data))
+    {
+        case 0:
+            scale /= 1.25;
+            break;
+
+        case 1:
+            scale *= 1.25;
+            break;
+
+        case 2:
+            scale = 1.0;
+            break;
+
+    }
+
+    gtk_display_panel_set_scale(panel, scale);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : menuitem = élément de menu ayant basculé.                    *
 *                unused   = adresse non utilisée ici.                         *
 *                                                                             *
diff --git a/src/gui/menus/view.h b/src/gui/menus/view.h
index 2271d4c..d3d70ab 100644
--- a/src/gui/menus/view.h
+++ b/src/gui/menus/view.h
@@ -44,7 +44,7 @@ void rebuild_menu_view_for_content(GtkWidget *, GLoadedContent *);
 void rebuild_menu_view_for_view(GtkWidget *, GLoadedPanel *);
 
 /* Met à jour les accès du menu "Affichage" selon le contenu. */
-void update_access_for_view_in_menu_view(GObject *, GLoadedPanel *);
+void update_access_for_view_in_menu_view(GLoadedPanel *);
 
 /* Réagit avec le menu "Affichage -> Panneaux latéraux". */
 void mcb_view_update_side_panels_list(GtkMenuItem *, GMenuBar *);
-- 
cgit v0.11.2-87-g4458