diff options
Diffstat (limited to 'src/gui/dialogs/preferences.c')
-rw-r--r-- | src/gui/dialogs/preferences.c | 421 |
1 files changed, 242 insertions, 179 deletions
diff --git a/src/gui/dialogs/preferences.c b/src/gui/dialogs/preferences.c index 4a3fb7c..68e7fd9 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,104 @@ #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" +#include "../../plugins/pglist.h" +#include "../../plugins/tweakable.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 */ - - const char *name; /* Désignation interne */ - const char *title; /* Désignation humaine */ - - GtkBuilder *builder; /* Constructeur GTK */ - GtkWidget *panel; /* Panneau GTK */ - - struct _pref_node_desc_t *children; /* Sous-arborescence */ - -} pref_node_desc_t; - - -#define PREF_NODE_NULL_ENTRY { .title = NULL } +/* --------------------------- BASES DE BOITE DE DIALOGUE --------------------------- */ -/* Liste des paramétrages à afficher */ -static pref_node_desc_t _prefs_nodes[] = { +/* Procède à l'initialisation de la fenêtre des paramètres. */ +static void gtk_preferences_dialog_class_init(GtkPreferencesDialogClass *); - { - .create = NULL, - - .title = "Analysis", +/* Procède à l'initialisation de la fenêtre des paramètres. */ +static void gtk_preferences_dialog_init(GtkPreferencesDialog *); - .children = (pref_node_desc_t []){ +/* Supprime toutes les références externes. */ +static void gtk_preferences_dialog_dispose(GObject *); - { - .create = create_labels_preferences, - .load = load_labels_configuration, - .store = store_labels_configuration, +/* Procède à la libération totale de la mémoire. */ +static void gtk_preferences_dialog_finalize(GObject *); - .name = "labels", - .title = "Colored labels", +/* 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 *); - PREF_NODE_NULL_ENTRY - } - }, +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ - { - .create = NULL, - .title = "Editor", - .children = (pref_node_desc_t []){ - { - .create = create_fgraph_preferences, - .load = load_fgraph_configuration, - .store = store_fgraph_configuration, - .name = "fgraph", - .title = "Function graph", +/* ---------------------------------------------------------------------------------- */ +/* BASES DE BOITE DE DIALOGUE */ +/* ---------------------------------------------------------------------------------- */ - }, - - PREF_NODE_NULL_ENTRY - - } - }, +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkPreferencesDialog, gtk_preferences_dialog, GTK_TYPE_WINDOW); - PREF_NODE_NULL_ENTRY - -}; +/****************************************************************************** +* * +* Paramètres : class = classe 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_class_init(GtkPreferencesDialogClass *class) { - PLI_TITLE, /* Etiquette de la section */ - PLI_PANEL, /* Panneau graphique associé */ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ -} PrefListItem; + object = G_OBJECT_CLASS(class); + object->dispose = gtk_preferences_dialog_dispose; + object->finalize = gtk_preferences_dialog_finalize; -/* Ajoute un panneau de paramétrage à la boîte de dialogue. */ -static void add_preferences_node(GtkTreeStore *, GtkTreeIter *, GGenConfig *, GtkStack *, pref_node_desc_t *); + widget = GTK_WIDGET_CLASS(class); -/* Affiche le panneau correspondant au noeud sélectionné. */ -static void on_prefs_selection_changed(GtkTreeSelection *, GtkBuilder *); + gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/preferences.ui"); -/* Lance la sauvegarde d'éléments de paramétrage. */ -static void store_preferences_node(GGenConfig *, pref_node_desc_t *); + 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); -/* Sauvegarde l'ensemble des paramètres de configuration. */ -static void on_prefs_apply_button_clicked(GtkButton *, GtkBuilder *); + /* 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); +} /****************************************************************************** * * -* 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 : dialog = composant GTK à initialiser. * * * -* Description : Ajoute un panneau de paramétrage à la boîte de dialogue. * +* Description : Procède à l'initialisation de la fenêtre des paramètres. * * * * Retour : - * * * @@ -154,95 +129,150 @@ 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_init(GtkPreferencesDialog *dialog) { - GtkTreeIter iter; /* Point d'insertion */ - pref_node_desc_t *child; /* Sous-élément à traiter */ + 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*/ + tweak_info_t *dyn_infos; /* Informations supplémentaires*/ + size_t dyn_count; /* Quantité de ces informations*/ + + 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)); - if (node->create == NULL) + dialog->navigations = g_hash_table_new_full(g_str_hash, g_str_equal, free, g_object_unref); + + /* Chargement des sections fixes */ + + for (i = 0; i < ARRAY_SIZE(infos); i++) { - node->builder = NULL; - node->panel = NULL; + 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)); + } - else + + /* Chargement des sections dynamiques */ + + dyn_infos = get_tweakable_plugins_info(&dyn_count); + + for (i = 0; i < dyn_count; i++) { - node->panel = node->create(&node->builder); + info = &dyn_infos[i]; - node->load(node->builder, config); + navigation = gtk_preferences_dialog_get_navigation(dialog, info->parent, true); + assert(navigation != NULL); - gtk_widget_show(node->panel); + section = gtk_tweak_section_new(info); - gtk_stack_add_named(stack, node->panel, node->name); + gtk_list_box_append(navigation, GTK_WIDGET(section)); } - gtk_tree_store_append(store, &iter, parent); + if (dyn_infos != NULL) + free(dyn_infos); + + /* Affichage de la liste racine */ - gtk_tree_store_set(store, &iter, - PLI_TITLE, _(node->title), - PLI_PANEL, node->panel, - -1); + navigation = gtk_preferences_dialog_get_navigation(dialog, "root", false); + assert(navigation != NULL); - if (node->children != NULL) - for (child = node->children; child->title != NULL; child++) - add_preferences_node(store, &iter, config, stack, child); + 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 : 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 : Supprime toutes les références externes. * * * -* Retour : Adresse de la fenêtre mise en place. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -GtkWidget *create_preferences_dialog(GtkWindow *parent, GtkBuilder **outb) +static void gtk_preferences_dialog_dispose(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 */ + GtkPreferencesDialog *dialog; /* Version spécialisée */ + + dialog = GTK_PREFERENCES_DIALOG(object); - builder = gtk_builder_new_from_resource("/org/chrysalide/gui/dialogs/preferences.ui"); - *outb = builder; + if (dialog->navigations != NULL) + { + /** + * Cf. documentation de g_hash_table_new_full(). + */ + g_hash_table_remove_all(dialog->navigations); - result = GTK_WIDGET(gtk_builder_get_object(builder, "window")); + g_hash_table_unref(dialog->navigations); + dialog->navigations = NULL; - gtk_window_set_transient_for(GTK_WINDOW(result), parent); + } - /* Intégration des différentes sections */ + gtk_widget_dispose_template(GTK_WIDGET(dialog), GTK_TYPE_PREFERENCES_DIALOG); + + G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->dispose(object); + +} - config = get_main_configuration(); - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - store = GTK_TREE_STORE(gtk_builder_get_object(builder, "pref_list")); +static void gtk_preferences_dialog_finalize(GObject *object) +{ + G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->finalize(object); - 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 : 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 : - * +* * +******************************************************************************/ - /* Connexion des signaux */ +GtkWindow *gtk_preferences_dialog_new(GtkWindow *parent) +{ + GtkWindow *result; /* Boite de dialogue à renvoyer*/ - gtk_builder_add_callback_symbols(builder, - BUILDER_CALLBACK(on_prefs_selection_changed), - BUILDER_CALLBACK(on_prefs_apply_button_clicked), - NULL); + result = g_object_new(GTK_TYPE_PREFERENCES_DIALOG, NULL); - gtk_builder_connect_signals(builder, builder); + if (!gtk_preferences_dialog_create(GTK_PREFERENCES_DIALOG(result), parent)) + g_clear_object(&result); return result; @@ -251,79 +281,85 @@ 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. * +* parent = fenêtre parente à surpasser. * * * -* Description : Affiche le panneau correspondant au noeud sélectionné. * +* Description : Met en place la boîte de dialogue pour les préférences. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void on_prefs_selection_changed(GtkTreeSelection *selection, GtkBuilder *builder) +bool gtk_preferences_dialog_create(GtkPreferencesDialog *dialog, GtkWindow *parent) { - GtkTreeModel *model; /* Gestionnaire de données */ - GtkTreeIter iter; /* Position courante */ - GtkWidget *panel; /* Panneau à mettre en avant */ - GtkStack *stack; /* Pile à mettre à jour */ - - if (gtk_tree_selection_get_selected(selection, &model, &iter)) - { - gtk_tree_model_get(model, &iter, PLI_PANEL, &panel, -1); + bool result; /* Bilan à retourner */ - stack = GTK_STACK(gtk_builder_get_object(builder, "stack")); + result = true; - if (panel == NULL) - gtk_stack_set_visible_child_name(stack, "empty"); - - else - { - gtk_stack_set_visible_child(stack, panel); + gtk_window_set_transient_for(GTK_WINDOW(dialog), parent); - g_object_unref(G_OBJECT(panel)); - - } - - } + return result; } /****************************************************************************** * * -* Paramètres : config = configuration globale à actualiser. * -* node = noeud de description courant à traiter. * +* 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 : Lance la sauvegarde d'éléments de paramétrage. * +* Description : Fournit la liste de section désignée par un nom. * * * -* Retour : - * +* Retour : Composant graphique à utiliser. * * * * Remarques : - * * * ******************************************************************************/ -static void store_preferences_node(GGenConfig *config, pref_node_desc_t *node) +static GtkListBox *gtk_preferences_dialog_get_navigation(GtkPreferencesDialog *dialog, const char *key, bool create) { - pref_node_desc_t *child; /* Sous-élément à traiter */ + GtkListBox *result; /* Instance à retourner */ +#ifndef NDEBUG + gboolean status; /* Bilan d'une insertion */ +#endif - if (node->create != NULL) - node->store(node->builder, config); + result = g_hash_table_lookup(dialog->navigations, key); - if (node->children != NULL) - for (child = node->children; child->title != NULL; child++) - store_preferences_node(config, child); + if (result == NULL && create) + { + result = GTK_LIST_BOX(gtk_list_box_new()); + g_object_ref_sink(G_OBJECT(result)); + + gtk_list_box_set_selection_mode(result, GTK_SELECTION_BROWSE); + gtk_widget_add_css_class(GTK_WIDGET(result), "navigation-sidebar"); + +#ifndef NDEBUG + status = g_hash_table_insert(dialog->navigations, strdup(key), result); + assert(status); +#else + g_hash_table_insert(dialog->navigations, key, result); +#endif + + } + + if (result != NULL) + ref_object(result); + + return result; } /****************************************************************************** * * -* Paramètres : button = bouton GTK à l'origine de l'opération. * -* builder = constructeur GTK avec toutes les références. * +* 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 : Sauvegarde l'ensemble des paramètres de configuration. * +* Description : Réagit à un changement de sélection dans les sections. * * * * Retour : - * * * @@ -331,14 +367,41 @@ static void store_preferences_node(GGenConfig *config, pref_node_desc_t *node) * * ******************************************************************************/ -static void on_prefs_apply_button_clicked(GtkButton *button, GtkBuilder *builder) +static void gtk_preferences_dialog_on_row_selected(GtkListBox *navigation, GtkListBoxRow *selected, GtkPreferencesDialog *dialog) { - GGenConfig *config; /* Configuration globale */ - pref_node_desc_t *iter; /* Boucle de parcours */ + GtkTweakSection *section; /* Nature réelle sélectionnée */ + GType type; /* Type de panneau de config. */ + GtkWidget *panel; /* Nouveau panneau à présenter */ + + if (selected != NULL) + { + if (!gtk_tweak_section_has_sub_section(GTK_TWEAK_SECTION(selected))) + { + section = GTK_TWEAK_SECTION(selected); + + 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); - config = get_main_configuration(); + gtk_scrolled_window_set_child(dialog->main_content, panel); - for (iter = _prefs_nodes; iter->title != NULL; iter++) - store_preferences_node(config, iter); + } + + else + { + + // TODO + + } + + } } + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + |