summaryrefslogtreecommitdiff
path: root/src/gui/dialogs/preferences.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/dialogs/preferences.c')
-rw-r--r--src/gui/dialogs/preferences.c421
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 */
+/* ---------------------------------------------------------------------------------- */
+