diff options
Diffstat (limited to 'src/gui/dialogs/preferences.c')
-rw-r--r-- | src/gui/dialogs/preferences.c | 399 |
1 files changed, 219 insertions, 180 deletions
diff --git a/src/gui/dialogs/preferences.c b/src/gui/dialogs/preferences.c index 4a3fb7c..aca562a 100644 --- a/src/gui/dialogs/preferences.c +++ b/src/gui/dialogs/preferences.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * preferences.c - (re)définition de l'identité de l'utilisateur + * preferences.c - boîte de dialogue d'édition des préférences de l'utilisateur * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -24,129 +24,160 @@ #include "preferences.h" -#include <i18n.h> +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <gdk/gdkkeysyms.h> -#include "prefs_fgraph.h" -#include "prefs_labels.h" -#include "../../core/params.h" -#include "../../gtkext/easygtk.h" +#include <config.h> +#include "preferences-int.h" +#include "prefs/security.h" +#include "../../common/cpp.h" +#include "../../gtkext/tweak.h" -/* Constructeur de panneau de paramétrage */ -typedef GtkWidget * (* prefs_panel_creation_cb) (GtkBuilder **); -/* Chargement de la configuration */ -typedef void (* prefs_config_update_cb) (GtkBuilder *, GGenConfig *); -/* Description d'un noeud de préférences */ -typedef struct _pref_node_desc_t -{ - prefs_panel_creation_cb create; /* Procédure de création */ - prefs_config_update_cb load; /* Procédure de chargement */ - prefs_config_update_cb store; /* Procédure d'enregistrement */ +/* --------------------------- BASES DE BOITE DE DIALOGUE --------------------------- */ - const char *name; /* Désignation interne */ - const char *title; /* Désignation humaine */ - GtkBuilder *builder; /* Constructeur GTK */ - GtkWidget *panel; /* Panneau GTK */ +/* Procède à l'initialisation de la fenêtre des paramètres. */ +static void gtk_preferences_dialog_class_init(GtkPreferencesDialogClass *); - struct _pref_node_desc_t *children; /* Sous-arborescence */ +/* Procède à l'initialisation de la fenêtre des paramètres. */ +static void gtk_preferences_dialog_init(GtkPreferencesDialog *); -} pref_node_desc_t; +/* Supprime toutes les références externes. */ +static void gtk_preferences_dialog_dispose(GObject *); +/* Procède à la libération totale de la mémoire. */ +static void gtk_preferences_dialog_finalize(GObject *); -#define PREF_NODE_NULL_ENTRY { .title = NULL } +/* Fournit la liste de section désignée par un nom. */ +static GtkListBox *gtk_preferences_dialog_get_navigation(GtkPreferencesDialog *, const char *, bool); +/* Réagit à un changement de sélection dans les sections. */ +static void gtk_preferences_dialog_on_row_selected(GtkListBox *, GtkListBoxRow *, GtkPreferencesDialog *); -/* Liste des paramétrages à afficher */ -static pref_node_desc_t _prefs_nodes[] = { - { - .create = NULL, - .title = "Analysis", +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ - .children = (pref_node_desc_t []){ - { - .create = create_labels_preferences, - .load = load_labels_configuration, - .store = store_labels_configuration, - .name = "labels", - .title = "Colored labels", - }, - PREF_NODE_NULL_ENTRY +/* ---------------------------------------------------------------------------------- */ +/* BASES DE BOITE DE DIALOGUE */ +/* ---------------------------------------------------------------------------------- */ - } - }, +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkPreferencesDialog, gtk_preferences_dialog, GTK_TYPE_WINDOW); - { - .create = NULL, - .title = "Editor", +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Procède à l'initialisation de la fenêtre des paramètres. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - .children = (pref_node_desc_t []){ +static void gtk_preferences_dialog_class_init(GtkPreferencesDialogClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ - { - .create = create_fgraph_preferences, - .load = load_fgraph_configuration, - .store = store_fgraph_configuration, + object = G_OBJECT_CLASS(class); - .name = "fgraph", - .title = "Function graph", + object->dispose = gtk_preferences_dialog_dispose; + object->finalize = gtk_preferences_dialog_finalize; - }, + widget = GTK_WIDGET_CLASS(class); - PREF_NODE_NULL_ENTRY + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/preferences.ui"); - } + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, side_title); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, side_content); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, main_title); + gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, main_content); - }, + /* Active une action native (cf. https://docs.gtk.org/gtk4/class.Window.html#actions) */ + gtk_widget_class_add_binding_action(widget, GDK_KEY_Escape, 0 /* GDK 4.14 : GDK_NO_MODIFIER_MASK */, "window.close", NULL); - PREF_NODE_NULL_ENTRY +} -}; +/****************************************************************************** +* * +* Paramètres : dialog = composant GTK à initialiser. * +* * +* Description : Procède à l'initialisation de la fenêtre des paramètres. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ -/* Eléments de la liste de sections */ -typedef enum _PrefListItem +static void gtk_preferences_dialog_init(GtkPreferencesDialog *dialog) { - PLI_TITLE, /* Etiquette de la section */ - PLI_PANEL, /* Panneau graphique associé */ + size_t i; /* Boucle de parcours */ + const tweak_info_t *info; /* Informations à considérer */ + GtkListBox *navigation; /* Liste de sections à afficher*/ + GtkTweakSection *section; /* Nouvelle section à présenter*/ -} PrefListItem; + tweak_info_t infos[] = { + TWEAK_SIMPLE_DEF("root", "Basics", + "security-high-symbolic", "security", "Security", GTK_TYPE_SECURITY_TWEAK_PANEL), + }; + gtk_widget_init_template(GTK_WIDGET(dialog)); -/* Ajoute un panneau de paramétrage à la boîte de dialogue. */ -static void add_preferences_node(GtkTreeStore *, GtkTreeIter *, GGenConfig *, GtkStack *, pref_node_desc_t *); + dialog->navigations = g_hash_table_new_full(g_str_hash, g_str_equal, free, g_object_unref); -/* Affiche le panneau correspondant au noeud sélectionné. */ -static void on_prefs_selection_changed(GtkTreeSelection *, GtkBuilder *); + /* Chargement des sections fixes */ -/* Lance la sauvegarde d'éléments de paramétrage. */ -static void store_preferences_node(GGenConfig *, pref_node_desc_t *); + for (i = 0; i < ARRAY_SIZE(infos); i++) + { + info = &infos[i]; + + navigation = gtk_preferences_dialog_get_navigation(dialog, info->parent, true); + assert(navigation != NULL); + + section = gtk_tweak_section_new(info); + + gtk_list_box_append(navigation, GTK_WIDGET(section)); + + } -/* Sauvegarde l'ensemble des paramètres de configuration. */ -static void on_prefs_apply_button_clicked(GtkButton *, GtkBuilder *); + /* Affichage de la liste racine */ + navigation = gtk_preferences_dialog_get_navigation(dialog, "root", false); + assert(navigation != NULL); + + g_signal_connect(navigation, "row-selected", + G_CALLBACK(gtk_preferences_dialog_on_row_selected), dialog); + + gtk_scrolled_window_set_child(dialog->side_content, GTK_WIDGET(navigation)); + + unref_object(navigation); + +} /****************************************************************************** * * -* Paramètres : store = arborescence des sections à compléter. * -* parent = point d'insertion du parent. * -* config = configuration globale à charger. * -* stack = pile de composants GTK à constituer. * -* node = noeud de description courant à traiter. * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Ajoute un panneau de paramétrage à la boîte de dialogue. * +* Description : Supprime toutes les références externes. * * * * Retour : - * * * @@ -154,95 +185,96 @@ static void on_prefs_apply_button_clicked(GtkButton *, GtkBuilder *); * * ******************************************************************************/ -static void add_preferences_node(GtkTreeStore *store, GtkTreeIter *parent, GGenConfig *config, GtkStack *stack, pref_node_desc_t *node) +static void gtk_preferences_dialog_dispose(GObject *object) { - GtkTreeIter iter; /* Point d'insertion */ - pref_node_desc_t *child; /* Sous-élément à traiter */ - - if (node->create == NULL) - { - node->builder = NULL; - node->panel = NULL; - } - else - { - node->panel = node->create(&node->builder); + GtkPreferencesDialog *dialog; /* Version spécialisée */ - node->load(node->builder, config); + dialog = GTK_PREFERENCES_DIALOG(object); - gtk_widget_show(node->panel); + if (dialog->navigations != NULL) + { + /** + * Cf. documentation de g_hash_table_new_full(). + */ + g_hash_table_remove_all(dialog->navigations); - gtk_stack_add_named(stack, node->panel, node->name); + g_hash_table_unref(dialog->navigations); + dialog->navigations = NULL; } - gtk_tree_store_append(store, &iter, parent); - - gtk_tree_store_set(store, &iter, - PLI_TITLE, _(node->title), - PLI_PANEL, node->panel, - -1); + gtk_widget_dispose_template(GTK_WIDGET(dialog), GTK_TYPE_PREFERENCES_DIALOG); - if (node->children != NULL) - for (child = node->children; child->title != NULL; child++) - add_preferences_node(store, &iter, config, stack, child); + G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->dispose(object); } /****************************************************************************** * * -* Paramètres : parent = fenêtre principale de l'éditeur. * -* outb = constructeur à détruire après usage. [OUT] * +* Paramètres : object = instance d'objet GLib à traiter. * * * -* Description : Propose une boîte de dialogue pour la configuration générale.* +* Description : Procède à la libération totale de la mémoire. * * * -* Retour : Adresse de la fenêtre mise en place. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -GtkWidget *create_preferences_dialog(GtkWindow *parent, GtkBuilder **outb) +static void gtk_preferences_dialog_finalize(GObject *object) { - GtkWidget *result; /* Fenêtre à renvoyer */ - GtkBuilder *builder; /* Constructeur utilisé */ - GGenConfig *config; /* Configuration globale */ - GtkStack *stack; /* Pile à mettre à jour */ - GtkTreeStore *store; /* Arborescence des sections */ - pref_node_desc_t *iter; /* Boucle de parcours */ - GtkTreeView *treeview; /* Arborescence principale */ + G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->finalize(object); - builder = gtk_builder_new_from_resource("/org/chrysalide/gui/dialogs/preferences.ui"); - *outb = builder; +} - result = GTK_WIDGET(gtk_builder_get_object(builder, "window")); - gtk_window_set_transient_for(GTK_WINDOW(result), parent); +/****************************************************************************** +* * +* Paramètres : parent = fenêtre parente à surpasser. * +* * +* Description : Construit une boîte de dialogue pour les préférences. * +* * +* Retour : Adresse de la fenêtre mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ - /* Intégration des différentes sections */ +GtkWindow *gtk_preferences_dialog_new(GtkWindow *parent) +{ + GtkWindow *result; /* Boite de dialogue à renvoyer*/ - config = get_main_configuration(); + result = g_object_new(GTK_TYPE_PREFERENCES_DIALOG, NULL); - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); + if (!gtk_preferences_dialog_create(GTK_PREFERENCES_DIALOG(result), parent)) + g_clear_object(&result); - store = GTK_TREE_STORE(gtk_builder_get_object(builder, "pref_list")); + return result; - for (iter = _prefs_nodes; iter->title != NULL; iter++) - add_preferences_node(store, NULL, config, stack, iter); +} - treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); - gtk_tree_view_expand_all(treeview); +/****************************************************************************** +* * +* Paramètres : dialog = boîte de dialogue à initialiser pleinement. * +* parent = fenêtre parente à surpasser. * +* * +* Description : Met en place la boîte de dialogue pour les préférences. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ - /* Connexion des signaux */ +bool gtk_preferences_dialog_create(GtkPreferencesDialog *dialog, GtkWindow *parent) +{ + bool result; /* Bilan à retourner */ - gtk_builder_add_callback_symbols(builder, - BUILDER_CALLBACK(on_prefs_selection_changed), - BUILDER_CALLBACK(on_prefs_apply_button_clicked), - NULL); + result = true; - gtk_builder_connect_signals(builder, builder); + gtk_window_set_transient_for(GTK_WINDOW(dialog), parent); return result; @@ -251,52 +283,59 @@ GtkWidget *create_preferences_dialog(GtkWindow *parent, GtkBuilder **outb) /****************************************************************************** * * -* Paramètres : selection = sélection courante de l'arborescence des options.* -* builder = constructeur GTK avec toutes les références. * +* Paramètres : dialog = boîte de dialogue à initialiser pleinement. * +* key = désignation de la liste à fournir. * +* create = autorisation d'une création si besoin est. * * * -* Description : Affiche le panneau correspondant au noeud sélectionné. * +* Description : Fournit la liste de section désignée par un nom. * * * -* Retour : - * +* Retour : Composant graphique à utiliser. * * * * Remarques : - * * * ******************************************************************************/ -static void on_prefs_selection_changed(GtkTreeSelection *selection, GtkBuilder *builder) +static GtkListBox *gtk_preferences_dialog_get_navigation(GtkPreferencesDialog *dialog, const char *key, bool create) { - GtkTreeModel *model; /* Gestionnaire de données */ - GtkTreeIter iter; /* Position courante */ - GtkWidget *panel; /* Panneau à mettre en avant */ - GtkStack *stack; /* Pile à mettre à jour */ + GtkListBox *result; /* Instance à retourner */ +#ifndef NDEBUG + gboolean status; /* Bilan d'une insertion */ +#endif - if (gtk_tree_selection_get_selected(selection, &model, &iter)) - { - gtk_tree_model_get(model, &iter, PLI_PANEL, &panel, -1); + result = g_hash_table_lookup(dialog->navigations, key); - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); + if (result == NULL && create) + { + result = GTK_LIST_BOX(gtk_list_box_new()); + g_object_ref_sink(G_OBJECT(result)); - if (panel == NULL) - gtk_stack_set_visible_child_name(stack, "empty"); + gtk_list_box_set_selection_mode(result, GTK_SELECTION_BROWSE); + gtk_widget_add_css_class(GTK_WIDGET(result), "navigation-sidebar"); - else - { - gtk_stack_set_visible_child(stack, panel); +#ifndef NDEBUG + status = g_hash_table_insert(dialog->navigations, strdup(key), result); + assert(status); +#else + g_hash_table_insert(dialog->navigations, key, result); +#endif - g_object_unref(G_OBJECT(panel)); + } - } + if (result != NULL) + ref_object(result); - } + return result; } /****************************************************************************** * * -* Paramètres : config = configuration globale à actualiser. * -* node = noeud de description courant à traiter. * +* Paramètres : navigation = liste concernée par l'événement. * +* selected = élément sélectionné (voire NULL). * +* dialog = boîte de dialogue à initialiser pleinement. * * * -* Description : Lance la sauvegarde d'éléments de paramétrage. * +* Description : Réagit à un changement de sélection dans les sections. * * * * Retour : - * * * @@ -304,41 +343,41 @@ static void on_prefs_selection_changed(GtkTreeSelection *selection, GtkBuilder * * * ******************************************************************************/ -static void store_preferences_node(GGenConfig *config, pref_node_desc_t *node) +static void gtk_preferences_dialog_on_row_selected(GtkListBox *navigation, GtkListBoxRow *selected, GtkPreferencesDialog *dialog) { - pref_node_desc_t *child; /* Sous-élément à traiter */ + GtkTweakSection *section; /* Nature réelle sélectionnée */ + GType type; /* Type de panneau de config. */ + GtkWidget *panel; /* Nouveau panneau à présenter */ - if (node->create != NULL) - node->store(node->builder, config); + if (selected != NULL) + { + if (!gtk_tweak_section_has_sub_section(GTK_TWEAK_SECTION(selected))) + { + section = GTK_TWEAK_SECTION(selected); - if (node->children != NULL) - for (child = node->children; child->title != NULL; child++) - store_preferences_node(config, child); + gtk_label_set_text(dialog->main_title, gtk_tweak_section_get_label(section)); -} + type = gtk_tweak_section_get_panel(section); + panel = g_object_new(type, NULL); + gtk_scrolled_window_set_child(dialog->main_content, panel); -/****************************************************************************** -* * -* Paramètres : button = bouton GTK à l'origine de l'opération. * -* builder = constructeur GTK avec toutes les références. * -* * -* Description : Sauvegarde l'ensemble des paramètres de configuration. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + } -static void on_prefs_apply_button_clicked(GtkButton *button, GtkBuilder *builder) -{ - GGenConfig *config; /* Configuration globale */ - pref_node_desc_t *iter; /* Boucle de parcours */ + else + { + + // TODO - config = get_main_configuration(); + } - for (iter = _prefs_nodes; iter->title != NULL; iter++) - store_preferences_node(config, iter); + } } + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + |