/* Chrysalide - Outil d'analyse de fichiers binaires
 * bookmarks.c - panneau d'affichage des signets d'un binaire
 *
 * Copyright (C) 2014-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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include "bookmarks.h"


#include <assert.h>
#include <cairo-gobject.h>
#include <malloc.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>


#include "../panel-int.h"
#include "../core/global.h"
#include "../../analysis/db/items/bookmark.h"
#include "../../common/cpp.h"
#include "../../common/extstr.h"
#include "../../core/params.h"
#include "../../core/paths.h"
#include "../../core/queue.h"
#include "../../glibext/chrysamarshal.h"
#include "../../glibext/signal.h"
#include "../../gtkext/easygtk.h"
#include "../../gtkext/gtkdisplaypanel.h"
#include "../../gtkext/gtkdockable-int.h"



/* -------------------------- PARTIE PRINCIPALE DU PANNEAU -------------------------- */


/* Panneau d'affichage des signets liés à un binaire (instance) */
struct _GBookmarksPanel
{
    GPanelItem parent;                      /* A laisser en premier        */

    const regex_t *filter;                  /* Filtre appliqué ou NULL     */

    GtkMenu *menu;                          /* Menu contextuel pour param. */

    GLoadedBinary *binary;                  /* Binaire en cours d'analyse  */

};

/* Panneau d'affichage des signets liés à un binaire (classe) */
struct _GBookmarksPanelClass
{
    GPanelItemClass parent;                 /* A laisser en premier        */

    cairo_surface_t *bookmark_img;          /* Image pour les signets      */

};


/* Colonnes de la liste visuelle */
typedef enum _BookmarkColumn
{
    BMC_BOOKMARK,                           /* Elément GLib représenté     */

    BMC_PICTURE,                            /* Image d'agrément            */
    BMC_PHYSICAL,                           /* Adresse phyisque            */
    BMC_VIRTUAL,                            /* Adresse virtuelle           */
    BMC_COMMENT,                            /* Commentaire associé         */

    BMC_COUNT                               /* Nombre de colonnes          */

} CfgParamColumn;




/* Initialise la classe des panneaux des paramètres de config. */
static void g_bookmarks_panel_class_init(GBookmarksPanelClass *);

/* Initialise une instance de panneau de paramètres de config. */
static void g_bookmarks_panel_init(GBookmarksPanel *);

/* Supprime toutes les références externes. */
static void g_bookmarks_panel_dispose(GBookmarksPanel *);

/* Procède à la libération totale de la mémoire. */
static void g_bookmarks_panel_finalize(GBookmarksPanel *);

/* Réagit à un changement d'affichage principal de contenu. */
static void change_bookmarks_panel_current_content(GBookmarksPanel *, GLoadedContent *, GLoadedContent *);


/* ------------------------- AFFICHAGE A L'AIDE D'UNE LISTE ------------------------- */


/* Recharge une collection de signets à l'affichage. */
static void reload_bookmarks_into_treeview(GBookmarksPanel *, GLoadedBinary *);

/* Met à jour une collection suite à une modification. */
static void on_collection_content_changed(GDbCollection *, DBAction, GDbBookmark *, GBookmarksPanel *);

/* Réagit au changement de sélection des signets. */
static void on_bookmarks_selection_change(GtkTreeSelection *, gpointer);



/* Actualise l'affichage des données d'un paramètre modifié. */
static void on_config_param_modified(GCfgParam *, GBookmarksPanel *);

/* Actualise la valeur affichée d'un paramètre de configuration. */
static void update_config_param_value(GtkListStore *, GtkTreeIter *);

/* Etablit une comparaison entre deux lignes de paramètres. */
static gint compare_bookmarks_list_columns(GtkTreeModel *, GtkTreeIter *, GtkTreeIter *, gpointer);

/* Réagit à une pression sur <Shift+F2> et simule l'édition. */
static gboolean on_key_pressed_over_params(GtkTreeView *, GdkEventKey *, GBookmarksPanel *);

/* Réagit à une édition de la valeur d'un paramètre. */
static void on_param_value_edited(GtkCellRendererText *, gchar *, gchar *, GtkTreeStore *);



/* ------------------------- FILTRAGE DES SYMBOLES PRESENTS ------------------------- */


/* Démarre l'actualisation du filtrage des paramètres. */
static void update_filtered_bookmarks(GBookmarksPanel *);

/* Détermine si un signet doit être filtré ou non. */
static bool is_bookmark_filtered(GBookmarksPanel *, const char *, const char *, const char *);



/* ------------------------ ATTRIBUTION D'UN MENU CONTEXTUEL ------------------------ */


/* Assure la gestion des clics de souris sur les signets. */
static gboolean on_button_press_over_bookmarks(GtkWidget *, GdkEventButton *, GBookmarksPanel *);

/* Construit le menu contextuel pour les signets. */
static GtkMenu *build_bookmarks_panel_menu(GBookmarksPanel *);

/* Fournit le signet sélectionné dans la liste. */
static GDbBookmark *get_selected_panel_bookmark(GtkTreeView *, GtkTreeIter *);

/* Réagit avec le menu "Editer". */
static void mcb_bookmarks_panel_edit(GtkMenuItem *, GBookmarksPanel *);

/* Réagit avec le menu "Supprimer". */
static void mcb_bookmarks_panel_delete(GtkMenuItem *, GBookmarksPanel *);

/* Réagit avec le menu "Filtrer...". */
static void mcb_bookmarks_panel_filter(GtkMenuItem *, GBookmarksPanel *);



/* ---------------------------------------------------------------------------------- */
/*                            PARTIE PRINCIPALE DU PANNEAU                            */
/* ---------------------------------------------------------------------------------- */


/* Indique le type défini pour un panneau d'affichage des signets liés à un binaire. */
G_DEFINE_TYPE(GBookmarksPanel, g_bookmarks_panel, G_TYPE_PANEL_ITEM);


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des panneaux des paramètres de config.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_bookmarks_panel_class_init(GBookmarksPanelClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    GEditorItemClass *editem;               /* Encore une autre vision...  */
    GPanelItemClass *panel;                 /* Version parente de la classe*/
    gchar *filename;                        /* Chemin d'accès à utiliser   */

    object = G_OBJECT_CLASS(klass);

    object->dispose = (GObjectFinalizeFunc/* ! */)g_bookmarks_panel_dispose;
    object->finalize = (GObjectFinalizeFunc)g_bookmarks_panel_finalize;

    editem = G_EDITOR_ITEM_CLASS(klass);

    editem->change_content = (change_item_content_fc)change_bookmarks_panel_current_content;

    panel = G_PANEL_ITEM_CLASS(klass);

    panel->can_search = true;

    panel->update_filtered = (update_filtered_fc)update_filtered_bookmarks;

    panel->gid = setup_tiny_global_work_group(1);

    filename = find_pixmap_file("bookmark.png");
    assert(filename != NULL);

    klass->bookmark_img = cairo_image_surface_create_from_png(filename);

    g_free(filename);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : panel = instance à initialiser.                              *
*                                                                             *
*  Description : Initialise une instance de panneau de paramètres de config.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_bookmarks_panel_init(GBookmarksPanel *panel)
{
    GEditorItem *base;                      /* Version basique d'instance  */
    GPanelItem *pitem;                      /* Version parente du panneau  */
    GtkBuilder *builder;                    /* Constructeur utilisé        */
    GtkTreeSortable *sortable;              /* Autre vision de la liste    */

    /* Eléments de base */

    base = G_EDITOR_ITEM(panel);

    base->name = PANEL_BOOKMARKS_ID;

    pitem = G_PANEL_ITEM(panel);

    pitem->personality = PIP_SINGLETON;
    pitem->lname = _("Bookmarks");
    pitem->dock_at_startup = false;
    pitem->path = strdup("Ms");

    /* Représentation graphique */

    builder = g_panel_item_build(pitem, "bookmarks");

    /* Tri de la liste */

    sortable = GTK_TREE_SORTABLE(gtk_builder_get_object(builder, "store"));

    gtk_tree_sortable_set_sort_func(sortable, BMC_PHYSICAL, compare_bookmarks_list_columns,
                                    GINT_TO_POINTER(BMC_PHYSICAL), NULL);

    gtk_tree_sortable_set_sort_func(sortable, BMC_VIRTUAL, compare_bookmarks_list_columns,
                                    GINT_TO_POINTER(BMC_VIRTUAL), NULL);

    gtk_tree_sortable_set_sort_func(sortable, BMC_COMMENT, compare_bookmarks_list_columns,
                                    GINT_TO_POINTER(BMC_COMMENT), NULL);

    gtk_tree_sortable_set_sort_column_id(sortable, BMC_COMMENT, GTK_SORT_ASCENDING);

    /* Préparation du menu contextuel */

    panel->menu = build_bookmarks_panel_menu(panel);

    /* Connexion des signaux */

    gtk_builder_add_callback_symbols(builder,
                                     "on_button_press_over_bookmarks", G_CALLBACK(on_button_press_over_bookmarks),
                                     "on_key_pressed_over_params", G_CALLBACK(on_key_pressed_over_params),
                                     "on_bookmarks_selection_change", G_CALLBACK(on_bookmarks_selection_change),
                                     NULL);

    gtk_builder_connect_signals(builder, panel);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : panel = instance d'objet GLib à traiter.                     *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_bookmarks_panel_dispose(GBookmarksPanel *panel)
{
    g_clear_object(&panel->binary);

    G_OBJECT_CLASS(g_bookmarks_panel_parent_class)->dispose(G_OBJECT(panel));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : panel = instance d'objet GLib à traiter.                     *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_bookmarks_panel_finalize(GBookmarksPanel *panel)
{
    G_OBJECT_CLASS(g_bookmarks_panel_parent_class)->finalize(G_OBJECT(panel));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un panneau d'affichage des paramètres de configuration. *
*                                                                             *
*  Retour      : Adresse de la structure mise en place.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GPanelItem *g_bookmarks_panel_new(void)
{
    GPanelItem *result;                     /* Structure à retourner       */

    result = g_object_new(G_TYPE_BOOKMARKS_PANEL, NULL);

    //reload_config_into_treeview(G_BOOKMARKS_PANEL(result), get_main_configuration());


    //GDbCollection *g_loaded_binary_find_collection(GLoadedBinary *binary, DBFeatures feature)




    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : panel = panneau à mettre à jour.                             *
*                old   = ancien contenu chargé analysé.                       *
*                new   = nouveau contenu chargé à analyser.                   *
*                                                                             *
*  Description : Réagit à un changement d'affichage principal de contenu.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void change_bookmarks_panel_current_content(GBookmarksPanel *panel, GLoadedContent *old, GLoadedContent *new)
{
    GLoadedBinary *binary;                  /* Autre version de l'instance */

    if (G_IS_LOADED_BINARY(new))
        binary = G_LOADED_BINARY(new);
    else
        binary = NULL;

    reload_bookmarks_into_treeview(panel, binary);

}



/* ---------------------------------------------------------------------------------- */
/*                           AFFICHAGE A L'AIDE D'UNE LISTE                           */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : panel  = panneau d'affichage des signets liés à un binaire.  *
*                binary = propriétaire de la collection à présenter.          *
*                                                                             *
*  Description : Recharge une collection de signets à l'affichage.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void reload_bookmarks_into_treeview(GBookmarksPanel *panel, GLoadedBinary *binary)
{
    GDbCollection *collec;                  /* Collection à lister ici     */
    GtkBuilder *builder;                    /* Constructeur utilisé        */
    GtkListStore *store;                    /* Modèle de gestion           */
    GArchProcessor *proc;                   /* Architecture du binaire     */
    MemoryDataSize msize;                   /* Taille par défaut           */
    GList *items;                           /* Liste des éléments groupés  */
    GList *b;                               /* Boucle de parcours          */
    GDbBookmark *bookmark;                  /* Signet en cours d'étude     */
    const vmpa2t *addr;                     /* Adressse associée au signet */
    VMPA_BUFFER(phys);                      /* Position physique           */
    VMPA_BUFFER(virt);                      /* Adresse virtuelle           */
    const char *comment;                    /* Commentaire associé         */
    GtkTreeIter iter;                       /* Point d'insertion           */

    /* Basculement du binaire utilisé */

    if (panel->binary != NULL)
    {
        collec = g_loaded_binary_find_collection(panel->binary, DBF_BOOKMARKS);
        g_signal_handlers_disconnect_by_func(collec, G_CALLBACK(on_collection_content_changed), panel);

        g_object_unref(G_OBJECT(panel->binary));

    }

    panel->binary = binary;

    if (panel->binary != NULL)
    {
        g_object_ref(G_OBJECT(binary));

        collec = g_loaded_binary_find_collection(binary, DBF_BOOKMARKS);
        //g_signal_connect_to_main(collec, "content-changed", G_CALLBACK(on_collection_content_changed), panel,
        //                         g_cclosure_user_marshal_VOID__ENUM_OBJECT);

    }

    builder = G_PANEL_ITEM(panel)->builder;

    store = GTK_LIST_STORE(gtk_builder_get_object(builder, "store"));

    gtk_list_store_clear(store);

    /* Si le panneau actif ne représente pas un binaire... */

    if (binary == NULL) return;

    /* Actualisation de l'affichage */

    proc = g_loaded_binary_get_processor(binary);
    msize = g_arch_processor_get_memory_size(proc);
    g_object_unref(G_OBJECT(proc));

    g_db_collection_rlock(collec);

    /*
    items = g_db_collection_get_items(collec);

    for (b = g_list_first(items); b != NULL; b = g_list_next(b))
    {
        bookmark = G_DB_BOOKMARK(b->data);
        if (!g_db_item_is_enabled(G_DB_ITEM(bookmark))) continue;

        addr = g_db_bookmark_get_address(bookmark);

        vmpa2_phys_to_string(addr, msize, phys, NULL);
        vmpa2_virt_to_string(addr, msize, virt, NULL);

        comment = g_db_bookmark_get_comment(bookmark);

        if (is_bookmark_filtered(panel, phys, virt, comment))
            continue;

        gtk_list_store_append(store, &iter);
        gtk_list_store_set(store, &iter,
                           BMC_BOOKMARK, bookmark,
                           BMC_PICTURE, G_BOOKMARKS_PANEL_GET_CLASS(panel)->bookmark_img,
                           BMC_PHYSICAL, phys,
                           BMC_VIRTUAL, virt,
                           BMC_COMMENT, comment,
                           -1);

    }
    */

    g_db_collection_runlock(collec);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : collec   = collection dont le contenu vient de changer.      *
*                action   = type de modification notifiée par la collection.  *
*                bookmark = élément en cause dans le changement survenu.      *
*                panel    = structure contenant les informations maîtresses.  *
*                                                                             *
*  Description : Met à jour une collection suite à une modification.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void on_collection_content_changed(GDbCollection *collec, DBAction action, GDbBookmark *bookmark, GBookmarksPanel *panel)
{
    GtkBuilder *builder;                    /* Constructeur utilisé        */
    GtkListStore *store;                    /* Modèle de gestion           */


    GArchProcessor *proc;                   /* Architecture du binaire     */
    MemoryDataSize msize;                   /* Taille par défaut           */

    const vmpa2t *addr;                     /* Adressse associée au signet */
    VMPA_BUFFER(phys);                      /* Position physique           */
    VMPA_BUFFER(virt);                      /* Adresse virtuelle           */
    GtkTreeIter iter;                       /* Point d'insertion           */

    GtkTreeModel *model;                    /* Modèle de gestion courant   */
    GDbBookmark *displayed;                 /* Elément de collection       */


    builder = G_PANEL_ITEM(panel)->builder;

    store = GTK_LIST_STORE(gtk_builder_get_object(builder, "store"));

    if (action == DBA_ADD_ITEM \
        || (action == DBA_CHANGE_STATE && !g_db_item_has_flag(G_DB_ITEM(bookmark), DIF_DISABLED)))
    {
        proc = g_loaded_binary_get_processor(panel->binary);
        msize = g_arch_processor_get_memory_size(proc);
        g_object_unref(G_OBJECT(proc));

        addr = g_db_bookmark_get_address(bookmark);

        vmpa2_phys_to_string(addr, msize, phys, NULL);
        vmpa2_virt_to_string(addr, msize, virt, NULL);

        gtk_list_store_append(store, &iter);
        gtk_list_store_set(store, &iter,
                           BMC_BOOKMARK, bookmark,
                           BMC_PICTURE, G_BOOKMARKS_PANEL_GET_CLASS(panel)->bookmark_img,
                           BMC_PHYSICAL, phys,
                           BMC_VIRTUAL, virt,
                           BMC_COMMENT, g_db_bookmark_get_comment(bookmark),
                           -1);




    }

    else
    {
        model = GTK_TREE_MODEL(store);


        if (gtk_tree_model_get_iter_first(model, &iter))
            do
            {
                gtk_tree_model_get(model, &iter, BMC_BOOKMARK, &displayed, -1);

                if (bookmark == displayed)
                {
                    gtk_list_store_remove(store, &iter);
                    break;
                }

                g_object_unref(G_OBJECT(displayed));

            }
            while (gtk_tree_model_iter_next(model, &iter));



    }


}


/******************************************************************************
*                                                                             *
*  Paramètres  : selection = sélection modifiée.                              *
*                unused    = adresse non utilisée ici.                        *
*                                                                             *
*  Description : Réagit au changement de sélection des signets.               *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void on_bookmarks_selection_change(GtkTreeSelection *selection, gpointer unused)
{
    GtkTreeIter iter;                       /* Point de sélection          */
    GtkTreeModel *model;                    /* Modèle de gestion           */
    GDbBookmark *bookmark;                  /* Signet en cours d'étude     */
    const vmpa2t *addr;                     /* Adressse associée au signet */
    GLoadedPanel *panel;                    /* Afficheur effectif de code  */

    if (gtk_tree_selection_get_selected(selection, &model, &iter))
    {
        gtk_tree_model_get(model, &iter, BMC_BOOKMARK, &bookmark, -1);

        addr = g_db_bookmark_get_address(bookmark);

        panel = get_current_view();

        if (GTK_IS_DISPLAY_PANEL(panel))
            gtk_display_panel_request_move(GTK_DISPLAY_PANEL(panel), addr);

        g_object_unref(G_OBJECT(panel));

        g_object_unref(G_OBJECT(bookmark));

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : param = instance dont le contenu a évolué.                   *
*                panel = panneau d'affichage de paramètres à mettre à jour.   *
*                                                                             *
*  Description : Actualise l'affichage des données d'un paramètre modifié.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void on_config_param_modified(GCfgParam *param, GBookmarksPanel *panel)
{
    GtkBuilder *builder;                    /* Constructeur utilisé        */
    GtkTreeModel *model;                    /* Gestionnaire de données     */
    GtkTreeIter iter;                       /* Point de recherche          */
    gboolean looping;                       /* Autorisation de bouclage    */
    GCfgParam *item;                        /* Elément de la liste         */

    builder = G_PANEL_ITEM(panel)->builder;

    model = GTK_TREE_MODEL(gtk_builder_get_object(builder, "store"));

    for (looping = gtk_tree_model_get_iter_first(model, &iter);
         looping;
         looping = gtk_tree_model_iter_next(model, &iter))
    {
        gtk_tree_model_get(model, &iter, BMC_BOOKMARK, &item, -1);

        if (item == param)
        {
            update_config_param_value(GTK_LIST_STORE(model), &iter);
            break;
        }

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : store = gestionnaire du tableau de données.                  *
*                iter  = point de modification dans les lignes.               *
*                param = paramètre dont la valeur est à afficher.             *
*                                                                             *
*  Description : Actualise la valeur affichée d'un paramètre de configuration.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void update_config_param_value(GtkListStore *store, GtkTreeIter *iter)
{
    GCfgParam *param;                       /* Paramètre à consulter       */
    ConfigParamState state;                 /* Etat du paramètre           */
    char *state_desc;                       /* Version chaînée de l'état   */
    bool boolean;                           /* Valeur booléenne            */
    int integer;                            /* Valeur entière              */
    char int_val[sizeof(XSTR(INT_MIN)) + 1];/* Valeur en chaîne de carac.  */
    char *string;                           /* Chaîne de caractères        */
    char *desc;                             /* Description à afficher      */

    gtk_tree_model_get(GTK_TREE_MODEL(store), iter, BMC_BOOKMARK, &param, -1);

    state = g_config_param_get_state(param);

    if (state & CPS_DEFAULT)
        state_desc = strdup(_("By default"));
    else
        state_desc = strdup(_("Changed"));

    if (state & CPS_EMPTY)
        state_desc = stradd(state_desc, _(" + empty"));

    if (state & CPS_EMPTY)
        desc = "";

    else
        switch (g_config_param_get_ptype(param))
        {
            case CPT_BOOLEAN:
                g_config_param_get_value(param, &boolean);
                desc = (boolean ? _("true") : _("false"));
                break;

            case CPT_INTEGER:
                g_config_param_get_value(param, &integer);
                snprintf(int_val, sizeof(int_val), "%d", integer);
                desc = int_val;
                break;

            case CPT_STRING:
                g_config_param_get_value(param, &string);
                desc = (string != NULL ? string : "");
                break;

            default:
                assert(false);
                desc = "???";
                break;

        }

    /*
    gtk_list_store_set(store, iter,
                       CPC_BOLD, state & CPS_DEFAULT ? 400 : 800,
                       CPC_STATUS, state_desc,
                       CPC_VALUE, desc, -1);
    */

    free(state_desc);

    g_object_unref(G_OBJECT(param));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : model  = gestionnaire du tableau de données.                 *
*                a      = première ligne de données à traiter.                *
*                b      = seconde ligne de données à traiter.                 *
*                column = indice de la colonne à considérer, encodée.         *
*                                                                             *
*  Description : Etablit une comparaison entre deux lignes de signets.        *
*                                                                             *
*  Retour      : Indication de tri entre les deux lignes fournies.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gint compare_bookmarks_list_columns(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer column)
{
    gint result;                            /* Valeur calculée à retourner */
    gchar *value_a;                         /* Cellule de la ligne 'a'     */
    gchar *value_b;                         /* Cellule de la ligne 'b'     */

    gtk_tree_model_get(model, a, GPOINTER_TO_INT(column), &value_a, -1);
    gtk_tree_model_get(model, b, GPOINTER_TO_INT(column), &value_b, -1);

    if (value_a == NULL || value_b == NULL)
    {
        if (value_a == NULL && value_b == NULL)
            result = 0;
        else
            result = (value_a == NULL ? -1 : 1);
    }
    else
        result = g_utf8_collate(value_a, value_b);

    g_free(value_a);
    g_free(value_b);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : treeview = composant graphique présentant les paramètres.    *
*                event    = informations liées à l'événement.                 *
*                panel    = panneau d'affichage sur lequel s'appuyer.         *
*                                                                             *
*  Description : Réagit à une pression sur <Shift+F2> et simule l'édition.    *
*                                                                             *
*  Retour      : FALSE pour poursuivre la propagation de l'événement.         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gboolean on_key_pressed_over_params(GtkTreeView *treeview, GdkEventKey *event, GBookmarksPanel *panel)
{
    const gchar *accelerator;               /* Combinaison de raccourci    */
    guint accel_key;                        /* Touche de raccourci         */
    GdkModifierType accel_mod;              /* Modifiateurs attendus aussi */

    if (!g_generic_config_get_value(get_main_configuration(), MPK_KEYBINDINGS_EDIT, &accelerator))
        return FALSE;

    if (accelerator == NULL)
        return FALSE;

    gtk_accelerator_parse(accelerator, &accel_key, &accel_mod);

    if (event->keyval == accel_key && event->state == accel_mod)
        mcb_bookmarks_panel_edit(NULL, panel);

    return FALSE;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : renderer = moteur de rendu pour la cellule.                  *
*                path     = chemin d'accès vers la cellule éditée.            *
*                new      = nouvelle valeur sous forme de texte à valider.    *
*                store    = gestionnaire des données de la liste affichée.    *
*                                                                             *
*  Description : Réagit à une édition de la valeur d'un paramètre.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void on_param_value_edited(GtkCellRendererText *renderer, gchar *path, gchar *new, GtkTreeStore *store)
{
    GtkTreePath *tree_path;                 /* Chemin d'accès natif        */
    GtkTreeIter iter;                       /* Point de la modification    */
    GCfgParam *param;                       /* Paramètre à actualiser      */
    bool boolean;                           /* Valeur booléenne            */
    int integer;                            /* Valeur entière              */
    char *end;                              /* Pointeur vers '\0' final ?  */

    tree_path = gtk_tree_path_new_from_string(path);
    if (tree_path == NULL) return;

    if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, tree_path))
        goto opve_bad_iter;

    gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, BMC_BOOKMARK, &param, -1);

    switch (g_config_param_get_ptype(param))
    {
        case CPT_BOOLEAN:

            if (strcmp(new, "true") != 0 && strcmp(new, "false") != 0)
                goto opve_bad_value;

            boolean = (strcmp(new, "true") == 0);
            g_config_param_set_value(param, boolean);

            break;

        case CPT_INTEGER:

            integer = strtol(new, &end, 10);
            if (*end != '\0') goto opve_bad_value;
 
            g_config_param_set_value(param, integer);

            break;

        case CPT_STRING:
            g_config_param_set_value(param, new);
            break;

        default:
            assert(false);
            goto opve_bad_value;
            break;

    }

 opve_bad_value:

    g_object_unref(G_OBJECT(param));

 opve_bad_iter:

    gtk_tree_path_free(tree_path);

}



/* ---------------------------------------------------------------------------------- */
/*                           FILTRAGE DES SYMBOLES PRESENTS                           */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : panel = panneau assurant l'affichage des paramètres.         *
*                                                                             *
*  Description : Démarre l'actualisation du filtrage des paramètres.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void update_filtered_bookmarks(GBookmarksPanel *panel)
{
    reload_bookmarks_into_treeview(panel, panel->binary);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : panel   = panneau assurant l'affichage des paramètres.       *
*                phys    = position physique du signet.                       *
*                virt    = adresse virtuelle du signet.                       *
*                comment = commentaire lisible associé au signet.             *
*                                                                             *
*  Description : Détermine si un signet doit être filtré ou non.              *
*                                                                             *
*  Retour      : true si le signet ne doit pas être affiché, false sinon.     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool is_bookmark_filtered(GBookmarksPanel *panel, const char *phys, const char *virt, const char *comment)
{
    bool result;                            /* Bilan à retourner           */
    regmatch_t match;                       /* Récupération des trouvailles*/
    int ret;                                /* Bilan du filtrage           */

    if (panel->filter == NULL)
        return false;

    result = true;

    ret = regexec(panel->filter, phys, 1, &match, 0);
    result &= (ret == REG_NOMATCH);

    ret = regexec(panel->filter, virt, 1, &match, 0);
    result &= (ret == REG_NOMATCH);

    ret = regexec(panel->filter, comment, 1, &match, 0);
    result &= (ret == REG_NOMATCH);

    return result;

}



/* ---------------------------------------------------------------------------------- */
/*                          ATTRIBUTION D'UN MENU CONTEXTUEL                          */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : widget = composant GTK visé par l'opération.                 *
*                event  = informations liées à l'événement.                   *
*                panel  = informations liées au panneau associé.              *
*                                                                             *
*  Description : Assure la gestion des clics de souris sur les signets.       *
*                                                                             *
*  Retour      : FALSE pour poursuivre la propagation de l'événement.         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gboolean on_button_press_over_bookmarks(GtkWidget *widget, GdkEventButton *event, GBookmarksPanel *panel)
{
    GtkTreeSelection *selection;            /* Sélection courante          */
    GtkTreeIter iter;                       /* Point de sélection          */
    GtkTreeModel *model;                    /* Modèle de gestion           */
    GDbBookmark *bookmark;                  /* Signet en cours d'étude     */
    const vmpa2t *addr;                     /* Adressse associée au signet */
    GLoadedPanel *display;                  /* Afficheur effectif de code  */

    switch (event->button)
    {
        case 1:

            selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));

            if (gtk_tree_selection_get_selected(selection, &model, &iter))
            {
                gtk_tree_model_get(model, &iter, BMC_BOOKMARK, &bookmark, -1);

                addr = g_db_bookmark_get_address(bookmark);

                display = get_current_view();

                if (GTK_IS_DISPLAY_PANEL(display))
                    gtk_display_panel_request_move(GTK_DISPLAY_PANEL(display), addr);

                g_object_unref(G_OBJECT(display));

                g_object_unref(G_OBJECT(bookmark));

            }

            break;

        case 3:
            gtk_menu_popup_at_pointer(panel->menu, (GdkEvent *)event);
            break;

    }

    return FALSE;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : panel = panneau d'affichage des signets liés à un binaire.   *
*                                                                             *
*  Description : Construit le menu contextuel pour les signets.               *
*                                                                             *
*  Retour      : Panneau de menus mis en place.                               *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GtkMenu *build_bookmarks_panel_menu(GBookmarksPanel *panel)
{
    GtkWidget *result;                      /* Support à retourner         */
    GtkWidget *submenuitem;                 /* Sous-élément de menu        */

    result = qck_create_menu(NULL);

    submenuitem = qck_create_menu_item(NULL, NULL, _("Edit"), G_CALLBACK(mcb_bookmarks_panel_edit), panel);
    gtk_container_add(GTK_CONTAINER(result), submenuitem);

    submenuitem = qck_create_menu_item(NULL, NULL, _("Delete"), G_CALLBACK(mcb_bookmarks_panel_delete), panel);
    gtk_container_add(GTK_CONTAINER(result), submenuitem);

    submenuitem = qck_create_menu_separator();
    gtk_container_add(GTK_CONTAINER(result), submenuitem);

    submenuitem = qck_create_menu_item(NULL, NULL, _("Filter..."), G_CALLBACK(mcb_bookmarks_panel_filter), panel);
    gtk_container_add(GTK_CONTAINER(result), submenuitem);

    return GTK_MENU(result);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : treeview = liste d'affichage à consulter.                    *
*                save     = zone de conservation du point de trouvaille. [OUT]*
*                                                                             *
*  Description : Fournit le signet sélectionné dans la liste.                 *
*                                                                             *
*  Retour      : Signet en cours d'édition ou NULL en cas de soucis.          *
*                                                                             *
*  Remarques   : Le résultat non nul est à déréférencer après usage.          *
*                                                                             *
******************************************************************************/

static GDbBookmark *get_selected_panel_bookmark(GtkTreeView *treeview, GtkTreeIter *save)
{
    GDbBookmark *result;                    /* Paramètre à renvoyer        */
    GtkTreeSelection *selection;            /* Représentation de sélection */
    GtkTreeModel *model;                    /* Gestionnaire des données    */
    GtkTreeIter iter;                       /* Point de la sélection       */

    result = NULL;

    selection = gtk_tree_view_get_selection(treeview);

    if (gtk_tree_selection_get_selected(selection, &model, &iter))
        gtk_tree_model_get(model, &iter, BMC_BOOKMARK, &result, -1);

    if (save != NULL)
        *save = iter;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : menuitem = élément de menu sélectionné.                      *
*                panel    = panneau d'affichage des signets liés à un binaire.*
*                                                                             *
*  Description : Réagit avec le menu "Editer".                                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void mcb_bookmarks_panel_edit(GtkMenuItem *menuitem, GBookmarksPanel *panel)
{
    GtkBuilder *builder;                    /* Constructeur utilisé        */
    GtkTreeView *treeview;                  /* Arborescence manipulée      */
    GtkTreeIter iter;                       /* Point de la sélection       */
    GDbBookmark *mark;                      /* Signet sélectionné          */
    GtkTreeModel *model;                    /* Gestionnaire de données     */
    GtkTreePath *path;                      /* Chemin d'accès à ce point   */

    builder = G_PANEL_ITEM(panel)->builder;

    treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview"));

    mark = get_selected_panel_bookmark(treeview, &iter);
    if (mark == NULL) return;

    model = gtk_tree_view_get_model(treeview);
    path = gtk_tree_model_get_path(model, &iter);

    gtk_tree_view_set_cursor(treeview, path,
                             gtk_tree_view_get_column(treeview, BMC_COMMENT - BMC_PHYSICAL),
                             TRUE);

    gtk_tree_path_free(path);

    g_object_unref(G_OBJECT(mark));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : menuitem = élément de menu sélectionné.                      *
*                panel    = panneau d'affichage des signets liés à un binaire.*
*                                                                             *
*  Description : Réagit avec le menu "Supprimer".                             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void mcb_bookmarks_panel_delete(GtkMenuItem *menuitem, GBookmarksPanel *panel)
{
    GtkBuilder *builder;                    /* Constructeur utilisé        */
    GtkTreeView *treeview;                  /* Affichage de la liste       */
    GDbBookmark *mark;                      /* Signet sélectionné          */

    builder = G_PANEL_ITEM(panel)->builder;

    treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview"));

    mark = get_selected_panel_bookmark(treeview, NULL);
    if (mark == NULL) return;

    //g_loaded_binary_remove_from_collection(panel->binary, DBF_BOOKMARKS, G_DB_ITEM(mark));

    g_object_unref(G_OBJECT(mark));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : menuitem = élément de menu sélectionné.                      *
*                panel    = panneau d'affichage des signets liés à un binaire.*
*                                                                             *
*  Description : Réagit avec le menu "Filtrer...".                            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void mcb_bookmarks_panel_filter(GtkMenuItem *menuitem, GBookmarksPanel *panel)
{
#if 0
    GCfgParam *param;                       /* Paramètre sélectionné       */

    param = get_selected_panel_bookmark(panel->treeview, NULL);
    if (param == NULL) return;

    g_config_param_make_empty(param);

    g_object_unref(G_OBJECT(param));
#endif
}