/* Chrysalide - Outil d'analyse de fichiers binaires
 * gtkgraphview.c - affichage de morceaux de code sous forme graphique
 *
 * Copyright (C) 2009-2014 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  OpenIDA 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.
 *
 *  OpenIDA is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "gtkgraphview.h"


#include <assert.h>


#include "gtkblockview.h"
#include "gtkbufferview.h"
#include "gtkviewpanel-int.h"
#include "graph/layout.h"
#include "../analysis/blocks/flow.h"
#include "../gui/editem.h"



/* Composant d'affichage sous forme graphique (instance) */
struct _GtkGraphView
{
    GtkViewPanel parent;                    /* A laisser en premier        */
    GtkWidget *support;                     /* Support des vues en bloc    */

    GBinRoutine *routine;                   /* Routine en cours d'affichage*/

    segcnt_list *highlighted;               /* Segments mis en évidence    */
    GtkBufferView **children;               /* Liste des sous-blocs        */
    GtkAllocation *allocs;                  /* Emplacements prévisibles    */
    size_t children_count;                  /* Taille de cette liste       */

    GGraphLayout *layout;                   /* Disposition en graphique    */

    gdouble start_x;                        /* Abscisse du point de souris */
    gdouble start_y;                        /* Ordonnée du point de souris */
    bool big_enough;                        /* Capacités de déplacement ?  */
    gdouble ref_h;                          /* Position horizontale de ref.*/
    gdouble ref_v;                          /* Position verticale de ref.  */

};

/* Composant d'affichage sous forme graphique (classe) */
struct _GtkGraphViewClass
{
    GtkViewPanelClass parent;               /* A laisser en premier        */

};



/* Initialise la classe générique des graphiques de code. */
static void gtk_graph_view_class_init(GtkGraphViewClass *);

/* Initialise une instance d'afficheur de code en graphique. */
static void gtk_graph_view_init(GtkGraphView *);

/* Indique les dimensions de travail du composant d'affichage. */
static void gtk_graph_view_compute_requested_size(GtkGraphView *, gint *, gint *);

/* Réagit à un défilement chez une barre associée au composant. */
static void gtk_graph_view_adjust_scroll_value(GtkGraphView *, GtkAdjustment *, GtkOrientation);

/*  Met à jour l'affichage de la vue sous forme graphique. */
static gboolean gtk_graph_view_draw(GtkWidget *, cairo_t *, GtkGraphView *);

/* Assure la gestion des clics de souris sur le composant. */
static gboolean gtk_graph_view_button_press(GtkWidget *, GdkEventButton *, GtkGraphView *);

/* Assure la gestion des clics de souris sur le composant. */
static gboolean gtk_graph_view_button_release(GtkWidget *, GdkEventButton *, GtkGraphView *);

/* Assure la suivi des déplacements de souris sur le composant. */
static gboolean gtk_graph_view_motion_notify(GtkWidget *, GdkEventMotion *, GtkGraphView *);

/* Actualise les besoins internes avant un redimensionnement. */
static void gtk_graph_view_prepare_resize(GtkGraphView *);

/* Réagit à la sélection externe d'une adresse. */
static void gtk_graph_view_define_main_address(GtkGraphView *, const vmpa2t *);

/* Indique la position courante du curseur. */
static const vmpa2t *gtk_graph_view_get_caret_location(const GtkGraphView *);

/* Indique la position d'affichage d'une adresse donnée. */
static bool gtk_graph_view_get_address_coordinates(const GtkGraphView *, const vmpa2t *addr, gint *x, gint *y, ScrollPositionTweak tweak);

/* Déplace le curseur à un emplacement défini. */
static bool gtk_graph_view_move_caret_to(GtkGraphView *, gint, gint);

/* Place en cache un rendu destiné à l'aperçu graphique rapide. */
static void gtk_graph_view_cache_glance(GtkGraphView *, cairo_t *, const GtkAllocation *, double);

/* Supprime tout contenu de l'afficheur de code en graphique. */
static void gtk_graph_view_reset(GtkGraphView *);

/* Définit la liste complète des éléments du futur graphique. */
static GtkBufferView **gtk_graph_view_load_nodes(GtkGraphView *, GLoadedBinary *, const GBinRoutine *);

/* Notifie un changement de surbrillance au sein d'un noeud. */
static void gtk_graph_view_changed_highlights(GtkBlockView *, GtkGraphView *);

/* Notifie une incapacité de déplacement au sein d'un noeud. */
static void gtk_graph_view_reach_caret_limit(GtkBufferView *, GdkScrollDirection, GtkGraphView *);



/* Détermine le type du composant d'affichage en graphique. */
G_DEFINE_TYPE(GtkGraphView, gtk_graph_view, GTK_TYPE_VIEW_PANEL)


/******************************************************************************
*                                                                             *
*  Paramètres  : class = classe GTK à initialiser.                            *
*                                                                             *
*  Description : Initialise la classe générique des graphiques de code.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_class_init(GtkGraphViewClass *class)
{
    GtkViewPanelClass *panel_class;         /* Classe parente              */

    panel_class = GTK_VIEW_PANEL_CLASS(class);

    panel_class->compute_size = (compute_requested_size_fc)gtk_graph_view_compute_requested_size;
    panel_class->adjust = (adjust_scroll_value_fc)gtk_graph_view_adjust_scroll_value;
    panel_class->define = (define_address_fc)gtk_graph_view_define_main_address;

    panel_class->get_caret_loc = (get_caret_location_fc)gtk_graph_view_get_caret_location;
    panel_class->get_coordinates = (get_addr_coordinates_fc)gtk_graph_view_get_address_coordinates;
    panel_class->move_caret_to = (move_caret_to_fc)gtk_graph_view_move_caret_to;
    panel_class->cache_glance = (cache_glance_fc)gtk_graph_view_cache_glance;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = instance GTK à initialiser.                           *
*                                                                             *
*  Description : Initialise une instance d'afficheur de code en graphique.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_init(GtkGraphView *view)
{
    GtkViewPanel *viewpanel;                /* Instance parente #1         */
    //GtkBinView *binview;                    /* Instance parente #2         */

    viewpanel = GTK_VIEW_PANEL(view);

    viewpanel->resize = (prepare_resize_fc)gtk_graph_view_prepare_resize;
    ////////viewpanel->get_coordinates = (get_addr_coordinates_fc)gtk_graph_view_get_address_coordinates;

    //binview = GTK_BIN_VIEW(view);

    //binview->set_lines = (set_rendering_lines_fc)gtk_graph_view_set_rendering_lines;
    //binview->define_address = (define_main_address_fc)gtk_graph_view_define_main_address;
    //binview->get_coordinates = (get_addr_coordinates_fc)gtk_graph_view_get_address_coordinates;

    view->support = gtk_fixed_new();
    gtk_widget_set_has_window(view->support, TRUE);
    gtk_widget_set_can_focus(view->support, TRUE);

    g_signal_connect(G_OBJECT(view->support), "draw",
                     G_CALLBACK(gtk_graph_view_draw), view);

    g_signal_connect(G_OBJECT(view->support), "button-press-event",
                      G_CALLBACK(gtk_graph_view_button_press), view);
    g_signal_connect(G_OBJECT(view->support), "button-release-event",
                      G_CALLBACK(gtk_graph_view_button_release), view);
    g_signal_connect(G_OBJECT(view->support), "motion-notify-event",
                      G_CALLBACK(gtk_graph_view_motion_notify), view);

    gtk_widget_add_events(view->support,
                          GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);

    gtk_widget_show(view->support);

    gtk_fixed_put(GTK_FIXED(view), view->support, 0, 0);

    //view->mutex = g_mutex_new();
    //view->cond = g_cond_new();

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view   = composant GTK à consulter.                          *
*                width  = largeur requise à renseigner ou NULL. [OUT]         *
*                height = hauteur requise à renseigner ou NULL. [OUT]         *
*                                                                             *
*  Description : Indique les dimensions de travail du composant d'affichage.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_compute_requested_size(GtkGraphView *view, gint *width, gint *height)
{
    GtkRequisition requisition;             /* Taille requise              */

    if (width != NULL)
    {
        if (view->layout != NULL)
        {
            g_graph_layout_size_request(view->layout, &requisition);
            *width = requisition.width;
        }
        else
            *width = 0;
    }

    if (height != NULL)
    {
        if (view->layout != NULL)
        {
            g_graph_layout_size_request(view->layout, &requisition);
            *height = requisition.height;
        }
        else
            *height = 0;
    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view        = panneau d'affichage concerné.                  *
*                adj         = défilement dont une valeur a changé.           *
*                orientation = indication sur le défilement à traiter.        *
*                                                                             *
*  Description : Réagit à un défilement chez une barre associée au composant. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_adjust_scroll_value(GtkGraphView *view, GtkAdjustment *adj, GtkOrientation orientation)
{
    gint fake_x;                            /* Abscisse virtuelle          */
    gint fake_y;                            /* Ordonnée virtuelle          */

    fake_x = 0;
    fake_y = 0;
    gtk_view_panel_compute_fake_coord(GTK_VIEW_PANEL(view), &fake_x, &fake_y);

    gtk_fixed_move(GTK_FIXED(view), view->support, fake_x, -fake_y);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : widget = composant GTK à redessiner.                         *
*                cr     = contexte graphique associé à l'événement.           *
*                view   = support maître à consulter.                         *
*                                                                             *
*  Description : Met à jour l'affichage de la vue sous forme graphique.       *
*                                                                             *
*  Retour      : FALSE pour poursuivre la propagation de l'événement.         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gboolean gtk_graph_view_draw(GtkWidget *widget, cairo_t *cr, GtkGraphView *view)
{
    if (view->layout != NULL)
        g_graph_layout_draw(view->layout, cr, true);

    return FALSE;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : widget = composant GTK visé par l'opération.                 *
*                event  = informations liées à l'événement.                   *
*                view   = support maître à consulter.                         *
*                                                                             *
*  Description : Assure la gestion des clics de souris sur le composant.      *
*                                                                             *
*  Retour      : FALSE pour poursuivre la propagation de l'événement.         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gboolean gtk_graph_view_button_press(GtkWidget *widget, GdkEventButton *event, GtkGraphView *view)
{
    gboolean result;                        /* Poursuite à faire suivre    */
    GtkScrolledWindow *support;             /* Support défilant associé    */
    GtkAdjustment *hadj;                    /* Gestionnaire du défilement  */
    GtkAdjustment *vadj;                    /* Gestionnaire du défilement  */
    GdkCursor *cursor;                      /* Pointeur pour la surface    */

    result = FALSE;

    if (event->button == 1)
    {
        support = GTK_SCROLLED_WINDOW(gtk_widget_get_parent(GTK_WIDGET(view)));

        hadj = gtk_scrolled_window_get_hadjustment(support);
        vadj = gtk_scrolled_window_get_vadjustment(support);

        view->big_enough = (gtk_adjustment_get_upper(hadj) > gtk_adjustment_get_page_size(hadj)
                            || gtk_adjustment_get_upper(vadj) > gtk_adjustment_get_page_size(vadj));

        if (view->big_enough)
        {
            view->start_x = event->x_root;
            view->start_y = event->y_root;

            view->ref_h = gtk_adjustment_get_value(hadj);
            view->ref_v = gtk_adjustment_get_value(vadj);

            cursor = gdk_cursor_new(GDK_FLEUR);
            gdk_window_set_cursor(gtk_widget_get_window(widget), cursor);
            g_object_unref(G_OBJECT(cursor));

            result = TRUE;

        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : widget = composant GTK visé par l'opération.                 *
*                event  = informations liées à l'événement.                   *
*                view   = support maître à consulter.                         *
*                                                                             *
*  Description : Assure la gestion des clics de souris sur le composant.      *
*                                                                             *
*  Retour      : FALSE pour poursuivre la propagation de l'événement.         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gboolean gtk_graph_view_button_release(GtkWidget *widget, GdkEventButton *event, GtkGraphView *view)
{
    if (event->button == 1 && view->big_enough)
        gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);

    return FALSE;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : widget = composant GTK visé par l'opération.                 *
*                event  = informations liées à l'événement.                   *
*                view   = support maître à consulter.                         *
*                                                                             *
*  Description : Assure la suivi des déplacements de souris sur le composant. *
*                                                                             *
*  Retour      : FALSE pour poursuivre la propagation de l'événement.         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gboolean gtk_graph_view_motion_notify(GtkWidget *widget, GdkEventMotion *event, GtkGraphView *view)
{
    gdouble diff_x;                         /* Evolution sur les abscisses */
    gdouble diff_y;                         /* Evolution sur les ordonnées */
    GtkScrolledWindow *support;             /* Support défilant associé    */
    GtkAdjustment *hadj;                    /* Gestionnaire du défilement  */
    GtkAdjustment *vadj;                    /* Gestionnaire du défilement  */
    gdouble value;                          /* Nouvelle valeur bornée      */

    if (event->state & GDK_BUTTON1_MASK && view->big_enough)
    {
        diff_x = view->start_x - event->x_root;
        diff_y = view->start_y - event->y_root;

        support = GTK_SCROLLED_WINDOW(gtk_widget_get_parent(GTK_WIDGET(view)));

        hadj = gtk_scrolled_window_get_hadjustment(support);
        vadj = gtk_scrolled_window_get_vadjustment(support);

        value = CLAMP(view->ref_h + diff_x, gtk_adjustment_get_lower(hadj),
                      gtk_adjustment_get_upper(hadj) - gtk_adjustment_get_page_size(hadj));
        gtk_adjustment_set_value(hadj, value);

        value = CLAMP(view->ref_v + diff_y, gtk_adjustment_get_lower(vadj),
                      gtk_adjustment_get_upper(vadj) - gtk_adjustment_get_page_size(vadj));
        gtk_adjustment_set_value(vadj, value);

    }

    return FALSE;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = composant GTK à mettre à jour.                        *
*                                                                             *
*  Description : Actualise les besoins internes avant un redimensionnement.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_prepare_resize(GtkGraphView *view)
{
    size_t i;                               /* Boucle de parcours          */

    if (view->children_count > 0)
    {
        for (i = 0; i < view->children_count; i++)
            gtk_widget_queue_resize(GTK_WIDGET(view->children[i]));

        g_graph_layout_refresh(view->layout);
        g_graph_layout_place(view->layout, view);

        change_editor_items_current_view_content(GTK_VIEW_PANEL(view));

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = composant GTK à mettre à jour.                        *
*                addr = adresse sélectionnée de manière externe.              *
*                                                                             *
*  Description : Réagit à la sélection externe d'une adresse.                 *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_define_main_address(GtkGraphView *view, const vmpa2t *addr)
{
    bool need_update;                       /* Mise à jour du contenu ?    */
    const mrange_t *range;                  /* Couverture courante         */
    GExeFormat *format;                     /* Type de fichier chargé      */
    GBinRoutine **routines;                 /* Liste des routines trouvées */
    size_t routines_count;                  /* Nombre de ces routines      */
    size_t i;                               /* Boucle de parcours          */
    gint width;                             /* Largeur idéale du composant */
    gint height;                            /* Hauteur idéale du composant */

    if (view->routine == NULL)
        need_update = true;
    else
    {
        range = g_binary_routine_get_range(view->routine);
        need_update = !mrange_contains_addr(range, addr);
    }

    if (need_update)
    {
        gtk_graph_view_reset(view);

        format = g_loaded_binary_get_format(GTK_VIEW_PANEL(view)->binary);
        routines = g_binary_format_get_routines(G_BIN_FORMAT(format), &routines_count);

        for (i = 0; i < routines_count; i++)
        {
            range = g_binary_routine_get_range(routines[i]);

            if (mrange_contains_addr(range, addr))
            {
                view->routine = routines[i];
                g_object_ref(G_OBJECT(view->routine));

                view->highlighted = init_segment_content_list();

                view->children = gtk_graph_view_load_nodes(view, GTK_VIEW_PANEL(view)->binary,
                                                           routines[i]);

                view->allocs = (GtkAllocation *)calloc(view->children_count,
                                                       sizeof(GtkAllocation));

                view->layout = g_graph_layout_new(g_binary_routine_get_basic_blocks(view->routine),
                                                  view->children, view->children_count);

                g_graph_layout_place(view->layout, view);

                break;

            }

        }

        gtk_graph_view_compute_requested_size(view, &width, &height);

        gtk_widget_size_allocate(GTK_WIDGET(view), (GtkAllocation []){ { 0, 0, width, height } });
        gtk_widget_size_allocate(view->support, (GtkAllocation []){ { 0, 0, width, height } });

        change_editor_items_current_view_content(GTK_VIEW_PANEL(view));

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = composant GTK à manipuler.                            *
*                                                                             *
*  Description : Indique la position courante du curseur.                     *
*                                                                             *
*  Retour      : Emplacement courant du curseur ou NULL si aucun.             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static const vmpa2t *gtk_graph_view_get_caret_location(const GtkGraphView *view)
{
    return NULL;    /* FIXME */

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view  = composant GTK à consulter.                           *
*                addr  = adresse à présenter à l'écran.                       *
*                x     = position horizontale au sein du composant. [OUT]     *
*                y     = position verticale au sein du composant. [OUT]       *
*                tweak = adaptation finale à effectuer.                       *
*                                                                             *
*  Description : Indique la position d'affichage d'une adresse donnée.        *
*                                                                             *
*  Retour      : true si l'adresse fait partie du composant, false sinon.     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool gtk_graph_view_get_address_coordinates(const GtkGraphView *view, const vmpa2t *addr, gint *x, gint *y, ScrollPositionTweak tweak)
{
    /* TODO */

    return false;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = composant GTK à manipuler.                            *
*                x    = abscisse proposée pour le nouvel emplacement.         *
*                y    = ordonnée proposée pour le nouvel emplacement.         *
*                                                                             *
*  Description : Déplace le curseur à un emplacement défini.                  *
*                                                                             *
*  Retour      : true si un traitement a été effectué, false sinon.           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool gtk_graph_view_move_caret_to(GtkGraphView *view, gint x, gint y)
{
    bool result;                            /* Bilan à retourner           */
    size_t i;                               /* Boucle de parcours          */
    GtkViewPanel *pview;                    /* Autre vision d'enfance      */
    gint sub_x;                             /* Abscisse relative à l'enfant*/
    gint sub_y;                             /* Ordonnée relative à l'enfant*/

    result = false;

    for (i = 0; i < view->children_count; i++)
    {
        if (x < view->allocs[i].x || x >= (view->allocs[i].x + view->allocs[i].width)) continue;
        if (y < view->allocs[i].y || y >= (view->allocs[i].y + view->allocs[i].height)) continue;

        pview = GTK_VIEW_PANEL(view->children[i]);

        sub_x = x - view->allocs[i].x;
        sub_y = y - view->allocs[i].y;

        result = GTK_VIEW_PANEL_GET_CLASS(pview)->move_caret_to(pview, sub_x, sub_y);
        break;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view  = composant GTK à manipuler.                           *
*                cairo = assistant pour la création de rendus.                *
*                area  = taille de la surface réduite à disposition.          *
*                scale = échelle vis à vis de la taille réelle.               *
*                                                                             *
*  Description : Place en cache un rendu destiné à l'aperçu graphique rapide. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_cache_glance(GtkGraphView *view, cairo_t *cairo, const GtkAllocation *area, double scale)
{
    size_t i;                               /* Boucle de parcours          */
    GtkAllocation sub_area;                 /* Emplacement réservé         */

    for (i = 0; i < view->children_count; i++)
    {
        sub_area.x = view->allocs[i].x * scale;
        sub_area.y = view->allocs[i].y * scale;
        sub_area.width = view->allocs[i].width * scale + 1;
        sub_area.height = view->allocs[i].height * scale + 1;

        gtk_view_panel_cache_glance(GTK_VIEW_PANEL(view->children[i]), cairo, &sub_area, scale);

    }

    cairo_scale(cairo, scale, scale);

    if (view->layout != NULL)
        g_graph_layout_draw(view->layout, cairo, false);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un nouveau composant pour l'affichage en graphique.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GtkWidget *gtk_graph_view_new(void)
{
    return g_object_new(GTK_TYPE_GRAPH_VIEW, NULL);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view   = composant GTK à mettre à jour.                      *
*                widget = composant GTK à insérer.                            *
*                x      = abscisse du point d'insertion.                      *
*                y      = ordonnée du point d'insertion.                      *
*                                                                             *
*  Description : Place une vue sous forme de bloc dans le graphique.          *
*                                                                             *
*  Retour      : Plutôt que de redéfinir *toutes* les méthodes de             *
*                GtkContainer, on étend !                                     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void gtk_graph_view_put(GtkGraphView *view, GtkWidget *widget, const GtkAllocation *alloc)
{
    size_t i;                               /* Boucle de parcours          */
    GtkWidget *parent;                      /* Parent en cas de réajustemt.*/

    for (i = 0; i < view->children_count; i++)
        if (GTK_WIDGET(view->children[i]) == widget)
        {
            view->allocs[i] = *alloc;
            break;
        }

    parent = gtk_widget_get_parent(widget);

    if (parent != NULL)
    {
        g_object_ref(G_OBJECT(widget));
        gtk_container_remove(GTK_CONTAINER(parent), widget);
    }

    gtk_fixed_put(GTK_FIXED(view->support), widget, alloc->x, alloc->y);

    if (parent != NULL)
        g_object_unref(G_OBJECT(widget));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view = instance GTK à réinitialiser.                         *
*                                                                             *
*  Description : Supprime tout contenu de l'afficheur de code en graphique.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_reset(GtkGraphView *view)
{
    size_t i;                               /* Boucle de parcours          */

    /*
    for (i = 0; i < view->links_count; i++)
        gtk_object_destroy(GTK_OBJECT(view->links[i]));

    if (view->links_count > 0)
    {
        free(view->links);
        view->links = NULL;

        view->links_count = 0;

    }
    */

    if (view->highlighted)
        exit_segment_content_list(view->highlighted);

    for (i = 0; i < view->children_count; i++)
    {
        g_signal_handlers_disconnect_by_func(view->children[i], gtk_graph_view_reach_caret_limit, view);
        gtk_widget_destroy(GTK_WIDGET(view->children[i]));
    }

    if (view->children_count > 0)
    {
        free(view->children);
        view->children = NULL;
        free(view->allocs);
        view->allocs = NULL;

        view->children_count = 0;

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : view    = composant d'affichage GTK à mettre à jour.         *
*                routine = routine à présenter via ledit composant.           *
*                                                                             *
*  Description : Définit la liste complète des éléments du futur graphique.   *
*                                                                             *
*  Retour      : Liste d'éléments du graphique à placer.                      *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GtkBufferView **gtk_graph_view_load_nodes(GtkGraphView *view, GLoadedBinary *binary, const GBinRoutine *routine)
{
    GtkBufferView **result;                 /* Liste à retourner           */
    GCodeBuffer *buffer;                    /* Tampon brut à découper      */
    size_t *count;                          /* Nombre d'éléments créés.    */
    GInstrBlock *main_block;                /* Premier bloc rattaché       */
    GInstrBlock **blocks;                   /* Liste des blocs basiques    */
    size_t i;                               /* Boucle de parcours          */
    vmpa2t first;                           /* Début d'un groupe de lignes */
    vmpa2t last;                            /* Fin d'un groupe de lignes   */
    GBufferView *subview;                   /* Partie affichée du tampon   */

    buffer = g_loaded_binary_get_disassembled_buffer(binary);

    count = &view->children_count;

    main_block = g_binary_routine_get_basic_blocks(routine);

    blocks = NULL;
    *count = 0;
    g_instr_block_list_all_blocks(main_block, &blocks, count);

    result = (GtkBufferView **)calloc(*count, sizeof(GtkBufferView *));

    for (i = 0; i < *count; i++)
    {
        result[i] = GTK_BUFFER_VIEW(gtk_block_view_new());
        g_signal_connect(result[i], "reach-limit", G_CALLBACK(gtk_graph_view_reach_caret_limit), view);
        g_signal_connect(result[i], "highlight-changed", G_CALLBACK(gtk_graph_view_changed_highlights), view);

        gtk_widget_show(GTK_WIDGET(result[i]));
        gtk_view_panel_attach_binary(GTK_VIEW_PANEL(result[i]), binary, BVW_GRAPH);

        gtk_view_panel_show_border(GTK_VIEW_PANEL(result[i]), true);

        g_flow_block_get_boundary_addresses(G_FLOW_BLOCK(blocks[i]), &first, &last);

        subview = g_buffer_view_new(buffer, view->highlighted);
        g_buffer_view_restrict(subview, &first, &last);
        gtk_buffer_view_attach_buffer(result[i], subview);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : node = composant d'affichage GTK impliqué dans la procédure. *
*                view = support graphique de tous les noeuds.                 *
*                                                                             *
*  Description : Notifie un changement de surbrillance au sein d'un noeud.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_changed_highlights(GtkBlockView *node, GtkGraphView *view)
{
    size_t i;                               /* Boucle de parcours          */

    for (i = 0; i < view->children_count; i++)
    {
        if (view->children[i] == GTK_BUFFER_VIEW(node))
            continue;

        gtk_widget_queue_draw(GTK_WIDGET(view->children[i]));

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : node = composant d'affichage GTK impliqué dans la procédure. *
*                dir  = direction du déplacement souhaité et impossible.      *
*                view = support graphique de tous les noeuds.                 *
*                                                                             *
*  Description : Notifie une incapacité de déplacement au sein d'un noeud.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_graph_view_reach_caret_limit(GtkBufferView *node, GdkScrollDirection dir, GtkGraphView *view)
{
    GBufferView *bview;                     /* Vue d'un tampon global      */
    vmpa2t first;                           /* Début d'un groupe de lignes */
    vmpa2t last;                            /* Fin d'un groupe de lignes   */
    const mrange_t *range;                  /* Couverture courante         */
    GArchProcessor *proc;                   /* Processeur pour instructions*/
    GArchInstruction *ref;                  /* Point de référence          */
#ifndef NDEBUG
    bool is_return;                         /* Est-ce une instruc. finale ?*/
#endif
    vmpa2t iaddr;                           /* Position de l'instructin    */
    size_t i;                               /* Boucle de parcours          */
    bool updated;                           /* Besoin d'une mise à jour ?  */

    /* Détermination de l'instruction à cibler */

    bview = gtk_buffer_view_get_buffer(node);
    g_buffer_view_get_restrictions(bview, &first, &last);

    range = g_binary_routine_get_range(view->routine);

    proc = g_loaded_binary_get_processor(GTK_VIEW_PANEL(view)->binary);

    ref = NULL;

#ifndef NDEBUG
    is_return = false;
#endif

    switch (dir)
    {
        case GDK_SCROLL_LEFT:
        case GDK_SCROLL_UP:
            if (cmp_vmpa(get_mrange_addr(range), &first) != 0)
            {
                ref = g_arch_processor_find_instr_by_address(proc, &first);

                if (ref != NULL)
                    ref = g_arch_processor_get_prev_instr(proc, ref);

                /* TODO : boucler si !HAS_CODE */

                if (ref != NULL)
                    copy_vmpa(&iaddr, get_mrange_addr(g_arch_instruction_get_range(ref)));

            }
            break;

        case GDK_SCROLL_RIGHT:
        case GDK_SCROLL_DOWN:

            ref = g_arch_processor_find_instr_by_address(proc, &last);

#ifndef NDEBUG
            if (ref != NULL)
                is_return = (g_arch_instruction_get_flags(ref) & AIF_RETURN_POINT);
#endif

            if (ref != NULL)
                ref = g_arch_processor_get_next_instr(proc, ref);

            /* TODO : boucler si !HAS_CODE */

            if (ref != NULL)
            {
                copy_vmpa(&iaddr, get_mrange_addr(g_arch_instruction_get_range(ref)));

                if (!mrange_contains_addr(range, &iaddr))
                    ref = NULL;

            }

            break;

        case GDK_SCROLL_SMOOTH:
            assert(0);  /* Argument jamais généré */
            break;

    }

    g_object_unref(G_OBJECT(proc));

    /* Recherche du bloc parent */

    if (ref == NULL)
        return;

    for (i = 0; i < view->children_count; i++)
    {
        bview = gtk_buffer_view_get_buffer(view->children[i]);
        g_buffer_view_get_restrictions(bview, &first, &last);

        if (cmp_vmpa(&first, &iaddr) <= 0 && cmp_vmpa(&iaddr, &last) <= 0)
        {
            assert(node != view->children[i]);
            break;
        }

    }

    assert(i < view->children_count || is_return);

    /* Affichage du nouveau curseur */

    /**
     * Il se peut qu'aucune adresse suivante ne soit disponible : c'est typiquement
     * le cas sous ARM, avec les valeurs brutes référencées dans le code. Ces valeurs
     * sont incluses dans la surface couverte par la routine concernée, mais ne sont
     * pas intégrées dans les blocs basiques associés.
     */

    if (i == view->children_count)
        return;

    gtk_widget_grab_focus(GTK_WIDGET(view->children[i]));

    switch (dir)
    {
        case GDK_SCROLL_LEFT:
            updated = gtk_buffer_view_move_caret_to(view->children[i], false, NULL);
            break;

        case GDK_SCROLL_UP:
            break;

        case GDK_SCROLL_RIGHT:
            updated = gtk_buffer_view_move_caret_to(view->children[i], true, NULL);
            break;

        case GDK_SCROLL_DOWN:
            break;

        case GDK_SCROLL_SMOOTH:
            assert(0);  /* Argument jamais généré */
            break;

    }

    /* TODO : scrolling... */

}