/* OpenIDA - Outil d'analyse de fichiers binaires
 * gtkdockpanel.c - manipulation et affichage de panneaux dockables
 *
 * Copyright (C) 2008 Cyrille Bagard
 *
 *  This file is part of OpenIDA.
 *
 *  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 "gtkdockpanel.h"


#include <malloc.h>
#include <string.h>


#include "gtkdropwindow.h"
#include "iodamarshal.h"



/* Valide ou non le terme d'un "Drag and drop". */
static gboolean gtk_dock_panel_drag_drop_cb(GtkDockPanel *, GdkDragContext *, gint, gint, guint, gpointer);

/* Procède au "Drag and drop" effectif. */
static void gtk_dock_panel_drag_data_received_cb(GtkDockPanel *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint, gpointer);

/* Accompagne le déplacement sur une cible de "Drag and drop". */
static gboolean gtk_dock_panel_drag_motion_cb(GtkDockPanel *, GdkDragContext *, gint, gint, guint, gpointer);

/* Note la fin des visites sur une cible de "Drag and drop". */
static void gtk_dock_panel_drag_leave_cb(GtkDockPanel *, GdkDragContext *, guint, gpointer);

/* Procède au démarrage d'un "Drag and drop". */
static void gtk_dock_panel_drag_begin_cb(GtkDockPanel *, GdkDragContext *, gpointer);

/* Procède à l'envoi depuis la source à la destination. */
static void gtk_dock_panel_drag_data_get_cb(GtkDockPanel *, GdkDragContext *, GtkSelectionData *, guint, guint, gpointer );

/* Marque l'arrêt d'un "Drag and drop". */
static void gtk_dock_panel_drag_end_cb(GtkDockPanel *, GdkDragContext *, gpointer);

/* Nettoie les traces d'un "Drag and drop". */
//static void gtk_dock_panel_drag_data_delete_cb(GtkDockPanel *, GdkDragContext *, gpointer);

/* Ajoute un paquet d'informations à la station dockable. */
static void _gtk_dock_panel_add_item(GtkDockPanel *, GDockItem *, gint);

/* Remplace le panneau d'un membre actuellement affiché. */
static void on_dock_item_content_changed(GDockItem *, GtkWidget *, GtkWidget *, GtkDockPanel *);

/* Supprime un paquet d'informations à la station dockable. */
static void _gtk_dock_panel_remove_item(GtkDockPanel *, GDockItem *, GtkWidget *);

/* Met à jour le titre du support de panneaux dockables. */
static gboolean gtk_dock_panel_update_title(GtkNotebook *, GtkNotebookPage *, guint, gpointer);



/******************************************************************************/
#define _BYTE   8
#define _WORD   16
#define _DWORD  32


/******************************************************************************/
/* Define a list of data types called "targets" that a destination widget will
 * accept. The string type is arbitrary, and negotiated between DnD widgets by
 * the developer. An enum or GQuark can serve as the integer target id. */
enum {
        TARGET_INT32,
        TARGET_STRING,
        TARGET_DOCKITEM,
        TARGET_ROOTWIN
};

/* datatype (string), restrictions on DnD (GtkTargetFlags), datatype (int) */
static GtkTargetEntry target_list[] = {
        { "INTEGER",    0, TARGET_INT32 },
        { "STRING",     0, TARGET_STRING },
        { "OpenIDA/dock-item", 0, TARGET_DOCKITEM },
        { "application/x-rootwindow-drop", 0, TARGET_ROOTWIN }
};

static guint n_targets = G_N_ELEMENTS (target_list);







/* Détermine le type du composant d'affichage des morceaux. */
G_DEFINE_TYPE(GtkDockPanel, gtk_dock_panel, GTK_TYPE_VBOX)




/******************************************************************************
*                                                                             *
*  Paramètres  : class = classe GTK à initialiser.                            *
*                                                                             *
*  Description : Procède à l'initialisation de l'afficheur de morceaux.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_dock_panel_class_init(GtkDockPanelClass *class)
{
    g_signal_new("switch-item",
                 GTK_TYPE_DOCK_PANEL,
                 G_SIGNAL_RUN_LAST,
                 G_STRUCT_OFFSET(GtkDockPanelClass, switch_item),
                 NULL, NULL,
                 g_cclosure_user_marshal_VOID__OBJECT,
                 G_TYPE_NONE, 1, G_TYPE_DOCK_ITEM);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel = composant GTK à initialiser.                        *
*                                                                             *
*  Description : Procède à l'initialisation de la station dockable.           *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_dock_panel_init(GtkDockPanel *dpanel)
{

  GtkWidget *eventbox1;
  GtkWidget *hbox1;
  GtkWidget *button1;
  GtkWidget *image1;
  GtkWidget *button2;
  GtkWidget *image2;



  eventbox1 = gtk_event_box_new ();
  gtk_widget_show (eventbox1);
  gtk_box_pack_start (GTK_BOX (dpanel), eventbox1, FALSE, TRUE, 0);

  hbox1 = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (hbox1);
  gtk_container_add (GTK_CONTAINER (eventbox1), hbox1);

  dpanel->title = GTK_LABEL(gtk_label_new(("<b>titre</b>")));
  gtk_widget_show(GTK_WIDGET(dpanel->title));
  gtk_box_pack_start(GTK_BOX (hbox1), GTK_WIDGET(dpanel->title), TRUE, TRUE, 0);
  gtk_label_set_use_markup(GTK_LABEL(dpanel->title), TRUE);
  gtk_misc_set_alignment(GTK_MISC(dpanel->title), 0, 0.5);

  button1 = gtk_button_new ();
  gtk_widget_show (button1);
  gtk_box_pack_start (GTK_BOX (hbox1), button1, FALSE, FALSE, 0);
  gtk_button_set_relief (GTK_BUTTON (button1), GTK_RELIEF_NONE);

  image1 = gtk_image_new_from_stock ("gtk-media-play", GTK_ICON_SIZE_MENU);
  gtk_widget_show (image1);
  gtk_container_add (GTK_CONTAINER (button1), image1);
  gtk_widget_set_size_request (image1, 10, 10);

  button2 = gtk_button_new ();
  gtk_widget_show (button2);
  gtk_box_pack_start (GTK_BOX (hbox1), button2, FALSE, FALSE, 0);
  gtk_button_set_relief (GTK_BUTTON (button2), GTK_RELIEF_NONE);

  image2 = gtk_image_new_from_stock ("gtk-close", GTK_ICON_SIZE_MENU);
  gtk_widget_show (image2);
  gtk_container_add (GTK_CONTAINER (button2), image2);
  gtk_widget_set_size_request (image2, 10, 10);

  dpanel->notebook = gtk_notebook_new ();
  gtk_widget_show (dpanel->notebook);
  gtk_box_pack_start (GTK_BOX (dpanel), dpanel->notebook, TRUE, TRUE, 0);
  gtk_notebook_set_show_border (GTK_NOTEBOOK (dpanel->notebook), FALSE);
  gtk_notebook_set_tab_pos (GTK_NOTEBOOK (dpanel->notebook), GTK_POS_BOTTOM);
  gtk_notebook_set_scrollable (GTK_NOTEBOOK (dpanel->notebook), TRUE);




        /* Make the "well label" a DnD destination. */
        gtk_drag_dest_set
        (
                GTK_WIDGET(dpanel),              /* widget that will accept a drop */
                GTK_DEST_DEFAULT_MOTION /* default actions for dest on DnD */
                | GTK_DEST_DEFAULT_HIGHLIGHT,
                target_list,            /* lists of target to support */
                n_targets,              /* size of list */
                GDK_ACTION_COPY         /* what to do with data after dropped */
        );

        /* Make the "coin button" a DnD source. */
        /* Why doesn't GtkLabel work here? */
        gtk_drag_source_set
        (
                GTK_WIDGET(dpanel),            /* widget will be drag-able */
                GDK_BUTTON1_MASK,       /* modifier that will start a drag */
                target_list,            /* lists of target to support */
                n_targets,              /* size of list */
                GDK_ACTION_COPY         /* what to do with data after dropped */
        );


    /* Côté destination */
    g_signal_connect(dpanel, "drag-drop", G_CALLBACK(gtk_dock_panel_drag_drop_cb), NULL);
    g_signal_connect(dpanel, "drag-data-received", G_CALLBACK(gtk_dock_panel_drag_data_received_cb), NULL);
    g_signal_connect(dpanel, "drag-motion", G_CALLBACK(gtk_dock_panel_drag_motion_cb), NULL);
    g_signal_connect(dpanel, "drag-leave", G_CALLBACK(gtk_dock_panel_drag_leave_cb), NULL);

    /* Côté source */
    g_signal_connect(dpanel, "drag-begin", G_CALLBACK(gtk_dock_panel_drag_begin_cb), NULL);
    g_signal_connect(dpanel, "drag-data-get", G_CALLBACK(gtk_dock_panel_drag_data_get_cb), NULL);
    g_signal_connect(dpanel, "drag-end", G_CALLBACK(gtk_dock_panel_drag_end_cb), NULL);
    //g_signal_connect(dpanel, "drag-data-delete", G_CALLBACK(gtk_dock_panel_drag_data_delete_cb), NULL);


    g_signal_connect(dpanel->notebook, "switch-page", G_CALLBACK(gtk_dock_panel_update_title), dpanel);




    dpanel->dropwin = gtk_drop_window_new();


        /* Make the "well label" a DnD destination. */
        gtk_drag_dest_set
        (
                dpanel->dropwin,              /* widget that will accept a drop */
                GTK_DEST_DEFAULT_MOTION /* default actions for dest on DnD */
                | GTK_DEST_DEFAULT_HIGHLIGHT,
                target_list,            /* lists of target to support */
                n_targets,              /* size of list */
                GDK_ACTION_COPY         /* what to do with data after dropped */
        );

}


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un nouveau composant pour station dockable.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GtkWidget *gtk_dock_panel_new(void)
{
    return g_object_new(GTK_TYPE_DOCK_PANEL, NULL);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel  = composant à l'origine de la manoeuvre.             *
*                context = contexte de l'opération de "Drag and drop".        *
*                x       = abscisse du pointeur relaché.                      *
*                y       = ordonnée du pointeur relaché.                      *
*                time    = date de l'opération.                               *
*                data    = adresse non utilisée ici.                          *
*                                                                             *
*  Description : Valide ou non le terme d'un "Drag and drop".                 *
*                                                                             *
*  Retour      : TRUE si l'opération peut continuer, FALSE sinon.             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gboolean gtk_dock_panel_drag_drop_cb(GtkDockPanel *dpanel, GdkDragContext *context, gint x, gint y, guint time, gpointer data)
{
    gboolean result;                        /* Ordre à retourner           */
    GdkAtom target;                         /* Type d'élément à déplacer   */

    result = (context->targets != NULL);

    printf(" ## DRAG DROP ## %p\n", dpanel);

    if (context->targets != NULL)
    {
        target = GDK_POINTER_TO_ATOM(g_list_nth_data(context->targets, TARGET_DOCKITEM));
        gtk_drag_get_data(GTK_WIDGET(dpanel), context, target, time);
    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel    = composant à l'origine de la manoeuvre.           *
*                context   = contexte de l'opération de "Drag and drop".      *
*                x         = abscisse du pointeur relaché.                    *
*                y         = ordonnée du pointeur relaché.                    *
*                selection = réceptacle pour la transmission.                 *
*                target    = type de données demandé.                         *
*                time      = date de l'opération.                             *
*                data      = adresse non utilisée ici.                        *
*                                                                             *
*  Description : Procède au "Drag and drop" effectif.                         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_dock_panel_drag_data_received_cb(GtkDockPanel *dpanel, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection, guint target, guint time, gpointer data)
{
    GDockItem *ditem;                       /* Elément transféré           */
    gboolean success;                       /* Bilan de l'opération        */

    success = FALSE;

    if (selection != NULL && selection->length >= 0)
        switch (target)
        {
            case TARGET_DOCKITEM:
                success = (selection->length == sizeof(GDockItem *));

                printf(" ## DRAG DATA RCV ## %p\n", dpanel);

                printf(" -- source :: %p\n", gtk_drag_get_source_widget(context));
                printf(" -- dest   :: %p\n", dpanel);

                if (success)
                {
                    ditem = G_DOCK_ITEM(*((GDockItem **)selection->data));

                    printf(" :: get ? %p - %d\n", ditem, G_IS_DOCK_ITEM(ditem));

                    gtk_dock_panel_remove_item(gtk_drag_get_source_widget(context), ditem);
                    gtk_dock_panel_add_item(dpanel, ditem);

                }

                break;

        }

    gtk_drag_finish(context, success, context->action == GDK_ACTION_MOVE, time);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel  = composant à l'origine de la manoeuvre.             *
*                context = contexte de l'opération de "Drag and drop".        *
*                x       = abscisse du pointeur relaché.                      *
*                y       = ordonnée du pointeur relaché.                      *
*                time    = date de l'opération.                               *
*                data    = adresse non utilisée ici.                          *
*                                                                             *
*  Description : Accompagne le déplacement sur une cible de "Drag and drop".  *
*                                                                             *
*  Retour      : TRUE pour continuer la propagation, FALSE sinon.             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gboolean gtk_dock_panel_drag_motion_cb(GtkDockPanel *dpanel, GdkDragContext *context, gint x, gint y, guint time, gpointer data)
{
    gint new_x;                             /* Abscisse de la fenêtre      */
    gint new_y;                             /* Ordonnée de la fenêtre      */
    GtkRequisition req;                     /* Taille actuelle du panneau  */

    if (!GTK_WIDGET_VISIBLE(dpanel->dropwin))
    {
        gdk_window_get_origin(GTK_WIDGET(dpanel)->window, &new_x, &new_y);

        new_x += GTK_WIDGET(dpanel)->allocation.x + (GTK_WIDGET(dpanel)->allocation.width - 89) / 2;
        new_y += GTK_WIDGET(dpanel)->allocation.y + (GTK_WIDGET(dpanel)->allocation.height - 89) / 2;

        gtk_widget_set_uposition(dpanel->dropwin, new_x, new_y);

        gtk_widget_show(dpanel->dropwin);

    }

    return FALSE;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel  = composant à l'origine de la manoeuvre.             *
*                context = contexte de l'opération de "Drag and drop".        *
*                x       = abscisse du pointeur relaché.                      *
*                y       = ordonnée du pointeur relaché.                      *
*                time    = date de l'opération.                               *
*                data    = adresse non utilisée ici.                          *
*                                                                             *
*  Description : Note la fin des visites sur une cible de "Drag and drop".    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_dock_panel_drag_leave_cb(GtkDockPanel *dpanel, GdkDragContext *context, guint time, gpointer data)
{
    gtk_widget_hide(dpanel->dropwin);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel  = composant à l'origine de la manoeuvre.             *
*                context = contexte de l'opération de "Drag and drop".        *
*                data    = adresse non utilisée ici.                          *
*                                                                             *
*  Description : Procède au démarrage d'un "Drag and drop".                   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_dock_panel_drag_begin_cb(GtkDockPanel *dpanel, GdkDragContext *context, gpointer data)
{
    printf(" ## DRAG BEGIN ## %p\n", dpanel);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel    = composant à l'origine de la manoeuvre.           *
*                context   = contexte de l'opération de "Drag and drop".      *
*                selection = réceptacle pour la transmission.                 *
*                target    = type de données demandé.                         *
*                time      = date de l'opération.                             *
*                data      = adresse non utilisée ici.                        *
*                                                                             *
*  Description : Procède à l'envoi depuis la source à la destination.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_dock_panel_drag_data_get_cb(GtkDockPanel *dpanel, GdkDragContext *context, GtkSelectionData *selection, guint target, guint time, gpointer data)
{
    gint current;                           /* Indice de l'onglet courant  */
    GDockItem *ditem;                       /* Elément à transférer        */

    switch (target)
    {
        case TARGET_DOCKITEM:

            printf(" ## DRAG GET DATA ## %p\n", dpanel);

            current = gtk_notebook_get_current_page(dpanel->notebook);

            ditem = G_DOCK_ITEM(g_list_nth_data(dpanel->ditems, current));

            printf(" %d nth item is %p\n", current, ditem);

            printf(" :: set ? %p - %d\n", ditem, G_IS_DOCK_ITEM(ditem));

            gtk_selection_data_set(selection, selection->target,
                                   32, (guchar *)&ditem, sizeof(GDockItem *));

            break;

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel  = composant à l'origine de la manoeuvre.             *
*                context = contexte de l'opération de "Drag and drop".        *
*                data    = adresse non utilisée ici.                          *
*                                                                             *
*  Description : Marque l'arrêt d'un "Drag and drop".                         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void gtk_dock_panel_drag_end_cb(GtkDockPanel *dpanel, GdkDragContext *context, gpointer data)
{
    printf(" ## DRAG END ## %p\n", dpanel);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel = composant dont le contenu est à parcourir.          *
*                name   = désignation humaine du membre à retrouver.          *
*                                                                             *
*  Description : Retrouve un membre du panneau d'après son nom.               *
*                                                                             *
*  Retour      : Membre trouvé ou NULL si aucun.                              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GDockItem *gtk_dock_panel_item_from_name(GtkDockPanel *dpanel, const char *name)
{
    GDockItem *result;                      /* Trouvaille à remonter       */
    GList *iter;                            /* Boucle de parcours          */
    const char *tmp;                        /* Autre nom à consulter       */

    result = NULL;

    for (iter = dpanel->ditems; iter != NULL && result == NULL; iter = g_list_next(iter))
    {
        tmp = g_dock_item_get_name(G_DOCK_ITEM(iter->data));

        if (strcmp(name, tmp) == 0)
            result = G_DOCK_ITEM(iter->data);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel = composant GTK à compléter.                          *
*                ditem  = nouvel élément à intégrer.                          *
*                                                                             *
*  Description : Ajoute un paquet d'informations à la station dockable.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void gtk_dock_panel_add_item(GtkDockPanel *dpanel, GDockItem *ditem)
{
    _gtk_dock_panel_add_item(dpanel, ditem, -1);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel   = composant GTK à compléter.                        *
*                ditem    = nouvel élément à intégrer.                        *
*                position = point d'insertion (-1 pour la fin).               *
*                                                                             *
*  Description : Ajoute un paquet d'informations à la station dockable.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void _gtk_dock_panel_add_item(GtkDockPanel *dpanel, GDockItem *ditem, gint position)
{
    GtkWidget *label;                       /* Etiquette d'onglet          */

    dpanel->ditems = g_list_insert(dpanel->ditems, ditem, position);

    printf("[add %p to %p] list len :: %u\n", ditem, dpanel, g_list_length(dpanel->ditems));

    label = gtk_label_new(g_dock_item_get_name(ditem));
    gtk_widget_show(label);

    gtk_notebook_insert_page(dpanel->notebook, g_dock_item_get_panel(ditem), label, position);

    gtk_notebook_set_show_tabs(dpanel->notebook, g_list_length(dpanel->ditems) > 1);

    g_signal_connect(ditem, "content-changed", G_CALLBACK(on_dock_item_content_changed), dpanel);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : ditem  = composant GTK déjà mis à jour.                      *
*                old    = ancien panneau retiré.                              *
*                new    = nouveau panneau présenté.                           *
*                dpanel = composant GTK à mettre à jour.                      *
*                                                                             *
*  Description : Remplace le panneau d'un membre actuellement affiché.        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void on_dock_item_content_changed(GDockItem *ditem, GtkWidget *old, GtkWidget *new, GtkDockPanel *dpanel)
{
    gint position;                          /* Position de l'onglet à maj  */

    position = gtk_notebook_page_num(dpanel->notebook, old);

    g_signal_handlers_disconnect_by_func(dpanel->notebook, G_CALLBACK(gtk_dock_panel_update_title), dpanel);

    //g_object_ref(G_OBJECT(ditem));

    _gtk_dock_panel_remove_item(dpanel, ditem, old);
    _gtk_dock_panel_add_item(dpanel, ditem, position);

    //g_object_unref(G_OBJECT(ditem));

    gtk_notebook_set_current_page(dpanel->notebook, position);

    g_signal_connect(dpanel->notebook, "switch-page", G_CALLBACK(gtk_dock_panel_update_title), dpanel);


}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel = composant GTK à mettre à jour.                      *
*                ditem  = nouvel élément à sortir.                            *
*                                                                             *
*  Description : Supprime un paquet d'informations à la station dockable.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void gtk_dock_panel_remove_item(GtkDockPanel *dpanel, GDockItem *ditem)
{
    gint pos;                               /* Position de l'élément visé  */

    g_signal_handlers_disconnect_by_func(ditem, G_CALLBACK(on_dock_item_content_changed), dpanel);

    pos = g_list_index(dpanel->ditems, ditem);

    dpanel->ditems = g_list_remove(dpanel->ditems, ditem);

    printf("[rem %p from %p] list len :: %u\n", ditem, dpanel, g_list_length(dpanel->ditems));

    gtk_widget_ref(g_dock_item_get_panel(ditem));
    gtk_container_remove(GTK_CONTAINER(dpanel->notebook), g_dock_item_get_panel(ditem));

    //gtk_notebook_remove_page(dpanel->notebook, pos);

    g_object_unref(G_OBJECT(ditem));

    gtk_notebook_set_show_tabs(dpanel->notebook, g_list_length(dpanel->ditems) > 1);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dpanel = composant GTK à mettre à jour.                      *
*                ditem  = nouvel élément à sortir.                            *
*                panel  = panneau GTK de l'élément à supprimer.               *
*                                                                             *
*  Description : Supprime un paquet d'informations à la station dockable.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void _gtk_dock_panel_remove_item(GtkDockPanel *dpanel, GDockItem *ditem, GtkWidget *panel)
{
    gint pos;                               /* Position de l'élément visé  */

    g_signal_handlers_disconnect_by_func(ditem, G_CALLBACK(on_dock_item_content_changed), dpanel);

    pos = g_list_index(dpanel->ditems, ditem);

    dpanel->ditems = g_list_remove(dpanel->ditems, ditem);

    printf("[rem %p from %p] list len :: %u\n", ditem, dpanel, g_list_length(dpanel->ditems));

    gtk_widget_ref(panel);
    gtk_container_remove(GTK_CONTAINER(dpanel->notebook), panel);

    //gtk_notebook_remove_page(dpanel->notebook, pos);

    gtk_notebook_set_show_tabs(dpanel->notebook, g_list_length(dpanel->ditems) > 1);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : notebook = support à l'origine de la mise à jour.            *
*                page     = onglet mis en avant.                              *
*                index    = indice de l'onglet actuellement actif.            *
*                data     = adresse du conteneur supérieur.                   *
*                                                                             *
*  Description : Met à jour le titre du support de panneaux dockables.        *
*                                                                             *
*  Retour      : TRUE ?                                                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gboolean gtk_dock_panel_update_title(GtkNotebook *notebook, GtkNotebookPage *page, guint index, gpointer data)
{
    GDockItem *ditem;                       /* Elément nouvellement actif  */
    const gchar *desc;                      /* Description à afficher      */
    char *str;                              /* Valeur finale reconstituée  */


    //printf("[%p] list len :: %u / %u\n", data, index, g_list_length(GTK_DOCK_PANEL(data)->ditems));

    if (index >= g_list_length(GTK_DOCK_PANEL(data)->ditems)) return FALSE;

    //printf(" >> ditem = %p\n", g_list_nth_data(GTK_DOCK_PANEL(data)->ditems, index));

    //printf(" >> index :: %u vs %d\n", index, gtk_notebook_get_current_page(GTK_DOCK_PANEL(data)->notebook));

    ditem = G_DOCK_ITEM(g_list_nth_data(GTK_DOCK_PANEL(data)->ditems, index));

    desc = g_dock_item_get_desc(ditem);

    str = calloc(strlen("<b>") + strlen(desc) + strlen("</b>") + 1, sizeof(char));

    strcpy(str, "<b>");
    strcat(str, desc);
    strcat(str, "</b>");

    gtk_label_set_markup(GTK_DOCK_PANEL(data)->title, str);

    free(str);

    g_signal_emit_by_name(GTK_DOCK_PANEL(data), "switch-item", ditem);

    return TRUE;

}