/* Chrysalide - Outil d'analyse de fichiers binaires
 * cluster.c - mise en place de graphiques
 *
 * Copyright (C) 2016-2017 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 Chrysalide.  If not, see .
 */
#include "cluster.h"
#include 
#include 
#include 
#include 
#include "../gtkblockdisplay.h"
#include "../gtkbufferdisplay.h"
#include "../gtkdisplaypanel.h"
#include "../../common/sort.h"
#include "../../glibext/gloadedpanel.h"
/* ------------------------- LIEN SORTANT D'UN BLOC DE CODE ------------------------- */
/* Détails sur le départ d'un lien */
typedef struct _leaving_link_t
{
    GGraphCluster *owner;                   /* Propriétaire du lien        */
    GdkPoint start[2];                      /* Point de départ d'un lien   */
    size_t index;                           /* Indice sur ligne de départ  */
} leaving_link_t;
/* Crée un point d'attache pour un lien sortant. */
static leaving_link_t *create_leaving_link(GGraphCluster *, size_t);
/* Détruit un point d'attache pour un lien sortant. */
static void delete_leaving_link(leaving_link_t *);
/* Calcule l'abscisse d'un lien à son départ d'un bloc. */
static gint compute_leaving_link_position(const leaving_link_t *);
/* ------------------------- LIEN ENTRANT D'UN BLOC DE CODE ------------------------- */
/* Définition du tracé d'un lien */
typedef struct _incoming_link_t
{
    GGraphCluster *owner;                   /* Propriétaire du lien        */
    InstructionLinkType type;               /* Complexité du tracé         */
    const size_t *hslot;                    /* Couche horizontale réservée */
    GdkPoint end[2];                        /* Point d'arrivée final       */
    GGraphEdge *edge;                       /* Lien complet en préparation */
    leaving_link_t *other;                  /* Autre extrémité du lien     */
} incoming_link_t;
/* Crée un point d'attache pour un lien entrant simple. */
static incoming_link_t *create_incoming_link(GGraphCluster *, InstructionLinkType, leaving_link_t *);
/* Crée un point d'attache pour un lien entrant de boucle. */
static incoming_link_t *create_incoming_loop_link(GGraphCluster *, const GdkPoint *, leaving_link_t *);
/* Détruit un point d'attache pour un lien entrant. */
static void delete_incoming_link(incoming_link_t *);
/* Compare deux liens entrants. */
static int cmp_incoming_links(const incoming_link_t **, const incoming_link_t **);
/* ------------------------ ENCADREMENTS D'ESPACES VERTICAUX ------------------------ */
/* Réservations d'espaces latéraux */
typedef struct _vspace_booking_t
{
    leaving_link_t *from;                   /* Bloc de départ du lien      */
    incoming_link_t *to;                    /* Bloc d'arrivée du lien      */
    GdkPoint *pts;                          /* Coordonnées des points      */
    bool external;                          /* Lien vers un cluster parent */
} vspace_booking_t;
/* Réservations d'espaces latéraux */
typedef struct _vspace_manager_t
{
    vspace_booking_t *pending;              /* Besoins exprimés            */
    size_t pending_count;                   /* Nombre de ces besoins       */
    vspace_booking_t **left;                /* Lignes disposées à gauche   */
    size_t left_count;                      /* Quantité de ces lignes      */
    vspace_booking_t **right;               /* Lignes disposées à droite  */
    size_t right_count;                     /* Quantité de ces lignes      */
} vspace_manager_t;
/* Initialise les réservations liens verticaux. */
static void init_vspace_manager(vspace_manager_t *);
/* Termine les réservations liens verticaux. */
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 *, bool);
/* Détermine l'emplacement requis pour les espaces latéraux. */
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 *);
/* Décale vers la droite un ensemble de points. */
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 *, 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 *);
/* ---------------------- DESCENDANTS DIRECTS CLASSES PAR RANG ---------------------- */
/* Réservation pour les lignes horizontales */
typedef struct _hspace_booking
{
    gint start;                             /* Abscisse de départ de ligne */
    size_t index;                           /* Indice de rangée verticale  */
} hspace_booking;
/* Prépare une réservation d'espace pour ligne horizontale. */
static hspace_booking *create_hspace_booking(gint);
/* Compare deux réservations d'espace. */
static int cmp_hspace_booking_r2l(const hspace_booking **, const hspace_booking **);
/* Compare deux réservations d'espace. */
static int cmp_hspace_booking_l2r(const hspace_booking **, const hspace_booking **);
/* Découpage vertical */
typedef struct _graph_rank_t
{
    hspace_booking **right2left;            /* Réservations de D -> G      */
    size_t r2l_count;                       /* Quantité de ces réservations*/
    hspace_booking **left2right;            /* Réservations de G -> D      */
    size_t l2r_count;                       /* Quantité de ces réservations*/
    GGraphCluster **clusters;               /* Ensembles de blocs          */
    size_t count;                           /* Nombre de ces ensembles     */
    vspace_manager_t vspaces;               /* Gestion des liens latéraux  */
} graph_rank_t;
/* Initialise la gestion d'un ensemble de blocs de même rang. */
static void init_graph_rank(graph_rank_t *, GGraphCluster *);
/* Termine la gestion d'un ensemble de blocs de même rang. */
static void exit_graph_rank(graph_rank_t *);
/* Assigne à un ensemble de blocs un emplacement initial. */
static void reset_graph_rank_allocation(const graph_rank_t *);
/* Fournit le rang d'un ensemble de blocs. */
static size_t get_graph_rank(const graph_rank_t *);
/* Etend un ensemble de blocs de même rang. */
static void extend_graph_rank(graph_rank_t *, GGraphCluster *);
/* Compare deux rangées de blocs de code. */
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 *, bool);
/* Détermine l'emplacement requis d'un ensemble de blocs. */
static void compute_graph_rank_needed_alloc(const graph_rank_t *, bool, GtkAllocation *);
/* Affine l'abscisse d'un ensemble de blocs de même rang. */
static void _place_graph_rank_clusters(GGraphCluster **, size_t, gint, int);
/* Organise la disposition d'un ensemble de blocs basiques. */
static void dispatch_x_graph_rank(const graph_rank_t *);
/* Réorganise au besoin les liens entrants un ensemble de blocs. */
static void sort_graph_rank_incoming_links(graph_rank_t *);
/* Réordonne les blocs de départ de boucle d'un ensemble. */
static void reorder_graph_rank_loop_blocks(graph_rank_t *);
/* Décale vers la droite un ensemble de blocs basiques. */
static void offset_x_graph_rank(graph_rank_t *, gint);
/* Détermine les abscisses des liens de boucle en place. */
static gint compute_loop_link_x_positions_with_graph_rank(const graph_rank_t *, const GtkAllocation *);
/* Décale vers le bas un ensemble de blocs basiques. */
static void set_y_for_graph_rank(const graph_rank_t *, gint *);
/* Détermine les ordonnées de tous les liens en place. */
static void compute_loop_link_with_graph_rank(const graph_rank_t *, const GtkAllocation *);
/* -------------------------- DEFINITION D'UN CHEF DE FILE -------------------------- */
/* Mise en disposition de blocs en graphique (instance) */
struct _GGraphCluster
{
    GObject parent;                         /* A laisser en premier        */
    GGraphCluster *owner;                   /* Ensemble lié parent         */
    size_t *parent_index;                   /* Indice du lien au départ    */
    ////////////////////////////////////////
    gint top_margin[2];                     /* Espaces gauches et droites  */
    gint left_margin;                       /* Espaces remontant à gauche  */
    gint right_margin;                      /* Espaces remontant à droite  */
    ////////////////////////////////////////
    incoming_link_t **top_anchors;          /* Accroches supérieures       */
    size_t ta_count;                        /* Quantité de ces accroches   */
    GCodeBlock *block;                      /* Bloc d'origine représenté   */
    GtkWidget *display;                     /* Vue graphique associée      */
    GtkAllocation alloc;                    /* Emplacement final du bloc   */
    leaving_link_t **bottom_anchors;        /* Accroches inférieures       */
    size_t ba_count;                        /* Quantité de ces accroches   */
    bool has_straight;                      /* Présence d'une ligne droite */
    size_t straight_level;                  /* Rang atteint en ligne droite*/
    size_t straight_index;                  /* Indice du lien vertical     */
    graph_rank_t *ranks;                    /* Répartition verticale       */
    size_t ranks_count;                     /* Nombre de divisions         */
    vspace_manager_t self;                  /* Gestion d'un retour direct  */
};
/* Mise en disposition de blocs en graphique (classe) */
struct _GGraphClusterClass
{
    GObjectClass parent;                    /* A laisser en premier        */
};
/* Espace minimal horizontal entre les blocs */
#define HORIZONTAL_MARGIN 20
/* Espace minimal vertical entre les blocs */
#define VERTICAL_MARGIN 15
/* Initialise la classe des mises en disposition graphique. */
static void g_graph_cluster_class_init(GGraphClusterClass *);
/* Initialise une mise en disposition de blocs en graphique. */
static void g_graph_cluster_init(GGraphCluster *);
/* Supprime toutes les références externes. */
static void g_graph_cluster_dispose(GGraphCluster *);
/* Procède à la libération totale de la mémoire. */
static void g_graph_cluster_finalize(GGraphCluster *);
/* Assigne à un bloc et son ensemble un emplacement initial. */
static void g_graph_cluster_reset_allocation(GGraphCluster *);
/* Complète un graphique avec un sous-ensemble de blocs. */
static void g_graph_cluster_add_sub(GGraphCluster *, GGraphCluster *);
/* Etablit les connexions entre blocs selon les rangs. */
static void g_graph_cluster_setup_link_for_target(GGraphCluster *, GGraphCluster *, leaving_link_t *);
/* Inscrit à l'endroit idéal une réservation d'espace latéral. */
static void g_graph_cluster_extend_vspace_manager(GGraphCluster *, leaving_link_t *, incoming_link_t *, GdkPoint *);
/* Met en place les embryons de liens nécessaires. */
static void g_graph_cluster_define_links(GGraphCluster *, GHashTable *);
/* Organise la disposition d'un ensemble de blocs basiques. */
static void g_graph_cluster_dispatch_x(GGraphCluster *);
/* Réorganise au besoin les liens entrants d'un bloc. */
static void g_graph_cluster_sort_incoming_links(GGraphCluster *);
/* Retrouve l'indice d'un lien entrant donné pour un bloc. */
static size_t g_graph_cluster_find_incoming_link(const GGraphCluster *, const leaving_link_t *);
/* Réordonne les blocs de départ de boucle au mieux. */
static void g_graph_cluster_reorder_loop_blocks(GGraphCluster *);
/* Décale vers la droite un ensemble de blocs basiques. */
static void g_graph_cluster_offset_x(GGraphCluster *, gint);
/* Décale vers le bas un ensemble de blocs basiques. */
static void g_graph_cluster_set_y(GGraphCluster *, gint);
/* Calcule l'abscisse d'un lien pour un bloc. */
static gint _g_graph_cluster_compute_link_position(const GGraphCluster *, size_t, size_t, bool);
/* Calcule l'abscisse d'un lien à son départ d'un bloc. */
static gint g_graph_cluster_compute_leaving_link_position(const GGraphCluster *, size_t);
/* Calcule l'abscisse d'un lien à son arrivée à un bloc. */
static gint g_graph_cluster_compute_incoming_link_position(const GGraphCluster *, size_t);
/* Ajoute une marge à gauche pour les liens remontants. */
static void g_graph_cluster_insert_left_margin(GGraphCluster *, gint);
/* Détermine les abscisses des liens de boucle en place. */
static void g_graph_cluster_compute_loop_link_x_positions(GGraphCluster *);
/* Détermine les abscisses de tous les liens en place. */
static void g_graph_cluster_compute_link_x_positions(GGraphCluster *);
/* Réserve de l'espace vertical pour les lignes horizontales. */
static void g_graph_cluster_book_hspace_for_links(GGraphCluster *);
/* Détermine les ordonnées de tous les liens en place. */
static void g_graph_cluster_compute_link_y_positions(GGraphCluster *);
/* Applique les positions calculées pour chaque lien graphique. */
static void g_graph_cluster_resolve_links(const GGraphCluster *);
/* ------------------------- CALCUL DE REPARTITION DE BLOCS ------------------------- */
/* Liste de blocs restants à traiter */
typedef struct _pending_blocks
{
    size_t count;                           /* Taille de la liste          */
    GCodeBlock *list[0];                    /* Liste de blocs à traiter    */
} pending_blocks;
/* Met en place un ensemble de blocs sous forme graphique. */
static GGraphCluster *setup_graph_clusters(GLoadedBinary *, const GBlockList *, size_t , segcnt_list *, pending_blocks *, GHashTable *);
/* ---------------------------------------------------------------------------------- */
/*                           LIEN SORTANT D'UN BLOC DE CODE                           */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
*                                                                             *
*  Paramètres  : owner = propriétaire du bloc de rattachement.                *
*                index = indice dans les liens de sortie.                     *
*                                                                             *
*  Description : Crée un point d'attache pour un lien sortant.                *
*                                                                             *
*  Retour      : Structure mise en place.                                     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static leaving_link_t *create_leaving_link(GGraphCluster *owner, size_t index)
{
    leaving_link_t *result;                 /* Structure à retourner       */
    result = malloc(sizeof(leaving_link_t));
    result->owner = owner;
    result->index = index;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : link = structure à libérer de la mémoire.                    *
*                                                                             *
*  Description : Détruit un point d'attache pour un lien sortant.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void delete_leaving_link(leaving_link_t *link)
{
    free(link);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : link = information sur un lien à consulter.                  *
*                                                                             *
*  Description : Calcule l'abscisse d'un lien à son départ d'un bloc.         *
*                                                                             *
*  Retour      : Abscisse à attribuer à un départ de lien.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static gint compute_leaving_link_position(const leaving_link_t *link)
{
    gint result;                            /* Position à retourner        */
    result = g_graph_cluster_compute_leaving_link_position(link->owner, link->index);
    return result;
}
/* ---------------------------------------------------------------------------------- */
/*                           LIEN ENTRANT D'UN BLOC DE CODE                           */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
*                                                                             *
*  Paramètres  : owner = propriétaire du bloc de rattachement.                *
*                type  = type de lien simple attendu.                         *
*                other = point de départ du lien formé.                       *
*                                                                             *
*  Description : Crée un point d'attache pour un lien entrant simple.         *
*                                                                             *
*  Retour      : Structure mise en place.                                     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static incoming_link_t *create_incoming_link(GGraphCluster *owner, InstructionLinkType type, leaving_link_t *other)
{
    incoming_link_t *result;                /* Structure à retourner       */
    GCodeBlock *src;                        /* Bloc d'origine du lien      */
    GCodeBlock *dst;                        /* Bloc de destination du lien */
    result = malloc(sizeof(incoming_link_t));
    result->owner = owner;
    result->type = type;
    src = other->owner->block;
    dst = owner->block;
    if (type == ILT_JUMP_IF_TRUE)
        result->edge = g_graph_edge_new_true(src, dst,
                                             &other->start[0], &other->start[1],
                                             &result->end[0], &result->end[1]);
    else if (type == ILT_JUMP_IF_FALSE)
        result->edge = g_graph_edge_new_false(src, dst,
                                              &other->start[0], &other->start[1],
                                              &result->end[0], &result->end[1]);
    else
        result->edge = g_graph_edge_new(src, dst,
                                        &other->start[0], &other->start[1],
                                        &result->end[0], &result->end[1]);
    result->other = other;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : owner = propriétaire du bloc de rattachement.                *
*                other = point de départ du lien formé.                       *
*                                                                             *
*  Description : Crée un point d'attache pour un lien entrant de boucle.      *
*                                                                             *
*  Retour      : Structure mise en place.                                     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static incoming_link_t *create_incoming_loop_link(GGraphCluster *owner, const GdkPoint *midpts, leaving_link_t *other)
{
    incoming_link_t *result;                /* Structure à retourner       */
    GCodeBlock *src;                        /* Bloc d'origine du lien      */
    GCodeBlock *dst;                        /* Bloc de destination du lien */
    result = malloc(sizeof(incoming_link_t));
    result->owner = owner;
    result->type = ILT_LOOP;
    src = other->owner->block;
    dst = owner->block;
    result->edge = g_graph_edge_new_loop(src, dst,
                                         &other->start[0], &other->start[1],
                                         &midpts[0], &midpts[1],
                                         &result->end[0], &result->end[1]);
    result->other = other;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : link = structure à libérer de la mémoire.                    *
*                                                                             *
*  Description : Détruit un point d'attache pour un lien entrant.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void delete_incoming_link(incoming_link_t *link)
{
    free(link);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : a = premier lien entrant à comparer.                         *
*                b = second lien entrant à comparer.                          *
*                                                                             *
*  Description : Compare deux liens entrants.                                 *
*                                                                             *
*  Retour      : Bilan de comparaison.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static int cmp_incoming_links(const incoming_link_t **a, const incoming_link_t **b)
{
    int result;                             /* Bilan à retourner           */
    gint pos_a;                             /* Point de départ pour A      */
    gint pos_b;                             /* Point de départ pour B      */
    pos_a = compute_leaving_link_position((*a)->other);
    pos_b = compute_leaving_link_position((*b)->other);
    if (pos_a < pos_b)
        result = -1;
    else if (pos_a > pos_b)
        result = 1;
    else
        result = 0;
    return result;
}
/* ---------------------------------------------------------------------------------- */
/*                          ENCADREMENTS D'ESPACES VERTICAUX                          */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
*                                                                             *
*  Paramètres  : manager = structure à initialiser.                           *
*                                                                             *
*  Description : Initialise les réservations liens verticaux.                 *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void init_vspace_manager(vspace_manager_t *manager)
{
    manager->pending = NULL;
    manager->pending_count = 0;
    manager->left = NULL;
    manager->left_count = 0;
    manager->right = NULL;
    manager->right_count = 0;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : manager = structure à vider.                                 *
*                                                                             *
*  Description : Termine les réservations liens verticaux.                    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void exit_vspace_manager(vspace_manager_t *manager)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < manager->pending_count; i++)
        free(manager->pending[i].pts);
    if (manager->pending != NULL)
        free(manager->pending);
    if (manager->left != NULL)
        free(manager->left);
    if (manager->right != NULL)
        free(manager->right);
}
/******************************************************************************
*                                                                             *
*  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.           *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
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    */
    manager->pending = realloc(manager->pending, ++manager->pending_count * sizeof(vspace_booking_t));
    new = &manager->pending[manager->pending_count - 1];
    new->from = from;
    new->to = to;
    new->pts = pts;
    new->external = external;
}
/******************************************************************************
*                                                                             *
*  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.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void compute_vspace_manager_needed_alloc(const vspace_manager_t *manager, bool external, GtkAllocation *alloc)
{
    gint width;                             /* Largeur supplémentaire      */
    size_t count;                           /* Nombre de liens retenus     */
    size_t i;                               /* Boucle de parcours          */
    width = 0;
    /* 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;
    /* 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 */
    if (!external)
        alloc->height += manager->pending_count * VERTICAL_MARGIN;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : manager = gestion d'espaces latéraux à manipuler.            *
*                                                                             *
*  Description : Réorganise au besoin les liens de boucle entre blocs.        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void sort_incoming_links_for_vspace_manager(vspace_manager_t *manager)
{
    size_t i;                               /* Boucle de parcours          */
    vspace_booking_t *pending;              /* Elément traité              */
    gint x1;                                /* Abscisse de départ de lien  */
    size_t idx;                             /* Indice du lien entrant      */
    gint x2;                                /* Abscisse d'arrivée de lien  */
    bool for_left;                          /* Répartition par la gauche ? */
    for (i = 0; i < manager->pending_count; i++)
    {
        pending = &manager->pending[i];
        x1 = g_graph_cluster_compute_leaving_link_position(pending->from->owner, pending->from->index);
        idx = g_graph_cluster_find_incoming_link(pending->to->owner, pending->from);
        x2 = g_graph_cluster_compute_incoming_link_position(pending->to->owner, idx);
        /**
         * Prise en compte d'une boucle while (1);
         */
        if (pending->from->owner == pending->to->owner)
            for_left = (x2 < x1);
        else
            for_left = (x1 < x2);
        if (for_left)
        {
            manager->left = realloc(manager->left, ++manager->left_count * sizeof(vspace_booking_t *));
            manager->left[manager->left_count - 1] = pending;
        }
        else
        {
            manager->right = realloc(manager->right, ++manager->right_count * sizeof(vspace_booking_t *));
            manager->right[manager->right_count - 1] = pending;
        }
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : manager = structure à actualiser.                            *
*                offset = décalage à appliquer.                               *
*                                                                             *
*  Description : Décale vers la droite un ensemble de points.                 *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void offset_x_vspace_manager(vspace_manager_t *manager, gint offset)
{
    size_t i;                               /* Boucle de parcours          */
    vspace_booking_t *booking;              /* Réservation à traiter       */
    for (i = 0; i < manager->left_count; i++)
    {
        booking = manager->left[i];
        booking->pts[0].x += offset;
        booking->pts[1].x += offset;
    }
    for (i = 0; i < manager->right_count; i++)
    {
        booking = manager->right[i];
        booking->pts[0].x += offset;
        booking->pts[1].x += offset;
    }
}
/******************************************************************************
*                                                                             *
*  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.          *
*                                                                             *
*  Retour      : Eventuelle marge à gauche devenue nécessaire.                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
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 = ++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 = ++count * LINK_MARGIN;
        booking->pts[0].x = needed->x + needed->width + x;
        booking->pts[1].x = needed->x + needed->width + x;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : manager = structure à consulter.                             *
*                needed  = espace nécessaire et alloué pour les blocs.        *
*                                                                             *
*  Description : Détermine les ordonnées de tous les liens en place.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void compute_loop_link_y_with_vspace_manager(const vspace_manager_t *manager, const GtkAllocation *needed)
{
    gint real_bottom;                       /* Point de départ réel        */
    size_t i;                               /* Boucle de parcours          */
    vspace_booking_t *booking;              /* Réservation à traiter       */
    real_bottom = needed->y + needed->height - manager->pending_count * VERTICAL_MARGIN;
    for (i = 0; i < manager->pending_count; i++)
    {
        booking = &manager->pending[i];
        /**
         * On corrige le raccourci pris sans distinction de type de lien dans
         * la fonction g_graph_cluster_compute_link_y_positions().
         */
        booking->from->start[1].y = real_bottom +  (i + 1) * VERTICAL_MARGIN;
        /* Définition de l'ordonnée des points du lien */
        booking->pts[0].y = booking->from->start[1].y;
        booking->pts[1].y = booking->to->end[0].y;
    }
}
/* ---------------------------------------------------------------------------------- */
/*                        DESCENDANTS DIRECTS CLASSES PAR RANG                        */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
*                                                                             *
*  Paramètres  : start = abscisse de départ de ligne.                         *
*                                                                             *
*  Description : Prépare une réservation d'espace pour ligne horizontale.     *
*                                                                             *
*  Retour      : Structure mise en place pour la conservation d'informations. *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static hspace_booking *create_hspace_booking(gint start)
{
    hspace_booking *result;                 /* Structure à retourner       */
    result = malloc(sizeof(hspace_booking));
    result->start = start;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : a = première réservation d'espace à comparer.                *
*                b = seconde réservation d'espace à comparer.                 *
*                                                                             *
*  Description : Compare deux réservations d'espace.                          *
*                                                                             *
*  Retour      : Bilan de comparaison.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static int cmp_hspace_booking_r2l(const hspace_booking **a, const hspace_booking **b)
{
    int result;                             /* Bilan à retourner           */
    if ((*a)->start > (*b)->start)
        result = -1;
    else if ((*a)->start < (*b)->start)
        result = 1;
    else
    {
        assert(false);
        result = 0;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : a = première réservation d'espace à comparer.                *
*                b = seconde réservation d'espace à comparer.                 *
*                                                                             *
*  Description : Compare deux réservations d'espace.                          *
*                                                                             *
*  Retour      : Bilan de comparaison.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static int cmp_hspace_booking_l2r(const hspace_booking **a, const hspace_booking **b)
{
    int result;                             /* Bilan à retourner           */
    if ((*a)->start < (*b)->start)
        result = -1;
    else if ((*a)->start > (*b)->start)
        result = 1;
    else
    {
        assert(false);
        result = 0;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank   = structure à initialiser. [OUT]                     *
*                cluster = chef de file d'un ensemble de blocs.               *
*                                                                             *
*  Description : Initialise la gestion d'un ensemble de blocs de même rang.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void init_graph_rank(graph_rank_t *grank, GGraphCluster *cluster)
{
    grank->right2left = NULL;
    grank->r2l_count = 0;
    grank->left2right = NULL;
    grank->l2r_count = 0;
    grank->clusters = malloc(sizeof(GGraphCluster *));
    grank->count = 1;
    grank->clusters[0] = cluster;
    init_vspace_manager(&grank->vspaces);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank = structure à vider.                                   *
*                                                                             *
*  Description : Termine la gestion d'un ensemble de blocs de même rang.      *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void exit_graph_rank(graph_rank_t *grank)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < grank->r2l_count; i++)
        free(grank->right2left[i]);
    if (grank->right2left != NULL)
        free(grank->right2left);
    for (i = 0; i < grank->l2r_count; i++)
        free(grank->left2right[i]);
    if (grank->left2right != NULL)
        free(grank->left2right);
    assert(grank->clusters != NULL);
    free(grank->clusters);
    exit_vspace_manager(&grank->vspaces);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank = ensemble de même rang à consulter.                   *
*                                                                             *
*  Description : Assigne à un ensemble de blocs un emplacement initial.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void reset_graph_rank_allocation(const graph_rank_t *grank)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < grank->count; i++)
        g_graph_cluster_reset_allocation(grank->clusters[i]);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank = ensemble de même rang à consulter.                   *
*                                                                             *
*  Description : Fournit le rang d'un ensemble de blocs.                      *
*                                                                             *
*  Retour      : Rang d'un ensemble de blocs de même rang.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static size_t get_graph_rank(const graph_rank_t *grank)
{
    size_t result;                          /* Rang à retourner            */
    result = g_code_block_get_rank(grank->clusters[0]->block);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : a = premier ensemble de même rang à comparer.                *
*                b = second ensemble de même rang à comparer.                 *
*                                                                             *
*  Description : Compare deux rangées de blocs de code.                       *
*                                                                             *
*  Retour      : Bilan de comparaison.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static int cmp_graph_rank(const graph_rank_t *a, const graph_rank_t *b)
{
    int result;                             /* Bilan à retourner           */
    size_t level_a;                         /* Niveau de l'ensemble A      */
    size_t level_b;                         /* Niveau de l'ensemble B      */
    level_a = get_graph_rank(a);
    level_b = get_graph_rank(b);
    if (level_a < level_b)
        result = -1;
    else if (level_a > level_b)
        result = 1;
    else
        result = 0;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank   = structure à compléter.                             *
*                cluster = chef de file d'un ensemble de blocs.               *
*                                                                             *
*  Description : Etend un ensemble de blocs de même rang.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void extend_graph_rank(graph_rank_t *grank, GGraphCluster *cluster)
{
    grank->count++;
    grank->clusters = realloc(grank->clusters, sizeof(GGraphCluster *) * grank->count);
    grank->clusters[grank->count - 1] = cluster;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank   = structure à compléter.                             *
*                cluster = chef de file d'un ensemble de blocs.               *
*                                                                             *
*  Description : Détermine si un groupe de blocs contient un bloc particulier.*
*                                                                             *
*  Retour      : true si le chef est bien contenu, false sinon.               *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static bool has_graph_rank_cluster(const graph_rank_t *grank, GGraphCluster *cluster)
{
    bool result;                            /* Bilan à renvoyer            */
    size_t i;                               /* Boucle de parcours          */
    result = false;
    for (i = 0; i < grank->count && !result; i++)
        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, external);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank = ensemble de descendants d'un même rang.              *
*                last  = indique s'il s'agit du dernier étage de l'ensemble.  *
*                alloc = emplacement idéal pour l'affichage. [OUT]            *
*                                                                             *
*  Description : Détermine l'emplacement requis d'un ensemble de blocs.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void compute_graph_rank_needed_alloc(const graph_rank_t *grank, bool last, GtkAllocation *alloc)
{
    GtkAllocation needed;                   /* Taille requise              */
    switch (grank->count)
    {
        case 1:
            g_graph_cluster_compute_needed_alloc(grank->clusters[0], &needed);
            if (needed.x < alloc->x)
            {
                alloc->width += (alloc->x - needed.x);
                alloc->x = needed.x;
            }
            if ((needed.x + needed.width) > (alloc->x + alloc->width))
                alloc->width += needed.x + needed.width - alloc->x - alloc->width;
            /* La hauteur maximale n'est présente qu'avec le dernier morceau */
            if (last)
            {
                if ((needed.y + needed.height) > (alloc->y + alloc->height))
                    alloc->height += needed.y + needed.height - alloc->y - alloc->height;
            }
            break;
        default:
            assert(grank->count >= 2);
            g_graph_cluster_compute_needed_alloc(grank->clusters[0], &needed);
            if (needed.x < alloc->x)
            {
                alloc->width += (alloc->x - needed.x);
                alloc->x = needed.x;
            }
            /* La hauteur maximale n'est présente qu'avec le dernier morceau */
            if (last)
            {
                if ((needed.y + needed.height) > (alloc->y + alloc->height))
                    alloc->height += needed.y + needed.height - alloc->y - alloc->height;
            }
            g_graph_cluster_compute_needed_alloc(grank->clusters[grank->count - 1], &needed);
            if ((needed.x + needed.width) > (alloc->x + alloc->width))
                alloc->width += needed.x + needed.width - alloc->x - alloc->width;
            /* La hauteur maximale n'est présente qu'avec le dernier morceau */
            if (last)
            {
                if ((needed.y + needed.height) > (alloc->y + alloc->height))
                    alloc->height += needed.y + needed.height - alloc->y - alloc->height;
            }
            break;
    }
    compute_vspace_manager_needed_alloc(&grank->vspaces, false, alloc);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : iter = début de la boucle de parcours.                       *
*                loop = nombre d'itérations à mener.                          *
*                base = position de base sur l'axe des abscisses.             *
*                dir  = direction du parcours.                                *
*                                                                             *
*  Description : Affine l'abscisse d'un ensemble de blocs de même rang.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void _place_graph_rank_clusters(GGraphCluster **iter, size_t loop, gint base, int dir)
{
    size_t i;                               /* Boucle de parcours          */
    GtkAllocation needed;                   /* Taille requise              */
    assert(dir == -1 || dir == 1);
    for (i = 0; i < loop; i++, iter += dir)
    {
        g_graph_cluster_dispatch_x(*iter);
        g_graph_cluster_compute_needed_alloc(*iter, &needed);
        if (dir > 0)
            g_graph_cluster_offset_x(*iter, base - needed.x);
        else
            g_graph_cluster_offset_x(*iter, base - needed.x - needed.width);
        base += dir * (needed.width + HORIZONTAL_MARGIN);
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank = ensemble de blocs de même rang à manipuler.          *
*                                                                             *
*  Description : Organise la disposition d'un ensemble de blocs basiques.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void dispatch_x_graph_rank(const graph_rank_t *grank)
{
    size_t mid;                             /* Position centrale de départ */
    gint start;                             /* Position initiale de départ */
    if (grank->count % 2 == 1)
    {
        if (grank->count > 1)
        {
            mid = grank->count / 2;
            start = grank->clusters[mid]->alloc.x - HORIZONTAL_MARGIN;
            _place_graph_rank_clusters(grank->clusters + mid - 1, mid, start, -1);
            start *= -1;
            _place_graph_rank_clusters(grank->clusters + mid + 1, mid, start, 1);
        }
        else
            g_graph_cluster_dispatch_x(grank->clusters[0]);
    }
    else
    {
        mid = grank->count / 2 - 1;
        start = - HORIZONTAL_MARGIN / 2;
        _place_graph_rank_clusters(grank->clusters + mid, mid + 1, start, -1);
        start *= -1;
        _place_graph_rank_clusters(grank->clusters + mid + 1, mid + 1, start, 1);
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank  = ensemble de blocs de même rang à actualiser.        *
*                                                                             *
*  Description : Réorganise au besoin les liens entrants un ensemble de blocs.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void sort_graph_rank_incoming_links(graph_rank_t *grank)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < grank->count; i++)
        g_graph_cluster_sort_incoming_links(grank->clusters[i]);
    sort_incoming_links_for_vspace_manager(&grank->vspaces);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank  = ensemble de blocs de même rang à actualiser.        *
*                                                                             *
*  Description : Réordonne les blocs de départ de boucle d'un ensemble.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void reorder_graph_rank_loop_blocks(graph_rank_t *grank)
{
    size_t i;                               /* Boucle de parcours #1       */
    size_t k;                               /* Boucle de parcours #2       */
    GGraphCluster *tmp;                     /* Stockage temporaire         */
    for (i = 0; i < grank->count; i++)
        g_graph_cluster_reorder_loop_blocks(grank->clusters[i]);
    if (grank->count > 1)
    {
        /* Placement des départs de boucle à gauche ! */
        for (i = 0; i < grank->vspaces.left_count; i++)
        {
            tmp = grank->vspaces.left[i]->from->owner;
            for (k = 0; k < grank->count; k++)
                if (grank->clusters[k] == tmp)
                    break;
            assert(k < grank->count);
            memmove(&grank->clusters[1], &grank->clusters[0], k * sizeof(GGraphCluster *));
            grank->clusters[0] = tmp;
        }
        /* Placement des départs de boucle à droite ! */
        for (i = 0; i < grank->vspaces.right_count; i++)
        {
            tmp = grank->vspaces.right[i]->from->owner;
            for (k = 0; k < grank->count; k++)
                if (grank->clusters[k] == tmp)
                    break;
            assert(k < grank->count);
            memmove(&grank->clusters[k], &grank->clusters[k + 1], (grank->count - k - 1) * sizeof(GGraphCluster *));
            grank->clusters[grank->count - 1] = tmp;
        }
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank  = ensemble de blocs de même rang à actualiser.        *
*                offset = décalage à appliquer.                               *
*                                                                             *
*  Description : Décale vers la droite un ensemble de blocs basiques.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void offset_x_graph_rank(graph_rank_t *grank, gint offset)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < grank->count; i++)
        g_graph_cluster_offset_x(grank->clusters[i], offset);
    offset_x_vspace_manager(&grank->vspaces, offset);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank  = ensemble de blocs de même rang à actualiser.        *
*                needed = espace nécessaire et alloué pour les blocs.         *
*                                                                             *
*  Description : Détermine les abscisses des liens de boucle en place.        *
*                                                                             *
*  Retour      : Eventuelle marge à gauche devenue nécessaire.                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static gint compute_loop_link_x_positions_with_graph_rank(const graph_rank_t *grank, const GtkAllocation *needed)
{
    gint result;                            /* Eventuelle marge à renvoyer */
    size_t i;                               /* Boucle de parcours          */
    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, false);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank = ensemble de blocs de même rang à actualiser.         *
*                base  = position ordonnée à appliquer.                       *
*                                                                             *
*  Description : Décale vers le bas un ensemble de blocs basiques.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void set_y_for_graph_rank(const graph_rank_t *grank, gint *base)
{
    gint max;                               /* Hauteur maximale rencontrée */
    size_t i;                               /* Boucle de parcours          */
    GGraphCluster *sub;                     /* Sous-ensemble traité        */
    GtkAllocation alloc;                    /* Allocation courante         */
    /* On ajoute l'espace vertical pour les lignes horizontales */
    if (grank->r2l_count > grank->l2r_count)
        max = grank->r2l_count;
    else
        max = grank->l2r_count;
    *base += VERTICAL_MARGIN;
    /**
     * Comme les liens purement verticaux n'entrainent pas de réservation,
     * il n'y a potentiellement pas toujours d'espace à ajouter.
     */
    if (max > 0)
    {
        *base += ((max - 1) * LINK_MARGIN);
        *base += VERTICAL_MARGIN;
    }
    /* On ajoute l'espace requis pour l'affichage des blocs */
    max = 0;
    for (i = 0; i < grank->count; i++)
    {
        sub = grank->clusters[i];
        g_graph_cluster_set_y(sub, *base);
        g_graph_cluster_compute_needed_alloc(sub, &alloc);
        if ((alloc.y + alloc.height) > max)
            max = alloc.y + alloc.height;
    }
    *base = max;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank = ensemble de blocs de même rang à actualiser.         *
*                needed  = espace nécessaire et alloué pour les blocs.        *
*                                                                             *
*  Description : Détermine les ordonnées de tous les liens en place.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void compute_loop_link_with_graph_rank(const graph_rank_t *grank, const GtkAllocation *needed)
{
    size_t i;                               /* Boucle de parcours #1       */
    for (i = 0; i < grank->count; i++)
        g_graph_cluster_compute_link_y_positions(grank->clusters[i]);
    compute_loop_link_y_with_vspace_manager(&grank->vspaces, needed);
}
/* ---------------------------------------------------------------------------------- */
/*                            DEFINITION D'UN CHEF DE FILE                            */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini par la GLib pour les mises en disposition graphique. */
G_DEFINE_TYPE(GGraphCluster, g_graph_cluster, G_TYPE_OBJECT);
/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des mises en disposition graphique.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_class_init(GGraphClusterClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    object = G_OBJECT_CLASS(klass);
    object->dispose = (GObjectFinalizeFunc/* ! */)g_graph_cluster_dispose;
    object->finalize = (GObjectFinalizeFunc)g_graph_cluster_finalize;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = instance à initialiser.                            *
*                                                                             *
*  Description : Initialise une mise en disposition de blocs en graphique.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_init(GGraphCluster *cluster)
{
    init_vspace_manager(&cluster->self);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = instance d'objet GLib à traiter.                   *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_dispose(GGraphCluster *cluster)
{
    g_clear_object(&cluster->block);
    g_clear_object(&cluster->display);
    G_OBJECT_CLASS(g_graph_cluster_parent_class)->dispose(G_OBJECT(cluster));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = instance d'objet GLib à traiter.                   *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_finalize(GGraphCluster *cluster)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < cluster->ta_count; i++)
        delete_incoming_link(cluster->top_anchors[i]);
    if (cluster->top_anchors != NULL)
        free(cluster->top_anchors);
    for (i = 0; i < cluster->ba_count; i++)
        delete_leaving_link(cluster->bottom_anchors[i]);
    if (cluster->bottom_anchors != NULL)
        free(cluster->bottom_anchors);
    for (i = 0; i < cluster->ranks_count; i++)
        exit_graph_rank(&cluster->ranks[i]);
    if (cluster->ranks != NULL)
        free(cluster->ranks);
    exit_vspace_manager(&cluster->self);
    G_OBJECT_CLASS(g_graph_cluster_parent_class)->finalize(G_OBJECT(cluster));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : block       = premier bloc du groupe.                        *
*                highlighted = gestionnaire de surbrillance pour segments.    *
*                binary      = binaire charger dont le code est à représenter.*
*                                                                             *
*  Description :             *
*                                                                             *
*  Retour      : Adresse de la structure mise en place.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GGraphCluster *g_graph_cluster_new(GCodeBlock *block, segcnt_list *highlighted, GLoadedBinary *binary)
{
    GGraphCluster *result;                  /* Structure à retourner       */
    GBufferView *view;                      /* Partie affichée du tampon   */
    result = g_object_new(G_TYPE_GRAPH_CLUSTER, NULL);
    /* Encapsulation du bloc d'entrée */
    result->block = block;
    g_object_ref(G_OBJECT(block));
    view = g_code_block_get_view(result->block, highlighted);
    result->display = gtk_block_display_new(view);
    gtk_widget_show(result->display);
    g_loaded_panel_set_content(G_LOADED_PANEL(result->display), G_LOADED_CONTENT(binary));
    gtk_block_display_override_view_index(GTK_BLOCK_DISPLAY(result->display), BVW_GRAPH);
    gtk_display_panel_show_border(GTK_DISPLAY_PANEL(result->display), true);
    g_graph_cluster_reset_allocation(result);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à compléter.                    *
*                                                                             *
*  Description : Assigne à un bloc et son ensemble un emplacement initial.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_reset_allocation(GGraphCluster *cluster)
{
    GtkRequisition requisition;             /* Taille à l'écran actuelle   */
    size_t i;                               /* Boucle de parcours          */
    /* Détermination d'une position initiale centrée */
    gtk_widget_get_preferred_size(cluster->display, NULL, &requisition);
    cluster->alloc.x = -requisition.width / 2;
    cluster->alloc.y = 0;
    cluster->alloc.width = requisition.width;
    cluster->alloc.height = requisition.height;
    /* Propagation aux sous-blocs */
    for (i = 0; i < cluster->ranks_count; i++)
        reset_graph_rank_allocation(&cluster->ranks[i]);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à compléter.                    *
*                sub     = sous-ensemble à intégrer.                          *
*                                                                             *
*  Description : Complète un graphique avec un sous-ensemble de blocs.        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_add_sub(GGraphCluster *cluster, GGraphCluster *sub)
{
    size_t level;                           /* Niveau du nouvel ensemble   */
    size_t i;                               /* Boucle de parcours          */
    graph_rank_t new;                       /* Nouvel étage à insérer      */
    level = g_code_block_get_rank(sub->block);
    for (i = 0; i < cluster->ranks_count; i++)
        if (get_graph_rank(&cluster->ranks[i]) == level)
            break;
    if (i == cluster->ranks_count)
    {
        init_graph_rank(&new, sub);
        cluster->ranks = qinsert(cluster->ranks, &cluster->ranks_count,
                                 sizeof(graph_rank_t), (__compar_fn_t)cmp_graph_rank, &new);
    }
    else
        extend_graph_rank(&cluster->ranks[i], sub);
    sub->owner = cluster;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : grank   = ensemble de descendants d'un même rang.            *
*                source  = bloc courant ou NULL pour limiter les calculs.     *
*                target  = bloc ciblé pour l'arrivée d'un lien.               *
*                leaving = représentation d'un lien sortant.                  *
*                                                                             *
*  Description : Etablit les connexions entre blocs selon les rangs.          *
*                                                                             *
*  Retour      : true si la cible a été rencontrée.                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_setup_link_for_target(GGraphCluster *source, GGraphCluster *target, leaving_link_t *leaving)
{
    size_t level;                           /* Niveau du nouvel ensemble   */
    size_t target_level;                    /* Rang du bloc ciblé          */
    target->parent_index = &leaving->index;
    if (source != NULL)
    {
        level = g_code_block_get_rank(source->block);
        target_level = g_code_block_get_rank(target->block);
        /* Est-ce un lien qui doit être vertical ? */
        if (target_level > (level + 1) && target_level > source->straight_level)
        {
            source->has_straight = true;
            source->straight_level = target_level;
            source->straight_index = source->ba_count - 1;
        }
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : target = 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.       *
*                                                                             *
*  Description : Inscrit à l'endroit idéal une réservation d'espace latéral.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
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;
    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);
        for (i = 0; i < container->ranks_count && !done; i++)
            done = extend_graph_rank_vspace_manager(&container->ranks[i], from, to, pts, true);
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à actualiser.                   *
*                all     = table regroupant tous les groupes créés.           *
*                                                                             *
*  Description : Met en place les embryons de liens nécessaires.              *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_define_links(GGraphCluster *cluster, GHashTable *all)
{
    size_t dcount;                          /* Nombre de liens de dest.    */
    block_link_t *links;                    /* Liens associés au bloc      */
    size_t i;                               /* Boucle de parcours #1       */
    block_link_t *dest;                     /* Bloc visé par un autre      */
    GGraphCluster *target;                  /* Bloc ciblé par un lien      */
    leaving_link_t *leaving;                /* Point de départ d'un lien   */
    incoming_link_t *incoming;              /* Définitions d'arrivée       */
    GdkPoint *midpts;                       /* Points intermédiaires       */
    size_t j;                               /* Boucle de parcours #2       */
    /* Au niveau du bloc courant */
    links = g_code_block_get_destinations(cluster->block, &dcount);
    for (i = 0; i < dcount; i++)
    {
        dest = &links[i];
        switch (dest->type)
        {
            case ILT_EXEC_FLOW:
            case ILT_JUMP:
            case ILT_CASE_JUMP:
            case ILT_JUMP_IF_TRUE:
            case ILT_JUMP_IF_FALSE:
                target = G_GRAPH_CLUSTER(g_hash_table_lookup(all, dest->linked));
                assert(target != NULL);
                /* Point de départ */
                leaving = create_leaving_link(cluster, cluster->ba_count);
                cluster->bottom_anchors = realloc(cluster->bottom_anchors,
                                                  ++cluster->ba_count * sizeof(leaving_link_t *));
                cluster->bottom_anchors[cluster->ba_count - 1] = leaving;
                /* Point d'arrivée */
                incoming = create_incoming_link(target, dest->type, leaving);
                target->top_anchors = realloc(target->top_anchors,
                                              ++target->ta_count * sizeof(incoming_link_t *));
                target->top_anchors[target->ta_count - 1] = incoming;
                /* Etablissement d'un embryon de lien */
                g_graph_cluster_setup_link_for_target(cluster, target, leaving);
                break;
            case ILT_LOOP:
                target = G_GRAPH_CLUSTER(g_hash_table_lookup(all, dest->linked));
                assert(target != NULL);
                /* Point de départ */
                leaving = create_leaving_link(cluster, cluster->ba_count);
                cluster->bottom_anchors = realloc(cluster->bottom_anchors,
                                                  ++cluster->ba_count * sizeof(leaving_link_t *));
                cluster->bottom_anchors[cluster->ba_count - 1] = leaving;
                /* Point d'arrivée */
                midpts = malloc(2 * sizeof(GdkPoint));
                incoming = create_incoming_loop_link(target, midpts, leaving);
                target->top_anchors = realloc(target->top_anchors,
                                              ++target->ta_count * sizeof(incoming_link_t *));
                target->top_anchors[target->ta_count - 1] = incoming;
                /* Réservation d'un espace latéral */
                g_graph_cluster_extend_vspace_manager(target, leaving, incoming, midpts);
                /* Etablissement d'un embryon de lien */
                g_graph_cluster_setup_link_for_target(NULL, target, leaving);
                break;
            default:
                break;
        }
        unref_block_link(dest);
    }
    if (links != NULL)
        free(links);
    /* Doit-on forcer un lien strictement vertical ? */
    if (cluster->ba_count == 1 && !cluster->has_straight)
    {
        /**
         * Attention : les boucles aussi ont un seul lien sortant !
         *
         * S'il n'y a qu'un seul lien, on peut s'appuyer sur la variable 'incoming'
         * manipulée dans la boucle : c'est forcément elle qui a été mise en place.
         *
         * Même chose pour 'target'.
         */
        if (incoming->type != ILT_LOOP)
        {
            cluster->has_straight = true;
            cluster->straight_level = g_code_block_get_rank(target->block);
            cluster->straight_index = 0;
        }
    }
    /* Déplacement d'un éventuel lien central en position centrale */
    if (cluster->has_straight)
    {
        size_t center;
        leaving_link_t *tmp;
        if (cluster->ba_count % 2 == 0)
            center = cluster->ba_count / 2 - 1;
        else
            center = cluster->ba_count / 2;
        if (cluster->straight_index < center)
        {
            tmp = cluster->bottom_anchors[cluster->straight_index];
            memmove(cluster->bottom_anchors + cluster->straight_index,
                    cluster->bottom_anchors + cluster->straight_index + 1,
                    (center - cluster->straight_index) * sizeof(leaving_link_t *));
            cluster->bottom_anchors[center] = tmp;
            for (i = cluster->straight_index; i <= center; i++)
                cluster->bottom_anchors[i]->index = i;
            cluster->straight_index = center;
        }
        else if (cluster->straight_index > center)
        {
            tmp = cluster->bottom_anchors[cluster->straight_index];
            memmove(cluster->bottom_anchors + center + 1,
                    cluster->bottom_anchors + center,
                    (cluster->straight_index - center) * sizeof(leaving_link_t *));
            cluster->bottom_anchors[center] = tmp;
            for (i = center; i <= cluster->straight_index ; i++)
                cluster->bottom_anchors[i]->index = i;
            cluster->straight_index = center;
        }
    }
    /* Propagation de la mise en place */
    for (i = 0; i < cluster->ranks_count; i++)
        for (j = 0; j < cluster->ranks[i].count; j++)
            g_graph_cluster_define_links(cluster->ranks[i].clusters[j], all);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à manipuler.                    *
*                                                                             *
*  Description : Organise la disposition d'un ensemble de blocs basiques.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_dispatch_x(GGraphCluster *cluster)
{
    size_t k;                               /* Boucle de parcours #1       */
    const graph_rank_t *rank;               /* Accès confortable au rang   */
    size_t j;                               /* Boucle de parcours #2       */
    gint start;                             /* Position initiale de départ */
    GGraphCluster *target;                  /* Unique sous-bloc visé       */
    size_t idx;                             /* Indice du lien entrant      */
    gint end;                               /* Position initiale d'arrivée */
    /**
     * Il est désormais temps de placer tous les blocs de code inférieurs.
     */
    for (k = 0; k < cluster->ranks_count; k++)
    {
        rank = &cluster->ranks[k];
        /* Répartition autour d'une ligne verticale */
        if (cluster->has_straight)
        {
            start = g_graph_cluster_compute_leaving_link_position(cluster, cluster->straight_index);
            if (get_graph_rank(rank) < cluster->straight_level)
            {
                /* Répartition à gauche du lien */
                for (j = rank->count; j > 0; j--)
                    if (*rank->clusters[j - 1]->parent_index < cluster->straight_index)
                        break;
                start -= HORIZONTAL_MARGIN / 2;
                _place_graph_rank_clusters(rank->clusters, j, start, -1);
                /* Répartition à droite du lien */
                for (j = 0; j < rank->count; j++)
                    if (*rank->clusters[j]->parent_index > cluster->straight_index)
                        break;
                start += HORIZONTAL_MARGIN;
                _place_graph_rank_clusters(rank->clusters + j, rank->count - j, start, 1);
            }
            else if (get_graph_rank(rank) == cluster->straight_level)
            {
                /**
                 * Si le bloc pointé en direct a plus d'un lien en entrée (comme
                 * dans le cas d'un bloc qui assure un début de boucle par exemple),
                 * le lien direct n'est pas centré sur le milieu de ce bloc pointé.
                 *
                 * On corrige ici le léger décalage.
                 */
                assert(rank->count == 1);
                target = rank->clusters[0];
                idx = g_graph_cluster_find_incoming_link(target, cluster->bottom_anchors[cluster->straight_index]);
                end = g_graph_cluster_compute_incoming_link_position(target, idx);
                g_graph_cluster_offset_x(target, start - end);
                dispatch_x_graph_rank(rank);
            }
            else
                dispatch_x_graph_rank(rank);
        }
        /* Répartition homogène */
        else
            dispatch_x_graph_rank(rank);
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à manipuler.                    *
*                                                                             *
*  Description : Réorganise au besoin les liens entrants d'un bloc.           *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_sort_incoming_links(GGraphCluster *cluster)
{
    size_t i;                               /* Boucle de parcours          */
    qsort(cluster->top_anchors, cluster->ta_count, sizeof(incoming_link_t *), (__compar_fn_t)cmp_incoming_links);
    for (i = 0; i < cluster->ranks_count; i++)
        sort_graph_rank_incoming_links(&cluster->ranks[i]);
    sort_incoming_links_for_vspace_manager(&cluster->self);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster  = graphique de blocs à consulter.                   *
*                incoming = adresse de l'autre bout du lien concerné.         *
*                                                                             *
*  Description : Retrouve l'indice d'un lien entrant donné pour un bloc.      *
*                                                                             *
*  Retour      : Indice à priori toujours valide.                             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static size_t g_graph_cluster_find_incoming_link(const GGraphCluster *cluster, const leaving_link_t *leaving)
{
    size_t result;                          /* Indice à retourner          */
    for (result = 0; result < cluster->ta_count; result++)
        if (cluster->top_anchors[result]->other == leaving)
            break;
    assert(cluster->ta_count);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à actualiser.                   *
*                                                                             *
*  Description : Réordonne les blocs de départ de boucle au mieux.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_reorder_loop_blocks(GGraphCluster *cluster)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < cluster->ranks_count; i++)
        reorder_graph_rank_loop_blocks(&cluster->ranks[i]);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à actualiser.                   *
*                offset  = décalage à appliquer.                              *
*                                                                             *
*  Description : Décale vers la droite un ensemble de blocs basiques.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_offset_x(GGraphCluster *cluster, gint offset)
{
    size_t i;                               /* Boucle de parcours          */
    cluster->alloc.x += offset;
    for (i = 0; i < cluster->ranks_count; i++)
        offset_x_graph_rank(&cluster->ranks[i], offset);
    offset_x_vspace_manager(&cluster->self, offset);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à actualiser.                   *
*                base    = position ordonnée à appliquer.                     *
*                                                                             *
*  Description : Décale vers le bas un ensemble de blocs basiques.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_set_y(GGraphCluster *cluster, gint base)
{
    size_t i;                               /* Boucle de parcours          */
    cluster->alloc.y = base;
    base += cluster->alloc.height;
    for (i = 0; i < cluster->ranks_count; i++)
        set_y_for_graph_rank(&cluster->ranks[i], &base);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = encapsulation à consulter.                         *
*                alloc   = emplacement idéal pour l'affichage.                *
*                                                                             *
*  Description : Détermine l'emplacement requis d'un ensemble de blocs.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void g_graph_cluster_compute_needed_alloc(const GGraphCluster *cluster, GtkAllocation *alloc)
{
    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);
    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);
        }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = encapsulation à consulter.                         *
*                                                                             *
*  Description : Fournit le composant graphique principal du groupe.          *
*                                                                             *
*  Retour      : Composant graphique principal utilisé.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GtkWidget *g_graph_cluster_get_widget(GGraphCluster *cluster)
{
    GtkWidget *result;
    result = cluster->display;
    g_object_ref(G_OBJECT(result));
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = encapsulation à traiter.                           *
*                display = support de destination finale.                     *
*                                                                             *
*  Description : Dispose chaque noeud sur la surface de destination donnée.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void g_graph_cluster_place(GGraphCluster *cluster, GtkGraphDisplay *display)
{
    size_t i;                               /* Boucle de parcours #1       */
    size_t j;                               /* Boucle de parcours #2       */
    g_object_ref(G_OBJECT(cluster->display));
    gtk_graph_display_put(display, cluster->display, &cluster->alloc);
    for (i = 0; i < cluster->ta_count; i++)
    {
        g_object_ref(G_OBJECT(cluster->top_anchors[i]->edge));
        gtk_graph_display_add_edge(display, cluster->top_anchors[i]->edge);
    }
    for (i = 0; i < cluster->ranks_count; i++)
        for (j = 0; j < cluster->ranks[i].count; j++)
            g_graph_cluster_place(cluster->ranks[i].clusters[j], display);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à consulter.                    *
*                index   = indice du lien à considérer.                       *
*                half    = moitié du nombre de liens en présence.             *
*                odd     = le nombre de liens considérés est-il impair ?      *
*                                                                             *
*  Description : Calcule l'abscisse d'un lien pour un bloc.                   *
*                                                                             *
*  Retour      : Abscisse à attribuer au lien.                                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static gint _g_graph_cluster_compute_link_position(const GGraphCluster *cluster, size_t index, size_t half, bool odd)
{
    gint result;                            /* Position à retourner        */
    gint mid_x;                             /* Abscisse centrale           */
    mid_x = cluster->alloc.x + (cluster->alloc.width / 2);
    if (odd)
    {
        if (index < half)
            result = mid_x - (half - index) * LINK_MARGIN;
        else if (index == half)
            result = mid_x;
        else
            result = mid_x + (index - half) * LINK_MARGIN;
    }
    else
    {
        if (index < half)
            result = mid_x - LINK_MARGIN / 2 - (half - index - 1) * LINK_MARGIN;
        else
            result = mid_x + LINK_MARGIN / 2 + (index - half) * LINK_MARGIN;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à consulter.                    *
*                index   = indice du lien à considérer.                       *
*                                                                             *
*  Description : Calcule l'abscisse d'un lien à son départ d'un bloc.         *
*                                                                             *
*  Retour      : Abscisse à attribuer à un départ de lien.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static gint g_graph_cluster_compute_leaving_link_position(const GGraphCluster *cluster, size_t index)
{
    gint result;                            /* Position à retourner        */
    size_t half;                            /* Indice de répartition égale */
    bool odd;                               /* Partité du nombre de liens  */
    assert(index < cluster->ba_count);
    half = cluster->ba_count / 2;
    odd = (cluster->ba_count % 2 == 1);
    result = _g_graph_cluster_compute_link_position(cluster, index, half, odd);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à consulter.                    *
*                index   = indice du lien à considérer.                       *
*                                                                             *
*  Description : Calcule l'abscisse d'un lien à son arrivée à un bloc.        *
*                                                                             *
*  Retour      : Abscisse à attribuer à une arrivée de lien.                  *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static gint g_graph_cluster_compute_incoming_link_position(const GGraphCluster *cluster, size_t index)
{
    gint result;                            /* Position à retourner        */
    size_t half;                            /* Indice de répartition égale */
    bool odd;                               /* Partité du nombre de liens  */
    assert(index < cluster->ta_count);
    half = cluster->ta_count / 2;
    odd = (cluster->ta_count % 2 == 1);
    result = _g_graph_cluster_compute_link_position(cluster, index, half, odd);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à actualiser.                   *
*                margin  = espace nécessaire à gauche aux liens de boucle.    *
*                                                                             *
*  Description : Ajoute une marge à gauche pour les liens remontants.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_insert_left_margin(GGraphCluster *cluster, gint margin)
{
    GGraphCluster *container;               /* Parent direct à décaler     */
    if (margin > 0)
    {
        /**
         * Si la routine est une boucle sans fin,
         * alors la boucle peut renvoyer vers le premier bloc.
         */
        if (cluster->owner != NULL)
        {
            container = cluster->owner;
            /**
             * On recherche le plus haut propritétaire bénéficiant d'une chaîne
             * de liens directs et droits, histoire de transmettre le décalage
             * et de garder ces liens bien verticaux.
             */
            while (container->owner != NULL)
            {
                if (!container->owner->has_straight)
                    break;
                if (container->owner->straight_index != *container->parent_index)
                    break;
                container = container->owner;
            }
            g_graph_cluster_offset_x(container, margin);
        }
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à actualiser.                   *
*                                                                             *
*  Description : Détermine les abscisses des liens de boucle en place.        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
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 #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 */
    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);
        margin = compute_loop_link_x_positions_with_graph_rank(&cluster->ranks[i], &alloc);
        g_graph_cluster_insert_left_margin(cluster, margin);
    }
    /* Liens de boucle (#1) */
    g_graph_cluster_compute_needed_alloc(cluster, &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);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à actualiser.                   *
*                                                                             *
*  Description : Détermine les abscisses de tous les liens en place.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_compute_link_x_positions(GGraphCluster *cluster)
{
    gint mid_x;                             /* Abscisse centrale           */
    size_t i;                               /* Boucle de parcours #1       */
    size_t half;                            /* Indice de répartition égale */
    GdkPoint *pt;                           /* Point à actualiser          */
    size_t j;                               /* Boucle de parcours #2       */
    mid_x = cluster->alloc.x + (cluster->alloc.width / 2);
    /* Du côté des départs... */
    if (cluster->ba_count > 0)
        for (i = 0; i < cluster->ba_count; i++)
        {
            pt = &cluster->bottom_anchors[i]->start[0];
            pt->x = g_graph_cluster_compute_leaving_link_position(cluster, i);
            cluster->bottom_anchors[i]->start[1].x = pt->x;
        }
    /* Du côté des arrivées... */
    if (cluster->ta_count > 0)
    {
        half = cluster->ta_count / 2;
        if (cluster->ta_count % 2 == 1)
        {
            for (i = half; i > 0; i--)
            {
                pt = &cluster->top_anchors[i - 1]->end[1];
                pt->x = mid_x - (half - i + 1) * LINK_MARGIN;
            }
            cluster->top_anchors[half]->end[1].x = mid_x;
            for (i = half + 1; i < cluster->ta_count; i++)
            {
                pt = &cluster->top_anchors[i]->end[1];
                pt->x = mid_x + (i - half) * LINK_MARGIN;
            }
        }
        else
        {
            for (i = half; i > 0; i--)
            {
                pt = &cluster->top_anchors[i - 1]->end[1];
                pt->x = mid_x - LINK_MARGIN / 2 - (half - i) * LINK_MARGIN;
            }
            for (i = half; i < cluster->ta_count; i++)
            {
                pt = &cluster->top_anchors[i]->end[1];
                pt->x = mid_x + LINK_MARGIN / 2 + (half - i) * LINK_MARGIN;
            }
        }
    }
    for (i = 0; i < cluster->ta_count; i++)
        cluster->top_anchors[i]->end[0].x = cluster->top_anchors[i]->end[1].x;
    /* Propagation des déterminations */
    for (i = 0; i < cluster->ranks_count; i++)
        for (j = 0; j < cluster->ranks[i].count; j++)
            g_graph_cluster_compute_link_x_positions(cluster->ranks[i].clusters[j]);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à traiter.                      *
*                                                                             *
*  Description : Réserve de l'espace vertical pour les lignes horizontales.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_book_hspace_for_links(GGraphCluster *cluster)
{
    size_t i;                               /* Boucle de parcours #1       */
    graph_rank_t *rank;                     /* Rangée à manipuler          */
    size_t j;                               /* Boucle de parcours #2       */
    GGraphCluster *sub;                     /* Bloc inférieur à manipuler  */
    size_t k;                               /* Boucle de parcours #3       */
    gint x1;                                /* Abscisse de départ de lien  */
    gint x2;                                /* Abscisse d'arrivée de lien  */
    hspace_booking *new;                    /* Nouvelle réservation        */
    for (i = 0; i < cluster->ranks_count; i++)
    {
        rank = &cluster->ranks[i];
        /* Enregistrement des besoins */
        for (j = 0; j < rank->count; j++)
        {
            sub = rank->clusters[j];
            for (k = 0; k < sub->ta_count; k++)
            {
                g_graph_edge_get_x_borders(sub->top_anchors[k]->edge, &x1, &x2);
                new = create_hspace_booking(x1);
                sub->top_anchors[k]->hslot = &new->index;
                if (x1 > x2)
                    rank->right2left = qinsert(rank->right2left, &rank->r2l_count,
                                               sizeof(hspace_booking *),
                                               (__compar_fn_t)cmp_hspace_booking_r2l, &new);
                else if (x1 < x2)
                    rank->left2right = qinsert(rank->left2right, &rank->l2r_count,
                                               sizeof(hspace_booking *),
                                               (__compar_fn_t)cmp_hspace_booking_l2r, &new);
                else
                    sub->top_anchors[k]->hslot = NULL;
            }
        }
        /* Définition des couches */
        for (j = 0; j < rank->r2l_count; j++)
            rank->right2left[j]->index = j;
        for (j = 0; j < rank->l2r_count; j++)
            rank->left2right[j]->index = j;
        /* Propagation des déterminations */
        for (j = 0; j < rank->count; j++)
            g_graph_cluster_book_hspace_for_links(rank->clusters[j]);
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à actualiser.                   *
*                                                                             *
*  Description : Détermine les ordonnées de tous les liens en place.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_compute_link_y_positions(GGraphCluster *cluster)
{
    gint y;                                 /* Ordonnée d'application      */
    size_t i;                               /* Boucle de parcours          */
    incoming_link_t *incoming;              /* Raccourci pour le confort   */
    GtkAllocation alloc;                    /* Emplacement à faire évoluer */
    /* Du côté des départs... */
    if (cluster->ba_count > 0)
    {
        y = cluster->alloc.y + cluster->alloc.height;
        for (i = 0; i < cluster->ba_count; i++)
            cluster->bottom_anchors[i]->start[0].y = y;
    }
    /* Du côté des arrivées... */
    if (cluster->ta_count > 0)
    {
        y = cluster->alloc.y;
        for (i = 0; i < cluster->ta_count; i++)
        {
            incoming = cluster->top_anchors[i];
            incoming->end[1].y = y;
            incoming->end[0].y = incoming->end[1].y - VERTICAL_MARGIN;
            if (incoming->hslot != NULL)
                incoming->end[0].y -= *incoming->hslot * LINK_MARGIN;
            incoming->other->start[1].y = incoming->end[0].y;
        }
    }
    /* Propagation des déterminations */
    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_loop_link_with_graph_rank(&cluster->ranks[i], &alloc);
    }
    /* Définition des liens de boucle */
    compute_loop_link_y_with_vspace_manager(&cluster->self, &cluster->alloc);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : cluster = graphique de blocs à consulter.                    *
*                                                                             *
*  Description : Applique les positions calculées pour chaque lien graphique. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_graph_cluster_resolve_links(const GGraphCluster *cluster)
{
    size_t i;                               /* Boucle de parcours #1       */
    size_t j;                               /* Boucle de parcours #2       */
    for (i = 0; i < cluster->ta_count; i++)
        g_graph_edge_resolve(cluster->top_anchors[i]->edge);
    for (i = 0; i < cluster->ranks_count; i++)
        for (j = 0; j < cluster->ranks[i].count; j++)
            g_graph_cluster_resolve_links(cluster->ranks[i].clusters[j]);
}
/* ---------------------------------------------------------------------------------- */
/*                           CALCUL DE REPARTITION DE BLOCS                           */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
*                                                                             *
*  Paramètres  : binary      = binaire charger dont le code est à représenter.*
*                list        = ensemble de blocs basiques à manipuler.        *
*                index       = indice du bloc principal à mettre en place.    *
*                highlighted = gestionnaire de surbrillance pour segments.    *
*                pending     = liste de blocs restant à traiter. [OUT]        *
*                all         = table regroupant tous les groupes créés.       *
*                                                                             *
*  Description : Met en place un ensemble de blocs sous forme graphique.      *
*                                                                             *
*  Retour      : Ensemble de blocs mis en place.                              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static GGraphCluster *setup_graph_clusters(GLoadedBinary *binary, const GBlockList *list, size_t index, segcnt_list *highlighted, pending_blocks *pending, GHashTable *all)
{
    GGraphCluster *result;                  /* Instance nouvelle à renvoyer*/
    GCodeBlock *block;                      /* Bloc à manipuler            */
#ifndef NDEBUG
    gboolean new;                           /* Bloc déjà traité ?          */
#endif
    size_t dcount;                          /* Nombre de liens de dest.    */
    block_link_t *links;                    /* Liens associés au bloc      */
    size_t i;                               /* Boucle de parcours #1       */
    block_link_t *dest;                     /* Bloc visé par un autre      */
    size_t j;                               /* Boucle de parcours #2       */
    bool changed;                           /* Un ajout a été effectué ?   */
    const bitfield_t *dominators;           /* Blocs dominant ce même bloc */
    size_t next;                            /* Indice du prochain bloc     */
    GGraphCluster *sub;                     /* Sous-ensemble à intégrer    */
    block = g_block_list_get_block(list, index);
    result = g_graph_cluster_new(block, highlighted, binary);
#ifndef NDEBUG
    new = g_hash_table_insert(all, block, result);
    assert(new);
#else
    g_hash_table_insert(all, block, result);
#endif
    /* Détermination des blocs suivants */
    links = g_code_block_get_destinations(block, &dcount);
    for (i = 0; i < dcount; i++)
    {
        dest = &links[i];
        switch (dest->type)
        {
            case ILT_EXEC_FLOW:
            case ILT_JUMP:
            case ILT_CASE_JUMP:
            case ILT_JUMP_IF_TRUE:
            case ILT_JUMP_IF_FALSE:
                for (j = 0; j < pending->count; j++)
                    if (pending->list[j] == dest->linked)
                        break;
                if (j == pending->count)
                {
                    /**
                     * Il faut vérifier ici si la destination n'a pas déjà été
                     * empruntée, sauf peine de faire réagir l'assertion plus
                     * haut au moment de l'insertion.
                     *
                     * Le type de code à problème est le suivant :
                     *
                     *    ...
                     *    if (...)
                     *        ...
                     *    ...
                     *
                     * Le code suivant le bloc conditionnel a deux origines,
                     * qui vont chacune poursuivre le traitement vers ce code
                     * commun.
                     *
                     * Et comme les origines ne sont pas dominantes, on utilise
                     * la table globale.
                     */
                    if (!g_hash_table_contains(all, dest->linked))
                    {
                        g_object_ref(G_OBJECT(dest->linked));
                        assert((pending->count + 1) < g_block_list_count_blocks(list));
                        pending->list[pending->count++] = dest->linked;
                    }
                }
                break;
            default:
                break;
        }
        unref_block_link(dest);
    }
    if (links != NULL)
        free(links);
    g_object_unref(G_OBJECT(block));
    /* Intégration de tous les blocs en attente */
    do
    {
        changed = false;
        for (i = 0; i < pending->count && !changed; i++)
        {
            block = pending->list[i];
            dominators = g_code_block_get_domination(block);
            if (test_in_bit_field(dominators, index))
            {
                /* Dépilement */
                changed = true;
                if ((i + 1) < pending->count)
                    memmove(&pending->list[i], &pending->list[i + 1],
                            (pending->count - i - 1) * sizeof(GCodeBlock *));
                pending->count--;
                /* Intégration */
                next = g_code_block_get_index(block);
                assert(next < g_block_list_count_blocks(list));
                sub = setup_graph_clusters(binary, list, next, highlighted, pending, all);
                g_graph_cluster_add_sub(result, sub);
            }
            g_object_ref(G_OBJECT(block));
        }
    }
    while (changed);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary      = binaire charger dont le code est à représenter.*
*                list        = ensemble de blocs basiques à manipuler.        *
*                highlighted = gestionnaire de surbrillance pour segments.    *
*                                                                             *
*  Description : Construit un graphique à partir de blocs basiques.           *
*                                                                             *
*  Retour      : Adresse de la structure mise en place.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GGraphCluster *bootstrap_graph_cluster(GLoadedBinary *binary, const GBlockList *list, segcnt_list *highlighted)
{
    GGraphCluster *result;                  /* Structure à retourner       */
    GHashTable *all;                        /* Collection des créations    */
    size_t count;                           /* Taille de la liste de blocs */
    pending_blocks *pending;                /* Suivi des blocs à traiter   */
    GtkAllocation needed;                   /* Taille requise              */
    /* Création des éléments */
    all = g_hash_table_new(NULL, NULL);
    count = g_block_list_count_blocks(list);
    pending = malloc(sizeof(pending_blocks) + count * sizeof(GCodeBlock *));
    pending->count = 0;
    result = setup_graph_clusters(binary, list, 0, highlighted, pending, all);
    free(pending);
    g_graph_cluster_define_links(result, all);
    /* Positionnements dans l'espace */
    g_graph_cluster_dispatch_x(result);
    /**
     * A ce point, tous les blocs sont placés.
     * On est donc en mesure de réorganiser les points d'arrivée
     * des liens afin d'éviter les croisements : un lien qui vient
     * de la gauche ne doit pas arriver tout à droite !
     *
     * Cette consigne est valable pour les liens de boucle également, dont
     * l'origine est toujours dans un bloc inférieur au bloc de destination.
     * Le premier étant traité après le second, cela oblige à appeler
     * g_graph_cluster_dispatch_x() au moins deux fois donc, car on ne peut
     * effectuer le tri des liens au début de cette fonction comme c'était
     * fait dans les premières versions du code.
     */
    g_graph_cluster_sort_incoming_links(result);
    /**
     * Même s'ils ne sont pas encore entièrement tracés, les liens de boucle
     * voient désormais leurs positions d'arrivée et de départ définies.
     *
     * On sait si lesdits liens partent vers la gauche ou la droite.
     *
     * On est donc en mesure de réorganiser latéralement les blocs
     * pour tirer les traits horizontaux au plus court !
     */
    g_graph_cluster_reset_allocation(result);
    g_graph_cluster_reorder_loop_blocks(result);
    /**
     * Placement horizontal définitif.
     */
    g_graph_cluster_reset_allocation(result);
    g_graph_cluster_dispatch_x(result);
    g_graph_cluster_sort_incoming_links(result);
    /* Réajustement vers la droite */
    g_graph_cluster_compute_needed_alloc(result, &needed);
    g_graph_cluster_offset_x(result, -needed.x);
    /* Application finale sur les liens */
    /**
     * Comme g_graph_cluster_offset_x() n'agit que sur les abscisses et non sur
     * les largeurs, on ne peut pas définir les positions pour les liens de boucle
     * en amont et les décaler ensuite.
     *
     * Et comme la mise en place de ce type de lien peut déplacer le bloc parent,
     * ses repères pour ses propres liens peuvent être décaler. Il faut ainsi
     * une procédure distincte de g_graph_cluster_compute_link_x_positions().
     *
     * On définit donc l'abscisse de ces liens ici, en redécalant encore un peu
     * les blocs au besoin.
     */
    g_graph_cluster_compute_loop_link_x_positions(result);
    g_graph_cluster_compute_link_x_positions(result);
    g_graph_cluster_book_hspace_for_links(result);
    g_graph_cluster_set_y(result, 0);
    g_graph_cluster_compute_link_y_positions(result);
    g_graph_cluster_resolve_links(result);
    /* Sortie propre */
    g_hash_table_unref(all);
    return result;
}