From 6be4c3c36378ae64117ba7b8e9c47bdeb189c7ae Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Sat, 21 Sep 2024 19:01:21 +0100 Subject: [PATCH] Support for enums as GtkRadioButton preferences The "=" in the widget name will separate the pref name from the enum string value that is used when the radio button is activated. When the radio button is deactivated, its preference update is ignored because another button must have been made active. --- gnucash/gnome-utils/dialog-preferences.c | 54 ++++++++++--------- gnucash/gnome/dialog-lot-viewer.c | 4 +- gnucash/gnome/dialog-tax-info.c | 2 +- gnucash/gnome/gnc-plugin-business.c | 4 +- .../csv-exp/assistant-csv-export.c | 2 +- gnucash/import-export/import-match-picker.cpp | 2 +- libgnucash/app-utils/gnc-gsettings.cpp | 45 +++++++++++++++- libgnucash/app-utils/gnc-gsettings.h | 3 ++ libgnucash/core-utils/gnc-prefs-p.h | 1 + libgnucash/core-utils/gnc-prefs.c | 3 +- libgnucash/core-utils/gnc-prefs.h | 5 +- 11 files changed, 91 insertions(+), 34 deletions(-) diff --git a/gnucash/gnome-utils/dialog-preferences.c b/gnucash/gnome-utils/dialog-preferences.c index 2904d011e9..f4de4a5510 100644 --- a/gnucash/gnome-utils/dialog-preferences.c +++ b/gnucash/gnome-utils/dialog-preferences.c @@ -781,14 +781,18 @@ gnc_prefs_sort_pages (GtkNotebook *notebook) /*******************************/ static void -gnc_prefs_split_widget_name (const gchar *name, gchar **group, gchar **pref) +gnc_prefs_split_widget_name (const gchar *name, gchar **group, gchar **pref, gchar **value) { const gchar *group_with_pref = name + PREF_PREFIX_LEN; gchar **splits = g_strsplit (group_with_pref, "/", 0); + gchar **value_splits = g_strsplit (splits[1], "=", 0); *group = g_strdup (splits[0]); - *pref = g_strdup (splits[1]); + *pref = g_strdup (value_splits[0]); + if (value) + *value = g_strdup (value_splits[1]); /* may be NULL */ g_strfreev (splits); + g_strfreev (value_splits); } /****************************************************************************/ @@ -806,8 +810,8 @@ gnc_prefs_connect_font_button (GtkFontButton *fb) g_return_if_fail (GTK_IS_FONT_BUTTON(fb)); - gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(fb)), &group, &pref); - gnc_prefs_bind (group, pref, G_OBJECT (fb), "font-name"); + gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(fb)), &group, &pref, NULL); + gnc_prefs_bind (group, pref, NULL, G_OBJECT (fb), "font-name"); g_free (group); g_free (pref); @@ -873,9 +877,9 @@ gnc_prefs_connect_file_chooser_button (GtkFileChooserButton *fcb, const gchar *b g_return_if_fail (GTK_FILE_CHOOSER_BUTTON(fcb)); if (boxname == NULL) - gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(fcb)), &group, &pref); + gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(fcb)), &group, &pref, NULL); else - gnc_prefs_split_widget_name (boxname, &group, &pref); + gnc_prefs_split_widget_name (boxname, &group, &pref, NULL); uri = gnc_prefs_get_string (group, pref); @@ -990,16 +994,17 @@ file_chooser_clear_cb (GtkButton *button, gpointer user_data) static void gnc_prefs_connect_radio_button (GtkRadioButton *button) { - gchar *group, *pref; + gchar *group, *pref, *value; g_return_if_fail (GTK_IS_RADIO_BUTTON(button)); - gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(button)), &group, &pref); + gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(button)), &group, &pref, &value); - gnc_prefs_bind (group, pref, G_OBJECT(button), "active"); + gnc_prefs_bind (group, pref, value, G_OBJECT(button), "active"); g_free (group); g_free (pref); + g_free (value); } /****************************************************************************/ @@ -1014,16 +1019,17 @@ gnc_prefs_connect_radio_button (GtkRadioButton *button) static void gnc_prefs_connect_check_button (GtkCheckButton *button) { - gchar *group, *pref; + gchar *group, *pref, *value; g_return_if_fail (GTK_IS_CHECK_BUTTON(button)); - gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(button)), &group, &pref); + gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(button)), &group, &pref, &value); - gnc_prefs_bind (group, pref, G_OBJECT(button), "active"); + gnc_prefs_bind (group, pref, NULL, G_OBJECT(button), "active"); g_free (group); g_free (pref); + g_free (value); } /****************************************************************************/ @@ -1042,9 +1048,9 @@ gnc_prefs_connect_spin_button (GtkSpinButton *spin) g_return_if_fail (GTK_IS_SPIN_BUTTON(spin)); - gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(spin)), &group, &pref); + gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(spin)), &group, &pref, NULL); - gnc_prefs_bind (group, pref, G_OBJECT(spin), "value"); + gnc_prefs_bind (group, pref, NULL, G_OBJECT(spin), "value"); g_free (group); g_free (pref); @@ -1065,9 +1071,9 @@ gnc_prefs_connect_combo_box (GtkComboBox *box) g_return_if_fail (GTK_IS_COMBO_BOX(box)); - gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(box)), &group, &pref); + gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(box)), &group, &pref, NULL); - gnc_prefs_bind (group, pref, G_OBJECT(box), "active"); + gnc_prefs_bind (group, pref, NULL, G_OBJECT(box), "active"); g_free (group); g_free (pref); @@ -1088,9 +1094,9 @@ gnc_prefs_connect_currency_edit (GNCCurrencyEdit *gce, const gchar *boxname ) g_return_if_fail (GNC_IS_CURRENCY_EDIT(gce)); - gnc_prefs_split_widget_name (boxname, &group, &pref); + gnc_prefs_split_widget_name (boxname, &group, &pref, NULL); - gnc_prefs_bind (group, pref, G_OBJECT(gce), "mnemonic"); + gnc_prefs_bind (group, pref, NULL, G_OBJECT(gce), "mnemonic"); g_free (group); g_free (pref); @@ -1113,9 +1119,9 @@ gnc_prefs_connect_entry (GtkEntry *entry) g_return_if_fail (GTK_IS_ENTRY(entry)); - gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(entry)), &group, &pref); + gnc_prefs_split_widget_name (gtk_buildable_get_name (GTK_BUILDABLE(entry)), &group, &pref, NULL); - gnc_prefs_bind (group, pref, G_OBJECT(entry), "text"); + gnc_prefs_bind (group, pref, NULL, G_OBJECT(entry), "text"); g_free (group); g_free (pref); @@ -1136,9 +1142,9 @@ gnc_prefs_connect_period_select (GncPeriodSelect *period, const gchar *boxname ) g_return_if_fail (GNC_IS_PERIOD_SELECT(period)); - gnc_prefs_split_widget_name (boxname, &group, &pref); + gnc_prefs_split_widget_name (boxname, &group, &pref, NULL); - gnc_prefs_bind (group, pref, G_OBJECT(period), "active"); + gnc_prefs_bind (group, pref, NULL, G_OBJECT(period), "active"); g_free (group); g_free (pref); @@ -1159,9 +1165,9 @@ gnc_prefs_connect_date_edit (GNCDateEdit *gde , const gchar *boxname ) g_return_if_fail (GNC_IS_DATE_EDIT(gde)); - gnc_prefs_split_widget_name (boxname, &group, &pref); + gnc_prefs_split_widget_name (boxname, &group, &pref, NULL); - gnc_prefs_bind (group, pref, G_OBJECT(gde), "time"); + gnc_prefs_bind (group, pref, NULL, G_OBJECT(gde), "time"); g_free (group); g_free (pref); diff --git a/gnucash/gnome/dialog-lot-viewer.c b/gnucash/gnome/dialog-lot-viewer.c index cf1d4a69ca..061617a9ab 100644 --- a/gnucash/gnome/dialog-lot-viewer.c +++ b/gnucash/gnome/dialog-lot-viewer.c @@ -1129,10 +1129,10 @@ lv_create (GNCLotViewer *lv, GtkWindow *parent) { GObject *object; object = gtk_builder_get_object (builder, "lot_vpaned"); - gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_VPOS, object, "position"); + gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_VPOS, NULL, object, "position"); object = gtk_builder_get_object (builder, "lot_hpaned"); - gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_HPOS, object, "position"); + gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_HPOS, NULL, object, "position"); } lv->selected_lot = NULL; diff --git a/gnucash/gnome/dialog-tax-info.c b/gnucash/gnome/dialog-tax-info.c index dd1feb2f79..dcb093b34a 100644 --- a/gnucash/gnome/dialog-tax-info.c +++ b/gnucash/gnome/dialog-tax-info.c @@ -1509,7 +1509,7 @@ gnc_tax_info_dialog_create (GtkWidget * parent, TaxInfoDialog *ti_dialog) if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY)) { GObject *object = gtk_builder_get_object (builder, "paned"); - gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_PANED_POS, object, "position"); + gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_PANED_POS, NULL, object, "position"); } g_object_unref (builder); } diff --git a/gnucash/gnome/gnc-plugin-business.c b/gnucash/gnome/gnc-plugin-business.c index a4c62e83f5..136e6d1082 100644 --- a/gnucash/gnome/gnc-plugin-business.c +++ b/gnucash/gnome/gnc-plugin-business.c @@ -968,7 +968,7 @@ bind_extra_toolbuttons_visibility (GncMainWindow *mainwindow) { gnc_prefs_bind (GNC_PREFS_GROUP_INVOICE, GNC_PREF_EXTRA_TOOLBUTTONS, - G_OBJECT(tool_item), "visible"); + NULL, G_OBJECT(tool_item), "visible"); } } @@ -984,7 +984,7 @@ bind_extra_toolbuttons_visibility (GncMainWindow *mainwindow) { gnc_prefs_bind (GNC_PREFS_GROUP_INVOICE, GNC_PREF_EXTRA_TOOLBUTTONS, - G_OBJECT(tool_item), "visible"); + NULL, G_OBJECT(tool_item), "visible"); } } } diff --git a/gnucash/import-export/csv-exp/assistant-csv-export.c b/gnucash/import-export/csv-exp/assistant-csv-export.c index eb0be03544..89fab55db0 100644 --- a/gnucash/import-export/csv-exp/assistant-csv-export.c +++ b/gnucash/import-export/csv-exp/assistant-csv-export.c @@ -991,7 +991,7 @@ csv_export_assistant_create (CsvExportInfo *info) if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY)) { GObject *object = gtk_builder_get_object (builder, "paned"); - gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_PANED_POS, object, "position"); + gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_PANED_POS, NULL, object, "position"); } gtk_builder_connect_signals (builder, info); diff --git a/gnucash/import-export/import-match-picker.cpp b/gnucash/import-export/import-match-picker.cpp index 732012b698..f43c20b572 100644 --- a/gnucash/import-export/import-match-picker.cpp +++ b/gnucash/import-export/import-match-picker.cpp @@ -394,7 +394,7 @@ init_match_picker_gui(GtkWidget *parent, GNCImportMatchPicker * matcher) gtk_window_set_transient_for (GTK_WINDOW (matcher->transaction_matcher), GTK_WINDOW(parent)); - gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_DISPLAY_RECONCILED, + gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_DISPLAY_RECONCILED, nullptr, matcher->reconciled_chk, "active"); gnc_import_match_picker_init_downloaded_view(matcher); diff --git a/libgnucash/app-utils/gnc-gsettings.cpp b/libgnucash/app-utils/gnc-gsettings.cpp index fcc60ed248..40aac9dd71 100644 --- a/libgnucash/app-utils/gnc-gsettings.cpp +++ b/libgnucash/app-utils/gnc-gsettings.cpp @@ -279,8 +279,36 @@ gnc_gsettings_remove_any_cb_by_func (const gchar *schema, } +static gboolean gnc_gsettings_enum_bool_mapping_get (GValue *value, + GVariant *variant, + gpointer user_data) +{ + g_value_set_boolean (value, + !g_strcmp0 ((const gchar *)user_data, + g_variant_get_string (variant, nullptr))); + + return true; +} + +static GVariant* gnc_gsettings_enum_bool_mapping_set (const GValue *value, + const GVariantType *expected_type, + gpointer user_data) +{ + if (g_value_get_boolean (value)) + { + return g_variant_new_string ((const gchar *)user_data); + } + else + { + /* GtkRadioButtons will set the value to false when another option is + * selected, just ignore this. */ + return nullptr; + } +} + void gnc_gsettings_bind (const gchar *schema, /*@ null @*/ const gchar *key, + /*@ null @*/ const gchar *value, gpointer object, const gchar *property) { @@ -288,9 +316,24 @@ void gnc_gsettings_bind (const gchar *schema, g_return_if_fail (G_IS_SETTINGS (gs_obj)); if (gnc_gsettings_is_valid_key (gs_obj, key)) - g_settings_bind (gs_obj, key, object, property, G_SETTINGS_BIND_DEFAULT); + { + if (value) + { + g_settings_bind_with_mapping (gs_obj, key, object, property, + G_SETTINGS_BIND_DEFAULT, + gnc_gsettings_enum_bool_mapping_get, + gnc_gsettings_enum_bool_mapping_set, + g_strdup (value), g_free); + } + else + { + g_settings_bind (gs_obj, key, object, property, G_SETTINGS_BIND_DEFAULT); + } + } else + { PERR ("Invalid key %s for schema %s", key, schema); + } } diff --git a/libgnucash/app-utils/gnc-gsettings.h b/libgnucash/app-utils/gnc-gsettings.h index 9c9059897a..2f60b7ad49 100644 --- a/libgnucash/app-utils/gnc-gsettings.h +++ b/libgnucash/app-utils/gnc-gsettings.h @@ -199,12 +199,15 @@ void gnc_gsettings_remove_any_cb_by_func (const gchar *schema, * @param key This string is the name of the particular key within * the named schema of gsettings. * + * @param value This string is the enum value of the particular setting. + * * @param object The object to be bound. * * @param property The property of the object to bind to. */ void gnc_gsettings_bind (const gchar *schema, /*@ null @*/ const gchar *key, + /*@ null @*/ const gchar *value, gpointer object, const gchar *property); diff --git a/libgnucash/core-utils/gnc-prefs-p.h b/libgnucash/core-utils/gnc-prefs-p.h index a5ab85580e..7897fe4e26 100644 --- a/libgnucash/core-utils/gnc-prefs-p.h +++ b/libgnucash/core-utils/gnc-prefs-p.h @@ -54,6 +54,7 @@ typedef struct void (*bind) (const gchar *group, /*@ null @*/ const gchar *pref_name, + /*@ null @*/ const gchar *pref_value, gpointer object, const gchar *property); diff --git a/libgnucash/core-utils/gnc-prefs.c b/libgnucash/core-utils/gnc-prefs.c index dec3b23421..f3d8503454 100644 --- a/libgnucash/core-utils/gnc-prefs.c +++ b/libgnucash/core-utils/gnc-prefs.c @@ -180,11 +180,12 @@ void gnc_prefs_remove_group_cb_by_func (const gchar *group, void gnc_prefs_bind (const gchar *group, /*@ null @*/ const gchar *pref_name, + /*@ null @*/ const gchar *pref_value, gpointer object, const gchar *property) { if (prefsbackend && prefsbackend->bind) - (prefsbackend->bind) (group, pref_name, object, property); + (prefsbackend->bind) (group, pref_name, pref_value, object, property); } diff --git a/libgnucash/core-utils/gnc-prefs.h b/libgnucash/core-utils/gnc-prefs.h index 5010bfc951..6e6a9f30f6 100644 --- a/libgnucash/core-utils/gnc-prefs.h +++ b/libgnucash/core-utils/gnc-prefs.h @@ -254,15 +254,18 @@ void gnc_prefs_remove_group_cb_by_func (const gchar *group, * * @param group This string contains the group name of the preference to bind to. * - * @param preference This string is the name of the particular preference to + * @param pref_name This string is the name of the particular preference to * bind to. * + * @param pref_value This string is the enum value of the preference to bind to. + * * @param object The object to be bound. * * @param property The property of the object to bind to. */ void gnc_prefs_bind (const gchar *group, /*@ null @*/ const gchar *pref_name, + /*@ null @*/ const gchar *pref_value, gpointer object, const gchar *property);