/* Chrysalide - Outil d'analyse de fichiers binaires
 * strings.c - panneau d'affichage des chaînes de caractères
 *
 * Copyright (C) 2008-2012 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include "strings.h"


#include <string.h>
#include <inttypes.h>


#include "panel-int.h"
#include "../dialogs/gotox.h"
#include "../../common/extstr.h"
#include "../../core/params.h"
#include "../../gtkext/easygtk.h"
#include "../../gtkext/gtkdockable-int.h"



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


/* Panneau d'aperçu de graphiques (instance) */
struct _GStringsPanel
{
    GPanelItem parent;                      /* A laisser en premier        */

    GtkTreeView *treeview;                  /* Composant d'affichage       */
    const regex_t *filter;                  /* Filtre appliqué ou NULL     */

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

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

};


/* Panneau d'aperçu de graphiques (classe) */
struct _GStringsPanelClass
{
    GPanelItemClass parent;                 /* A laisser en premier        */

};


/* Colonnes de la liste des symboles */
typedef enum _StringsColumn
{
    STC_STRING,                             /* Elément GLib représenté     */

    STC_PHYSICAL,                           /* Adresse phyisque            */
    STC_VIRTUAL,                            /* Adresse virtuelle           */
    STC_AREA,                               /* Zone de localisation        */
    STC_NAME,                               /* Désignation humaine         */
    STC_VALUE,                              /* Chaîne de caractères        */

    STC_COUNT                               /* Nombre de colonnes          */

} StringsColumn;


/* Initialise la classe des panneaux d'affichage de chaînes. */
static void g_strings_panel_class_init(GStringsPanelClass *);

/* Initialise une instance de panneau d'affichage des chaînes. */
static void g_strings_panel_init(GStringsPanel *);

/* Procède à l'initialisation de l'interface de rassemblement. */
static void g_strings_panel_dockable_interface_init(GtkDockableInterface *);

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

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



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


/* Réagit à un changement d'affichage principal de contenu. */
static void change_strings_panel_current_binary(GStringsPanel *, GLoadedBinary *);

/* Réagit au changement de sélection des chaînes textuelles. */
static void on_strings_selection_change(GtkTreeSelection *, GStringsPanel *);

/* Etablit une comparaison entre deux chaînes de caractères. */
static gint compare_strings_list_columns(GtkTreeModel *, GtkTreeIter *, GtkTreeIter *, gpointer);

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

/* Réagit à une édition de l'étiquette d'une chaîne textuelle. */
static void on_string_value_edited(GtkCellRendererText *, gchar *, gchar *, GtkTreeStore *);



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


/* Démarre l'actualisation du filtrage des chaînes. */
static void update_filtered_strings(GStringsPanel *, const regex_t *);

/* Détermine si une chaîne textuelle doit être filtrée ou non. */
static bool is_string_filtered(GStringsPanel *, const char *, const char *);



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


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

/* Construit le menu contextuel pour les signets. */
static GtkMenu *build_strings_panel_menu(GStringsPanel *);

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

/* Réagit avec le menu "Editer le nom". */
static void mcb_strings_panel_edit(GtkMenuItem *, GStringsPanel *);

/* Réagit avec le menu "Copier dans le presse-papiers". */
static void mcb_strings_panel_copy(GtkMenuItem *, GStringsPanel *);

/* Réagit avec le menu "Trouver les références...". */
static void mcb_strings_panel_find_refs(GtkMenuItem *, GStringsPanel *);

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



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


/* Indique le type définit pour un panneau d'affichage des chaînes. */
G_DEFINE_TYPE_WITH_CODE(GStringsPanel, g_strings_panel, G_TYPE_PANEL_ITEM,
                        G_IMPLEMENT_INTERFACE(GTK_TYPE_DOCKABLE, g_strings_panel_dockable_interface_init))


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des panneaux d'affichage de chaînes.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_strings_panel_class_init(GStringsPanelClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    GEditorItemClass *editem;               /* Encore une autre vision...  */
    GPanelItemClass *panel;                 /* Version parente de la classe*/

    object = G_OBJECT_CLASS(klass);

    object->dispose = (GObjectFinalizeFunc/* ! */)g_strings_panel_dispose;
    object->finalize = (GObjectFinalizeFunc)g_strings_panel_finalize;

    editem = G_EDITOR_ITEM_CLASS(klass);

    editem->update_binary = (update_item_binary_fc)change_strings_panel_current_binary;

    panel = G_PANEL_ITEM_CLASS(klass);

    panel->unique = true;
    panel->bindings = "<Shift>F12";

}


/******************************************************************************
*                                                                             *
*  Paramètres  : panel = instance à initialiser.                              *
*                                                                             *
*  Description : Initialise une instance de panneau d'affichage des chaînes.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_strings_panel_init(GStringsPanel *panel)
{
    GEditorItem *base;                      /* Version basique d'instance  */
    GPanelItem *pitem;                      /* Version parente du panneau  */
    GObject *ref;                           /* Espace de référencement     */
    GtkTreeStore *store;                    /* Modèle de gestion           */
    GtkWidget *treeview;                    /* Affichage de la liste       */
    GtkCellRenderer *renderer;              /* Moteur de rendu de colonne  */
    GtkTreeViewColumn *column;              /* Colonne de la liste         */
    GtkTreeSortable *sortable;              /* Autre vision de la liste    */
    GtkTreeSelection *select;               /* Sélection dans la liste     */
    bool display;                           /* Affichage si sélection ?    */

    /* Eléments de base */

    base = G_EDITOR_ITEM(panel);

    base->name = PANEL_STRINGS_ID;

    pitem = G_PANEL_ITEM(panel);

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

    /* Représentation graphique */

    base->widget = gtk_scrolled_window_new(NULL, NULL);
    gtk_widget_show(base->widget);

    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(base->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(base->widget), GTK_SHADOW_IN);

    ref = G_OBJECT(base->widget);
    g_object_set_data(ref, "panel", panel);

    /* Partie chaînes */

    store = gtk_tree_store_new(STC_COUNT, G_TYPE_OBJECT,
                               G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);

    treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
    panel->treeview = GTK_TREE_VIEW(treeview);

    g_signal_connect(G_OBJECT(treeview), "button-press-event",
                     G_CALLBACK(on_button_event_over_strings), panel);
    g_signal_connect(G_OBJECT(treeview), "button-release-event",
                     G_CALLBACK(on_button_event_over_strings), panel);
    g_signal_connect(G_OBJECT(treeview), "key-press-event",
                     G_CALLBACK(on_key_pressed_over_strings), panel);

    gtk_widget_show(treeview);
    gtk_container_add(GTK_CONTAINER(base->widget), treeview);

    g_object_unref(G_OBJECT(store));

    /* Cellules d'affichage */

    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes(_("Physical address"), renderer,
                                                      "text", STC_PHYSICAL,
                                                      NULL);
    gtk_tree_view_column_set_sort_column_id(column, STC_PHYSICAL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes(_("Virtual address"), renderer,
                                                      "text", STC_VIRTUAL,
                                                      NULL);
    gtk_tree_view_column_set_sort_column_id(column, STC_VIRTUAL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

    renderer = gtk_cell_renderer_text_new();
    g_object_set(renderer, "xpad", 16, NULL);
    column = gtk_tree_view_column_new_with_attributes(_("Area"), renderer,
                                                      "text", STC_AREA,
                                                      NULL);
    gtk_tree_view_column_set_sort_column_id(column, STC_AREA);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

    renderer = gtk_cell_renderer_text_new();
    g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
    g_signal_connect(renderer, "edited", G_CALLBACK(on_string_value_edited), store);
    column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
                                                      "text", STC_NAME,
                                                      NULL);
    gtk_tree_view_column_set_sort_column_id(column, STC_NAME);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes(_("Value"), renderer,
                                                      "markup", STC_VALUE,
                                                      NULL);
    gtk_tree_view_column_set_sort_column_id(column, STC_VALUE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

    /* Tri de la liste */

    sortable = GTK_TREE_SORTABLE(store);

    gtk_tree_sortable_set_sort_func(sortable, STC_PHYSICAL, compare_strings_list_columns,
                                    GINT_TO_POINTER(STC_PHYSICAL), NULL);

    gtk_tree_sortable_set_sort_func(sortable, STC_VIRTUAL, compare_strings_list_columns,
                                    GINT_TO_POINTER(STC_VIRTUAL), NULL);

    gtk_tree_sortable_set_sort_func(sortable, STC_AREA, compare_strings_list_columns,
                                    GINT_TO_POINTER(STC_AREA), NULL);

    gtk_tree_sortable_set_sort_func(sortable, STC_NAME, compare_strings_list_columns,
                                    GINT_TO_POINTER(STC_NAME), NULL);

    gtk_tree_sortable_set_sort_func(sortable, STC_VALUE, compare_strings_list_columns,
                                    GINT_TO_POINTER(STC_VALUE), NULL);

    gtk_tree_sortable_set_sort_column_id(sortable, STC_VIRTUAL, GTK_SORT_ASCENDING);

    /* Prise en compte de la sélection */

    select = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
    gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);

    g_generic_config_get_value(get_main_configuration(), MPK_DISPLAY_ON_SEL, &display);

    if (display)
        g_signal_connect(G_OBJECT(select), "changed", G_CALLBACK(on_strings_selection_change), panel);

    /* Préparation du menu contextuel */

    panel->menu = build_strings_panel_menu(panel);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : iface = interface GTK à initialiser.                         *
*                                                                             *
*  Description : Procède à l'initialisation de l'interface de rassemblement.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_strings_panel_dockable_interface_init(GtkDockableInterface *iface)
{
    GtkDockableInterface *parent_iface;     /* Définition précédente       */

    parent_iface = (GtkDockableInterface *)g_type_interface_peek_parent(iface);

    iface->can_search = true;
    iface->can_be_closed = true;

    iface->get_name = parent_iface->get_name;
    iface->get_desc = parent_iface->get_desc;
    iface->get_widget = parent_iface->get_widget;
    iface->update_filtered = (update_filtered_data_fc)update_filtered_strings;

}


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

static void g_strings_panel_dispose(GStringsPanel *panel)
{
    if (panel->binary != NULL)
        g_object_unref(G_OBJECT(panel->binary));

    G_OBJECT_CLASS(g_strings_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_strings_panel_finalize(GStringsPanel *panel)
{
    G_OBJECT_CLASS(g_strings_panel_parent_class)->finalize(G_OBJECT(panel));

}


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

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

    result = g_object_new(G_TYPE_STRINGS_PANEL, NULL);

    return result;

}



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


/******************************************************************************
*                                                                             *
*  Paramètres  : panel  = panneau à mettre à jour.                            *
*                binary = nouvelle instance de binaire analysé.               *
*                                                                             *
*  Description : Réagit à un changement d'affichage principal de contenu.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void change_strings_panel_current_binary(GStringsPanel *panel, GLoadedBinary *binary)
{
    GtkTreeStore *store;                    /* Modèle de gestion           */
    GArchProcessor *proc;                   /* Architecture du binaire     */
    MemoryDataSize msize;                   /* Taille par défaut           */
    GExeFormat *format;                     /* Format de travail           */
    GPortionLayer *layer;                   /* Couche première de portions */
    GBinContent *content;                   /* Contenu binaire en mémoire  */
    size_t count;                           /* Nombre des chaînes          */
    GBinSymbol **symbols;                   /* Liste des chaînes trouvées  */
    size_t i;                               /* Boucle de parcours          */
    const mrange_t *range;                  /* Couverture mémoire          */
    const vmpa2t *addr;                     /* Adressse liée à la chaîne   */
    VMPA_BUFFER(phys);                      /* Position physique           */
    VMPA_BUFFER(virt);                      /* Adresse virtuelle           */
    GBinPortion *portion;                   /* Zone mémoire d'appartenance */
    const char *area;                       /* Description de la zone      */
    const char *label;                      /* Etiquette liée au symbole   */
    vmpa2t pos;                             /* Tête de lecture modifiable  */
    char *text;                             /* Version imprimable du texte */
    GtkTreeIter iter;                       /* Point d'insertion           */

    /* Basculement du binaire utilisé */

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

    panel->binary = binary;

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

    store = GTK_TREE_STORE(gtk_tree_view_get_model(panel->treeview));
    gtk_tree_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));

    format = g_loaded_binary_get_format(binary);
    layer = g_exe_format_get_main_layer(format);
    content = g_binary_format_get_content(G_BIN_FORMAT(format));

    symbols = g_binary_format_get_symbols(G_BIN_FORMAT(format), &count);

    for (i = 0; i < count; i++)
    {
        if (g_binary_symbol_get_target_type(symbols[i]) != STP_RO_STRING) continue;

        range = g_binary_symbol_get_range(symbols[i]);
        addr = get_mrange_addr(range);

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

        portion = g_portion_layer_find_portion_at_addr(layer, addr, (GdkRectangle []) { });
        area = g_binary_portion_get_desc(portion);

        label = g_binary_symbol_get_label(symbols[i]);

        text = (char *)calloc(get_mrange_length(range) + 1, sizeof(char));

        copy_vmpa(&pos, addr);

        if (!g_binary_content_read_raw(content, &pos, get_mrange_length(range), (uint8_t *)text))
        {
            free(text);
            continue;
        }

        if (is_string_filtered(panel, label, text))
        {
            free(text);
            continue;
        }

        text = strrpl(text, "&", "&amp;");
        text = strrpl(text, "<", "&lt;");
        text = strrpl(text, ">", "&gt;");
        text = strrpl(text, "\r", "<b>\\r</b>");
        text = strrpl(text, "\n", "<b>\\n</b>");

        gtk_tree_store_append(store, &iter, NULL);
        gtk_tree_store_set(store, &iter,
                           STC_STRING, symbols[i],
                           STC_PHYSICAL, phys,
                           STC_VIRTUAL, virt,
                           STC_AREA, area,
                           STC_NAME, label,
                           STC_VALUE, text,
                           -1);

        free(text);

    }

    g_object_unref(G_OBJECT(layer));
    g_object_unref(G_OBJECT(content));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : selection = sélection modifiée.                              *
*                panel     = structure contenant les informations maîtresses. *
*                                                                             *
*  Description : Réagit au changement de sélection des chaînes textuelles.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void on_strings_selection_change(GtkTreeSelection *selection, GStringsPanel *panel)
{
    GtkTreeIter iter;                       /* Point de sélection          */
    GtkTreeModel *model;                    /* Modèle de gestion           */
    GBinSymbol *symbol;                     /* Symbole en cours d'étude    */
    const vmpa2t *addr;                     /* Adressse associée au signet */
    GtkViewPanel *vpanel;                   /* Afficheur effectif de code  */

    if (gtk_tree_selection_get_selected(selection, &model, &iter))
    {
        gtk_tree_model_get(model, &iter, STC_STRING, &symbol, -1);

        addr = get_mrange_addr(g_binary_symbol_get_range(symbol));

        vpanel = g_editor_item_get_current_view(G_EDITOR_ITEM(panel));
        gtk_view_panel_request_move(vpanel, addr);

        g_object_unref(G_OBJECT(symbol));

    }

}


/******************************************************************************
*                                                                             *
*  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 chaînes de caractères.    *
*                                                                             *
*  Retour      : Indication de tri entre les deux lignes fournies.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gint compare_strings_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_strings(GtkTreeView *treeview, GdkEventKey *event, GStringsPanel *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_strings_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 l'étiquette d'une chaîne textuelle.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void on_string_value_edited(GtkCellRendererText *renderer, gchar *path, gchar *new, GtkTreeStore *store)
{
    GtkTreePath *tree_path;                 /* Chemin d'accès natif        */
    GtkTreeIter iter;                       /* Point de la modification    */
    GBinSymbol *symbol;                     /* Symbole à actualiser        */

    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, STC_STRING, &symbol, -1);

    g_binary_symbol_set_alt_label(symbol, new);

    g_object_unref(G_OBJECT(symbol));

 opve_bad_iter:

    gtk_tree_path_free(tree_path);

}



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


/******************************************************************************
*                                                                             *
*  Paramètres  : panel = panneau assurant l'affichage des paramètres.         *
*                preg  = expression régulière compilée à utiliser.            *
*                                                                             *
*  Description : Démarre l'actualisation du filtrage des chaînes.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void update_filtered_strings(GStringsPanel *panel, const regex_t *preg)
{
    panel->filter = preg;

    change_strings_panel_current_binary(panel, panel->binary);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : panel = panneau assurant l'affichage des paramètres.         *
*                label = étiquette liée au symbole à traiter.                 *
*                value = chaîne de caractères représentée par le symbole.     *
*                                                                             *
*  Description : Détermine si une chaîne textuelle doit être filtrée ou non.  *
*                                                                             *
*  Retour      : true si le symbole ne doit pas être affiché, false sinon.    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool is_string_filtered(GStringsPanel *panel, const char *label, const char *value)
{
    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;

    if (label != NULL)
    {
        ret = regexec(panel->filter, label, 1, &match, 0);
        result &= (ret == REG_NOMATCH);
    }

    ret = regexec(panel->filter, value, 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_event_over_strings(GtkWidget *widget, GdkEventButton *event, GStringsPanel *panel)
{
    GtkTreeSelection *selection;            /* Sélection courante          */
    GtkTreeIter iter;                       /* Point de sélection          */
    GtkTreeModel *model;                    /* Modèle de gestion           */
    GBinSymbol *symbol;                     /* Symbole en cours d'étude    */
    const vmpa2t *addr;                     /* Adressse associée au signet */
    GtkViewPanel *vpanel;                   /* Afficheur effectif de code  */

    switch (event->button)
    {
        case 1:

            if (event->type != GDK_2BUTTON_PRESS)
                break;

            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, STC_STRING, &symbol, -1);

                addr = get_mrange_addr(g_binary_symbol_get_range(symbol));

                vpanel = g_editor_item_get_current_view(G_EDITOR_ITEM(panel));
                gtk_view_panel_request_move(vpanel, addr);

                g_object_unref(G_OBJECT(symbol));

            }

            break;

        case 3:
            if (event->type == GDK_BUTTON_RELEASE)
                gtk_menu_popup(panel->menu, NULL, NULL, NULL, NULL, event->button, event->time);
            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_strings_panel_menu(GStringsPanel *panel)
{
    GtkWidget *result;                      /* Support à retourner         */
    GtkWidget *submenuitem;                 /* Sous-élément de menu        */

    result = gtk_menu_new();

    submenuitem = qck_create_menu_item(NULL, NULL, _("_Edit name"), NULL, NULL);
    gtk_container_add(GTK_CONTAINER(result), submenuitem);

    submenuitem = qck_create_menu_item(NULL, NULL, _("_Copy to clipboard"),
                                       G_CALLBACK(mcb_strings_panel_copy), panel);
    gtk_container_add(GTK_CONTAINER(result), submenuitem);

    submenuitem = qck_create_menu_item(NULL, NULL, _("_Find references..."),
                                       G_CALLBACK(mcb_strings_panel_find_refs), 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_strings_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 GBinSymbol *get_selected_panel_symbol(GtkTreeView *treeview, GtkTreeIter *save)
{
    GBinSymbol *result;                     /* Chaîne textuelle à 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, STC_STRING, &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 le nom".                         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void mcb_strings_panel_edit(GtkMenuItem *menuitem, GStringsPanel *panel)
{
    GtkTreeIter iter;                       /* Point de la sélection       */
    GBinSymbol *symbol;                     /* Symbole sélectionné         */
    GtkTreeModel *model;                    /* Gestionnaire de données     */
    GtkTreePath *path;                      /* Chemin d'accès à ce point   */

    symbol = get_selected_panel_symbol(panel->treeview, &iter);
    if (symbol == NULL) return;

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

    gtk_tree_view_set_cursor(panel->treeview, path,
                             gtk_tree_view_get_column(panel->treeview, STC_NAME - STC_PHYSICAL),
                             TRUE);

    gtk_tree_path_free(path);

    g_object_unref(G_OBJECT(symbol));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : menuitem = élément de menu sélectionné.                      *
*                treeview = arbre contenant la sélection à exporter.          *
*                                                                             *
*  Description : Réagit avec le menu "Copier dans le presse-papiers".         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void mcb_strings_panel_copy(GtkMenuItem *menuitem, GStringsPanel *panel)
{
    GtkTreeSelection *selection;            /* Sélection de l'arbre        */
    GtkTreeIter iter;                       /* Point de sélection          */
    GtkTreeModel *model;                    /* Modèle de gestion           */
    gchar *string;                          /* Chaîne sélectionnée         */
    GtkClipboard *clipboard;                /* Presse-papiers d'arrivée    */

    selection = gtk_tree_view_get_selection(panel->treeview);

    if (gtk_tree_selection_get_selected(selection, &model, &iter))
    {
        gtk_tree_model_get(model, &iter, STC_VALUE, &string, -1);

        if (string != NULL)
        {
            clipboard = gtk_clipboard_get_for_display(gdk_display_get_default(),
                                                      GDK_SELECTION_PRIMARY);

            gtk_clipboard_set_text(clipboard, string, strlen(string));
            g_free(string);

        }

    }

}


/******************************************************************************
*                                                                             *
*  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 "Trouver les références...".             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void mcb_strings_panel_find_refs(GtkMenuItem *menuitem, GStringsPanel *panel)
{
    GBinSymbol *symbol;                     /* Symbole sélectionné         */
    const mrange_t *range;                  /* Couverture en mémoire       */
    GLoadedBinary *binary;                  /* Représentation binaire      */
    GArchProcessor *proc;                   /* Processeur de l'architecture*/
    GArchInstruction *list;                 /* Ensemble des instructions   */
    GArchInstruction *instr;                /* Point de croisements        */
    GObject *ref;                           /* Espace de référencements    */
    GtkWidget *dialog;                      /* Boîte de dialogue à montrer */
    vmpa2t *addr;                           /* Adresse de destination      */
    GtkViewPanel *vpanel;                   /* Afficheur effectif de code  */

    symbol = get_selected_panel_symbol(panel->treeview, NULL);
    if (symbol == NULL) return;

    range = g_binary_symbol_get_range(symbol);

    binary = g_editor_item_get_current_binary(G_EDITOR_ITEM(panel));
    proc = g_loaded_binary_get_processor(binary);
    list = g_arch_processor_get_disassembled_instructions(proc);

    /**
     * Se rapporter aux commentaires de mcb_edition_list_xrefs() pour les questions
     * concernant l'usage d'une adresse d'instruction au lieu de son emplacement.
     */
    instr = g_arch_instruction_find_by_address(list, get_mrange_addr(range), true);

    ref = g_editor_item_get_global_ref(G_EDITOR_ITEM(panel));

    dialog = create_gotox_dialog_for_cross_references(GTK_WINDOW(ref), binary, instr, true);

    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
    {
        addr = get_address_from_gotox_dialog(dialog);

        vpanel = g_editor_item_get_current_view(G_EDITOR_ITEM(panel));
        gtk_view_panel_request_move(vpanel, addr);

        delete_vmpa(addr);

    }

    gtk_widget_destroy(dialog);

    g_object_unref(G_OBJECT(proc));

    g_object_unref(G_OBJECT(symbol));

}


/******************************************************************************
*                                                                             *
*  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_strings_panel_filter(GtkMenuItem *menuitem, GStringsPanel *panel)
{
#if 0
    GCfgParam *param;                       /* Paramètre sélectionné       */

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

    g_config_param_make_empty(param);

    g_object_unref(G_OBJECT(param));
#endif
}