From 5ed40f0afbd58fbfdf05be7b2a10ab8d0819759f Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 14 Feb 2019 12:28:45 +0100
Subject: Highlighted graph view edges.

---
 src/gtkext/graph/cluster.c   |   3 -
 src/gtkext/graph/edge.c      | 135 +++++++++++++++++++++++++++++++++++--------
 src/gtkext/graph/edge.h      |   9 ++-
 src/gtkext/gtkgraphdisplay.c |  44 ++++++++++++--
 4 files changed, 158 insertions(+), 33 deletions(-)

diff --git a/src/gtkext/graph/cluster.c b/src/gtkext/graph/cluster.c
index 12f88bd..c4deeaa 100644
--- a/src/gtkext/graph/cluster.c
+++ b/src/gtkext/graph/cluster.c
@@ -279,9 +279,6 @@ struct _GGraphClusterClass
 /* Espace minimal vertical entre les blocs */
 #define VERTICAL_MARGIN 15
 
-/* Espace minimal entre les liens */
-#define LINK_MARGIN 10
-
 
 /* Initialise la classe des mises en disposition graphique. */
 static void g_graph_cluster_class_init(GGraphClusterClass *);
diff --git a/src/gtkext/graph/edge.c b/src/gtkext/graph/edge.c
index e3844f3..9a2f848 100644
--- a/src/gtkext/graph/edge.c
+++ b/src/gtkext/graph/edge.c
@@ -285,42 +285,131 @@ void g_graph_edge_offset(GGraphEdge *edge, gint dx, gint dy)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : edge  = ligne de rendu à manipuler.                          *
-*                cairo = assistant pour le rendu graphique.                   *
-*                arrow = indique le besoin en flèche à l'arrivée.             *
+*  Paramètres  : edge = ligne de rendu avec positions à consulter.            *
+*                x    = emplacement de la souris sur l'axe des abscisses.     *
+*                y    = emplacement de la souris sur l'axe des ordonnées.     *
 *                                                                             *
-*  Description : Dessine les liens graphiques enregistrés dans le moteur.     *
+*  Description : Opère un décalage du lien dans une direction donnée.         *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : true si un survol est en cours, false sinon.                 *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-void g_graph_edge_draw(const GGraphEdge *edge, cairo_t *cairo, bool arrow)
+bool g_graph_edge_detect_at(const GGraphEdge *edge, gint x, gint y)
 {
+    bool result;                            /* Bilan à retourner           */
     size_t i;                               /* Boucle de parcours          */
+    gint margin;                            /* Marge de précision          */
+    gint pts[2];                            /* Points d'analyse ordonnés   */
+    gint inter[2];                          /* Bilan d'intersection        */
 
-    switch (edge->color)
+    result = false;
+
+    for (i = 1; i < edge->count; i++)
     {
-        default:
-        case EGC_DEFAULT:
-            cairo_set_source_rgb(cairo, 0, 0, 0);
-            break;
-        case EGC_GREEN:
-            cairo_set_source_rgb(cairo, 0, 0.6, 0);
-            break;
-        case EGC_RED:
-            cairo_set_source_rgb(cairo, 0.8, 0, 0);
-            break;
-        case EGC_BLUE:
-            cairo_set_source_rgb(cairo, 0, 0, 0.8);
-            break;
-        case EGC_DASHED_GRAY:
-            cairo_set_source_rgb(cairo, 0.4, 0.4, 0.4);
-            break;
+        /* Au niveau des abscisses */
+
+        if (edge->points[i - 1].x <= edge->points[i].x)
+        {
+            pts[0] = edge->points[i - 1].x;
+            pts[1] = edge->points[i].x;
+            margin = 1;
+        }
+        else
+        {
+            pts[0] = edge->points[i].x;
+            pts[1] = edge->points[i - 1].x;
+            margin = 1;
+        }
+
+        margin *= (LINK_MARGIN / 2);
+
+        inter[0] = MAX(pts[0] - margin, x);
+        inter[1] = MIN(pts[1] + margin, x);
+
+        if (inter[0] > inter[1])
+            continue;
+
+        /* Au niveau des ordonnées */
+
+        if (edge->points[i - 1].y <= edge->points[i].y)
+        {
+            pts[0] = edge->points[i - 1].y;
+            pts[1] = edge->points[i].y;
+            margin = 1;
+        }
+        else
+        {
+            pts[0] = edge->points[i].y;
+            pts[1] = edge->points[i - 1].y;
+            margin = 1;
+        }
+
+        margin *= (LINK_MARGIN / 2);
+
+        inter[0] = MAX(pts[0] - margin, y);
+        inter[1] = MIN(pts[1] + margin, y);
+
+        if (inter[0] > inter[1])
+            continue;
+
+        /* Détection ! */
+
+        result = true;
+        break;
+
     }
 
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : edge     = ligne de rendu à manipuler.                       *
+*                cairo    = assistant pour le rendu graphique.                *
+*                arrow    = indique le besoin en flèche à l'arrivée.          *
+*                selected = s'agit-il d'un lien sélectionné ?                 *
+*                                                                             *
+*  Description : Dessine les liens graphiques enregistrés dans le moteur.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_graph_edge_draw(const GGraphEdge *edge, cairo_t *cairo, bool arrow, bool selected)
+{
+    size_t i;                               /* Boucle de parcours          */
+
+    if (selected)
+        cairo_set_source_rgb(cairo, 1.0, 1.0, 1.0);
+
+    else
+        switch (edge->color)
+        {
+            default:
+            case EGC_DEFAULT:
+                cairo_set_source_rgb(cairo, 0, 0, 0);
+                break;
+            case EGC_GREEN:
+                cairo_set_source_rgb(cairo, 0, 0.6, 0);
+                break;
+            case EGC_RED:
+                cairo_set_source_rgb(cairo, 0.8, 0, 0);
+                break;
+            case EGC_BLUE:
+                cairo_set_source_rgb(cairo, 0, 0, 0.8);
+                break;
+            case EGC_DASHED_GRAY:
+                cairo_set_source_rgb(cairo, 0.4, 0.4, 0.4);
+                break;
+        }
+
     switch (edge->color)
     {
         default:
diff --git a/src/gtkext/graph/edge.h b/src/gtkext/graph/edge.h
index 23fcf8d..e905685 100644
--- a/src/gtkext/graph/edge.h
+++ b/src/gtkext/graph/edge.h
@@ -60,6 +60,10 @@ typedef enum _EdgeColor
 } EdgeColor;
 
 
+/* Espace minimal entre les liens */
+#define LINK_MARGIN 10
+
+
 /* Indique le type défini par la GLib pour les liens graphiques entre noeuds. */
 GType g_graph_edge_get_type(void);
 
@@ -87,8 +91,11 @@ void g_graph_edge_resolve(GGraphEdge *);
 /* Opère un décalage du lien dans une direction donnée. */
 void g_graph_edge_offset(GGraphEdge *, gint, gint);
 
+/* Opère un décalage du lien dans une direction donnée. */
+bool g_graph_edge_detect_at(const GGraphEdge *, gint, gint);
+
 /* Dessine les liens graphiques enregistrés dans le moteur. */
-void g_graph_edge_draw(const GGraphEdge *, cairo_t *, bool);
+void g_graph_edge_draw(const GGraphEdge *, cairo_t *, bool, bool);
 
 
 
diff --git a/src/gtkext/gtkgraphdisplay.c b/src/gtkext/gtkgraphdisplay.c
index 51ca344..0915f3a 100644
--- a/src/gtkext/gtkgraphdisplay.c
+++ b/src/gtkext/gtkgraphdisplay.c
@@ -54,6 +54,7 @@ struct _GtkGraphDisplay
 
     GGraphEdge **edges;                     /* Liens entre les noeuds      */
     size_t edges_count;                     /* Quantité de ces liens       */
+    size_t hl_edge_index;                   /* Indice du lien survolé      */
 
     gdouble start_x;                        /* Abscisse du point de souris */
     gdouble start_y;                        /* Ordonnée du point de souris */
@@ -214,7 +215,8 @@ static void gtk_graph_display_init(GtkGraphDisplay *display)
                       G_CALLBACK(gtk_graph_display_motion_notify), display);
 
     gtk_widget_add_events(display->support,
-                          GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+                          GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+                          | GDK_POINTER_MOTION_MASK);
 
     gtk_widget_show(display->support);
 
@@ -481,7 +483,7 @@ static gboolean gtk_graph_display_draw(GtkWidget *widget, cairo_t *cr, GtkGraphD
     gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)draw_shadow, NULL);
 
     for (i = 0; i < display->edges_count; i++)
-        g_graph_edge_draw(display->edges[i], cr, true);
+        g_graph_edge_draw(display->edges[i], cr, true, display->hl_edge_index == i);
 
     return FALSE;
 
@@ -591,9 +593,14 @@ 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      */
+    size_t i;                               /* Boucle de parcours          */
 
-    if (event->state & GDK_BUTTON1_MASK && display->big_enough)
+    /* Déplacement du graphique ? */
+    if (event->state & GDK_BUTTON1_MASK)
     {
+        if (!display->big_enough)
+            goto done;
+
         diff_x = display->start_x - event->x_root;
         diff_y = display->start_y - event->y_root;
 
@@ -612,6 +619,29 @@ static gboolean gtk_graph_display_motion_notify(GtkWidget *widget, GdkEventMotio
 
     }
 
+    /* Survol d'un lien ? */
+    else
+    {
+        for (i = 0; i < display->edges_count; i++)
+            if (g_graph_edge_detect_at(display->edges[i], event->x, event->y))
+            {
+                display->hl_edge_index = i;
+                break;
+            }
+
+        if (i < display->edges_count)
+            gtk_widget_queue_draw(widget);
+
+        else if (display->hl_edge_index < display->edges_count)
+        {
+            display->hl_edge_index = display->edges_count;
+            gtk_widget_queue_draw(widget);
+        }
+
+    }
+
+ done:
+
     return FALSE;
 
 }
@@ -922,7 +952,7 @@ static void gtk_graph_display_cache_glance(GtkGraphDisplay *display, cairo_t *cr
     cairo_scale(cr, scale, scale);
 
     for (i = 0; i < display->edges_count; i++)
-        g_graph_edge_draw(display->edges[i], cr, false);
+        g_graph_edge_draw(display->edges[i], cr, false, false);
 
 }
 
@@ -990,11 +1020,12 @@ void gtk_graph_display_add_edge(GtkGraphDisplay *display, GGraphEdge *edge)
 {
     g_graph_edge_offset(edge, GRAPH_MARGIN, GRAPH_MARGIN);
 
-    display->edges = (GGraphEdge **)realloc(display->edges,
-                                         ++display->edges_count * sizeof(GGraphEdge *));
+    display->edges = realloc(display->edges, ++display->edges_count * sizeof(GGraphEdge *));
 
     display->edges[display->edges_count - 1] = edge;
 
+    display->hl_edge_index = display->edges_count;
+
 }
 
 
@@ -1055,6 +1086,7 @@ static void gtk_graph_display_reset(GtkGraphDisplay *display, bool dispose)
         display->edges = NULL;
 
         display->edges_count = 0;
+        display->hl_edge_index = 0;
 
     }
 
-- 
cgit v0.11.2-87-g4458