From 427f80181b1d839bf97d9a7caec88f81bf961e25 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sat, 23 Feb 2019 01:55:50 +0100
Subject: Limited the length of some loop links in the graph view.

---
 src/gtkext/graph/cluster.c | 216 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 163 insertions(+), 53 deletions(-)

diff --git a/src/gtkext/graph/cluster.c b/src/gtkext/graph/cluster.c
index 51d73c7..4f39b28 100644
--- a/src/gtkext/graph/cluster.c
+++ b/src/gtkext/graph/cluster.c
@@ -108,6 +108,8 @@ typedef struct _vspace_booking_t
 
     GdkPoint *pts;                          /* Coordonnées des points      */
 
+    bool external;                          /* Lien vers un cluster parent */
+
 } vspace_booking_t;
 
 /* Réservations d'espaces latéraux */
@@ -132,10 +134,10 @@ static void init_vspace_manager(vspace_manager_t *);
 static void exit_vspace_manager(vspace_manager_t *);
 
 /* Inscrit une nouvelle réservation d'espace latéral. */
-static void extend_vspace_manager(vspace_manager_t *, leaving_link_t *, incoming_link_t *, GdkPoint *);
+static void extend_vspace_manager(vspace_manager_t *, leaving_link_t *, incoming_link_t *, GdkPoint *, bool);
 
 /* Détermine l'emplacement requis pour les espaces latéraux. */
-static void compute_vspace_manager_needed_alloc(const vspace_manager_t *, GtkAllocation *);
+static void compute_vspace_manager_needed_alloc(const vspace_manager_t *, bool, GtkAllocation *);
 
 /* Réorganise au besoin les liens de boucle entre blocs. */
 static void sort_incoming_links_for_vspace_manager(vspace_manager_t *);
@@ -144,7 +146,7 @@ static void sort_incoming_links_for_vspace_manager(vspace_manager_t *);
 static void offset_x_vspace_manager(vspace_manager_t *, gint);
 
 /* Détermine les abscisses de tous les liens en place. */
-static gint compute_loop_link_x_with_vspace_manager(const vspace_manager_t *, const GtkAllocation *);
+static gint compute_loop_link_x_with_vspace_manager(const vspace_manager_t *, const GtkAllocation *, bool);
 
 /* Détermine les ordonnées de tous les liens en place. */
 static void compute_loop_link_y_with_vspace_manager(const vspace_manager_t *, const GtkAllocation *);
@@ -211,8 +213,11 @@ static int cmp_graph_rank(const graph_rank_t *, const graph_rank_t *);
 /* Etend un ensemble de blocs de même rang. */
 static void extend_graph_rank(graph_rank_t *, GGraphCluster *);
 
+/* Détermine si un groupe de blocs contient un bloc particulier. */
+static bool has_graph_rank_cluster(const graph_rank_t *, GGraphCluster *);
+
 /* Inscrit à l'endroit idéal une réservation d'espace latéral. */
-static bool extend_graph_rank_vspace_manager(graph_rank_t *, leaving_link_t *, incoming_link_t *, GdkPoint *);
+static bool extend_graph_rank_vspace_manager(graph_rank_t *, leaving_link_t *, incoming_link_t *, GdkPoint *, bool);
 
 /* Détermine l'emplacement requis d'un ensemble de blocs. */
 static void compute_graph_rank_needed_alloc(const graph_rank_t *, bool, GtkAllocation *);
@@ -275,7 +280,7 @@ struct _GGraphCluster
     graph_rank_t *ranks;                    /* Répartition verticale       */
     size_t ranks_count;                     /* Nombre de divisions         */
 
-    vspace_manager_t vspaces;               /* Gestion des liens latéraux  */
+    vspace_manager_t self;                  /* Gestion d'un retour direct  */
 
 };
 
@@ -675,10 +680,11 @@ static void exit_vspace_manager(vspace_manager_t *manager)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : manager = structure à compléter.                             *
-*                from    = point de départ du lien concerné.                  *
-*                to      = point d'arrivée du lien concerné.                  *
-*                pts     = points intermédiaires du tracé complet final.      *
+*  Paramètres  : manager  = structure à compléter.                            *
+*                from     = point de départ du lien concerné.                 *
+*                to       = point d'arrivée du lien concerné.                 *
+*                pts      = points intermédiaires du tracé complet final.     *
+*                external = précise une sortie du cadre du cluster premier.   *
 *                                                                             *
 *  Description : Inscrit une nouvelle réservation d'espace latéral.           *
 *                                                                             *
@@ -688,7 +694,7 @@ static void exit_vspace_manager(vspace_manager_t *manager)
 *                                                                             *
 ******************************************************************************/
 
-static void extend_vspace_manager(vspace_manager_t *manager, leaving_link_t *from, incoming_link_t *to, GdkPoint *pts)
+static void extend_vspace_manager(vspace_manager_t *manager, leaving_link_t *from, incoming_link_t *to, GdkPoint *pts, bool external)
 {
     vspace_booking_t *new;                  /* Réservation à constituer    */
 
@@ -701,13 +707,16 @@ static void extend_vspace_manager(vspace_manager_t *manager, leaving_link_t *fro
 
     new->pts = pts;
 
+    new->external = external;
+
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : manager = gestion des espaces latéraux à consulter.          *
-*                alloc   = emplacement idéal pour l'affichage. [OUT]          *
+*  Paramètres  : manager  = gestion des espaces latéraux à consulter.         *
+*                external = précise une sortie du cadre du cluster premier.   *
+*                alloc    = emplacement idéal pour l'affichage. [OUT]         *
 *                                                                             *
 *  Description : Détermine l'emplacement requis pour les espaces latéraux.    *
 *                                                                             *
@@ -717,25 +726,42 @@ static void extend_vspace_manager(vspace_manager_t *manager, leaving_link_t *fro
 *                                                                             *
 ******************************************************************************/
 
-static void compute_vspace_manager_needed_alloc(const vspace_manager_t *manager, GtkAllocation *alloc)
+static void compute_vspace_manager_needed_alloc(const vspace_manager_t *manager, bool external, GtkAllocation *alloc)
 {
     gint width;                             /* Largeur supplémentaire      */
-
-    /* Extension de la largeur */
+    size_t count;                           /* Nombre de liens retenus     */
+    size_t i;                               /* Boucle de parcours          */
 
     width = 0;
 
-    width += manager->left_count * LINK_MARGIN;
+    /* Extension de la largeur, à gauche */
+
+    count = 0;
+
+    for (i = 0; i < manager->left_count; i++)
+        if (manager->left[i]->external == external)
+            count++;
+
+    width += count * LINK_MARGIN;
 
     alloc->x -= width;
 
-    width += manager->right_count * LINK_MARGIN;
+    /* Extension de la largeur, à droite */
+
+    count = 0;
+
+    for (i = 0; i < manager->right_count; i++)
+        if (manager->right[i]->external == external)
+            count++;
+
+    width += count * LINK_MARGIN;
 
     alloc->width += width;
 
     /* Extension de la hauteur */
 
-    alloc->height += manager->pending_count * VERTICAL_MARGIN;
+    if (!external)
+        alloc->height += manager->pending_count * VERTICAL_MARGIN;
 
 }
 
@@ -836,8 +862,9 @@ static void offset_x_vspace_manager(vspace_manager_t *manager, gint offset)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : manager = structure à consulter.                             *
-*                needed  = espace nécessaire et alloué pour les blocs.        *
+*  Paramètres  : manager  = structure à consulter.                            *
+*                needed   = espace nécessaire et alloué pour les blocs.       *
+*                external = précise une sortie du cadre du cluster premier.   *
 *                                                                             *
 *  Description : Détermine les abscisses de tous les liens en place.          *
 *                                                                             *
@@ -847,37 +874,42 @@ static void offset_x_vspace_manager(vspace_manager_t *manager, gint offset)
 *                                                                             *
 ******************************************************************************/
 
-static gint compute_loop_link_x_with_vspace_manager(const vspace_manager_t *manager, const GtkAllocation *needed)
+static gint compute_loop_link_x_with_vspace_manager(const vspace_manager_t *manager, const GtkAllocation *needed, bool external)
 {
     gint result;                            /* Eventuelle marge à renvoyer */
+    size_t count;                           /* Quantité de liens traités   */
     size_t i;                               /* Boucle de parcours          */
     vspace_booking_t *booking;              /* Réservation à traiter       */
     gint x;                                 /* Position à appliquer        */
 
+    count = 0;
+
     for (i = 0; i < manager->left_count; i++)
     {
         booking = manager->left[i];
 
-        x = (i + 1) * LINK_MARGIN;
+        x = ++count * LINK_MARGIN;
 
         booking->pts[0].x = needed->x - x;
         booking->pts[1].x = needed->x - x;
 
     }
 
+    result = count * LINK_MARGIN;
+
+    count = 0;
+
     for (i = 0; i < manager->right_count; i++)
     {
         booking = manager->right[i];
 
-        x = (i + 1) * LINK_MARGIN;
+        x = ++count * LINK_MARGIN;
 
         booking->pts[0].x = needed->x + needed->width + x;
         booking->pts[1].x = needed->x + needed->width + x;
 
     }
 
-    result = manager->left_count * LINK_MARGIN;
-
     return result;
 
 }
@@ -1199,20 +1231,18 @@ static void extend_graph_rank(graph_rank_t *grank, GGraphCluster *cluster)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : grank = ensemble de descendants d'un même rang.              *
-*                from  = point de départ du lien concerné.                    *
-*                to    = point d'arrivée du lien concerné.                    *
-*                pts   = points intermédiaires du tracé complet final.        *
+*  Paramètres  : grank   = structure à compléter.                             *
+*                cluster = chef de file d'un ensemble de blocs.               *
 *                                                                             *
-*  Description : Inscrit à l'endroit idéal une réservation d'espace latéral.  *
+*  Description : Détermine si un groupe de blocs contient un bloc particulier.*
 *                                                                             *
-*  Retour      : true si la demande a bien été traitée.                       *
+*  Retour      : true si le chef est bien contenu, false sinon.               *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool extend_graph_rank_vspace_manager(graph_rank_t *grank, leaving_link_t *from, incoming_link_t *to, GdkPoint *pts)
+static bool has_graph_rank_cluster(const graph_rank_t *grank, GGraphCluster *cluster)
 {
     bool result;                            /* Bilan à renvoyer            */
     size_t i;                               /* Boucle de parcours          */
@@ -1220,10 +1250,37 @@ static bool extend_graph_rank_vspace_manager(graph_rank_t *grank, leaving_link_t
     result = false;
 
     for (i = 0; i < grank->count && !result; i++)
-        result = (grank->clusters[i] == from->owner);
+        result = (grank->clusters[i] == cluster);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : grank    = ensemble de descendants d'un même rang.           *
+*                from     = point de départ du lien concerné.                 *
+*                to       = point d'arrivée du lien concerné.                 *
+*                pts      = points intermédiaires du tracé complet final.     *
+*                external = précise une sortie du cadre du cluster premier.   *
+*                                                                             *
+*  Description : Inscrit à l'endroit idéal une réservation d'espace latéral.  *
+*                                                                             *
+*  Retour      : true si la demande a bien été traitée.                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool extend_graph_rank_vspace_manager(graph_rank_t *grank, leaving_link_t *from, incoming_link_t *to, GdkPoint *pts, bool external)
+{
+    bool result;                            /* Bilan à renvoyer            */
+
+    result = has_graph_rank_cluster(grank, from->owner);
 
     if (result)
-        extend_vspace_manager(&grank->vspaces, from, to, pts);
+        extend_vspace_manager(&grank->vspaces, from, to, pts, external);
 
     return result;
 
@@ -1307,7 +1364,7 @@ static void compute_graph_rank_needed_alloc(const graph_rank_t *grank, bool last
 
     }
 
-    compute_vspace_manager_needed_alloc(&grank->vspaces, alloc);
+    compute_vspace_manager_needed_alloc(&grank->vspaces, false, alloc);
 
 }
 
@@ -1459,7 +1516,7 @@ static void offset_x_graph_rank(graph_rank_t *grank, gint offset)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : grank  = ensemble de blocs de même rang à actualiser.        *
-*                needed  = espace nécessaire et alloué pour les blocs.        *
+*                needed = espace nécessaire et alloué pour les blocs.         *
 *                                                                             *
 *  Description : Détermine les abscisses des liens de boucle en place.        *
 *                                                                             *
@@ -1477,7 +1534,7 @@ static gint compute_loop_link_x_positions_with_graph_rank(const graph_rank_t *gr
     for (i = 0; i < grank->count; i++)
         g_graph_cluster_compute_loop_link_x_positions(grank->clusters[i]);
 
-    result = compute_loop_link_x_with_vspace_manager(&grank->vspaces, needed);
+    result = compute_loop_link_x_with_vspace_manager(&grank->vspaces, needed, false);
 
     return result;
 
@@ -1619,7 +1676,7 @@ static void g_graph_cluster_class_init(GGraphClusterClass *klass)
 
 static void g_graph_cluster_init(GGraphCluster *cluster)
 {
-    init_vspace_manager(&cluster->vspaces);
+    init_vspace_manager(&cluster->self);
 
 }
 
@@ -1680,7 +1737,7 @@ static void g_graph_cluster_finalize(GGraphCluster *cluster)
     if (cluster->ranks != NULL)
         free(cluster->ranks);
 
-    exit_vspace_manager(&cluster->vspaces);
+    exit_vspace_manager(&cluster->self);
 
     G_OBJECT_CLASS(g_graph_cluster_parent_class)->finalize(G_OBJECT(cluster));
 
@@ -1867,17 +1924,28 @@ static void g_graph_cluster_setup_link_for_target(GGraphCluster *source, GGraphC
 static void g_graph_cluster_extend_vspace_manager(GGraphCluster *target, leaving_link_t *from, incoming_link_t *to, GdkPoint *pts)
 {
     bool done;                              /* Bilan des traitements       */
+    GGraphCluster *container;               /* Arrivée de boucle extérieure*/
     size_t i;                               /* Boucle de parcours          */
 
     assert(target == to->owner);
 
     done = false;
 
-    for (i = 0; i < target->ranks_count && !done; i++)
-        done = extend_graph_rank_vspace_manager(&target->ranks[i], from, to, pts);
+    if (from->owner == target)
+        extend_vspace_manager(&target->self, from, to, pts, false);
+
+    else
+    {
+        for (i = 0; i < target->ranks_count && !done; i++)
+            done = extend_graph_rank_vspace_manager(&target->ranks[i], from, to, pts, false);
+
+        container = from->owner->owner;
+        assert(container != NULL);
 
-    if (!done)
-        extend_vspace_manager(&target->vspaces, from, to, pts);
+        for (i = 0; i < container->ranks_count && !done; i++)
+            done = extend_graph_rank_vspace_manager(&container->ranks[i], from, to, pts, true);
+
+    }
 
 }
 
@@ -2193,7 +2261,7 @@ static void g_graph_cluster_sort_incoming_links(GGraphCluster *cluster)
     for (i = 0; i < cluster->ranks_count; i++)
         sort_graph_rank_incoming_links(&cluster->ranks[i]);
 
-    sort_incoming_links_for_vspace_manager(&cluster->vspaces);
+    sort_incoming_links_for_vspace_manager(&cluster->self);
 
 }
 
@@ -2248,7 +2316,7 @@ static void g_graph_cluster_offset_x(GGraphCluster *cluster, gint offset)
     for (i = 0; i < cluster->ranks_count; i++)
         offset_x_graph_rank(&cluster->ranks[i], offset);
 
-    offset_x_vspace_manager(&cluster->vspaces, offset);
+    offset_x_vspace_manager(&cluster->self, offset);
 
 }
 
@@ -2295,14 +2363,34 @@ static void g_graph_cluster_set_y(GGraphCluster *cluster, gint base)
 
 void g_graph_cluster_compute_needed_alloc(const GGraphCluster *cluster, GtkAllocation *alloc)
 {
-    size_t i;                               /* Boucle de parcours          */
+    size_t i;                               /* Boucle de parcours #1       */
+    GGraphCluster *start;                   /* Départ de boucle extérieure */
+    GGraphCluster *container;               /* Arrivée de boucle extérieure*/
+    size_t k;                               /* Boucle de parcours #2       */
 
     *alloc = cluster->alloc;
 
     for (i = 0; i < cluster->ranks_count; i++)
         compute_graph_rank_needed_alloc(&cluster->ranks[i], (i + 1) == cluster->ranks_count, alloc);
 
-    compute_vspace_manager_needed_alloc(&cluster->vspaces, alloc);
+    for (i = 0; i < cluster->ta_count; i++)
+        if (cluster->top_anchors[i]->type == ILT_LOOP)
+        {
+            start = cluster->top_anchors[i]->other->owner;
+
+            container = start->owner;
+            assert(container != NULL);
+
+            for (k = 0; k < container->ranks_count; k++)
+                if (has_graph_rank_cluster(&container->ranks[k], start))
+                {
+                    compute_vspace_manager_needed_alloc(&container->ranks[k].vspaces, true, alloc);
+                    break;
+                }
+
+            assert(k < container->ranks_count);
+
+        }
 
 }
 
@@ -2549,7 +2637,10 @@ static void g_graph_cluster_compute_loop_link_x_positions(GGraphCluster *cluster
 {
     GtkAllocation alloc;                    /* Emplacement à faire évoluer */
     gint margin;                            /* Marge à gauche éventuelle   */
-    size_t i;                               /* Boucle de parcours          */
+    size_t i;                               /* Boucle de parcours #1       */
+    GGraphCluster *start;                   /* Départ de boucle extérieure */
+    GGraphCluster *container;               /* Arrivée de boucle extérieure*/
+    size_t k;                               /* Boucle de parcours #2       */
 
     /* Propagation des déterminations */
 
@@ -2565,11 +2656,32 @@ static void g_graph_cluster_compute_loop_link_x_positions(GGraphCluster *cluster
 
     }
 
-    /* Liens de boucle */
+    /* Liens de boucle (#1) */
 
     g_graph_cluster_compute_needed_alloc(cluster, &alloc);
 
-    margin = compute_loop_link_x_with_vspace_manager(&cluster->vspaces, &alloc);
+    margin = compute_loop_link_x_with_vspace_manager(&cluster->self, &alloc, false);
+
+    /* Liens de boucle (#2) */
+
+    for (i = 0; i < cluster->ta_count; i++)
+        if (cluster->top_anchors[i]->type == ILT_LOOP)
+        {
+            start = cluster->top_anchors[i]->other->owner;
+
+            container = start->owner;
+            assert(container != NULL);
+
+            for (k = 0; k < container->ranks_count; k++)
+                if (has_graph_rank_cluster(&container->ranks[k], start))
+                {
+                    margin += compute_loop_link_x_with_vspace_manager(&container->ranks[k].vspaces, &alloc, true);
+                    break;
+                }
+
+            assert(k < container->ranks_count);
+
+        }
 
     g_graph_cluster_insert_left_margin(cluster, margin);
 
@@ -2813,9 +2925,7 @@ static void g_graph_cluster_compute_link_y_positions(GGraphCluster *cluster)
 
     /* Définition des liens de boucle */
 
-    g_graph_cluster_compute_needed_alloc(cluster, &alloc);
-
-    compute_loop_link_y_with_vspace_manager(&cluster->vspaces, &alloc);
+    compute_loop_link_y_with_vspace_manager(&cluster->self, &cluster->alloc);
 
 }
 
-- 
cgit v0.11.2-87-g4458