diff --git a/CMakeLists.txt b/CMakeLists.txt index a2714b93c7..6c2518e268 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -568,7 +568,7 @@ endif() add_definitions(-D_GNU_SOURCE) -# Also, set the C++ version to c++11 +# Set up the language standards: set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/bindings/engine.i b/bindings/engine.i index 6edd61abc6..02851a952a 100644 --- a/bindings/engine.i +++ b/bindings/engine.i @@ -401,10 +401,6 @@ void qof_book_set_string_option(QofBook* book, const char* opt_name, const char* SET_ENUM("OPTION-SECTION-ACCOUNTS"); SET_ENUM("OPTION-NAME-TRADING-ACCOUNTS"); - SET_ENUM("OPTION-NAME-CURRENCY-ACCOUNTING"); - SET_ENUM("OPTION-NAME-BOOK-CURRENCY"); - SET_ENUM("OPTION-NAME-DEFAULT-GAINS-POLICY"); - SET_ENUM("OPTION-NAME-DEFAULT-GAINS-LOSS-ACCT-GUID"); SET_ENUM("OPTION-NAME-AUTO-READONLY-DAYS"); SET_ENUM("OPTION-NAME-NUM-FIELD-SOURCE"); diff --git a/bindings/guile/glib-guile.c b/bindings/guile/glib-guile.c index 992df69a10..b38c476dd7 100644 --- a/bindings/guile/glib-guile.c +++ b/bindings/guile/glib-guile.c @@ -77,7 +77,7 @@ glist_to_scm_list_helper(GList *glist, swig_type_info *wct) } SCM -gnc_glist_to_scm_list(GList *glist, gchar *wct) +gnc_glist_to_scm_list(GList *glist, const gchar *wct) { swig_type_info *stype = SWIG_TypeQuery(wct); g_return_val_if_fail(stype, SCM_UNDEFINED); diff --git a/bindings/guile/glib-guile.h b/bindings/guile/glib-guile.h index 2b1e6d047e..fd642a428b 100644 --- a/bindings/guile/glib-guile.h +++ b/bindings/guile/glib-guile.h @@ -28,7 +28,7 @@ #include #include -SCM gnc_glist_to_scm_list(GList *glist, gchar *wct); +SCM gnc_glist_to_scm_list(GList *glist, const gchar *wct); GList* gnc_scm_list_to_glist(SCM wcp_list); SCM gnc_glist_string_to_scm(GList * list); diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt index 3dbf10a2c7..ca95d104e6 100644 --- a/gnucash/gnome-utils/CMakeLists.txt +++ b/gnucash/gnome-utils/CMakeLists.txt @@ -36,7 +36,7 @@ set (gnome_utils_SOURCES dialog-dup-trans.c dialog-file-access.c dialog-object-references.c - dialog-options.c + dialog-options.cpp dialog-preferences.c dialog-query-view.c dialog-reset-warnings.c @@ -60,7 +60,7 @@ set (gnome_utils_SOURCES gnc-currency-edit.c gnc-date-delta.c gnc-date-edit.c - gnc-date-format.c + gnc-date-format.c gnc-dense-cal.c gnc-dense-cal-model.c gnc-dense-cal-store.c @@ -75,7 +75,7 @@ set (gnome_utils_SOURCES gnc-gui-query.c gnc-icons.c gnc-keyring.c - gnc-main-window.c + gnc-main-window.cpp gnc-menu-extensions.c gnc-plugin-file-history.c gnc-plugin-manager.c @@ -110,6 +110,7 @@ set (gnome_utils_SOURCES set(gnome_utils_noinst_HEADERS dialog-tax-table.h + dialog-options.hpp gnc-autosave.h gnc-gobject-utils.h gnc-gtk-utils.h @@ -127,7 +128,6 @@ set (gnome_utils_HEADERS dialog-file-access.h dialog-preferences.h dialog-object-references.h - dialog-options.h dialog-query-view.h dialog-reset-warnings.h dialog-totd.h diff --git a/gnucash/gnome-utils/dialog-options.c b/gnucash/gnome-utils/dialog-options.c deleted file mode 100644 index c4f081826c..0000000000 --- a/gnucash/gnome-utils/dialog-options.c +++ /dev/null @@ -1,4351 +0,0 @@ -/********************************************************************\ - * dialog-options.c -- GNOME option handling * - * Copyright (C) 1998-2000 Linas Vepstas * - * Copyright (c) 2006 David Hampton * - * Copyright (c) 2011 Robert Fewell * - * * - * This program 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 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * -\********************************************************************/ - -#include - -#include -#include -#include -#include "swig-runtime.h" - -#include "gnc-tree-model-budget.h" //FIXME? -#include "gnc-budget.h" - -#include "dialog-options.h" -#include "dialog-utils.h" -#include "gnc-engine-guile.h" -#include "glib-guile.h" -#include "gnc-account-sel.h" -#include "gnc-tree-view-account.h" -#include "gnc-tree-model-account.h" -#include "gnc-commodity-edit.h" -#include "gnc-component-manager.h" -#include "gnc-general-select.h" -#include "gnc-currency-edit.h" -#include "gnc-date-edit.h" -#include "gnc-engine.h" -#include "gnc-prefs.h" -#include "gnc-gui-query.h" -#include "gnc-session.h" -#include "gnc-ui.h" -#include "gnc-guile-utils.h" -#include "option-util.h" -#include "guile-mappings.h" -#include "gnc-date-format.h" -#include "misc-gnome-utils.h" - -#define GNC_PREF_CLOCK_24H "clock-24h" - -#define FUNC_NAME G_STRFUNC -/* TODO: clean up "register-stocks" junk - */ - - -/* This static indicates the debugging module that this .o belongs to. */ -static QofLogModule log_module = GNC_MOD_GUI; - -#define DIALOG_OPTIONS_CM_CLASS "dialog-options" -#define DIALOG_BOOK_OPTIONS_CM_CLASS "dialog-book-options" - -#define GNC_PREFS_GROUP "dialogs.options" - -/* - * Point where preferences switch control method from a set of - * notebook tabs to a list. - */ -#define MAX_TAB_COUNT 6 - -/* A pointer to the last selected filename */ -#define LAST_SELECTION "last-selection" - -/* A Hash-table of GNCOptionDef_t keyed with option names. */ -static GHashTable *optionTable = NULL; - -static int gain_loss_accounts_in_filter = 0; - -struct gnc_option_win -{ - GtkWidget * window; - GtkWidget * notebook; - GtkWidget * page_list_view; - GtkWidget * page_list; - - gboolean toplevel; - - GNCOptionWinCallback apply_cb; - gpointer apply_cb_data; - - GNCOptionWinCallback help_cb; - gpointer help_cb_data; - - GNCOptionWinCallback close_cb; - gpointer close_cb_data; - - /* Hold onto this for a complete reset */ - GNCOptionDB *option_db; - - /* Hold on to this to unregister the right class */ - const char *component_class; - - /* widget being destroyed */ - gboolean destroyed; -}; - -typedef enum -{ - GNC_RD_WID_AB_BUTTON_POS = 0, - GNC_RD_WID_AB_WIDGET_POS, - GNC_RD_WID_REL_BUTTON_POS, - GNC_RD_WID_REL_WIDGET_POS -} GNCRdPositions; - -enum page_tree -{ - PAGE_INDEX = 0, - PAGE_NAME, - NUM_COLUMNS -}; - -typedef struct -{ - GtkWidget *gnc_currency_radiobutton_0; - GtkWidget *gnc_currency_radiobutton_1; - GtkWidget *gnc_currency_radiobutton_2; - GtkWidget *book_currency_widget; - GtkWidget *default_cost_policy_widget; - GtkWidget *default_gain_loss_account_widget; - GtkWidget *book_currency_table; - GtkWidget *book_currency_vbox; - GtkWidget *gain_loss_account_del_button; - GtkWidget *gain_loss_account_table; - GtkWidget *default_gain_loss_account_text; - GNCOption *option; - Account *prior_gain_loss_account; - gnc_commodity *retrieved_book_currency; - - SCM retrieved_policy_scm; - SCM retrieved_gain_loss_acct_guid_scm; - -} currency_accounting_data; - -static currency_accounting_data *book_currency_data = NULL; - -static GNCOptionWinCallback global_help_cb = NULL; -gpointer global_help_cb_data = NULL; - -static void gnc_options_dialog_reset_cb (GtkWidget * w, gpointer data); -void gnc_options_dialog_list_select_cb (GtkTreeSelection *selection, - gpointer data); -void gnc_set_default_cost_policy_widget (SCM list_symbol); -void gnc_set_default_gain_loss_account_widget (gnc_commodity *commodity); -void gnc_option_changed_book_currency_widget_cb (GtkWidget *widget); -void gnc_option_changed_gain_loss_account_widget_cb (GtkTreeSelection *selection, - gpointer data); -void gnc_option_changed_gain_loss_account_del_button_widget_cb (GtkButton *button, - gpointer data); -static void component_close_handler (gpointer data); - -GtkWidget * -gnc_option_get_gtk_widget (GNCOption *option) -{ - return (GtkWidget *)gnc_option_get_widget (option); -} - -static void -gnc_options_dialog_changed_internal (GtkWidget *widget, gboolean sensitive) -{ - GtkButton *button = NULL; - - while (widget && !GTK_IS_WINDOW(widget)) - widget = gtk_widget_get_parent (widget); - if (widget == NULL) - return; - - /* find the ok and cancel buttons, we know where they will be so do it - this way as opposed to using gtk_container_foreach, much less iteration */ - if (GTK_IS_CONTAINER(widget)) - { - GList *children = gtk_container_get_children (GTK_CONTAINER(widget)); - for (GList *it = children; it; it = it->next) - { - if (GTK_IS_BOX(GTK_WIDGET(it->data))) - { - GList *children = gtk_container_get_children (GTK_CONTAINER(it->data)); - for (GList *it = children; it; it = it->next) - { - if (GTK_IS_BUTTON_BOX(GTK_WIDGET(it->data))) - { - GList *children = gtk_container_get_children (GTK_CONTAINER(it->data)); - for (GList *it = children; it; it = it->next) - { - if (g_strcmp0 (gtk_widget_get_name (GTK_WIDGET(it->data)), "ok_button") == 0) - gtk_widget_set_sensitive (GTK_WIDGET(it->data), sensitive); - - if (g_strcmp0 (gtk_widget_get_name (GTK_WIDGET(it->data)), "apply_button") == 0) - gtk_widget_set_sensitive (GTK_WIDGET(it->data), sensitive); - - if (g_strcmp0 (gtk_widget_get_name (GTK_WIDGET(it->data)), "cancel_button") == 0) - button = GTK_BUTTON(it->data); - } - g_list_free (children); - } - } - g_list_free (children); - } - } - g_list_free (children); - } - - if (button) - { - if (sensitive) - gtk_button_set_label (button, _("_Cancel")); - else - gtk_button_set_label (button, _("_Close")); - } -} - -void -gnc_options_dialog_changed (GNCOptionWin *win) -{ - if (!win) return; - - gnc_options_dialog_changed_internal (win->window, TRUE); -} - -void -gnc_option_changed_widget_cb (GtkWidget *widget, GNCOption *option) -{ - gnc_option_set_changed (option, TRUE); - gnc_option_call_option_widget_changed_proc (option, FALSE); - gnc_options_dialog_changed_internal (widget, TRUE); -} - -void -gnc_option_changed_option_cb (GtkWidget *dummy, GNCOption *option) -{ - GtkWidget *widget = gnc_option_get_gtk_widget (option); - gnc_option_changed_widget_cb (widget, option); -} - -static void -gnc_date_option_set_select_method (GNCOption *option, - gboolean use_absolute, - gboolean set_buttons) -{ - GList* widget_list; - GtkWidget *ab_button, *rel_button, *rel_widget, *ab_widget; - GtkWidget *widget; - - widget = gnc_option_get_gtk_widget (option); - - widget_list = gtk_container_get_children (GTK_CONTAINER(widget)); - ab_button = g_list_nth_data (widget_list, GNC_RD_WID_AB_BUTTON_POS); - ab_widget = g_list_nth_data (widget_list, GNC_RD_WID_AB_WIDGET_POS); - rel_button = g_list_nth_data (widget_list, GNC_RD_WID_REL_BUTTON_POS); - rel_widget = g_list_nth_data (widget_list, GNC_RD_WID_REL_WIDGET_POS); - g_list_free (widget_list); - - if (use_absolute) - { - gtk_widget_set_sensitive (ab_widget, TRUE); - gtk_widget_set_sensitive (rel_widget, FALSE); - if (set_buttons) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(ab_button), TRUE); - } - else - { - gtk_widget_set_sensitive (rel_widget, TRUE); - gtk_widget_set_sensitive (ab_widget, FALSE); - if (set_buttons) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(rel_button), TRUE); - } -} - -static void -gnc_rd_option_ab_set_cb (GtkWidget *widget, gpointer *raw_option) -{ - GNCOption *option = (GNCOption *) raw_option; - gnc_date_option_set_select_method (option, TRUE, FALSE); - gnc_option_changed_option_cb (widget, option); -} - -static void -gnc_rd_option_rel_set_cb (GtkWidget *widget, gpointer *raw_option) -{ - GNCOption *option = (GNCOption *) raw_option; - gnc_date_option_set_select_method (option, FALSE, FALSE); - gnc_option_changed_option_cb (widget, option); - return; -} - -static void -gnc_image_option_update_preview_cb (GtkFileChooser *chooser, - GNCOption *option) -{ - gchar *filename; - GtkImage *image; - GdkPixbuf *pixbuf; - gboolean have_preview; - - g_return_if_fail (chooser != NULL); - - ENTER("chooser %p, option %p", chooser, option); - filename = gtk_file_chooser_get_preview_filename (chooser); - DEBUG("chooser preview name is %s.", filename ? filename : "(null)"); - if (filename == NULL) - { - filename = g_strdup (g_object_get_data (G_OBJECT(chooser), LAST_SELECTION)); - DEBUG("using last selection of %s", filename ? filename : "(null)"); - if (filename == NULL) - { - LEAVE("no usable name"); - return; - } - } - - image = GTK_IMAGE(gtk_file_chooser_get_preview_widget (chooser)); - pixbuf = gdk_pixbuf_new_from_file_at_size (filename, 128, 128, NULL); - g_free (filename); - have_preview = (pixbuf != NULL); - - gtk_image_set_from_pixbuf (image, pixbuf); - if (pixbuf) - g_object_unref (pixbuf); - - gtk_file_chooser_set_preview_widget_active (chooser, have_preview); - LEAVE("preview visible is %d", have_preview); -} - -static void -gnc_image_option_selection_changed_cb (GtkFileChooser *chooser, - GNCOption *option) -{ - gchar *filename = gtk_file_chooser_get_preview_filename (chooser); - if (!filename) - return; - g_object_set_data_full (G_OBJECT(chooser), LAST_SELECTION, filename, g_free); -} - -/********************************************************************\ - * gnc_option_set_ui_value_internal * - * sets the GUI representation of an option with either its * - * current guile value, or its default value * - * * - * Args: option - option structure containing option * - * use_default - if true, use the default value, otherwise * - * use the current value * - * Return: nothing * -\********************************************************************/ -static void -gnc_option_set_ui_value_internal (GNCOption *option, gboolean use_default) -{ - gboolean bad_value = FALSE; - GtkWidget *widget; - char *type; - SCM getter; - SCM value; - GNCOptionDef_t *option_def; - - widget = gnc_option_get_gtk_widget (option); - if (!widget) - return; - - type = gnc_option_type (option); - - if (use_default) - { - SCM opt_getter = gnc_option_getter (option); - SCM opt_value = scm_call_0 (opt_getter); - SCM def_value; - - getter = gnc_option_default_getter (option); - def_value = scm_call_0 (getter); - - // only set changed if the values have changed - if (!scm_is_true (scm_equal_p (opt_value, def_value))) - gnc_option_set_changed (option, TRUE); - } - else - getter = gnc_option_getter (option); - - value = scm_call_0 (getter); - - option_def = gnc_options_ui_get_option (type); - if (option_def && option_def->set_value) - { - bad_value = option_def->set_value (option, use_default, widget, value); - if (bad_value) - { - gchar *name = gnc_option_name (option); - gchar *val = scm_to_locale_string (scm_object_to_string - (value, scm_c_eval_string ("write"))); - PERR ("option '%s' bad value '%s'\n", name, val); - g_free (name); - g_free (val); - } - } - else - PERR("Unknown type. Ignoring.\n"); - - free (type); -} - -/********************************************************************\ - * gnc_option_get_ui_value_internal * - * returns the SCM representation of the GUI option value * - * * - * Args: option - option structure containing option * - * Return: SCM handle to GUI option value * -\********************************************************************/ -static SCM -gnc_option_get_ui_value_internal (GNCOption *option) -{ - SCM result = SCM_UNDEFINED; - GtkWidget *widget; - char *type; - GNCOptionDef_t *option_def; - - widget = gnc_option_get_gtk_widget (option); - if (!widget) - return result; - - type = gnc_option_type (option); - - option_def = gnc_options_ui_get_option (type); - - if (option_def && option_def->get_value) - result = option_def->get_value (option, widget); - else - PERR("Unknown type for refresh. Ignoring.\n"); - - free (type); - - return result; -} - -/********************************************************************\ - * gnc_option_set_selectable_internal * - * Change the selectable state of the widget that represents a * - * GUI option. * - * * - * Args: option - option to change widget state for * - * selectable - if false, update the widget so that it * - * cannot be selected by the user. If true, * - * update the widget so that it can be selected.* - * Return: nothing * -\********************************************************************/ -static void -gnc_option_set_selectable_internal (GNCOption *option, gboolean selectable) -{ - GtkWidget *widget; - - widget = gnc_option_get_gtk_widget (option); - if (!widget) - return; - - gtk_widget_set_sensitive (widget, selectable); -} - -static void -gnc_option_default_cb (GtkWidget *widget, GNCOption *option) -{ - gnc_option_set_ui_value (option, TRUE); - gnc_option_set_changed (option, TRUE); - gnc_options_dialog_changed_internal (widget, TRUE); -} - -static void -gnc_option_show_hidden_toggled_cb (GtkWidget *widget, GNCOption* option) -{ - AccountViewInfo avi; - GncTreeViewAccount *tree_view; - - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option)); - gnc_tree_view_account_get_view_info (tree_view, &avi); - avi.show_hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)); - gnc_tree_view_account_set_view_info (tree_view, &avi); - gnc_option_changed_widget_cb (widget, option); -} - -static void -gnc_option_multichoice_cb (GtkWidget *widget, gpointer data) -{ - GNCOption *option = data; - gnc_option_changed_widget_cb (widget, option); -} - -static void -gnc_option_radiobutton_cb (GtkWidget *w, gpointer data) -{ - GNCOption *option = data; - GtkWidget *widget; - gpointer _current, _new_value; - gint current, new_value; - - widget = gnc_option_get_gtk_widget (option); - - _current = g_object_get_data (G_OBJECT(widget), "gnc_radiobutton_index"); - current = GPOINTER_TO_INT(_current); - - _new_value = g_object_get_data (G_OBJECT(w), "gnc_radiobutton_index"); - new_value = GPOINTER_TO_INT(_new_value); - - if (current == new_value) - return; - - g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index", - GINT_TO_POINTER(new_value)); - gnc_option_changed_widget_cb (widget, option); -} - -static gboolean -gnc_gain_loss_account_view_filter (Account *account, gpointer data) -{ - GNCAccountType type = xaccAccountGetType (account); - - /* gain/loss accts must be an Income or Expense accts and not hidden; - placeholder accounts must be included, irrespective of their currency, - so their children are available to be considered */ - if (((type == ACCT_TYPE_INCOME) || (type == ACCT_TYPE_EXPENSE)) && - (!xaccAccountIsHidden(account))) - { - if (xaccAccountGetPlaceholder (account)) - { - GList *placeholder_children = gnc_account_get_children (account); - - if (placeholder_children) - { /* determine if any children qualify; just need one but don't - double count in gain_loss_accounts_in_filter */ - int saved_gain_loss_accounts_in_filter = - gain_loss_accounts_in_filter; - gboolean child_pass_filter = FALSE; - GList *l = NULL; - for (l = placeholder_children; l != NULL; l = l->next) - { - Account *child_account = l->data; - child_pass_filter = - gnc_gain_loss_account_view_filter (child_account, NULL); - if (child_pass_filter) - break; - } - g_list_free (placeholder_children); - gain_loss_accounts_in_filter = - saved_gain_loss_accounts_in_filter; - return child_pass_filter; - } - else return FALSE; // no children, not interested - } - else - { - gnc_commodity *commodity = NULL; - - /* gain/loss accts must be in book-currency; if a book currency has been - specified in the widget, use it to filter */ - if (gtk_combo_box_get_active (GTK_COMBO_BOX( - book_currency_data->book_currency_widget)) != -1) - commodity = gnc_currency_edit_get_currency ( - GNC_CURRENCY_EDIT( - book_currency_data->book_currency_widget)); - if (commodity) - { - if (gnc_commodity_equal (xaccAccountGetCommodity (account), - commodity)) - { - gain_loss_accounts_in_filter++; - return TRUE; - } - else return FALSE; - } - /* else use the default currency */ - else if (gnc_commodity_equal (xaccAccountGetCommodity (account), - gnc_default_currency ())) - { - gain_loss_accounts_in_filter++; - return TRUE; - } - else return FALSE; - } - } - else return FALSE; -} - -static gboolean -gnc_gain_loss_account_all_fail_filter (Account *account, gpointer data) -{ - return FALSE; -} - -void -gnc_set_default_cost_policy_widget (SCM list_symbol) -{ - GList *list_of_policies = gnc_get_valid_policy_list (); - - if (list_of_policies) - { - GList *l = NULL; - gint i = 0; - for (l = list_of_policies; l != NULL; l = l->next) - { - GNCPolicy *pcy = l->data; - if (g_strcmp0 (PolicyGetName (pcy), - gnc_scm_symbol_to_locale_string (list_symbol)) - == 0) - { - gtk_combo_box_set_active ( - GTK_COMBO_BOX( - book_currency_data->default_cost_policy_widget), i); - } - i++; - } - g_list_free (list_of_policies); - } - else - { - gtk_combo_box_set_active ( - GTK_COMBO_BOX(book_currency_data->default_cost_policy_widget), -1); - } -} - -void -gnc_set_default_gain_loss_account_widget (gnc_commodity *commodity) -{ - if (book_currency_data->default_gain_loss_account_widget) - { - gtk_widget_destroy ( - book_currency_data->default_gain_loss_account_widget); - book_currency_data->default_gain_loss_account_widget = NULL; - book_currency_data->prior_gain_loss_account = NULL; - gain_loss_accounts_in_filter = 0; - } - if (book_currency_data->gain_loss_account_del_button) - { - gtk_widget_destroy ( - book_currency_data->gain_loss_account_del_button); - book_currency_data->gain_loss_account_del_button = NULL; - } - if (book_currency_data->default_gain_loss_account_text) - { - gtk_widget_destroy ( - book_currency_data->default_gain_loss_account_text); - book_currency_data->default_gain_loss_account_text = NULL; - } - if (gnc_is_new_book ()) - { - book_currency_data->default_gain_loss_account_text = - gtk_label_new ( _("Because no accounts have " \ - "been set up yet, you will need to return to this " \ - "dialog (via File->Properties), after account setup, " \ - "if you want to set a default gain/loss account.") ); - - gtk_label_set_line_wrap (GTK_LABEL(book_currency_data->default_gain_loss_account_text), TRUE); - - gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table), - book_currency_data->default_gain_loss_account_text, 0, 1, 2, 1); - } - else - { - GtkTreeSelection *selection = NULL; - book_currency_data->default_gain_loss_account_widget = - GTK_WIDGET(gnc_tree_view_account_new (FALSE)); - gain_loss_accounts_in_filter = 0; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW( - book_currency_data->default_gain_loss_account_widget)); - if (!commodity) // that means not book currency - { - /* set the default_gain_loss_account_widget to be blank with a - no-acct filter */ - gnc_tree_view_account_set_filter (GNC_TREE_VIEW_ACCOUNT( - book_currency_data->default_gain_loss_account_widget), - gnc_gain_loss_account_all_fail_filter, - NULL, /* user data */ - NULL /* destroy callback */ ); - gtk_tree_selection_unselect_all (selection); - } - else // that means book currency - { - /* see if there are any accounts after filter */ - gnc_tree_view_account_set_filter (GNC_TREE_VIEW_ACCOUNT( - book_currency_data->default_gain_loss_account_widget), - gnc_gain_loss_account_view_filter, - NULL, /* user data */ - NULL /* destroy callback */); - if (gain_loss_accounts_in_filter > 0) - { /* there are accounts; find out if one is selected */ - Account *gain_loss_account = NULL; - Account *selected_account = NULL; - GtkTreeViewColumn *col; - - book_currency_data->gain_loss_account_del_button = - gtk_button_new_with_label ( _("Select no account") ); - - g_signal_connect (GTK_BUTTON( - book_currency_data->gain_loss_account_del_button), - "clicked", - G_CALLBACK( - gnc_option_changed_gain_loss_account_del_button_widget_cb), - NULL); - gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table), - book_currency_data->gain_loss_account_del_button, 1, 0, 1, 1); - - gtk_tree_view_set_headers_visible (GTK_TREE_VIEW( - book_currency_data->default_gain_loss_account_widget), - TRUE); - col = gnc_tree_view_add_text_column (GNC_TREE_VIEW( - book_currency_data->default_gain_loss_account_widget), - _("Currency"), /* title */ - "commodity", /* pref name */ - NULL, - "Currency--", /* sizing text */ - GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY, - GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, - NULL); - g_object_set_data (G_OBJECT(col), DEFAULT_VISIBLE, - GINT_TO_POINTER(1)); - - // add the color background data function to the column - gnc_tree_view_account_column_add_color (GNC_TREE_VIEW_ACCOUNT( - book_currency_data->default_gain_loss_account_widget), col); - - col = gnc_tree_view_add_toggle_column (GNC_TREE_VIEW( - book_currency_data->default_gain_loss_account_widget), - _("Placeholder"), - C_("Column header for 'Placeholder'", "P"), - "placeholder", - GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER, - GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, - NULL, - NULL); - g_object_set_data (G_OBJECT(col), DEFAULT_VISIBLE, - GINT_TO_POINTER(1)); - - // add the color background data function to the column - gnc_tree_view_account_column_add_color (GNC_TREE_VIEW_ACCOUNT( - book_currency_data->default_gain_loss_account_widget), col); - - gnc_tree_view_configure_columns (GNC_TREE_VIEW( - book_currency_data->default_gain_loss_account_widget)); - gnc_tree_view_set_show_column_menu (GNC_TREE_VIEW( - book_currency_data->default_gain_loss_account_widget), - FALSE); - if (book_currency_data->retrieved_gain_loss_acct_guid_scm && - (scm_is_string ( - book_currency_data->retrieved_gain_loss_acct_guid_scm))) - { - GncGUID *guid= g_new (GncGUID, 1); - - if (string_to_guid ( - gnc_scm_to_utf8_string ( - book_currency_data->retrieved_gain_loss_acct_guid_scm), - guid)) - gain_loss_account = - xaccAccountLookup (guid, gnc_get_current_book ()); - g_free (guid); - } - if (gain_loss_account) - { - (gnc_tree_view_account_set_selected_account - (GNC_TREE_VIEW_ACCOUNT( - book_currency_data->default_gain_loss_account_widget), - gain_loss_account)); - selected_account = - gnc_tree_view_account_get_selected_account ( - GNC_TREE_VIEW_ACCOUNT( - book_currency_data->default_gain_loss_account_widget)); - } - if (selected_account) - { - book_currency_data->prior_gain_loss_account = - selected_account; - gtk_widget_set_sensitive ( - book_currency_data->gain_loss_account_del_button, - TRUE); - } - else /* none selected */ - { - gtk_tree_selection_unselect_all (selection); - gtk_widget_set_sensitive ( - book_currency_data->gain_loss_account_del_button, - FALSE); - } - } - else /* no accts in widget?; replace widget with text */ - { - gtk_widget_destroy ( - book_currency_data->default_gain_loss_account_widget); - book_currency_data->default_gain_loss_account_widget = NULL; - book_currency_data->prior_gain_loss_account = NULL; - gain_loss_accounts_in_filter = 0; - book_currency_data->default_gain_loss_account_text = - gtk_label_new ( _("There are no income " \ - "or expense accounts of the specified\n" \ - "book currency; you will have to return to this " \ - "dialog\n(via File->Properties), after account setup, " \ - "to select a\ndefault gain/loss account.") ); - gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table), - book_currency_data->default_gain_loss_account_text, 0, 1, 2, 1); - } - } - if (book_currency_data->default_gain_loss_account_widget) - { - gtk_widget_set_hexpand (GTK_WIDGET(book_currency_data->default_gain_loss_account_widget), TRUE); - g_signal_connect (G_OBJECT(selection), - "changed", - G_CALLBACK(gnc_option_changed_gain_loss_account_widget_cb), - NULL); - gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table), - book_currency_data->default_gain_loss_account_widget, 0, 1, 2, 1); - } - } -} - -void -gnc_option_changed_book_currency_widget_cb (GtkWidget *widget) -{ - /* Once the book currency widget is set, need to set the - default_gain_loss_account_widget and/or del-button or text*/ - if (gtk_combo_box_get_active (GTK_COMBO_BOX(book_currency_data->book_currency_widget)) != -1) - { - gnc_commodity *commodity = gnc_currency_edit_get_currency ( - GNC_CURRENCY_EDIT( - book_currency_data->book_currency_widget)); - - gnc_set_default_gain_loss_account_widget (commodity); - } - gtk_widget_show_all (book_currency_data->book_currency_vbox); - gnc_option_changed_widget_cb (widget, book_currency_data->option); -} - -void -gnc_option_changed_gain_loss_account_widget_cb (GtkTreeSelection *selection, - gpointer data) -{ - Account *account = NULL; - gboolean new_eq_prior_acct = FALSE; - - g_return_if_fail (book_currency_data->default_gain_loss_account_widget); - account = gnc_tree_view_account_get_selected_account ( - GNC_TREE_VIEW_ACCOUNT( - book_currency_data->default_gain_loss_account_widget)); - if (account && book_currency_data->prior_gain_loss_account) - new_eq_prior_acct = xaccAccountEqual (account, - book_currency_data->prior_gain_loss_account, - TRUE); - if (account && (!new_eq_prior_acct)) - { /* a new account has been selected */ - if (!xaccAccountGetPlaceholder (account)) - { - GtkWidget *option_widget = - gnc_option_get_gtk_widget (book_currency_data->option); - book_currency_data->prior_gain_loss_account = account; - gtk_widget_set_sensitive ( - book_currency_data->gain_loss_account_del_button, TRUE); - gtk_widget_show_all (book_currency_data->book_currency_vbox); - gnc_option_changed_option_cb (option_widget, book_currency_data->option); - } - else /* new account, but placeholder */ - { - const char *message = _("The account %s is a placeholder account " \ - "and does not allow transactions. " \ - "Please choose a different account."); - - gnc_error_dialog (gnc_ui_get_gtk_window (book_currency_data->default_gain_loss_account_widget), - message, xaccAccountGetName (account)); - if (book_currency_data->prior_gain_loss_account) - { - (gnc_tree_view_account_set_selected_account - (GNC_TREE_VIEW_ACCOUNT( - book_currency_data->default_gain_loss_account_widget), - book_currency_data->prior_gain_loss_account)); - } - else - { - gtk_tree_selection_unselect_all (selection); - } - } - } - else /* a new account has not been selected */ - { - if (book_currency_data->prior_gain_loss_account == NULL) - { - gtk_tree_selection_unselect_all (selection); - if (book_currency_data->gain_loss_account_del_button) - { - gtk_widget_set_sensitive ( - book_currency_data->gain_loss_account_del_button, FALSE); - } - } - } -} - -void -gnc_option_changed_gain_loss_account_del_button_widget_cb (GtkButton *button, - gpointer data) -{ - GtkTreeSelection *selection = NULL; - GtkWidget *option_widget = - gnc_option_get_gtk_widget (book_currency_data->option); - - g_return_if_fail (book_currency_data->default_gain_loss_account_widget); - g_return_if_fail (book_currency_data->gain_loss_account_del_button); - - selection = gtk_tree_view_get_selection ( - GTK_TREE_VIEW( - book_currency_data->default_gain_loss_account_widget)); - gtk_tree_selection_unselect_all (selection); - book_currency_data->prior_gain_loss_account = NULL; - gtk_widget_set_sensitive ( - book_currency_data->gain_loss_account_del_button, FALSE); - gnc_option_changed_option_cb (option_widget, book_currency_data->option); -} - -static void -gnc_option_currency_accounting_non_book_cb (GtkWidget *widget, gpointer data) -{ - gnc_currency_edit_clear_display (GNC_CURRENCY_EDIT( - book_currency_data->book_currency_widget)); - gtk_combo_box_set_active (GTK_COMBO_BOX( - book_currency_data->default_cost_policy_widget), - -1); - gnc_set_default_gain_loss_account_widget (NULL); - gtk_widget_show_all (book_currency_data->book_currency_vbox); - gtk_widget_set_sensitive (book_currency_data->book_currency_vbox, FALSE); - gnc_option_radiobutton_cb (widget, (gpointer) book_currency_data->option); -} - -static void -gnc_option_currency_accounting_book_cb (GtkWidget *widget, gpointer data) -{ - SCM list_symbol = - gnc_currency_accounting_option_get_default_policy ( - book_currency_data->option); - SCM curr_scm = gnc_currency_accounting_option_get_default_currency ( - book_currency_data->option); - gnc_commodity *commodity = gnc_scm_to_commodity (curr_scm); - - if (book_currency_data->retrieved_book_currency) - { - gnc_currency_edit_set_currency - (GNC_CURRENCY_EDIT(book_currency_data->book_currency_widget), - book_currency_data->retrieved_book_currency); - } - else if (commodity) - { - gnc_currency_edit_set_currency - (GNC_CURRENCY_EDIT(book_currency_data->book_currency_widget), - commodity); - } - else - { - gnc_currency_edit_set_currency - (GNC_CURRENCY_EDIT(book_currency_data->book_currency_widget), - gnc_default_currency()); - } - if (book_currency_data->retrieved_policy_scm) - { - gnc_set_default_cost_policy_widget ( - book_currency_data->retrieved_policy_scm); - } - else - { - gnc_set_default_cost_policy_widget (list_symbol); - } - gtk_widget_show_all (book_currency_data->book_currency_vbox); - gtk_widget_set_sensitive (book_currency_data->book_currency_vbox, TRUE); - gnc_option_radiobutton_cb (widget, (gpointer) book_currency_data->option); -} - -static GtkWidget * -gnc_option_create_date_widget (GNCOption *option) -{ - GtkWidget * box = NULL; - GtkWidget *rel_button = NULL, *ab_button = NULL; - GtkWidget *rel_widget = NULL, *ab_widget = NULL; - GtkWidget *entry; - gboolean show_time, use24; - char *type; - int num_values; - - type = gnc_option_date_option_get_subtype (option); - show_time = gnc_option_show_time (option); - use24 = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_CLOCK_24H); - - if (g_strcmp0 (type, "relative") != 0) - { - ab_widget = gnc_date_edit_new (time (NULL), show_time, use24); - entry = GNC_DATE_EDIT(ab_widget)->date_entry; - g_signal_connect (G_OBJECT(entry), "changed", - G_CALLBACK(gnc_option_changed_option_cb), option); - if (show_time) - { - entry = GNC_DATE_EDIT(ab_widget)->time_entry; - g_signal_connect (G_OBJECT(entry), "changed", - G_CALLBACK(gnc_option_changed_option_cb), option); - } - } - - if (g_strcmp0 (type, "absolute") != 0) - { - int i; - num_values = gnc_option_num_permissible_values (option); - - g_return_val_if_fail (num_values >= 0, NULL); - - { - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); - GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); - GtkTreeIter iter; - char *itemstring; - - /* Add values to the list store */ - for (i = 0; i < num_values; i++) - { - itemstring = gnc_option_permissible_value_name (option, i); - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, 0, itemstring, -1); - if (itemstring) - g_free (itemstring); - } - /* Create the new Combo and add the store */ - rel_widget = GTK_WIDGET(gtk_combo_box_new_with_model (GTK_TREE_MODEL(store))); - - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(rel_widget), renderer, TRUE); - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(rel_widget), - renderer, "text", 0); - g_object_unref (store); - - g_signal_connect (G_OBJECT(rel_widget), "changed", - G_CALLBACK(gnc_option_multichoice_cb), option); - } - } - - if (g_strcmp0 (type, "absolute") == 0) - { - free (type); - gnc_option_set_widget (option, ab_widget); - return ab_widget; - } - else if (g_strcmp0 (type, "relative") == 0) - { - gnc_option_set_widget (option, rel_widget); - free (type); - - return rel_widget; - } - else if (g_strcmp0 (type, "both") == 0) - { - box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(box), FALSE); - - ab_button = gtk_radio_button_new (NULL); - g_signal_connect (G_OBJECT(ab_button), "toggled", - G_CALLBACK(gnc_rd_option_ab_set_cb), option); - - rel_button = gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON(ab_button)); - g_signal_connect (G_OBJECT(rel_button), "toggled", - G_CALLBACK(gnc_rd_option_rel_set_cb), option); - - gtk_box_pack_start (GTK_BOX(box), ab_button, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(box), ab_widget, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(box), rel_button, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(box), rel_widget, FALSE, FALSE, 0); - - free (type); - - gnc_option_set_widget (option, box); - - return box; - } - else /* can't happen */ - return NULL; -} - -static GtkWidget * -gnc_option_create_budget_widget (GNCOption *option) -{ - GtkTreeModel *tm; - GtkComboBox *cb; - GtkCellRenderer *cr; - - tm = gnc_tree_model_budget_new (gnc_get_current_book()); - cb = GTK_COMBO_BOX(gtk_combo_box_new_with_model (tm)); - g_object_unref (tm); - cr = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(cb), cr, TRUE); - - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT(cb), cr, "text", - BUDGET_NAME_COLUMN, NULL); - return GTK_WIDGET(cb); -} - -static GtkWidget * -gnc_option_create_multichoice_widget (GNCOption *option) -{ - GtkWidget *widget; - int num_values; - int i; - - num_values = gnc_option_num_permissible_values (option); - - g_return_val_if_fail (num_values >= 0, NULL); - - { - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); - GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); - GtkTreeIter iter; - char *itemstring; - - /* Add values to the list store */ - for (i = 0; i < num_values; i++) - { - itemstring = gnc_option_permissible_value_name (option, i); - - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, 0, - (itemstring && *itemstring) ? _(itemstring) : "", -1); - - if (itemstring) - g_free (itemstring); - } - /* Create the new Combo and add the store */ - widget = GTK_WIDGET(gtk_combo_box_new_with_model (GTK_TREE_MODEL(store))); - - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(widget), renderer, TRUE); - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(widget), - renderer, "text", 0); - g_object_unref (store); - - g_signal_connect (G_OBJECT(widget), "changed", - G_CALLBACK(gnc_option_multichoice_cb), option); - } - return widget; -} - -static GtkWidget * -gnc_option_create_radiobutton_widget (char *name, GNCOption *option) -{ - GtkWidget *frame, *box; - GtkWidget *widget = NULL; - int num_values; - char *label; - int i; - - num_values = gnc_option_num_permissible_values (option); - - g_return_val_if_fail (num_values >= 0, NULL); - - /* Create our button frame */ - frame = gtk_frame_new (name); - - /* Create the button box */ - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); - gtk_box_set_homogeneous (GTK_BOX(box), FALSE); - gtk_container_add (GTK_CONTAINER(frame), box); - - /* Iterate over the options and create a radio button for each one */ - for (i = 0; i < num_values; i++) - { - label = gnc_option_permissible_value_name (option, i); - - widget = - gtk_radio_button_new_with_label_from_widget (widget ? - GTK_RADIO_BUTTON(widget) : - NULL, - label && *label ? _(label) : ""); - g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index", - GINT_TO_POINTER (i)); - - g_signal_connect (G_OBJECT(widget), "toggled", - G_CALLBACK(gnc_option_radiobutton_cb), option); - gtk_box_pack_start (GTK_BOX(box), widget, FALSE, FALSE, 0); - - if (label) - free (label); - } - return frame; -} - -static GtkWidget * -gnc_option_create_currency_accounting_widget (char *name, GNCOption *option) -{ - GtkWidget *frame = NULL, - *widget = NULL, - *vbox = NULL; - int i; - int num_values = gnc_option_num_permissible_values (option); - - g_return_val_if_fail (num_values == 3, NULL); - book_currency_data = g_new0 (currency_accounting_data, 1); - book_currency_data->option = option; - - /* Create the button frame */ - frame = gtk_frame_new (name); - gtk_widget_set_halign (GTK_WIDGET(frame), GTK_ALIGN_FILL); - gtk_widget_set_hexpand (GTK_WIDGET(frame), TRUE); - - /* Create the vertical button box */ - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); - gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE); - gtk_container_add (GTK_CONTAINER(frame), vbox); - gtk_widget_set_halign (GTK_WIDGET(vbox), GTK_ALIGN_FILL); - gtk_widget_set_hexpand (GTK_WIDGET(vbox), TRUE); - - gtk_widget_set_margin_end (GTK_WIDGET(vbox), 12); - gtk_widget_set_margin_bottom (GTK_WIDGET(vbox), 12); - - /* Iterate over the three options and create a radio button for each one */ - for (i = 0; i < num_values; i++) - { - char *label; - char *tip = NULL; - GtkWidget *table = NULL; - - label = gnc_option_permissible_value_name (option, i); - - widget = - gtk_radio_button_new_with_label_from_widget (widget ? - GTK_RADIO_BUTTON(widget) : - NULL, - label && *label ? _(label) : ""); - g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index", - GINT_TO_POINTER(i)); - switch (i) - { - case 0: - book_currency_data->gnc_currency_radiobutton_0 = widget; - break; - - case 1: - book_currency_data->gnc_currency_radiobutton_1 = widget; - break; - - case 2: - book_currency_data->gnc_currency_radiobutton_2 = widget; - break; - - default: - break; - } - if (g_strcmp0 (gnc_option_permissible_value_name (option, i), - "Use a Book Currency") == 0) - { - GtkWidget *widget_label, - *policy_table = gtk_grid_new (); - - book_currency_data->book_currency_widget = gnc_currency_edit_new (); - book_currency_data->default_cost_policy_widget = - gnc_cost_policy_select_new (); - book_currency_data->default_gain_loss_account_widget = NULL; - book_currency_data->gain_loss_account_del_button = NULL; - book_currency_data->default_gain_loss_account_text = NULL; - book_currency_data->prior_gain_loss_account = NULL; - - book_currency_data->book_currency_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_box_set_homogeneous (GTK_BOX(book_currency_data->book_currency_vbox), FALSE); - - table = gtk_grid_new (); - gtk_grid_attach (GTK_GRID(table), widget, 0, 0, 2, 1); - g_signal_connect (G_OBJECT(widget), "toggled", - G_CALLBACK(gnc_option_currency_accounting_book_cb), - book_currency_data); - - book_currency_data->book_currency_table = gtk_grid_new (); - gtk_grid_set_row_spacing (GTK_GRID(book_currency_data->book_currency_table), 6); - gtk_grid_set_column_spacing (GTK_GRID(book_currency_data->book_currency_table), 6); - - tip = gnc_currency_accounting_option_currency_documentation (option); - widget_label = gtk_label_new ( _("Book currency") ); - gtk_widget_set_tooltip_text (book_currency_data->book_currency_table, - tip && *tip ? _(tip) : ""); - - gtk_widget_set_halign (GTK_WIDGET(widget_label), GTK_ALIGN_START); - gtk_widget_set_hexpand (GTK_WIDGET(widget_label), TRUE); - - gtk_grid_attach (GTK_GRID(book_currency_data->book_currency_table), widget_label, 0, 0, 1, 1); - - g_signal_connect (G_OBJECT(book_currency_data->book_currency_widget), - "changed", - G_CALLBACK(gnc_option_changed_book_currency_widget_cb), - NULL); - - gtk_grid_attach (GTK_GRID(book_currency_data->book_currency_table), - book_currency_data->book_currency_widget, 1, 0, 1, 1); - - gtk_box_pack_start (GTK_BOX(book_currency_data->book_currency_vbox), - book_currency_data->book_currency_table, - TRUE, TRUE, 0); - gtk_widget_set_margin_start (GTK_WIDGET(book_currency_data->book_currency_table), 12); - gtk_grid_set_row_spacing (GTK_GRID(policy_table), 6); - gtk_grid_set_column_spacing (GTK_GRID(policy_table), 6); - - tip = gnc_currency_accounting_option_policy_documentation (option); - widget_label = gtk_label_new ( _("Default lot tracking policy") ); - gtk_widget_set_tooltip_text (policy_table, tip && *tip ? _(tip) : ""); - - gtk_widget_set_halign (GTK_WIDGET(widget_label), GTK_ALIGN_START); - gtk_widget_set_hexpand (GTK_WIDGET(widget_label), TRUE); - - gtk_grid_attach (GTK_GRID(policy_table), widget_label, 0, 1, 1, 1); - - g_signal_connect (G_OBJECT(book_currency_data->default_cost_policy_widget), - "changed", - G_CALLBACK(gnc_option_multichoice_cb), option); - - gtk_grid_attach (GTK_GRID(policy_table), - book_currency_data->default_cost_policy_widget, 1, 1, 1, 1); - - gtk_box_pack_start (GTK_BOX(book_currency_data->book_currency_vbox), - policy_table, TRUE, TRUE, 0); - gtk_widget_set_margin_start (GTK_WIDGET(policy_table), 12); - book_currency_data->gain_loss_account_table = gtk_grid_new (); - gtk_grid_set_row_spacing (GTK_GRID(book_currency_data->gain_loss_account_table), 6); - gtk_grid_set_column_spacing (GTK_GRID(book_currency_data->gain_loss_account_table), 6); - - tip = gnc_currency_accounting_option_gain_loss_account_documentation (option); - widget_label = gtk_label_new ( _("Default gain/loss account") ); - gnc_label_set_alignment (GTK_WIDGET(widget_label), 0.0, 0.5); - - gtk_widget_set_tooltip_text (book_currency_data->gain_loss_account_table, - tip && *tip ? _(tip) : ""); - - gtk_grid_attach (GTK_GRID(book_currency_data->gain_loss_account_table), - widget_label, 0, 0, 1, 1); - - widget_label = NULL; - gtk_box_pack_start (GTK_BOX (book_currency_data->book_currency_vbox), - book_currency_data->gain_loss_account_table, - TRUE, TRUE, 0); - gtk_widget_set_margin_start (GTK_WIDGET(book_currency_data->gain_loss_account_table), 12); - gtk_grid_attach (GTK_GRID(table), book_currency_data->book_currency_vbox, 1, 2, 1, 1); - } - else /* trading or neither */ - { - table = gtk_grid_new (); - gtk_grid_attach (GTK_GRID(table), widget, 0, 1, 1, 1); - - g_signal_connect (G_OBJECT(widget), "toggled", - G_CALLBACK(gnc_option_currency_accounting_non_book_cb), - book_currency_data); - } - gtk_box_pack_start (GTK_BOX(vbox), table, TRUE, TRUE, 0); - - if (label) - free (label); - if (tip) - free (tip); - } - return frame; -} - -static void -gnc_option_account_cb (GtkTreeSelection *selection, gpointer data) -{ - GNCOption *option = data; - GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection); - gnc_option_changed_widget_cb (GTK_WIDGET(tree_view), option); -} - -static void -gnc_option_account_select_all_cb (GtkWidget *widget, gpointer data) -{ - GNCOption *option = data; - GncTreeViewAccount *tree_view; - GtkTreeSelection *selection; - - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option)); - gtk_tree_view_expand_all (GTK_TREE_VIEW(tree_view)); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree_view)); - gtk_tree_selection_select_all (selection); - gnc_option_changed_widget_cb (widget, option); -} - -static void -gnc_option_account_clear_all_cb (GtkWidget *widget, gpointer data) -{ - GNCOption *option = data; - GncTreeViewAccount *tree_view; - GtkTreeSelection *selection; - - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option)); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree_view)); - gtk_tree_selection_unselect_all (selection); - gnc_option_changed_widget_cb (widget, option); -} - -static void -gnc_option_account_select_children_cb (GtkWidget *widget, gpointer data) -{ - GNCOption *option = data; - GncTreeViewAccount *tree_view; - GList *acct_list = NULL, *acct_iter = NULL; - - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option)); - acct_list = gnc_tree_view_account_get_selected_accounts (tree_view); - - for (acct_iter = acct_list; acct_iter; acct_iter = acct_iter->next) - gnc_tree_view_account_select_subaccounts (tree_view, acct_iter->data); - - g_list_free (acct_list); -} - -static GtkWidget * -gnc_option_create_account_widget (GNCOption *option, char *name) -{ - gboolean multiple_selection; - GtkWidget *scroll_win; - GtkWidget *button; - GtkWidget *frame; - GtkWidget *tree; - GtkWidget *vbox; - GtkWidget *bbox; - GList *acct_type_list; - GtkTreeSelection *selection; - - multiple_selection = gnc_option_multiple_selection (option); - acct_type_list = gnc_option_get_account_type_list (option); - - frame = gtk_frame_new (name); - - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_box_set_homogeneous (GTK_BOX(vbox), FALSE); - - gtk_container_add (GTK_CONTAINER(frame), vbox); - - tree = GTK_WIDGET(gnc_tree_view_account_new (FALSE)); - gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(tree), FALSE); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree)); - if (multiple_selection) - gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); - else - gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); - - if (acct_type_list) - { - GList *node; - AccountViewInfo avi; - int i; - - gnc_tree_view_account_get_view_info (GNC_TREE_VIEW_ACCOUNT(tree), &avi); - - for (i = 0; i < NUM_ACCOUNT_TYPES; i++) - avi.include_type[i] = FALSE; - avi.show_hidden = FALSE; - - for (node = acct_type_list; node; node = node->next) - { - GNCAccountType type = GPOINTER_TO_INT(node->data); - avi.include_type[type] = TRUE; - } - - gnc_tree_view_account_set_view_info (GNC_TREE_VIEW_ACCOUNT(tree), &avi); - g_list_free (acct_type_list); - } - else - { - AccountViewInfo avi; - int i; - - gnc_tree_view_account_get_view_info (GNC_TREE_VIEW_ACCOUNT(tree), &avi); - - for (i = 0; i < NUM_ACCOUNT_TYPES; i++) - avi.include_type[i] = TRUE; - avi.show_hidden = FALSE; - gnc_tree_view_account_set_view_info (GNC_TREE_VIEW_ACCOUNT(tree), &avi); - } - - scroll_win = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scroll_win), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - - gtk_box_pack_start (GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0); - gtk_container_set_border_width (GTK_CONTAINER(scroll_win), 5); - gtk_container_add (GTK_CONTAINER(scroll_win), tree); - - bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); - gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD); - gtk_box_pack_start (GTK_BOX(vbox), bbox, FALSE, FALSE, 10); - - if (multiple_selection) - { - button = gtk_button_new_with_label (_("Select All")); - gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_tooltip_text (button, _("Select all accounts.")); - - g_signal_connect (G_OBJECT(button), "clicked", - G_CALLBACK(gnc_option_account_select_all_cb), option); - - button = gtk_button_new_with_label (_("Clear All")); - gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_tooltip_text (button, _("Clear the selection and unselect all accounts.")); - - g_signal_connect (G_OBJECT(button), "clicked", - G_CALLBACK(gnc_option_account_clear_all_cb), option); - - button = gtk_button_new_with_label (_("Select Children")); - gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_tooltip_text (button, _("Select all descendents of selected account.")); - - g_signal_connect (G_OBJECT(button), "clicked", - G_CALLBACK(gnc_option_account_select_children_cb), option); - } - - button = gtk_button_new_with_label (_("Select Default")); - gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_tooltip_text (button, _("Select the default account selection.")); - - g_signal_connect (G_OBJECT(button), "clicked", - G_CALLBACK(gnc_option_default_cb), option); - - gtk_widget_set_margin_start (GTK_WIDGET(bbox), 6); - gtk_widget_set_margin_end (GTK_WIDGET(bbox), 6); - - if (multiple_selection) - { - /* Put the "Show hidden" checkbox on a separate line since the 4 buttons make - the dialog too wide. */ - bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); - gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START); - gtk_box_pack_start (GTK_BOX(vbox), bbox, FALSE, FALSE, 0); - } - - button = gtk_check_button_new_with_label (_("Show Hidden Accounts")); - gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_tooltip_text (button, _("Show accounts that have been marked hidden.")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), FALSE); - g_signal_connect (G_OBJECT(button), "toggled", - G_CALLBACK(gnc_option_show_hidden_toggled_cb), option); - - gnc_option_set_widget (option, tree); - - return frame; -} - -static void -gnc_option_list_changed_cb (GtkTreeSelection *selection, - GNCOption *option) -{ - GtkTreeView *view = gtk_tree_selection_get_tree_view (selection); - gnc_option_changed_widget_cb (GTK_WIDGET(view), option); -} - -static void -gnc_option_list_select_all_cb (GtkWidget *widget, gpointer data) -{ - GNCOption *option = data; - GtkTreeView *view; - GtkTreeSelection *selection; - - view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option)); - selection = gtk_tree_view_get_selection (view); - gtk_tree_selection_select_all (selection); - gnc_option_changed_widget_cb (GTK_WIDGET(view), option); -} - -static void -gnc_option_list_clear_all_cb (GtkWidget *widget, gpointer data) -{ - GNCOption *option = data; - GtkTreeView *view; - GtkTreeSelection *selection; - - view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option)); - selection = gtk_tree_view_get_selection (view); - gtk_tree_selection_unselect_all (selection); - gnc_option_changed_widget_cb (GTK_WIDGET(view), option); -} - -static GtkWidget * -gnc_option_create_list_widget (GNCOption *option, char *name) -{ - GtkListStore *store; - GtkTreeView *view; - GtkTreeIter iter; - GtkTreeViewColumn *column; - GtkCellRenderer *renderer; - GtkTreeSelection *selection; - - GtkWidget *button; - GtkWidget *frame; - GtkWidget *hbox; - GtkWidget *bbox; - gint num_values; - gint i; - - frame = gtk_frame_new (name); - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_set_homogeneous (GTK_BOX(hbox), FALSE); - gtk_container_add (GTK_CONTAINER(frame), hbox); - - store = gtk_list_store_new (1, G_TYPE_STRING); - view = GTK_TREE_VIEW(gtk_tree_view_new_with_model (GTK_TREE_MODEL(store))); - g_object_unref (store); - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("", renderer, - "text", 0, - NULL); - gtk_tree_view_append_column (view, column); - gtk_tree_view_set_headers_visible (view, FALSE); - - num_values = gnc_option_num_permissible_values (option); - for (i = 0; i < num_values; i++) - { - gchar *raw_string, *string; - - raw_string = gnc_option_permissible_value_name (option, i); - string = (raw_string && *raw_string) ? _(raw_string) : ""; - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, - 0, string ? string : "", - -1); - g_free (raw_string); - } - - gtk_box_pack_start (GTK_BOX(hbox), GTK_WIDGET(view), FALSE, FALSE, 0); - - selection = gtk_tree_view_get_selection (view); - gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); - g_signal_connect (selection, "changed", - G_CALLBACK(gnc_option_list_changed_cb), option); - - bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL); - gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD); - gtk_box_pack_end (GTK_BOX(hbox), bbox, FALSE, FALSE, 0); - - button = gtk_button_new_with_label (_("Select All")); - gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_tooltip_text (button, _("Select all entries.")); - - g_signal_connect (G_OBJECT(button), "clicked", - G_CALLBACK(gnc_option_list_select_all_cb), option); - - button = gtk_button_new_with_label (_("Clear All")); - gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_tooltip_text (button, _("Clear the selection and unselect all entries.")); - - g_signal_connect (G_OBJECT(button), "clicked", - G_CALLBACK(gnc_option_list_clear_all_cb), option); - - button = gtk_button_new_with_label (_("Select Default")); - gtk_box_pack_start (GTK_BOX(bbox), button, FALSE, FALSE, 0); - gtk_widget_set_tooltip_text (button, _("Select the default selection.")); - - g_signal_connect (G_OBJECT(button), "clicked", - G_CALLBACK(gnc_option_default_cb), option); - - g_object_set (G_OBJECT(hbox), "margin", 3, NULL); - - gnc_option_set_widget (option, GTK_WIDGET(view)); - - return frame; -} - -static void -gnc_option_color_changed_cb (GtkColorButton *color_button, GNCOption *option) -{ - gnc_option_changed_widget_cb (GTK_WIDGET(color_button), option); -} - -static void -gnc_option_font_changed_cb (GtkFontButton *font_button, GNCOption *option) -{ - gnc_option_changed_widget_cb (GTK_WIDGET(font_button), option); -} - -static void -gnc_option_set_ui_widget (GNCOption *option, GtkGrid *page_box, gint grid_row) -{ - GtkWidget *enclosing = NULL; - GtkWidget *value = NULL; - gboolean packed = FALSE; - char *raw_name, *raw_documentation; - char *name, *documentation; - char *type; - GNCOptionDef_t *option_def; - GtkLabel *name_label; - - ENTER("option %p(%s), box %p", - option, gnc_option_name (option), page_box); - type = gnc_option_type (option); - if (type == NULL) - { - LEAVE("bad type"); - return; - } - else if (g_strcmp0 (type, "internal") == 0) - { - LEAVE("internal type"); - return; - } - - raw_name = gnc_option_name (option); - if (raw_name && *raw_name) - name = _(raw_name); - else - name = NULL; - - raw_documentation = gnc_option_documentation (option); - if (raw_documentation && *raw_documentation) - documentation = _(raw_documentation); - else - documentation = NULL; - - name_label = GTK_LABEL(gtk_label_new (name)); - - option_def = gnc_options_ui_get_option (type); - if (option_def && option_def->set_widget) - { - value = option_def->set_widget (option, page_box, - name_label, documentation, - /* Return values */ - &enclosing, &packed); - } - else - { - PERR("Unknown option type. Ignoring option \"%s\".\n", name); - } - - /* attach the name label to the first column of the grid and align to the end - * if option is a check button, do not add a label as we are using the built - * in one */ - if (!GTK_IS_CHECK_BUTTON(value)) - gtk_grid_attach (GTK_GRID(page_box), GTK_WIDGET(name_label), - 0, grid_row, // left, top - 1, 1); // width, height - - gtk_widget_set_halign (GTK_WIDGET(name_label), GTK_ALIGN_END); - - if (!packed && (enclosing != NULL)) - { - /* Pack option widget into an extra eventbox because otherwise the - "documentation" tooltip is not displayed. */ - GtkWidget *eventbox = gtk_event_box_new (); - - gtk_container_add (GTK_CONTAINER(eventbox), enclosing); - - /* attach the option widget to the second column of the grid */ - gtk_grid_attach (GTK_GRID(page_box), eventbox, - 1, grid_row, // left, top - 1, 1); // width, height - - gtk_widget_set_tooltip_text (eventbox, documentation); - } - - if (value != NULL) - gtk_widget_set_tooltip_text (value, documentation); - - if (raw_name != NULL) - free (raw_name); - if (raw_documentation != NULL) - free (raw_documentation); - free (type); - LEAVE(" "); -} - -static void -gnc_options_dialog_add_option (GtkWidget *page, - GNCOption *option, gint row) -{ - g_object_set_data (G_OBJECT(page), "options-grid-row", GINT_TO_POINTER(row)); - gnc_option_set_ui_widget (option, GTK_GRID(page), row); -} - -static gint -gnc_options_dialog_append_page (GNCOptionWin * propertybox, - GNCOptionSection *section) -{ - GNCOption *option; - GtkWidget *page_label; - GtkWidget *options_box; - GtkWidget *page_content_box; - GtkWidget* notebook_page; - GtkWidget *reset_button; - GtkWidget *listitem = NULL; - GtkWidget *buttonbox; - GtkWidget *options_scrolled_win; - GtkTreeView *view; - GtkListStore *list; - GtkTreeIter iter; - gint num_options; - const char *name; - gint i, page_count, name_offset; - gboolean advanced; - - name = gnc_option_section_name (section); - if (!name) - return -1; - - if (strncmp (name, "__", 2) == 0) - return -1; - advanced = (strncmp (name, "_+", 2) == 0); - name_offset = (advanced) ? 2 : 0; - page_label = gtk_label_new (_(name + name_offset)); - PINFO("Page_label is %s", _(name + name_offset)); - gtk_widget_show (page_label); - - /* Build this options page */ - page_content_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); - gtk_widget_set_name (page_content_box, "page-content-box"); - gtk_box_set_homogeneous (GTK_BOX (page_content_box), FALSE); - - gtk_container_set_border_width (GTK_CONTAINER(page_content_box), 12); - - options_scrolled_win = gtk_scrolled_window_new (NULL, NULL); - gtk_box_pack_start (GTK_BOX(page_content_box), options_scrolled_win, TRUE, TRUE, 0); - - /* Build space for the content - the options box */ - options_box = gtk_grid_new (); // this will have two columns - gtk_widget_set_name (options_box, "options-box"); - gtk_grid_set_row_homogeneous (GTK_GRID(options_box), FALSE); - gtk_grid_set_column_homogeneous (GTK_GRID(options_box), FALSE); - gtk_grid_set_row_spacing (GTK_GRID(options_box), 6); - gtk_grid_set_column_spacing (GTK_GRID(options_box), 6); - - gtk_container_set_border_width (GTK_CONTAINER(options_box), 0); - gtk_container_add (GTK_CONTAINER(options_scrolled_win), GTK_WIDGET(options_box)); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(options_scrolled_win), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - - /* Create all the options */ - num_options = gnc_option_section_num_options (section); - for (i = 0; i < num_options; i++) - { - option = gnc_get_option_section_option (section, i); - gnc_options_dialog_add_option (options_box, option, i); - } - - /* Add a button box at the bottom of the page */ - buttonbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); - gtk_button_box_set_layout (GTK_BUTTON_BOX (buttonbox), - GTK_BUTTONBOX_EDGE); - gtk_container_set_border_width (GTK_CONTAINER(buttonbox), 5); - gtk_box_pack_end (GTK_BOX(page_content_box), buttonbox, FALSE, FALSE, 0); - - /* The reset button on each option page */ - reset_button = gtk_button_new_with_label (_("Reset defaults")); - gtk_widget_set_tooltip_text (reset_button, - _("Reset all values to their defaults.")); - - g_signal_connect (G_OBJECT(reset_button), "clicked", - G_CALLBACK(gnc_options_dialog_reset_cb), propertybox); - g_object_set_data (G_OBJECT(reset_button), "section", section); - gtk_box_pack_end (GTK_BOX(buttonbox), reset_button, FALSE, FALSE, 0); - gtk_widget_show_all (page_content_box); - gtk_notebook_append_page (GTK_NOTEBOOK(propertybox->notebook), - page_content_box, page_label); - - /* Switch to selection from a list if the page count threshold is reached */ - page_count = gtk_notebook_page_num (GTK_NOTEBOOK(propertybox->notebook), - page_content_box); - - if (propertybox->page_list_view) - { - /* Build the matching list item for selecting from large page sets */ - view = GTK_TREE_VIEW(propertybox->page_list_view); - list = GTK_LIST_STORE(gtk_tree_view_get_model (view)); - - PINFO("Page name is %s and page_count is %d", name, page_count); - gtk_list_store_append (list, &iter); - gtk_list_store_set (list, &iter, - PAGE_NAME, _(name), - PAGE_INDEX, page_count, - -1); - - if (page_count > MAX_TAB_COUNT - 1) /* Convert 1-based -> 0-based */ - { - gtk_widget_show (propertybox->page_list); - gtk_notebook_set_show_tabs (GTK_NOTEBOOK(propertybox->notebook), FALSE); - gtk_notebook_set_show_border (GTK_NOTEBOOK(propertybox->notebook), FALSE); - } - else - gtk_widget_hide (propertybox->page_list); - - /* Tweak "advanced" pages for later handling. */ - if (advanced) - { - notebook_page = gtk_notebook_get_nth_page (GTK_NOTEBOOK(propertybox->notebook), - page_count); - - g_object_set_data (G_OBJECT(notebook_page), "listitem", listitem); - g_object_set_data (G_OBJECT(notebook_page), "advanced", - GINT_TO_POINTER(advanced)); - } - } - return (page_count); -} - -/********************************************************************\ - * gnc_options_dialog_build_contents * - * builds an options dialog given a property box and an options * - * database and make the dialog visible * - * * - * Args: propertybox - gnome property box to use * - * odb - option database to use * - * Return: nothing * -\********************************************************************/ -void -gnc_options_dialog_build_contents (GNCOptionWin *propertybox, - GNCOptionDB *odb) -{ - gnc_options_dialog_build_contents_full (propertybox, odb, TRUE); -} - -/********************************************************************\ - * gnc_options_dialog_build_contents_full * - * builds an options dialog given a property box and an options * - * database and make the dialog visible depending on the * - * show_dialog flag * - * * - * Args: propertybox - gnome property box to use * - * odb - option database to use * - * show_dialog - should dialog be made visible or not * - * Return: nothing * -\********************************************************************/ -void -gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox, - GNCOptionDB *odb, gboolean show_dialog) -{ - GNCOptionSection *section; - gchar *default_section_name; - gint default_page = -1; - gint num_sections; - gint page; - gint i; - guint j; - - g_return_if_fail (propertybox != NULL); - g_return_if_fail (odb != NULL); - - gnc_option_db_set_ui_callbacks (odb, - gnc_option_get_ui_value_internal, - gnc_option_set_ui_value_internal, - gnc_option_set_selectable_internal); - - propertybox->option_db = odb; - - num_sections = gnc_option_db_num_sections (odb); - default_section_name = gnc_option_db_get_default_section (odb); - - PINFO("Default Section name is %s", default_section_name); - - for (i = 0; i < num_sections; i++) - { - const char *section_name; - - section = gnc_option_db_get_section (odb, i); - page = gnc_options_dialog_append_page (propertybox, section); - - section_name = gnc_option_section_name (section); - if (g_strcmp0 (section_name, default_section_name) == 0) - default_page = page; - } - - if (default_section_name != NULL) - free (default_section_name); - - /* call each option widget changed callbacks once at this point, - * now that all options widgets exist. - */ - for (i = 0; i < num_sections; i++) - { - section = gnc_option_db_get_section (odb, i); - - for (j = 0; j < gnc_option_section_num_options (section); j++) - { - // setting TRUE will clear the changed flag after proc - gnc_option_call_option_widget_changed_proc ( - gnc_get_option_section_option(section, j), TRUE); - } - } - - gtk_notebook_popup_enable (GTK_NOTEBOOK(propertybox->notebook)); - if (default_page >= 0) - { - /* Find the page list and set the selection to the default page */ - GtkTreeSelection* selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(propertybox->page_list_view)); - GtkTreeIter iter; - GtkTreeModel *model; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW(propertybox->page_list_view)); - gtk_tree_model_iter_nth_child (model, &iter, NULL, default_page); - gtk_tree_selection_select_iter (selection, &iter); - gtk_notebook_set_current_page (GTK_NOTEBOOK(propertybox->notebook), default_page); - } - gnc_options_dialog_changed_internal (propertybox->window, FALSE); - if (show_dialog) - gtk_widget_show (propertybox->window); -} - -GtkWidget * -gnc_options_dialog_widget (GNCOptionWin * win) -{ - return win->window; -} - -GtkWidget * -gnc_options_page_list (GNCOptionWin * win) -{ - return win->page_list; -} - -GtkWidget * -gnc_options_dialog_notebook (GNCOptionWin * win) -{ - return win->notebook; -} - -static void -gnc_options_dialog_help_button_cb (GtkWidget * widget, GNCOptionWin *win) -{ - if (win->help_cb) - (win->help_cb)(win, win->help_cb_data); -} - -static void -gnc_options_dialog_cancel_button_cb (GtkWidget * widget, GNCOptionWin *win) -{ - gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window)); - - if (win->close_cb) - (win->close_cb)(win, win->close_cb_data); - else - gtk_widget_hide (win->window); -} - -static void -gnc_options_dialog_apply_button_cb (GtkWidget * widget, GNCOptionWin *win) -{ - GNCOptionWinCallback close_cb = win->close_cb; - - win->close_cb = NULL; - if (win->apply_cb) - win->apply_cb (win, win->apply_cb_data); - win->close_cb = close_cb; - gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window)); - gnc_options_dialog_changed_internal (win->window, FALSE); -} - -static void -gnc_options_dialog_ok_button_cb (GtkWidget * widget, GNCOptionWin *win) -{ - GNCOptionWinCallback close_cb = win->close_cb; - - win->close_cb = NULL; - if (win->apply_cb) - win->apply_cb (win, win->apply_cb_data); - win->close_cb = close_cb; - - gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window)); - - if (win->close_cb) - (win->close_cb)(win, win->close_cb_data); - else - gtk_widget_hide (win->window); -} - -static void -gnc_options_dialog_destroy_cb (GtkWidget *object, GNCOptionWin *win) -{ - if (!win) return; - - if (win->destroyed == FALSE) - { - if (win->close_cb) - (win->close_cb)(win, win->close_cb_data); - } -} - -static gboolean -gnc_options_dialog_window_key_press_cb (GtkWidget *widget, - GdkEventKey *event, - gpointer data) -{ - GNCOptionWin *win = data; - - if (event->keyval == GDK_KEY_Escape) - { - component_close_handler (win); - return TRUE; - } - else - return FALSE; -} - -static void -gnc_options_dialog_reset_cb (GtkWidget * w, gpointer data) -{ - GNCOptionWin *win = data; - GNCOptionSection *section; - gpointer val; - - val = g_object_get_data (G_OBJECT(w), "section"); - g_return_if_fail (val); - g_return_if_fail (win); - - section = (GNCOptionSection*)val; - - gnc_option_db_section_reset_widgets (section); - - if (gnc_option_db_get_changed (win->option_db)) - gnc_options_dialog_changed_internal (win->window, TRUE); -} - -void -gnc_options_dialog_list_select_cb (GtkTreeSelection *selection, - gpointer data) -{ - GNCOptionWin * win = data; - GtkTreeModel *list; - GtkTreeIter iter; - gint index = 0; - - if (!gtk_tree_selection_get_selected (selection, &list, &iter)) - return; - gtk_tree_model_get (list, &iter, - PAGE_INDEX, &index, - -1); - PINFO("Index is %d", index); - gtk_notebook_set_current_page (GTK_NOTEBOOK(win->notebook), index); -} - -void -gnc_options_register_stocks (void) -{ -#if 0 - static gboolean done = FALSE; - - GtkStockItem items[] = - { - { GTK_STOCK_APPLY , "gnc_option_apply_button", 0, 0, NULL }, - { GTK_STOCK_HELP , "gnc_options_dialog_help", 0, 0, NULL }, - { GTK_STOCK_OK , "gnc_options_dialog_ok", 0, 0, NULL }, - { GTK_STOCK_CANCEL , "gnc_options_dialog_cancel", 0, 0, NULL }, - }; - - if (done) - { - return; - } - done = TRUE; - - gtk_stock_add (items, G_N_ELEMENTS (items)); -#endif -} - -static void -component_close_handler (gpointer data) -{ - GNCOptionWin *win = data; - gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window)); - gnc_options_dialog_cancel_button_cb (NULL, win); -} - -static void -refresh_handler (GHashTable *changes, gpointer user_data) -{ - gnc_commodity *commodity = NULL; - GtkTreeIter iter; - - /* The default_gain_loss_account_widget needs to be refreshed if any - changes have been made via account maintenance, if it exists and - if the book currency widget has a selection */ -/* if (book_currency_data->default_gain_loss_account_widget && - gtk_combo_box_get_active_iter( - GTK_COMBO_BOX(book_currency_data->book_currency_widget), &iter)) - { - commodity = gnc_currency_edit_get_currency( - GNC_CURRENCY_EDIT( - book_currency_data->book_currency_widget)); - gnc_set_default_gain_loss_account_widget(commodity); - gtk_widget_show_all(book_currency_data->book_currency_vbox); - } */ -} - -/* gnc_options_dialog_new: - * - * - Opens the dialog-options glade file - * - Connects signals specified in the builder file - * - Sets the window's title - * - Initializes a new GtkNotebook, and adds it to the window - * - */ -GNCOptionWin * -gnc_options_dialog_new (gchar *title, GtkWindow *parent) -{ - return gnc_options_dialog_new_modal (FALSE, title, NULL, parent); -} - -/* gnc_options_dialog_new_modal: - * - * - Opens the dialog-options glade file - * - Connects signals specified in the builder file - * - Sets the window's title - * - Initializes a new GtkNotebook, and adds it to the window - * - If modal TRUE, hides 'apply' button - * - If component_class is provided, it is used, otherwise, - * DIALOG_OPTIONS_CM_CLASS is used; this is used to distinguish the - * book-option dialog from report dialogs. The book-option dialog is a - * singleton, so if a dialog already exists it will be raised to the top of - * the window stack instead of creating a new dialog. - */ -GNCOptionWin * -gnc_options_dialog_new_modal (gboolean modal, gchar *title, - const char *component_class, - GtkWindow *parent) -{ - GNCOptionWin *retval; - GtkBuilder *builder; - GtkWidget *hbox; - gint component_id; - GtkWidget *button; - - retval = g_new0 (GNCOptionWin, 1); - builder = gtk_builder_new (); - gnc_builder_add_from_file (builder, "dialog-options.glade", "gnucash_options_window"); - retval->window = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_options_window")); - retval->page_list = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_scroll")); - - // Set the name for this dialog so it can be easily manipulated with css - gtk_widget_set_name (GTK_WIDGET(retval->window), "gnc-id-options"); - - /* Page List */ - { - GtkTreeView *view; - GtkListStore *store; - GtkTreeSelection *selection; - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - - retval->page_list_view = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_treeview")); - - view = GTK_TREE_VIEW(retval->page_list_view); - - store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING); - gtk_tree_view_set_model (view, GTK_TREE_MODEL(store)); - g_object_unref (store); - - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes (_("Page"), renderer, - "text", PAGE_NAME, - NULL); - gtk_tree_view_append_column (view, column); - - gtk_tree_view_column_set_alignment (column, 0.5); - - selection = gtk_tree_view_get_selection (view); - gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); - g_signal_connect (selection, "changed", - G_CALLBACK (gnc_options_dialog_list_select_cb), retval); - } - - button = GTK_WIDGET(gtk_builder_get_object (builder, "helpbutton")); - g_signal_connect (button, "clicked", G_CALLBACK(gnc_options_dialog_help_button_cb), retval); - button = GTK_WIDGET(gtk_builder_get_object (builder, "cancelbutton")); - g_signal_connect (button, "clicked", G_CALLBACK(gnc_options_dialog_cancel_button_cb), retval); - button = GTK_WIDGET(gtk_builder_get_object (builder, "applybutton")); - g_signal_connect (button, "clicked", G_CALLBACK(gnc_options_dialog_apply_button_cb), retval); - button = GTK_WIDGET(gtk_builder_get_object (builder, "okbutton")); - g_signal_connect (button, "clicked", G_CALLBACK(gnc_options_dialog_ok_button_cb), retval); - - gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, retval); - - if (parent) // when added to a page of the hierarchy assistant there will be no parent - gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(retval->window), parent); - - if (title) - gtk_window_set_title (GTK_WINDOW(retval->window), title); - - /* modal */ - if (modal == TRUE) - { - GtkWidget *apply_button; - - apply_button = GTK_WIDGET(gtk_builder_get_object (builder, "applybutton")); - gtk_widget_hide (apply_button); - } - - /* glade doesn't support a notebook with zero pages */ - hbox = GTK_WIDGET(gtk_builder_get_object (builder, "notebook_placeholder")); - retval->notebook = gtk_notebook_new (); - - gtk_widget_set_vexpand (retval->notebook, TRUE); - - gtk_widget_show (retval->notebook); - gtk_box_pack_start (GTK_BOX(hbox), retval->notebook, TRUE, TRUE, 5); - - retval->component_class = - (component_class ? component_class : DIALOG_OPTIONS_CM_CLASS); - component_id = gnc_register_gui_component (retval->component_class, - refresh_handler, component_close_handler, - retval); - gnc_gui_component_set_session (component_id, gnc_get_current_session()); - - /* Watch account maintenance events only if book option dialog */ - if (g_strcmp0 (retval->component_class, DIALOG_BOOK_OPTIONS_CM_CLASS) == 0) - { - gnc_gui_component_watch_entity_type (component_id, - GNC_ID_ACCOUNT, - QOF_EVENT_MODIFY | QOF_EVENT_DESTROY); - } - - g_signal_connect (retval->window, "destroy", - G_CALLBACK(gnc_options_dialog_destroy_cb), retval); - - g_signal_connect (retval->window, "key_press_event", - G_CALLBACK(gnc_options_dialog_window_key_press_cb), retval); - - g_object_unref (G_OBJECT(builder)); - - retval->destroyed = FALSE; - return retval; -} - -/* Creates a new GNCOptionWin structure, but assumes you have your own - dialog widget you want to plugin */ -GNCOptionWin * -gnc_options_dialog_new_w_dialog (gchar *title, GtkWidget *window) -{ - GNCOptionWin * retval; - - retval = g_new0 (GNCOptionWin, 1); - retval->window = window; - return retval; -} - -void -gnc_options_dialog_set_apply_cb (GNCOptionWin * win, GNCOptionWinCallback cb, - gpointer data) -{ - win->apply_cb = cb; - win->apply_cb_data = data; -} - -void -gnc_options_dialog_set_help_cb (GNCOptionWin * win, GNCOptionWinCallback cb, - gpointer data) -{ - win->help_cb = cb; - win->help_cb_data = data; -} - -void -gnc_options_dialog_set_close_cb (GNCOptionWin * win, GNCOptionWinCallback cb, - gpointer data) -{ - win->close_cb = cb; - win->close_cb_data = data; -} - -void -gnc_options_dialog_set_global_help_cb (GNCOptionWinCallback thunk, - gpointer cb_data) -{ - global_help_cb = thunk; - global_help_cb_data = cb_data; -} - -/* This is for global program preferences. */ -void -gnc_options_dialog_destroy (GNCOptionWin * win) -{ - if (!win) return; - - gnc_unregister_gui_component_by_data (win->component_class, win); - - win->destroyed = TRUE; - gtk_widget_destroy (win->window); - - win->window = NULL; - win->notebook = NULL; - win->apply_cb = NULL; - win->help_cb = NULL; - win->component_class = NULL; - - g_free (win); -} - -/*****************************************************************/ -/* Option Registration */ - -/************************* - * SET WIDGET * - ************************* - * - * gnc_option_set_ui_widget_(): - * - * You should create the widget representation for the option type, - * and set the top-level container widget for your control in - * *enclosing. If you want to pack the widget into the page yourself, - * then you may -- just set *packed to TRUE. Otherwise, the widget - * you return in *enclosing will be packed for you. (*packed is - * initialized to FALSE, so if you're not setting it to TRUE, you - * don't have to touch it at all.) - * - * If you need to initialize the state of your control or to connect - * any signals to you widgets, then you should do so in this function. - * If you want to create a label for the widget you should use 'name' - * for the label text. - * - * Somewhere in this function, you should also call - * gnc_option_set_widget(option, value); where 'value' is the - * GtkWidget you will actually store the value in. - * - * Also call gnc_option_set_ui_value(option, FALSE); - * - * You probably want to end with something like: - * gtk_widget_show_all(*enclosing); - * - * If you can detect state changes for your widget's value, you should also - * gnc_option_changed_widget_cb() upon changes. - * - * The widget you return from this function should be the widget in - * which you're storing the option value. - */ - -static void -gnc_option_set_ui_label_alignment (GtkLabel *name_label) -{ - /* some option widgets have a large vertical foot print so align - the label name to the top and add a small top margin */ - gtk_widget_set_valign (GTK_WIDGET(name_label), GTK_ALIGN_START); - gtk_widget_set_margin_top (GTK_WIDGET(name_label), 6); -} - -static GtkWidget * -gnc_option_set_ui_widget_boolean (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - value = gtk_check_button_new_with_label (gtk_label_get_text (name_label)); - - gnc_option_set_widget (option, value); - gnc_option_set_ui_value (option, FALSE); - - g_signal_connect (G_OBJECT(value), "toggled", - G_CALLBACK(gnc_option_changed_widget_cb), option); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_string (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - value = gtk_entry_new (); - - gnc_option_set_widget (option, value); - gnc_option_set_ui_value (option, FALSE); - - if (gtk_widget_get_direction (GTK_WIDGET(value)) == GTK_TEXT_DIR_RTL) - gtk_entry_set_alignment (GTK_ENTRY(value), 1.0); - - g_signal_connect (G_OBJECT(value), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), option); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, TRUE, TRUE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_text (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - GtkWidget *frame; - GtkWidget *scroll; - GtkTextBuffer* text_buffer; - - frame = gtk_frame_new(NULL); - - gnc_option_set_ui_label_alignment (name_label); - - scroll = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scroll), - GTK_POLICY_NEVER, - GTK_POLICY_AUTOMATIC); - gtk_container_set_border_width (GTK_CONTAINER(scroll), 2); - - gtk_container_add (GTK_CONTAINER(frame), scroll); - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); - gtk_widget_set_vexpand (GTK_WIDGET(*enclosing), TRUE); - gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE); - gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); - value = gtk_text_view_new (); - gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW(value), GTK_WRAP_WORD); - gtk_text_view_set_editable (GTK_TEXT_VIEW(value), TRUE); - gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW(value), FALSE); - gtk_container_add (GTK_CONTAINER(scroll), value); - - gnc_option_set_widget (option, value); - gnc_option_set_ui_value (option, FALSE); - - text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(value)); - g_signal_connect (G_OBJECT(text_buffer), "changed", - G_CALLBACK(gnc_option_changed_option_cb), option); - - gtk_box_pack_start (GTK_BOX(*enclosing), frame, TRUE, TRUE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_currency (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - value = gnc_currency_edit_new (); - - gnc_option_set_widget (option, value); - gnc_option_set_ui_value (option, FALSE); - - g_signal_connect (G_OBJECT(value), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), option); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_commodity (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - value = gnc_general_select_new (GNC_GENERAL_SELECT_TYPE_SELECT, - gnc_commodity_edit_get_string, - gnc_commodity_edit_new_select, - NULL); - - gnc_option_set_widget (option, value); - gnc_option_set_ui_value (option, FALSE); - - if (documentation != NULL) - gtk_widget_set_tooltip_text (GNC_GENERAL_SELECT(value)->entry, - documentation); - - g_signal_connect (G_OBJECT(GNC_GENERAL_SELECT(value)->entry), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), option); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_multichoice (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - - value = gnc_option_create_multichoice_widget (option); - gnc_option_set_widget (option, value); - - gnc_option_set_ui_value (option, FALSE); - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_date (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - gchar *colon_name; - GtkWidget *eventbox; - gchar *type = gnc_option_date_option_get_subtype (option); - - - gint grid_row = GPOINTER_TO_INT(g_object_get_data - (G_OBJECT(page_box), "options-grid-row")); - - value = gnc_option_create_date_widget (option); - - gnc_option_set_widget (option, value); - - if (g_strcmp0 (type, "relative") == 0) - { - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - } - else - { - *enclosing = gtk_frame_new (NULL); - g_object_set (G_OBJECT(value), "margin", 3, NULL); - - gtk_container_add (GTK_CONTAINER(*enclosing), value); - } - g_free (type); - - gtk_widget_set_halign (GTK_WIDGET(*enclosing), GTK_ALIGN_START); - - /* Pack option widget into an extra eventbox because otherwise the - "documentation" tooltip is not displayed. */ - eventbox = gtk_event_box_new (); - gtk_container_add (GTK_CONTAINER(eventbox), *enclosing); - - gtk_grid_attach (GTK_GRID(page_box), eventbox, 1, grid_row, 1, 1); - *packed = TRUE; - - gtk_widget_set_tooltip_text (eventbox, documentation); - - gnc_option_set_ui_value (option, FALSE); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_account_list (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - GtkTreeSelection *selection; - gint grid_row = GPOINTER_TO_INT(g_object_get_data - (G_OBJECT(page_box), "options-grid-row")); - - gnc_option_set_ui_label_alignment (name_label); - - *enclosing = gnc_option_create_account_widget (option, NULL); - gtk_widget_set_vexpand (GTK_WIDGET(*enclosing), TRUE); - gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE); - value = gnc_option_get_gtk_widget (option); - - gtk_widget_set_tooltip_text (*enclosing, documentation); - - gtk_grid_attach (GTK_GRID(page_box), *enclosing, 1, grid_row, 1, 1); - *packed = TRUE; - - //gtk_widget_realize(value); - - gnc_option_set_ui_value (option, FALSE); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(value)); - g_signal_connect (G_OBJECT(selection), "changed", - G_CALLBACK(gnc_option_account_cb), option); - - // gtk_clist_set_row_height(GTK_CLIST(value), 0); - // gtk_widget_set_size_request(value, -1, GTK_CLIST(value)->row_height * 10); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_account_sel (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value = gnc_account_sel_new (); - GList *acct_type_list = gnc_option_get_account_type_list (option); - - gnc_account_sel_set_acct_filters (GNC_ACCOUNT_SEL(value), acct_type_list, NULL); - - g_signal_connect (value, "account_sel_changed", - G_CALLBACK(gnc_option_changed_widget_cb), option); - - gnc_option_set_widget (option, value); - - gnc_option_set_ui_value (option, FALSE); - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_list (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - GtkWidget *eventbox; - gint grid_row = GPOINTER_TO_INT(g_object_get_data - (G_OBJECT(page_box), "options-grid-row")); - - *enclosing = gnc_option_create_list_widget (option, NULL); - value = gnc_option_get_gtk_widget (option); - - gnc_option_set_ui_label_alignment (name_label); - - /* Pack option widget into an extra eventbox because otherwise the - "documentation" tooltip is not displayed. */ - eventbox = gtk_event_box_new (); - gtk_container_add (GTK_CONTAINER(eventbox), *enclosing); - - gtk_grid_attach (GTK_GRID(page_box), eventbox, 1, grid_row, 1, 1); - *packed = TRUE; - - gtk_widget_set_tooltip_text (eventbox, documentation); - - gnc_option_set_ui_value (option, FALSE); - gtk_widget_show (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_number_range (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - GtkAdjustment *adj; - gdouble lower_bound = G_MINDOUBLE; - gdouble upper_bound = G_MAXDOUBLE; - gdouble step_size = 1.0; - int num_decimals = 0; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - - gnc_option_get_range_info (option, &lower_bound, &upper_bound, - &num_decimals, &step_size); - adj = GTK_ADJUSTMENT(gtk_adjustment_new (lower_bound, lower_bound, - upper_bound, step_size, - step_size * 5.0, - 0)); - value = gtk_spin_button_new (adj, step_size, num_decimals); - gtk_spin_button_set_numeric (GTK_SPIN_BUTTON(value), TRUE); - - { - gdouble biggest; - gint num_digits; - - biggest = ABS(lower_bound); - biggest = MAX(biggest, ABS(upper_bound)); - - num_digits = 0; - while (biggest >= 1) - { - num_digits++; - biggest = biggest / 10; - } - - if (num_digits == 0) - num_digits = 1; - - num_digits += num_decimals; - - gtk_entry_set_width_chars (GTK_ENTRY(value), num_digits); - } - - gnc_option_set_widget (option, value); - gnc_option_set_ui_value (option, FALSE); - - g_signal_connect (G_OBJECT(value), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), option); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_color (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - gboolean use_alpha; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - - use_alpha = gnc_option_use_alpha (option); - - value = gtk_color_button_new (); - gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER(value), use_alpha); - - gnc_option_set_widget (option, value); - gnc_option_set_ui_value (option, FALSE); - - g_signal_connect (G_OBJECT(value), "color-set", - G_CALLBACK(gnc_option_color_changed_cb), option); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_font (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - value = gtk_font_button_new (); - g_object_set (G_OBJECT(value), - "use-font", TRUE, - "show-style", TRUE, - "show-size", TRUE, - (char *)NULL); - - gnc_option_set_widget (option, value); - - gnc_option_set_ui_value (option, FALSE); - - g_signal_connect (G_OBJECT(value), "font-set", - G_CALLBACK(gnc_option_font_changed_cb), option); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_pixmap (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - GtkWidget *button; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - - button = gtk_button_new_with_label (_("Clear")); - gtk_widget_set_tooltip_text (button, _("Clear any selected image file.")); - - value = gtk_file_chooser_button_new (_("Select image"), - GTK_FILE_CHOOSER_ACTION_OPEN); - gtk_widget_set_tooltip_text (value, _("Select an image file.")); - g_object_set (G_OBJECT(value), - "width-chars", 30, - "preview-widget", gtk_image_new(), - (char *)NULL); - g_signal_connect (G_OBJECT(value), "selection-changed", - G_CALLBACK(gnc_option_changed_widget_cb), option); - g_signal_connect (G_OBJECT(value), "selection-changed", - G_CALLBACK(gnc_image_option_selection_changed_cb), option); - g_signal_connect (G_OBJECT(value), "update-preview", - G_CALLBACK(gnc_image_option_update_preview_cb), option); - g_signal_connect_swapped (G_OBJECT(button), "clicked", - G_CALLBACK(gtk_file_chooser_unselect_all), value); - - gnc_option_set_widget (option, value); - gnc_option_set_ui_value (option, FALSE); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(*enclosing), button, FALSE, FALSE, 0); - - gtk_widget_show (value); - gtk_widget_show (*enclosing); - - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_radiobutton (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - - gnc_option_set_ui_label_alignment (name_label); - - value = gnc_option_create_radiobutton_widget (NULL, option); - gnc_option_set_widget (option, value); - - gnc_option_set_ui_value (option, FALSE); - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_dateformat (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - *enclosing = gnc_date_format_new_without_label (); - gnc_option_set_widget (option, *enclosing); - - gnc_option_set_ui_label_alignment (name_label); - - gnc_option_set_ui_value (option, FALSE); - g_signal_connect (G_OBJECT(*enclosing), "format_changed", - G_CALLBACK(gnc_option_changed_option_cb), option); - gtk_widget_show_all (*enclosing); - return *enclosing; -} - -static void -gnc_plot_size_option_set_select_method (GNCOption *option, gboolean set_buttons) -{ - GList* widget_list; - GtkWidget *px_widget, *p_widget; - GtkWidget *widget; - - widget = gnc_option_get_gtk_widget (option); - - widget_list = gtk_container_get_children (GTK_CONTAINER(widget)); - // px_button item 0 - px_widget = g_list_nth_data (widget_list, 1); - // p_button item 2 - p_widget = g_list_nth_data (widget_list, 3); - g_list_free (widget_list); - - if (set_buttons) - { - gtk_widget_set_sensitive (px_widget, TRUE); - gtk_widget_set_sensitive (p_widget, FALSE); - } - else - { - gtk_widget_set_sensitive (p_widget, TRUE); - gtk_widget_set_sensitive (px_widget, FALSE); - } -} - -static void -gnc_rd_option_px_set_cb (GtkWidget *widget, gpointer *raw_option) -{ - GNCOption *option = (GNCOption *) raw_option; - gnc_plot_size_option_set_select_method (option, TRUE); - gnc_option_changed_option_cb (widget, option); -} - -static void -gnc_rd_option_p_set_cb (GtkWidget *widget, gpointer *raw_option) -{ - GNCOption *option = (GNCOption *) raw_option; - gnc_plot_size_option_set_select_method (option, FALSE); - gnc_option_changed_option_cb (widget, option); - return; -} - -static GtkWidget * -gnc_option_set_ui_widget_plot_size (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value_px, *value_percent; - GtkWidget *px_butt, *p_butt; - GtkWidget *hbox; - GtkAdjustment *adj_px, *adj_percent; - gdouble lower_bound = G_MINDOUBLE; - gdouble upper_bound = G_MAXDOUBLE; - gdouble step_size = 1.0; - int num_decimals = 0; - - *enclosing = gtk_frame_new (NULL); - gtk_widget_set_halign (GTK_WIDGET(*enclosing), GTK_ALIGN_START); - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(hbox), FALSE); - g_object_set (G_OBJECT(hbox), "margin", 3, NULL); - - gtk_container_add (GTK_CONTAINER(*enclosing), hbox); - - gnc_option_get_range_info (option, &lower_bound, &upper_bound, - &num_decimals, &step_size); - adj_px = GTK_ADJUSTMENT(gtk_adjustment_new (lower_bound, lower_bound, - upper_bound, step_size, - step_size * 5.0, - 0)); - - value_px = gtk_spin_button_new (adj_px, step_size, num_decimals); - gtk_spin_button_set_numeric (GTK_SPIN_BUTTON(value_px), TRUE); - - { - gdouble biggest; - gint num_digits; - - biggest = ABS(lower_bound); - biggest = MAX(biggest, ABS(upper_bound)); - - num_digits = 0; - while (biggest >= 1) - { - num_digits++; - biggest = biggest / 10; - } - - if (num_digits == 0) - num_digits = 1; - - num_digits += num_decimals; - - gtk_entry_set_width_chars (GTK_ENTRY(value_px), num_digits); - } - gtk_spin_button_set_value (GTK_SPIN_BUTTON(value_px), (upper_bound / 2)); //default - g_signal_connect (G_OBJECT(value_px), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), option); - - adj_percent = GTK_ADJUSTMENT(gtk_adjustment_new (1, 10, 100, 1, 5.0, 0)); - value_percent = gtk_spin_button_new (adj_percent, 1, 0); - gtk_spin_button_set_numeric (GTK_SPIN_BUTTON(value_percent), TRUE); - gtk_spin_button_set_value (GTK_SPIN_BUTTON(value_percent), 100); //default - gtk_entry_set_width_chars (GTK_ENTRY(value_percent), 3); - gtk_widget_set_sensitive (value_percent, FALSE); - - g_signal_connect (G_OBJECT(value_percent), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), option); - - px_butt = gtk_radio_button_new_with_label (NULL, _("Pixels")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(px_butt), TRUE); - - g_signal_connect (G_OBJECT(px_butt), "toggled", - G_CALLBACK(gnc_rd_option_px_set_cb), option); - - p_butt = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(px_butt), _("Percent")); - g_signal_connect (G_OBJECT(p_butt), "toggled", - G_CALLBACK(gnc_rd_option_p_set_cb), option); - - gtk_box_pack_start (GTK_BOX(hbox), px_butt, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(hbox), value_px, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(hbox), p_butt, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(hbox), value_percent, FALSE, FALSE, 0); - - gnc_option_set_widget (option, hbox); - gnc_option_set_ui_value (option, FALSE); - - gtk_widget_show_all (*enclosing); - return hbox; -} - -static GtkWidget * -gnc_option_set_ui_widget_budget (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - - value = gnc_option_create_budget_widget (option); - - gnc_option_set_widget (option, value); - gnc_option_set_ui_value (option, FALSE); - - /* Maybe connect destroy handler for tree model here? */ - g_signal_connect (G_OBJECT(value), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), option); - - gtk_box_pack_start (GTK_BOX(*enclosing), value, FALSE, FALSE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -static GtkWidget * -gnc_option_set_ui_widget_currency_accounting (GNCOption *option, - GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, - gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX(*enclosing), FALSE); - - value = gnc_option_create_currency_accounting_widget (NULL, option); - gnc_option_set_widget (option, value); - - gnc_option_set_ui_value (option, FALSE); - gtk_box_pack_start (GTK_BOX(*enclosing), value, TRUE, TRUE, 0); - gtk_widget_show_all (*enclosing); - return value; -} - -/************************* - * SET VALUE * - ************************* - * - * gnc_option_set_ui_value_(): - * - * In this function you should set the state of the gui widget to - * correspond to the value provided in 'value'. You should return - * TRUE if there was an error, FALSE otherwise. - * - * - */ -static gboolean -gnc_option_set_ui_value_boolean (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - if (scm_is_bool (value)) - { - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget), - scm_is_true(value)); - return FALSE; - } - else - return TRUE; -} - -static gboolean -gnc_option_set_ui_value_string (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - if (scm_is_string (value)) - { - const gchar *string; - - string = gnc_scm_to_utf8_string (value); - gtk_entry_set_text (GTK_ENTRY(widget), string); - g_free ((gpointer *) string); - return FALSE; - } - else - return TRUE; -} - -static gboolean -gnc_option_set_ui_value_text (GNCOption *option, gboolean use_default, - GObject *object, SCM value) -{ - GtkTextBuffer *buffer; - - if (GTK_IS_TEXT_BUFFER(object)) - buffer = GTK_TEXT_BUFFER(object); - else - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(object)); - - if (scm_is_string (value)) - { - const gchar *string; - - string = gnc_scm_to_utf8_string (value); - gtk_text_buffer_set_text (buffer, string, -1); - free ((void*) string); - return FALSE; - } - else - return TRUE; -} - -static gboolean -gnc_option_set_ui_value_currency (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - gnc_commodity *commodity; - - commodity = gnc_scm_to_commodity (value); - if (commodity) - { - gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(widget), commodity); - return FALSE; - } - else - return TRUE; -} - -static gboolean -gnc_option_set_ui_value_commodity (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - gnc_commodity *commodity; - - commodity = gnc_scm_to_commodity (value); - if (commodity) - { - gnc_general_select_set_selected (GNC_GENERAL_SELECT(widget), commodity); - return FALSE; - } - else - return TRUE; -} - -static gboolean -gnc_option_set_ui_value_multichoice (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - int index; - - index = gnc_option_permissible_value_index (option, value); - if (index < 0) - return TRUE; - else - { - gtk_combo_box_set_active (GTK_COMBO_BOX(widget), index); - return FALSE; - } -} - -static gboolean -gnc_option_set_ui_value_date (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - int index; - char *date_option_type; - char *symbol_str; - gboolean bad_value = FALSE; - - date_option_type = gnc_option_date_option_get_subtype (option); - - if (scm_is_pair (value)) - { - symbol_str = gnc_date_option_value_get_type (value); - if (symbol_str) - { - if (g_strcmp0 (symbol_str, "relative") == 0) - { - SCM relative = gnc_date_option_value_get_relative (value); - - index = gnc_option_permissible_value_index (option, relative); - if (g_strcmp0 (date_option_type, "relative") == 0) - { - gtk_combo_box_set_active (GTK_COMBO_BOX(widget), index); - } - else if (g_strcmp0 (date_option_type, "both") == 0) - { - GList *widget_list; - GtkWidget *rel_date_widget; - - widget_list = gtk_container_get_children (GTK_CONTAINER(widget)); - rel_date_widget = g_list_nth_data (widget_list, - GNC_RD_WID_REL_WIDGET_POS); - g_list_free (widget_list); - gnc_date_option_set_select_method (option, FALSE, TRUE); - gtk_combo_box_set_active (GTK_COMBO_BOX(rel_date_widget), index); - } - else - bad_value = TRUE; - } - else if (g_strcmp0 (symbol_str, "absolute") == 0) - { - time64 time; - - time = gnc_date_option_value_get_absolute (value); - - if (g_strcmp0 (date_option_type, "absolute") == 0) - { - gnc_date_edit_set_time (GNC_DATE_EDIT(widget), time); - } - else if (g_strcmp0 (date_option_type, "both") == 0) - { - GList *widget_list; - GtkWidget *ab_widget; - - widget_list = gtk_container_get_children (GTK_CONTAINER(widget)); - ab_widget = g_list_nth_data (widget_list, - GNC_RD_WID_AB_WIDGET_POS); - g_list_free (widget_list); - gnc_date_option_set_select_method (option, TRUE, TRUE); - gnc_date_edit_set_time (GNC_DATE_EDIT(ab_widget), time); - } - else - bad_value = TRUE; - } - else - bad_value = TRUE; - - if (symbol_str) - free (symbol_str); - } - } - else - bad_value = TRUE; - - if (date_option_type) - free (date_option_type); - - return bad_value; -} - -static gboolean -gnc_option_set_ui_value_account_list (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GList *list; - - list = gnc_scm_list_to_glist (value); - - gnc_tree_view_account_set_selected_accounts (GNC_TREE_VIEW_ACCOUNT(widget), - list, TRUE); - g_list_free (list); - return FALSE; -} - -static gboolean -gnc_option_set_ui_value_account_sel (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - Account *acc = NULL; - - if (value != SCM_BOOL_F) - { - if (!SWIG_IsPointer (value)) - scm_misc_error ("gnc_option_set_ui_value_account_sel", - "Option Value not a wcp.", value); - - acc = SWIG_MustGetPtr (value, SWIG_TypeQuery ("_p_Account"), 4, 0); - } - - //doesn't default because this function is called to set a specific account - gnc_account_sel_set_account (GNC_ACCOUNT_SEL(widget), acc, FALSE); - - return FALSE; -} - -static gboolean -gnc_option_set_ui_value_list (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GtkTreeSelection *selection; - GtkTreePath *path; - gint row; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(widget)); - gtk_tree_selection_unselect_all (selection); - - while (scm_is_list (value) && !scm_is_null (value)) - { - SCM item; - - item = SCM_CAR(value); - value = SCM_CDR(value); - - row = gnc_option_permissible_value_index (option, item); - if (row < 0) - { - return TRUE; - } - - path = gtk_tree_path_new_from_indices (row, -1); - gtk_tree_selection_select_path (selection, path); - gtk_tree_path_free (path); - } - - if (!scm_is_list (value) || !scm_is_null (value)) - return TRUE; - - return FALSE; -} - -static gboolean -gnc_option_set_ui_value_number_range (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GtkSpinButton *spinner; - gdouble d_value; - - spinner = GTK_SPIN_BUTTON(widget); - - if (scm_is_number (value)) - { - d_value = scm_to_double (value); - gtk_spin_button_set_value (spinner, d_value); - return FALSE; - } - else - return TRUE; -} - -static gboolean -gnc_option_set_ui_value_color (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - - GdkRGBA color; - if (gnc_option_get_color_info (option, use_default, - &color.red, &color.green, - &color.blue, &color.alpha)) - { - GtkColorChooser *color_button; - - DEBUG("red %f, green %f, blue %f, alpha %f", - color.red, color.green, color.blue, color.alpha); - color_button = GTK_COLOR_CHOOSER(widget); - - gtk_color_chooser_set_rgba (color_button, &color); - return FALSE; - } - - LEAVE("TRUE"); - return TRUE; -} - -static gboolean -gnc_option_set_ui_value_font (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - if (scm_is_string (value)) - { - const gchar *string; - - string = gnc_scm_to_utf8_string (value); - if ((string != NULL) && (*string != '\0')) - { - GtkFontButton *font_button = GTK_FONT_BUTTON(widget); - gtk_font_button_set_font_name (font_button, string); - } - g_free ((gpointer *) string); - return FALSE; - } - else - return TRUE; -} - -static gboolean -gnc_option_set_ui_value_pixmap (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - ENTER("option %p(%s)", option, gnc_option_name (option)); - if (scm_is_string (value)) - { - const gchar *string; - - string = gnc_scm_to_locale_string (value); - if (string && *string) - { - gchar *test; - DEBUG("string = %s", string); - gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(widget), string); - test = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(widget)); - g_object_set_data_full (G_OBJECT(widget), LAST_SELECTION, - g_strdup (string), g_free); - DEBUG("Set %s, retrieved %s", string, test ? test : "(null)"); - gnc_image_option_update_preview_cb (GTK_FILE_CHOOSER(widget), option); - } - LEAVE("FALSE"); - g_free ((gpointer *) string); - return FALSE; - } - - LEAVE("TRUE"); - return TRUE; -} - -static gboolean gnc_option_set_ui_value_budget (GNCOption *option, - gboolean use_default, - GtkWidget *widget, - SCM value) -{ - GncBudget *bgt; - -// if (!scm_is_null(value)) { - if (value != SCM_BOOL_F) - { - if (!SWIG_IsPointer (value)) - scm_misc_error ("gnc_option_set_ui_value_budget", - "Option Value not a wcp.", value); - - bgt = SWIG_MustGetPtr (value, SWIG_TypeQuery ("GncBudget *"), 4, 0); - if (bgt) - { - GtkComboBox *cb = GTK_COMBO_BOX(widget); - GtkTreeModel *tm = gtk_combo_box_get_model (cb); - GtkTreeIter iter; - if (gnc_tree_model_budget_get_iter_for_budget (tm, &iter, bgt)) - gtk_combo_box_set_active_iter (cb, &iter); - } - } - //FIXME: Unimplemented. - return FALSE; -} - -static gboolean -gnc_option_set_ui_value_radiobutton (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - int index; - - index = gnc_option_permissible_value_index (option, value); - if (index < 0) - return TRUE; - else - { - GtkWidget *box, *button; - GList *list; - int i; - gpointer val; - - list = gtk_container_get_children (GTK_CONTAINER(widget)); - box = list->data; - g_list_free(list); - - list = gtk_container_get_children (GTK_CONTAINER(box)); - button = g_list_nth_data (list, index); - g_list_free (list); - g_return_val_if_fail (button, TRUE); - val = g_object_get_data (G_OBJECT(button), "gnc_radiobutton_index"); - g_return_val_if_fail (GPOINTER_TO_INT(val) == index, TRUE); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE); - // g_object_set_data(G_OBJECT(widget), "gnc_radiobutton_index", - // GINT_TO_POINTER(index)); - return FALSE; - } -} - -static gboolean -gnc_option_set_ui_value_dateformat (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GNCDateFormat * gdf = GNC_DATE_FORMAT(widget); - QofDateFormat format; - GNCDateMonthFormat months; - gboolean years; - char *custom; - - if (gnc_dateformat_option_value_parse (value, &format, &months, &years, &custom)) - return TRUE; - - gnc_date_format_set_format (gdf, format); - gnc_date_format_set_months (gdf, months); - gnc_date_format_set_years (gdf, years); - gnc_date_format_set_custom (gdf, custom); - gnc_date_format_refresh (gdf); - - if (custom) - free (custom); - - return FALSE; -} - -static gboolean -gnc_option_set_ui_value_plot_size (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GList* widget_list; - GtkWidget *px_button, *p_button, *px_widget, *p_widget; - char *symbol_str; - gdouble d_value; - - widget_list = gtk_container_get_children (GTK_CONTAINER(widget)); - px_button = g_list_nth_data (widget_list, 0); - px_widget = g_list_nth_data (widget_list, 1); - p_button = g_list_nth_data (widget_list, 2); - p_widget = g_list_nth_data (widget_list, 3); - g_list_free (widget_list); - - if (scm_is_pair (value)) - { - symbol_str = gnc_plot_size_option_value_get_type (value); - d_value = gnc_plot_size_option_value_get_value (value); - - if (symbol_str) - { - if (g_strcmp0 (symbol_str, "pixels") == 0) // pixel values - { - gtk_spin_button_set_value (GTK_SPIN_BUTTON(px_widget), d_value); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(px_button), TRUE); - } - else // percent values - { - gtk_spin_button_set_value (GTK_SPIN_BUTTON(p_widget), (d_value)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(p_button), TRUE); - } - return FALSE; - } - } - return TRUE; -} - -static gboolean -gnc_option_set_ui_value_currency_accounting (GNCOption *option, - gboolean use_default, - GtkWidget *widget, SCM value) -{ - if (scm_is_pair (value)) - { - SCM rb_symbol; - - rb_symbol = gnc_currency_accounting_option_value_get_method (value); - - if (rb_symbol) - { - int index; - - index = gnc_option_permissible_value_index (option, rb_symbol); - if (index < 0) - return TRUE; - else - { - GtkWidget *button = NULL; - gpointer val; - - switch (index) - { - case 0: - button = book_currency_data->gnc_currency_radiobutton_0; - break; - case 1: - button = book_currency_data->gnc_currency_radiobutton_1; - break; - case 2: - button = book_currency_data->gnc_currency_radiobutton_2; - break; - default: - return TRUE; - } - - val = g_object_get_data (G_OBJECT(button), - "gnc_radiobutton_index"); - g_return_val_if_fail (GPOINTER_TO_INT(val) == index, TRUE); - - if (g_strcmp0 (gnc_option_permissible_value_name (option, - index), - "Use a Book Currency") == 0) - { - gnc_commodity *commodity = NULL; - SCM curr_scm = - gnc_currency_accounting_option_value_get_book_currency - (value); - SCM list_symbol = - gnc_currency_accounting_option_value_get_default_policy - (value); - SCM acct_guid_scm = - gnc_currency_accounting_option_value_get_default_account - (value); - - commodity = gnc_scm_to_commodity (curr_scm); - if (commodity) - { - book_currency_data->retrieved_book_currency = commodity; - } - else - { - book_currency_data->retrieved_book_currency = NULL; - } - if (list_symbol) - { - book_currency_data->retrieved_policy_scm = list_symbol; - } - else - { - book_currency_data->retrieved_policy_scm = NULL; - } - if (acct_guid_scm) - { - book_currency_data->retrieved_gain_loss_acct_guid_scm = - acct_guid_scm; - } - else - { - book_currency_data->retrieved_gain_loss_acct_guid_scm = - NULL; - } - } - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), TRUE); - /* when an unselected button in a group is clicked the clicked - button receives the “toggled” signal, as does the - previously selected button; however, if the first button - is active when the currency-accounting dialog is created, - that is, it's read from the option, the "toggled" handler - is not called while it is if any other button is active. - To get desired result, that is, to set sensitivity to - FALSE, explicitly call the handler here if first button */ - if (index == 0) - { - gnc_option_currency_accounting_non_book_cb (button, - (gpointer) book_currency_data); - } - return FALSE; - } - } - return TRUE; - } - return TRUE; -} - -/************************* - * GET VALUE * - ************************* - * - * gnc_option_get_ui_value_(): - * - * 'widget' will be the widget returned from the - * gnc_option_set_ui_widget_() function. - * - * You should return a SCM value corresponding to the current state of the - * gui widget. - * - */ -static SCM -gnc_option_get_ui_value_boolean (GNCOption *option, GtkWidget *widget) -{ - gboolean active; - - active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)); - return SCM_BOOL(active); -} - -static SCM -gnc_option_get_ui_value_string (GNCOption *option, GtkWidget *widget) -{ - char * string; - SCM result; - - string = gtk_editable_get_chars (GTK_EDITABLE(widget), 0, -1); - result = scm_from_utf8_string (string ? string : ""); - g_free (string); - return result; -} - -static SCM -gnc_option_get_ui_value_text (GNCOption *option, GtkWidget *widget) -{ - char * string; - SCM result; - - string = xxxgtk_textview_get_text (GTK_TEXT_VIEW(widget)); - result = scm_from_utf8_string (string ? string : ""); - g_free (string); - return result; -} - -static SCM -gnc_option_get_ui_value_currency (GNCOption *option, GtkWidget *widget) -{ - gnc_commodity *commodity; - - commodity = - gnc_currency_edit_get_currency (GNC_CURRENCY_EDIT(widget)); - - return (gnc_commodity_to_scm (commodity)); -} - -static SCM -gnc_option_get_ui_value_commodity (GNCOption *option, GtkWidget *widget) -{ - gnc_commodity *commodity; - - commodity = - gnc_general_select_get_selected (GNC_GENERAL_SELECT(widget)); - - return (gnc_commodity_to_scm (commodity)); -} - -static SCM -gnc_option_get_ui_value_multichoice (GNCOption *option, GtkWidget *widget) -{ - int index = gtk_combo_box_get_active (GTK_COMBO_BOX(widget)); - return (gnc_option_permissible_value (option, index)); -} - -static SCM -gnc_option_get_ui_value_date (GNCOption *option, GtkWidget *widget) -{ - int index; - SCM type, val, result = SCM_UNDEFINED; - char *subtype = gnc_option_date_option_get_subtype (option); - - if (g_strcmp0 (subtype, "relative") == 0) - { - index = gtk_combo_box_get_active (GTK_COMBO_BOX(widget)); - - type = scm_from_locale_symbol ("relative"); - val = gnc_option_permissible_value (option, index); - result = scm_cons (type, val); - } - else if (g_strcmp0 (subtype, "absolute") == 0) - { - time64 time; - time = gnc_date_edit_get_date (GNC_DATE_EDIT(widget)); - result = scm_cons (scm_from_locale_symbol ("absolute"), scm_from_int64 (time)); - } - else if (g_strcmp0 (subtype, "both") == 0) - { - time64 time; - int index; - SCM val; - GList *widget_list; - GtkWidget *ab_button, *rel_widget, *ab_widget; - - widget_list = gtk_container_get_children (GTK_CONTAINER(widget)); - ab_button = g_list_nth_data (widget_list, GNC_RD_WID_AB_BUTTON_POS); - ab_widget = g_list_nth_data (widget_list, GNC_RD_WID_AB_WIDGET_POS); - rel_widget = g_list_nth_data (widget_list, GNC_RD_WID_REL_WIDGET_POS); - g_list_free (widget_list); - - /* if it's an absolute date */ - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(ab_button))) - { - time = gnc_date_edit_get_date (GNC_DATE_EDIT(ab_widget)); - result = scm_cons (scm_from_locale_symbol ("absolute"), scm_from_int64 (time)); - } - else - { - index = gtk_combo_box_get_active (GTK_COMBO_BOX(rel_widget)); - - val = gnc_option_permissible_value (option, index); - result = scm_cons (scm_from_locale_symbol ("relative"), val); - } - } - g_free (subtype); - return result; -} - -static SCM -gnc_option_get_ui_value_account_list (GNCOption *option, GtkWidget *widget) -{ - GncTreeViewAccount *tree; - GList *list; - SCM result; - - tree = GNC_TREE_VIEW_ACCOUNT(widget); - list = gnc_tree_view_account_get_selected_accounts (tree); - - /* handover list */ - result = gnc_glist_to_scm_list (list, "_p_Account"); - g_list_free (list); - return result; -} - -static SCM -gnc_option_get_ui_value_account_sel (GNCOption *option, GtkWidget *widget) -{ - GNCAccountSel *gas; - Account* acc; - - gas = GNC_ACCOUNT_SEL(widget); - acc = gnc_account_sel_get_account (gas); - - if (!acc) - return SCM_BOOL_F; - - return SWIG_NewPointerObj (acc, SWIG_TypeQuery ("_p_Account"), 0); -} - -static SCM -gnc_option_get_ui_value_budget (GNCOption *option, GtkWidget *widget) -{ - GncBudget *bgt; - GtkComboBox *cb; - GtkTreeModel *tm; - GtkTreeIter iter; - - cb = GTK_COMBO_BOX(widget); - gtk_combo_box_get_active_iter (cb, &iter); - tm = gtk_combo_box_get_model (cb); - bgt = gnc_tree_model_budget_get_budget (tm, &iter); - - if (!bgt) - return SCM_BOOL_F; - - return SWIG_NewPointerObj (bgt, SWIG_TypeQuery ("_p_budget_s"), 0); -} - -static SCM -gnc_option_get_ui_value_list (GNCOption *option, GtkWidget *widget) -{ - GtkTreeSelection *selection; - GtkTreePath *path; - SCM result; - gboolean selected; - gint num_rows; - gint row; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(widget)); - num_rows = gnc_option_num_permissible_values (option); - result = scm_c_eval_string ("'()"); - - for (row = 0; row < num_rows; row++) - { - path = gtk_tree_path_new_from_indices (row, -1); - selected = gtk_tree_selection_path_is_selected (selection, path); - gtk_tree_path_free (path); - if (selected) - result = scm_cons (gnc_option_permissible_value (option, row), result); - } - return (scm_reverse (result)); -} - -static SCM -gnc_option_get_ui_value_number_range (GNCOption *option, GtkWidget *widget) -{ - GtkSpinButton *spinner; - gdouble value; - - spinner = GTK_SPIN_BUTTON(widget); - - value = gtk_spin_button_get_value (spinner); - - return (scm_from_double (value)); -} - -static SCM -gnc_option_get_ui_value_color (GNCOption *option, GtkWidget *widget) -{ - SCM result; - GtkColorChooser *color_button; - GdkRGBA color; - gdouble scale; - - ENTER("option %p(%s), widget %p", - option, gnc_option_name (option), widget); - - color_button = GTK_COLOR_CHOOSER(widget); - gtk_color_chooser_get_rgba (color_button, &color); - - scale = gnc_option_color_range (option); - - result = SCM_EOL; - result = scm_cons (scm_from_double (color.alpha * scale), result); - result = scm_cons (scm_from_double (color.blue * scale), result); - result = scm_cons (scm_from_double (color.green * scale), result); - result = scm_cons (scm_from_double (color.red * scale), result); - return result; -} - -static SCM -gnc_option_get_ui_value_font (GNCOption *option, GtkWidget *widget) -{ - GtkFontButton *font_button = GTK_FONT_BUTTON(widget); - const gchar * string; - - string = gtk_font_button_get_font_name (font_button); - return (string ? scm_from_utf8_string (string) : SCM_BOOL_F); -} - -static SCM -gnc_option_get_ui_value_pixmap (GNCOption *option, GtkWidget *widget) -{ - gchar *string; - SCM result; - - string = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(widget)); - DEBUG("filename %s", string ? string : "(null)"); - result = scm_from_utf8_string (string ? string : ""); - g_free (string); - return result; -} - -static SCM -gnc_option_get_ui_value_radiobutton (GNCOption *option, GtkWidget *widget) -{ - gpointer _index; - int index; - - _index = g_object_get_data (G_OBJECT(widget), "gnc_radiobutton_index"); - index = GPOINTER_TO_INT(_index); - - return (gnc_option_permissible_value (option, index)); -} - -static SCM -gnc_option_get_ui_value_dateformat (GNCOption *option, GtkWidget *widget) -{ - GNCDateFormat *gdf = GNC_DATE_FORMAT(widget); - QofDateFormat format; - GNCDateMonthFormat months; - gboolean years; - const char* custom; - - format = gnc_date_format_get_format (gdf); - months = gnc_date_format_get_months (gdf); - years = gnc_date_format_get_years (gdf); - custom = gnc_date_format_get_custom (gdf); - - return (gnc_dateformat_option_set_value (format, months, years, custom)); -} - -static SCM -gnc_option_get_ui_value_plot_size (GNCOption *option, GtkWidget *widget) -{ - GList* widget_list; - GtkWidget *px_button, *px_widget, *p_widget; - gdouble d_value; - SCM type, val; - - widget_list = gtk_container_get_children (GTK_CONTAINER(widget)); - px_button = g_list_nth_data (widget_list, 0); - px_widget = g_list_nth_data (widget_list, 1); - // p_button item 2 - p_widget = g_list_nth_data (widget_list, 3); - g_list_free (widget_list); - - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(px_button))) - { - type = scm_from_locale_symbol ("pixels"); - d_value = gtk_spin_button_get_value (GTK_SPIN_BUTTON(px_widget)); - } - else - { - type = scm_from_locale_symbol ("percent"); - d_value = gtk_spin_button_get_value (GTK_SPIN_BUTTON(p_widget)); - } - val = scm_from_double (d_value); - return scm_cons (type, val); -} - -static SCM -gnc_option_get_ui_value_currency_accounting (GNCOption *option, - GtkWidget *widget) -{ - gpointer _index; - int index; - SCM value = SCM_EOL; - - _index = g_object_get_data (G_OBJECT(widget), "gnc_radiobutton_index"); - index = GPOINTER_TO_INT(_index); - - /* build the return list in reverse order */ - if (g_strcmp0 (gnc_option_permissible_value_name (option, index), - "Use a Book Currency") == 0) - { - gnc_commodity *commodity = NULL; - int policy_index; - SCM val; - GList *list_of_policies = NULL; - const char *str = NULL; - - if (book_currency_data->default_gain_loss_account_widget) - { - /* get account from widget, if one selected */ - Account *gain_loss_account = NULL; - - gain_loss_account = - gnc_tree_view_account_get_selected_account - (GNC_TREE_VIEW_ACCOUNT( - book_currency_data->default_gain_loss_account_widget)); - - if (gain_loss_account == NULL) - { - val = SCM_BOOL_F; - } - else - { - gchar *gain_loss_account_guid = guid_to_string ( - xaccAccountGetGUID (gain_loss_account)); - - val = scm_from_utf8_string (gain_loss_account_guid); - g_free (gain_loss_account_guid); - } - } - else - { - val = SCM_BOOL_F; - } - value = scm_cons (val, value); - - list_of_policies = gnc_get_valid_policy_list (); - if (list_of_policies && book_currency_data->default_cost_policy_widget) - { - GList *l = NULL; - gint i = 0; - - policy_index = - gtk_combo_box_get_active (GTK_COMBO_BOX( - book_currency_data->default_cost_policy_widget)); - for (l = list_of_policies; l != NULL; l = l->next) - { - GNCPolicy *pcy = l->data; - if(i == policy_index) - str = PolicyGetName (pcy); - i++; - } - g_list_free (list_of_policies); - } - if (str) - { - val = scm_from_locale_symbol (str); - } - else - { - val = SCM_BOOL_F; - } - value = scm_cons (val, value); - - if (gtk_combo_box_get_active (GTK_COMBO_BOX(book_currency_data->book_currency_widget)) != -1) - { - commodity = - gnc_currency_edit_get_currency ( - GNC_CURRENCY_EDIT( - book_currency_data->book_currency_widget)); - if (commodity) - { - val = gnc_commodity_to_scm (commodity); - } - else - { - val = SCM_BOOL_F; - } - } - else - { - val = SCM_BOOL_F; - } - value = scm_cons (val, value); - } - - return (scm_cons (gnc_option_permissible_value (option, index), value)); -} - -/************************************/ -/* INITIALIZATION */ -/************************************/ -static void gnc_options_initialize_options (void) -{ - static GNCOptionDef_t options[] = - { - { - "boolean", gnc_option_set_ui_widget_boolean, - gnc_option_set_ui_value_boolean, gnc_option_get_ui_value_boolean - }, - { - "string", gnc_option_set_ui_widget_string, - gnc_option_set_ui_value_string, gnc_option_get_ui_value_string - }, - { - "text", gnc_option_set_ui_widget_text, - (GNCOptionUISetValue)gnc_option_set_ui_value_text, - gnc_option_get_ui_value_text - }, - { - "currency", gnc_option_set_ui_widget_currency, - gnc_option_set_ui_value_currency, gnc_option_get_ui_value_currency - }, - { - "commodity", gnc_option_set_ui_widget_commodity, - gnc_option_set_ui_value_commodity, gnc_option_get_ui_value_commodity - }, - { - "multichoice", gnc_option_set_ui_widget_multichoice, - gnc_option_set_ui_value_multichoice, gnc_option_get_ui_value_multichoice - }, - { - "date", gnc_option_set_ui_widget_date, - gnc_option_set_ui_value_date, gnc_option_get_ui_value_date - }, - { - "account-list", gnc_option_set_ui_widget_account_list, - gnc_option_set_ui_value_account_list, gnc_option_get_ui_value_account_list - }, - { - "account-sel", gnc_option_set_ui_widget_account_sel, - gnc_option_set_ui_value_account_sel, gnc_option_get_ui_value_account_sel - }, - { - "list", gnc_option_set_ui_widget_list, - gnc_option_set_ui_value_list, gnc_option_get_ui_value_list - }, - { - "number-range", gnc_option_set_ui_widget_number_range, - gnc_option_set_ui_value_number_range, gnc_option_get_ui_value_number_range - }, - { - "color", gnc_option_set_ui_widget_color, - gnc_option_set_ui_value_color, gnc_option_get_ui_value_color - }, - { - "font", gnc_option_set_ui_widget_font, - gnc_option_set_ui_value_font, gnc_option_get_ui_value_font - }, - { - "pixmap", gnc_option_set_ui_widget_pixmap, - gnc_option_set_ui_value_pixmap, gnc_option_get_ui_value_pixmap - }, - { - "radiobutton", gnc_option_set_ui_widget_radiobutton, - gnc_option_set_ui_value_radiobutton, gnc_option_get_ui_value_radiobutton - }, - { - "dateformat", gnc_option_set_ui_widget_dateformat, - gnc_option_set_ui_value_dateformat, gnc_option_get_ui_value_dateformat - }, - { - "plot-size", gnc_option_set_ui_widget_plot_size, - gnc_option_set_ui_value_plot_size, gnc_option_get_ui_value_plot_size - }, - { - "budget", gnc_option_set_ui_widget_budget, - gnc_option_set_ui_value_budget, gnc_option_get_ui_value_budget - }, - { - "currency-accounting", - gnc_option_set_ui_widget_currency_accounting, - gnc_option_set_ui_value_currency_accounting, - gnc_option_get_ui_value_currency_accounting - }, - { NULL, NULL, NULL, NULL } - }; - int i; - - for (i = 0; options[i].option_name; i++) - gnc_options_ui_register_option (&(options[i])); -} - -/* Register a new option type in the UI */ -void gnc_options_ui_register_option (GNCOptionDef_t *option) -{ - g_return_if_fail (optionTable); - g_return_if_fail (option); - - /* FIXME: should protect against repeat insertion. */ - g_hash_table_insert (optionTable, (gpointer)(option->option_name), option); -} - -GNCOptionDef_t * gnc_options_ui_get_option (const char *option_name) -{ - GNCOptionDef_t *retval; - g_return_val_if_fail (optionTable, NULL); - g_return_val_if_fail (option_name, NULL); - - retval = g_hash_table_lookup (optionTable, option_name); - if (!retval) - { - PERR("Option lookup for type '%s' failed!", option_name); - } - return retval; -} - -void gnc_options_ui_initialize (void) -{ - SWIG_GetModule (NULL); /* Work-around for SWIG bug. */ - // gnc_options_register_stocks (); - g_return_if_fail (optionTable == NULL); - optionTable = g_hash_table_new (g_str_hash, g_str_equal); - - /* add known types */ - gnc_options_initialize_options (); -} - -struct scm_cb -{ - SCM apply_cb; - SCM close_cb; -}; - -static void -scm_apply_cb (GNCOptionWin *win, gpointer data) -{ - struct scm_cb *cbdata = data; - - if (gnc_option_db_get_changed (win->option_db)) - { - GList *results = NULL, *iter; - results = gnc_option_db_commit (win->option_db); - for (iter = results; iter; iter = iter->next) - { - GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(gnc_options_dialog_widget (win)), - 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - "%s", - (char*)iter->data); - gtk_dialog_run (GTK_DIALOG(dialog)); - gtk_widget_destroy (dialog); - g_free (iter->data); - } - g_list_free (results); - - if (cbdata->apply_cb != SCM_BOOL_F) - { - scm_call_0 (cbdata->apply_cb); - } - } -} - -static void -scm_close_cb (GNCOptionWin *win, gpointer data) -{ - struct scm_cb *cbdata = data; - - if (cbdata->close_cb != SCM_BOOL_F) - { - scm_call_0 (cbdata->close_cb); - scm_gc_unprotect_object (cbdata->close_cb); - } - - if (cbdata->apply_cb != SCM_BOOL_F) - scm_gc_unprotect_object (cbdata->apply_cb); - - g_free (cbdata); -} - -/* Both apply_cb and close_cb should be scheme functions with 0 arguments. - * References to these functions will be held until the close_cb is called - */ -void -gnc_options_dialog_set_scm_callbacks (GNCOptionWin *win, SCM apply_cb, - SCM close_cb) -{ - struct scm_cb *cbdata; - - cbdata = g_new0 (struct scm_cb, 1); - cbdata->apply_cb = apply_cb; - cbdata->close_cb = close_cb; - - if (apply_cb != SCM_BOOL_F) - scm_gc_protect_object (cbdata->apply_cb); - - if (close_cb != SCM_BOOL_F) - scm_gc_protect_object (cbdata->close_cb); - - gnc_options_dialog_set_apply_cb (win, scm_apply_cb, cbdata); - gnc_options_dialog_set_close_cb (win, scm_close_cb, cbdata); -} diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp new file mode 100644 index 0000000000..71ee5f3df5 --- /dev/null +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -0,0 +1,2789 @@ +/********************************************************************\ + * dialog-options.cpp -- option handling * + * Copyright (C) 1998-2000 Linas Vepstas * + * Copyright (c) 2006 David Hampton * + * Copyright (c) 2011 Robert Fewell * + * Copyright 2020 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +extern "C" +{ +#include // Need this to include Account.h +} + +#include // To include as C++ overriding later indirect includes +#include +#include +#include +#include + +extern "C" +{ +#include "swig-runtime.h" + +#include "gnc-tree-model-budget.h" //FIXME? +#include "gnc-budget.h" +#include + +#include "dialog-utils.h" +#include "gnc-engine-guile.h" +#include "glib-guile.h" +#include "gnc-account-sel.h" +#include "gnc-tree-view-account.h" +#include "gnc-commodity-edit.h" +#include "gnc-component-manager.h" +#include "gnc-general-select.h" +#include "gnc-currency-edit.h" +#include "gnc-date-edit.h" +#include "gnc-engine.h" +#include "gnc-prefs.h" +#include "gnc-gui-query.h" +#include "gnc-session.h" +#include "gnc-ui.h" +#include "gnc-guile-utils.h" +#include "guile-mappings.h" +#include "gnc-date-format.h" +#include "misc-gnome-utils.h" +} + +#include +#include + +#include "dialog-options.hpp" +#include +#include + +#define GNC_PREF_CLOCK_24H "clock-24h" + + +#include +//#include +#include +#include +#include + +#define FUNC_NAME G_STRFUNC +/* TODO: clean up "register-stocks" junk + */ + + +/* This static indicates the debugging module that this .o belongs to. */ +static QofLogModule log_module = GNC_MOD_GUI; + +static constexpr const char* DIALOG_OPTIONS_CM_CLASS{"dialog-options"}; +static constexpr const char* GNC_PREFS_GROUP{"dialogs.options"}; + +/* + * Point where preferences switch control method from a set of + * notebook tabs to a list. + */ +#define MAX_TAB_COUNT 6 + +/* A pointer to the last selected filename */ +#define LAST_SELECTION "last-selection" + + +enum page_tree +{ + PAGE_INDEX = 0, + PAGE_NAME, + NUM_COLUMNS +}; + +//Init the class static. +std::vector GncOptionUIFactory::s_registry{static_cast(GncOptionUIType::MAX_VALUE)}; +bool GncOptionUIFactory::s_initialized{false}; +static void gnc_options_ui_factory_initialize (void); + +void +GncOptionUIFactory::set_func(GncOptionUIType type, WidgetCreateFunc func) +{ + s_registry[static_cast(type)] = func; +} + +GtkWidget* +GncOptionUIFactory::create(GncOption& option, GtkGrid* page, GtkLabel* name, + char* description, GtkWidget** enclosing, bool* packed) +{ + if (!s_initialized) + { + gnc_options_ui_factory_initialize(); + s_initialized = true; + } + auto type{option.get_ui_type()}; + auto func{s_registry[static_cast(type)]}; + if (func) + return func(option, page, name, description, enclosing, packed); + PERR("No function registered for type %d", static_cast(type)); + return nullptr; +} + +GncOptionGtkUIItem::GncOptionGtkUIItem(GtkWidget* widget, + GncOptionUIType type) : + GncOptionUIItem{type}, + m_widget{static_cast(g_object_ref(widget))} {} + +GncOptionGtkUIItem::GncOptionGtkUIItem(const GncOptionGtkUIItem& item) : + GncOptionUIItem{item.get_ui_type()}, + m_widget{static_cast(g_object_ref(item.get_widget()))} {} + +GncOptionGtkUIItem::~GncOptionGtkUIItem() +{ + if (m_widget) + g_object_unref(m_widget); +} + +void +GncOptionGtkUIItem::set_selectable(bool selectable) const noexcept +{ + if (m_widget) + gtk_widget_set_sensitive (m_widget, selectable); +} + +void +GncOptionGtkUIItem::clear_ui_item() +{ + if (m_widget) + g_object_unref(m_widget); + m_widget = nullptr; +} + +void +GncOptionGtkUIItem::set_widget(GtkWidget* widget) +{ + if (get_ui_type() == GncOptionUIType::INTERNAL) + { + std::string error{"INTERNAL option, setting the UI item forbidden."}; + throw std::logic_error(std::move(error)); + } + + if (m_widget) + g_object_unref(m_widget); + + m_widget = static_cast(g_object_ref(widget)); +} + + +static void dialog_reset_cb(GtkWidget * w, gpointer data); +static void dialog_list_select_cb (GtkTreeSelection *selection, gpointer data); +static void component_close_handler (gpointer data); + +static void +section_reset_widgets(GncOptionSection* section) +{ +} + +static inline GtkWidget* const +option_get_gtk_widget (const GncOption* option) +{ + if (!option) return nullptr; + auto ui_item{dynamic_cast(option->get_ui_item())}; + if (ui_item) + return ui_item->get_widget(); + + return nullptr; +} + +static void +dialog_changed_internal (GtkWidget *widget, bool sensitive) +{ + g_return_if_fail(widget); + + auto toplevel{gtk_widget_get_toplevel(widget)}; + if (toplevel == widget && !GTK_IS_WINDOW(toplevel)) + return; + g_assert(toplevel && GTK_IS_WINDOW(toplevel)); + + auto option_win = + static_cast(g_object_get_data(G_OBJECT(toplevel), + "optionwin")); + option_win->set_sensitive(sensitive); +} + +void +GncOptionsDialog::set_sensitive(bool sensitive) noexcept +{ + gtk_widget_set_sensitive (GTK_WIDGET(m_apply_button), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET(m_ok_button), sensitive); + gtk_button_set_label (m_cancel_button, + sensitive ? _("_Cancel") : _("_Close")); +} + +void +GncOptionsDialog::changed() noexcept +{ + set_sensitive(true); +} + +void +gnc_option_changed_widget_cb(GtkWidget *widget, GncOption* option) +{ + if (!option) return; + const_cast(option->get_ui_item())->set_dirty(true); + dialog_changed_internal(widget, true); +} + +void +gnc_option_changed_option_cb(GtkWidget *dummy, GncOption* option) +{ + if (!option) return; + auto widget{option_get_gtk_widget(option)}; + gnc_option_changed_widget_cb(widget, option); +} + + +// This do-nothing template is specialized for each GncOptionUIType. +template GtkWidget* +create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**, + bool*) +{ + return nullptr; +} + +static void +gnc_option_set_ui_widget(GncOption& option, GtkGrid *page_box, gint grid_row) +{ + GtkWidget *enclosing = NULL; + GtkWidget *value = NULL; + bool packed = FALSE; + char *name, *documentation; + GtkLabel *name_label; + + ENTER("option %p(%s), box %p", + &option, option.get_name().c_str(), page_box); + auto type = option.get_ui_type(); + if (type == GncOptionUIType::INTERNAL) + { + LEAVE("internal type"); + return; + } + + + + const char* raw_name = option.get_name().c_str(); + if (raw_name && *raw_name) + name = _(raw_name); + else + name = nullptr; + + const char* raw_documentation = option.get_docstring().c_str(); + if (raw_documentation && *raw_documentation) + documentation = _(raw_documentation); + else + documentation = nullptr; + + name_label = GTK_LABEL(gtk_label_new (name)); + auto widget = GncOptionUIFactory::create(option, page_box, name_label, + documentation, &enclosing, + &packed); + /* Attach the name label to the first column of the grid and + align to the end unless it's a check button, which has its own label. */ + if (!GTK_IS_CHECK_BUTTON(widget)) + { + gtk_grid_attach (GTK_GRID(page_box), GTK_WIDGET(name_label), + 0, grid_row, // left, top + 1, 1); // width, height + gtk_widget_set_halign (GTK_WIDGET(name_label), GTK_ALIGN_END); + } + if (!packed && (enclosing != NULL)) + { + /* Pack option widget into an extra eventbox because otherwise the + "documentation" tooltip is not displayed. */ + GtkWidget *eventbox = gtk_event_box_new(); + + gtk_container_add (GTK_CONTAINER (eventbox), enclosing); + + /* attach the option widget to the second column of the grid */ + gtk_grid_attach (GTK_GRID(page_box), eventbox, + 1, grid_row, // left, top + 1, 1); // width, height + + gtk_widget_set_tooltip_text (eventbox, documentation); + } + + if (value != NULL) + gtk_widget_set_tooltip_text(value, documentation); + + LEAVE(" "); +} + +static GtkBox* +create_content_box() +{ + auto content_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + gtk_widget_set_name (content_box, "page-content-box"); + gtk_box_set_homogeneous (GTK_BOX (content_box), FALSE); + + gtk_container_set_border_width(GTK_CONTAINER(content_box), 12); + return GTK_BOX(content_box); +} + +static GtkGrid* +create_options_box(GtkBox* content_box) +{ + auto options_scrolled_win = gtk_scrolled_window_new(NULL, NULL); + gtk_box_pack_start(GTK_BOX(content_box), options_scrolled_win, + TRUE, TRUE, 0); + + /* Build space for the content - the options box */ + auto options_box = gtk_grid_new(); // this will have two columns + gtk_widget_set_name (options_box, "options-box"); + gtk_grid_set_row_homogeneous (GTK_GRID(options_box), FALSE); + gtk_grid_set_column_homogeneous (GTK_GRID(options_box), FALSE); + gtk_grid_set_row_spacing (GTK_GRID(options_box), 6); + gtk_grid_set_column_spacing (GTK_GRID(options_box), 6); + + gtk_container_set_border_width(GTK_CONTAINER(options_box), 0); + gtk_container_add (GTK_CONTAINER(options_scrolled_win), + GTK_WIDGET(options_box)); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(options_scrolled_win), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + return GTK_GRID(options_box); +} + +static GtkButtonBox* +create_reset_button_box(GtkBox* page_content_box) +{ + auto buttonbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); + gtk_button_box_set_layout (GTK_BUTTON_BOX (buttonbox), + GTK_BUTTONBOX_EDGE); + gtk_container_set_border_width(GTK_CONTAINER (buttonbox), 5); + gtk_box_pack_end(GTK_BOX(page_content_box), buttonbox, FALSE, FALSE, 0); + return GTK_BUTTON_BOX(buttonbox); +} + +static int +setup_notebook_pages(GncOptionsDialog* dlg, GtkBox* page_content_box, + const char* name) +{ + auto notebook{dlg->get_notebook()}; + auto page_count = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), + GTK_WIDGET(page_content_box)); + + if (dlg->get_page_list_view()) + { + /* Build the matching list item for selecting from large page sets */ + auto view = GTK_TREE_VIEW(dlg->get_page_list_view()); + auto list = GTK_LIST_STORE(gtk_tree_view_get_model(view)); + + PINFO("Page name is %s and page_count is %d", name, page_count); + GtkTreeIter iter; + gtk_list_store_append(list, &iter); + gtk_list_store_set(list, &iter, + PAGE_NAME, _(name), + PAGE_INDEX, page_count, + -1); + + if (page_count > MAX_TAB_COUNT - 1) /* Convert 1-based -> 0-based */ + { + gtk_widget_show(dlg->get_page_list()); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE); + gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE); + } + else + gtk_widget_hide(dlg->get_page_list()); + + } + return page_count; +} + +static int +dialog_append_page(GncOptionsDialog* dlg, GncOptionSectionPtr& section) +{ + auto name = section->get_name().c_str(); + if (!name || *name == '\0') + return -1; + + if (strncmp(name, "__", 2) == 0) + return -1; + + auto page_label = gtk_label_new(_(name)); + PINFO("Page_label is %s", _(name)); + gtk_widget_show(page_label); + + /* Build this options page */ + auto page_content_box = create_content_box(); + auto options_box = create_options_box(page_content_box); + + /* Create all the options */ + size_t row = 0; + section->foreach_option( + [options_box, &row](GncOption& option) { + g_object_set_data (G_OBJECT(options_box), "options-grid-row", + GINT_TO_POINTER(row)); + gnc_option_set_ui_widget(option, GTK_GRID(options_box), row); + ++row; + }); + + /* Add a button box at the bottom of the page */ + auto buttonbox = create_reset_button_box(page_content_box); + /* The reset button on each option page */ + auto reset_button = gtk_button_new_with_label (_("Reset defaults")); + gtk_widget_set_tooltip_text(reset_button, + _("Reset all values to their defaults.")); + + g_signal_connect(G_OBJECT(reset_button), "clicked", + G_CALLBACK(dialog_reset_cb), dlg); + g_object_set_data(G_OBJECT(reset_button), "section", + static_cast(section.get())); + gtk_box_pack_end(GTK_BOX(buttonbox), reset_button, FALSE, FALSE, 0); + gtk_widget_show_all(GTK_WIDGET(page_content_box)); + gtk_notebook_append_page(GTK_NOTEBOOK(dlg->get_notebook()), + GTK_WIDGET(page_content_box), page_label); + + /* Switch to selection from a list if the page count threshold is reached */ + return setup_notebook_pages(dlg, page_content_box, name); +} + +/** + * Populate the dialog's notebook with the contents of odb. + * + * @param odb - option database to use * + * @param show_dialog - should dialog be made visible or not * + */ +void +GncOptionsDialog::build_contents(GncOptionDB *odb, bool show_dialog) +{ + gint default_page = -1; + + g_return_if_fail (odb != NULL); + + m_option_db = odb; + + auto num_sections = odb->num_sections(); + auto default_section = odb->get_default_section(); + + PINFO("Default Section name is %s", + default_section ? default_section->get_name().c_str() : "NULL"); + + odb->foreach_section( + [this, default_section, &default_page] + (GncOptionSectionPtr& section) { + auto page = dialog_append_page(this, section); + if (default_section && section.get() == default_section) + default_page = page; + }); + + gtk_notebook_popup_enable(GTK_NOTEBOOK(m_notebook)); + if (default_page >= 0) + { + /* Find the page list and set the selection to the default page */ + auto selection{gtk_tree_view_get_selection(GTK_TREE_VIEW(m_page_list_view))}; + GtkTreeIter iter; + + auto model{gtk_tree_view_get_model(GTK_TREE_VIEW(m_page_list_view))}; + gtk_tree_model_iter_nth_child(model, &iter, NULL, default_page); + gtk_tree_selection_select_iter (selection, &iter); + gtk_notebook_set_current_page(GTK_NOTEBOOK(m_notebook), default_page); + } + dialog_changed_internal(m_window, FALSE); + if (show_dialog) + gtk_widget_show(m_window); +} + +void GncOptionsDialog::call_apply_cb() noexcept +{ + auto close_cb = m_close_cb; + + m_close_cb = nullptr; + if (m_apply_cb) + (m_apply_cb)(this, m_apply_cb_data); + m_close_cb = close_cb; + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(m_window)); + set_sensitive(false); +} + +void GncOptionsDialog::call_help_cb() noexcept +{ + if (m_help_cb) + (m_help_cb)(this, m_help_cb_data); +} + +void GncOptionsDialog::call_close_cb() noexcept +{ + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(m_window)); + if (m_close_cb) + { + gtk_window_close(GTK_WINDOW(m_window)); + (m_close_cb)(this, m_close_cb_data); + } + else + { + gtk_widget_hide(m_window); + } +} + +void GncOptionsDialog::call_book_help_cb() noexcept +{ +/* if (m_book_options_help_cb) + (m_book_options_help_cb)(this, m_book_options_help_cb_data); +*/ +} + +void GncOptionsDialog::call_style_sheet_help_cb() noexcept +{ +/* + if (m_style_sheet_help_cb) + (m_style_shet_help_cb)(this, m_style_sheet_help_cb_data); +*/ +} + +// Help button signal handler +static void +dialog_help_button_cb(GtkWidget * widget, GncOptionsDialog *win) +{ + win->call_help_cb(); +} + +// Cancel/close button clicked signal handler +static void +dialog_cancel_button_cb(GtkWidget * widget, GncOptionsDialog *win) +{ + win->call_close_cb(); +} + +// Apply button clicked signal handler +static void +dialog_apply_button_cb(GtkWidget * widget, GncOptionsDialog *win) +{ + win->call_apply_cb(); +} + +// OK Button clicked signal handler +static void +dialog_ok_button_cb(GtkWidget * widget, GncOptionsDialog *win) +{ + win->call_apply_cb(); + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->get_widget())); + win->call_close_cb(); +} + +// "destroy" signal handler +static void +dialog_destroy_cb (GtkWidget *object, GncOptionsDialog *win) +{ + win->call_close_cb(); +} + +// "key_press_event" signal handler +static bool +dialog_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + GncOptionsDialog *win = static_cast(data); + + if (event->keyval == GDK_KEY_Escape) + { + component_close_handler (win); + return TRUE; + } + else + return FALSE; +} + +static void +dialog_reset_cb(GtkWidget * w, gpointer data) +{ + GncOptionsDialog *win = static_cast(data); + gpointer val; + bool dialog_changed = false; + + val = g_object_get_data(G_OBJECT(w), "section"); + g_return_if_fail (val); + g_return_if_fail (win); + + auto section = static_cast(val); + section->foreach_option( + [&dialog_changed](GncOption& option) { + if (option.is_changed()) + { + option.reset_default_value(); + option.get_ui_item()->set_dirty(true); + dialog_changed = true; + } + option.set_ui_item_from_option(); + }); + + dialog_changed_internal (win->get_widget(), dialog_changed); +} + +// changed signal handler +static void +dialog_list_select_cb (GtkTreeSelection *selection, gpointer data) +{ + GncOptionsDialog * win = static_cast(data); + GtkTreeModel *list; + GtkTreeIter iter; + gint index = 0; + + if (!gtk_tree_selection_get_selected(selection, &list, &iter)) + return; + gtk_tree_model_get(list, &iter, + PAGE_INDEX, &index, + -1); + PINFO("Index is %d", index); + gtk_notebook_set_current_page(GTK_NOTEBOOK(win->get_notebook()), index); +} + +static void +component_close_handler (gpointer data) +{ + GncOptionsDialog *win = static_cast(data); + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->get_widget())); + dialog_cancel_button_cb (NULL, win); +} + +/** Constructs a GncOptionsDialog + * + * Based on the description in the GtkBuilder file. Initializes signals. + * Two component classes might be used, DIALOG_BOOK_OPTIONS_CM_CLASS or DIALOG_OPTIONS_CM_CLASS of which the latter is the default. + * + * @param modal: If true the "Apply" button is hidden. It doesn't make the dialog run in its own event loop so it's not truly modal. + * @param title: The title that will appear in the dialog's title bar. + * @param component_class: For registering the dialog in the component manager. + * @param parent: The widget for which the dialog will be transient-for. + */ +GncOptionsDialog::GncOptionsDialog(bool modal, const char* title, + const char* component_class, + GtkWindow *parent) : + m_component_class{component_class ? component_class : DIALOG_OPTIONS_CM_CLASS} +{ + auto builder = gtk_builder_new(); + gnc_builder_add_from_file (builder, "dialog-options.glade", "gnucash_options_window"); + m_window = GTK_WIDGET(gtk_builder_get_object (builder, "gnucash_options_window")); + g_object_ref(m_window); + m_page_list = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_scroll")); + g_object_set_data(G_OBJECT(m_window), "optionwin", this); + + // Set the name for this dialog so it can be easily manipulated with css + gtk_widget_set_name (GTK_WIDGET(m_window), "gnc-id-options"); + + /* Page List */ + + m_page_list_view = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_treeview")); + + auto view = GTK_TREE_VIEW(m_page_list_view); + + auto store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING); + gtk_tree_view_set_model(view, GTK_TREE_MODEL(store)); + g_object_unref(store); + + auto renderer = gtk_cell_renderer_text_new(); + auto column = + gtk_tree_view_column_new_with_attributes(_("Page"), renderer, + "text", PAGE_NAME, + nullptr); + gtk_tree_view_append_column(view, column); + + gtk_tree_view_column_set_alignment(column, 0.5); + + auto selection = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); + g_signal_connect (selection, "changed", + G_CALLBACK (dialog_list_select_cb), this); + + m_help_button = GTK_BUTTON(gtk_builder_get_object (builder, "helpbutton")); + g_signal_connect(m_help_button, "clicked", + G_CALLBACK(dialog_help_button_cb), this); + m_cancel_button = GTK_BUTTON(gtk_builder_get_object (builder, "cancelbutton")); + g_signal_connect(m_cancel_button, "clicked", + G_CALLBACK(dialog_cancel_button_cb), this); + m_apply_button = GTK_BUTTON(gtk_builder_get_object (builder, "applybutton")); + g_signal_connect(m_apply_button, "clicked", + G_CALLBACK(dialog_apply_button_cb), this); + m_ok_button = GTK_BUTTON(gtk_builder_get_object (builder, "okbutton")); + g_signal_connect(m_ok_button, "clicked", + G_CALLBACK(dialog_ok_button_cb), this); + + gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, + this); + + // when added to a page of the hierarchy assistant there will be no parent + if (parent) + gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(m_window), + parent); + + if (title) + gtk_window_set_title(GTK_WINDOW(m_window), title); + + /* modal */ + if (modal) + gtk_widget_hide (GTK_WIDGET(m_apply_button)); + + /* glade doesn't support a notebook with zero pages */ + auto hbox = GTK_WIDGET(gtk_builder_get_object (builder, + "notebook_placeholder")); + m_notebook = gtk_notebook_new(); + + gtk_widget_set_vexpand (m_notebook, TRUE); + + gtk_widget_show(m_notebook); + gtk_box_pack_start(GTK_BOX(hbox), m_notebook, TRUE, TRUE, 5); + + auto component_id = gnc_register_gui_component (m_component_class, + nullptr, + component_close_handler, + this); + gnc_gui_component_set_session (component_id, gnc_get_current_session()); + + g_signal_connect (m_window, "destroy", G_CALLBACK(dialog_destroy_cb), this); + + g_signal_connect (m_window, "key_press_event", + G_CALLBACK(dialog_window_key_press_cb), this); + + g_object_unref(G_OBJECT(builder)); +} + +GncOptionsDialog::~GncOptionsDialog() +{ + if (m_destroying) + return; + m_destroying = true; + gnc_unregister_gui_component_by_data(m_component_class, this); + g_signal_handlers_disconnect_by_func(m_window, (gpointer)dialog_destroy_cb, this); + g_signal_handlers_disconnect_by_func(m_window, (gpointer)dialog_window_key_press_cb, this); + g_object_unref(m_window); +} + +void +GncOptionsDialog::set_apply_cb(GncOptionsDialogCallback cb, gpointer data) noexcept +{ + m_apply_cb = cb; + m_apply_cb_data = data; +} + +void +GncOptionsDialog::set_help_cb(GncOptionsDialogCallback cb, gpointer data) noexcept +{ + m_help_cb = cb; + m_help_cb_data = data; +} + +void +GncOptionsDialog::set_close_cb( GncOptionsDialogCallback cb, gpointer data) noexcept +{ + m_close_cb = cb; + m_close_cb_data = data; +} + + +static void +gnc_book_options_help_cb (GncOptionsDialog *win, gpointer dat) +{ + gnc_gnome_help (GTK_WINDOW (win->get_widget()), HF_HELP, HL_BOOK_OPTIONS); +} + +void +GncOptionsDialog::set_book_help_cb() noexcept +{ + set_help_cb((GncOptionsDialogCallback)gnc_book_options_help_cb, nullptr); +} + +static void +gnc_global_options_help_cb (GncOptionsDialog *win, gpointer dat) +{ + gnc_gnome_help (GTK_WINDOW(win->get_widget()), HF_HELP, HL_GLOBPREFS); +} + +static void +gnc_style_sheet_options_help_cb (GncOptionsDialog *win, gpointer dat) +{ + gnc_gnome_help (GTK_WINDOW(win->get_widget()), HF_HELP, HL_STYLE_SHEET); +} + +void +GncOptionsDialog::set_style_sheet_help_cb () noexcept +{ + set_help_cb ((GncOptionsDialogCallback)gnc_style_sheet_options_help_cb, + nullptr); +} + + +/* ****************************************************************/ +/* Option Widgets */ +/* ***************************************************************/ + +static void +align_label (GtkLabel *name_label) +{ + /* some option widgets have a large vertical foot print so align + the label name to the top and add a small top margin */ + gtk_widget_set_valign (GTK_WIDGET(name_label), GTK_ALIGN_START); + gtk_widget_set_margin_top (GTK_WIDGET(name_label), 6); +} + +class GncGtkBooleanUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkBooleanUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::BOOLEAN} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GTK_TOGGLE_BUTTON(get_widget())}; + gtk_toggle_button_set_active(widget, option.get_value()); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GTK_TOGGLE_BUTTON(get_widget())}; + option.set_value(static_cast(gtk_toggle_button_get_active(widget))); + } +}; + +template <> GtkWidget * +create_option_widget (GncOption& option, + GtkGrid* page_box, + GtkLabel* name_label, + char* documentation, + /* Return values */ + GtkWidget** enclosing, + bool* packed) +{ + + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + auto widget = + gtk_check_button_new_with_label (gtk_label_get_text(name_label)); + + auto ui_item{std::make_unique(widget)}; + + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(widget), "toggled", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + + return widget; +} + +class GncGtkStringUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkStringUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::STRING} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GTK_ENTRY(get_widget())}; + gtk_entry_set_text(widget, option.get_value().c_str()); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GTK_ENTRY(get_widget())}; + option.set_value(std::string{gtk_entry_get_text(widget)}); + } +}; + +template<> GtkWidget* +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + auto widget = gtk_entry_new(); + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + gtk_entry_set_alignment (GTK_ENTRY(widget), 1.0); + auto ui_item{std::make_unique(widget)}; + + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + gtk_box_pack_start(GTK_BOX(*enclosing), widget, TRUE, TRUE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +class GncGtkTextUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkTextUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::TEXT} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GTK_TEXT_VIEW(get_widget())}; + xxxgtk_textview_set_text(widget, option.get_value().c_str()); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GTK_TEXT_VIEW(get_widget())}; + option.set_value(std::string{xxxgtk_textview_get_text(widget)}); + } +}; + +template<> GtkWidget* +create_option_widget (GncOption& option, GtkGrid *page_box, + GtkLabel *name_label, char *documentation, + /* Return values */ + GtkWidget **enclosing, bool *packed) +{ + align_label (name_label); + + auto scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_container_set_border_width(GTK_CONTAINER(scroll), 2); + + auto frame = gtk_frame_new(NULL); + gtk_container_add(GTK_CONTAINER(frame), scroll); + + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); + gtk_widget_set_vexpand (GTK_WIDGET(*enclosing), TRUE); + gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + auto widget = gtk_text_view_new(); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD); + gtk_text_view_set_editable(GTK_TEXT_VIEW(widget), TRUE); + gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW(widget), FALSE); + + auto ui_item{std::make_unique(widget)}; + auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(text_buffer), "changed", + G_CALLBACK(gnc_option_changed_option_cb), &option); + gtk_container_add (GTK_CONTAINER (scroll), widget); + gtk_box_pack_start(GTK_BOX(*enclosing), frame, TRUE, TRUE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +class GncGtkCurrencyUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkCurrencyUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::CURRENCY} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GNC_CURRENCY_EDIT(get_widget())}; + auto instance{option.get_value()}; + if (instance) + gnc_currency_edit_set_currency(widget, GNC_COMMODITY(instance)); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GNC_CURRENCY_EDIT(get_widget())}; + auto currency = gnc_currency_edit_get_currency(widget); + option.set_value(qof_instance_cast(currency)); + } +}; + +template<> GtkWidget* +create_option_widget (GncOption& option, GtkGrid *page_box, + GtkLabel *name_label, char *documentation, + /* Return values */ + GtkWidget **enclosing, bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + auto widget = gnc_currency_edit_new(); + auto ui_item{std::make_unique(widget)}; + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +class GncGtkCommodityUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkCommodityUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::COMMODITY} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GNC_GENERAL_SELECT(get_widget())}; + auto instance{option.get_value()}; + if (instance) + gnc_general_select_set_selected(widget, GNC_COMMODITY(instance)); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GNC_GENERAL_SELECT(get_widget())}; + auto commodity{gnc_general_select_get_selected(widget)}; + option.set_value(qof_instance_cast(commodity)); + } +}; + +template<> GtkWidget* +create_option_widget (GncOption& option, GtkGrid *page_box, + GtkLabel *name_label, char *documentation, + /* Return values */ + GtkWidget **enclosing, bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + auto widget = gnc_general_select_new(GNC_GENERAL_SELECT_TYPE_SELECT, + gnc_commodity_edit_get_string, + gnc_commodity_edit_new_select, + NULL); + + auto ui_item{std::make_unique(widget)}; + + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_option(); + + if (documentation != NULL) + gtk_widget_set_tooltip_text(GNC_GENERAL_SELECT(widget)->entry, + documentation); + + g_signal_connect(G_OBJECT(GNC_GENERAL_SELECT(widget)->entry), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +static GtkWidget* +create_multichoice_widget(GncOption& option) +{ + auto num_values = option.num_permissible_values(); + + g_return_val_if_fail(num_values >= 0, NULL); + auto renderer = gtk_cell_renderer_text_new(); + auto store = gtk_list_store_new(1, G_TYPE_STRING); + /* Add values to the list store, entry and tooltip */ + for (decltype(num_values) i = 0; i < num_values; i++) + { + GtkTreeIter iter; + auto itemstring = option.permissible_value_name(i); + gtk_list_store_append (store, &iter); + gtk_list_store_set(store, &iter, 0, + (itemstring && *itemstring) ? _(itemstring) : "", -1); + } + /* Create the new Combo with tooltip and add the store */ + auto widget{GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)))}; + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(widget), renderer, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(widget), + renderer, "text", 0); + g_object_unref(store); + + return widget; +} + +class GncGtkMultichoiceUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkMultichoiceUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::MULTICHOICE} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GTK_COMBO_BOX(get_widget())}; + gtk_combo_box_set_active(widget, option.get_value()); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GTK_COMBO_BOX(get_widget())}; + option.set_value(static_cast(gtk_combo_box_get_active(widget))); + } +}; + +template<> GtkWidget* +create_option_widget (GncOption& option, GtkGrid *page_box, + GtkLabel *name_label, char *documentation, + /* Return values */ + GtkWidget **enclosing, bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + + auto widget = create_multichoice_widget(option); + auto ui_item{std::make_unique(widget)}; + + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + + +class GncDateEntry +{ +public: + GncDateEntry() = default; + virtual ~GncDateEntry() = default; + virtual void set_entry_from_option(GncOption& option) = 0; + virtual void set_option_from_entry(GncOption& option) = 0; + // Get the widget that has data + virtual GtkWidget* get_entry() = 0; + // Get the widget that gets put on the page + virtual GtkWidget* get_widget() = 0; + virtual void toggle_relative(bool) {} //BothDateEntry only + virtual void block_signals(bool) = 0; +}; + + +using GncDateEntryPtr = std::unique_ptr; + +class AbsoluteDateEntry : public GncDateEntry +{ +public: + AbsoluteDateEntry(GncOption& option); + ~AbsoluteDateEntry() = default; + void set_entry_from_option(GncOption& option) override; + void set_option_from_entry(GncOption& option) override; + GtkWidget* get_entry() override { return GTK_WIDGET(m_entry); } + GtkWidget* get_widget() override { return GTK_WIDGET(m_entry); } + void block_signals(bool) override; +private: + GNCDateEdit* m_entry; + unsigned long m_handler_id; +}; + +AbsoluteDateEntry::AbsoluteDateEntry(GncOption& option) : + m_entry{GNC_DATE_EDIT(gnc_date_edit_new(time(NULL), FALSE, FALSE))} +{ + auto entry = GNC_DATE_EDIT(m_entry)->date_entry; + m_handler_id = g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(gnc_option_changed_option_cb), + &option); +} + +void +AbsoluteDateEntry::block_signals(bool block) +{ + auto entry{G_OBJECT(GNC_DATE_EDIT(m_entry)->date_entry)}; + if (block) + g_signal_handler_block(entry, m_handler_id); + else + g_signal_handler_unblock(entry, m_handler_id); +} + +void +AbsoluteDateEntry::set_entry_from_option(GncOption& option) +{ + gnc_date_edit_set_time(m_entry, option.get_value()); +} + +void +AbsoluteDateEntry::set_option_from_entry(GncOption& option) +{ + option.set_value(gnc_date_edit_get_date(m_entry)); +} + +class RelativeDateEntry : public GncDateEntry +{ +public: + RelativeDateEntry(GncOption& option); + ~RelativeDateEntry() = default; + void set_entry_from_option(GncOption& option) override; + void set_option_from_entry(GncOption& option) override; + GtkWidget* get_widget() override { return m_entry; } + GtkWidget* get_entry() override { return m_entry; } + void block_signals(bool) override; +private: + GtkWidget* m_entry; + unsigned long m_handler_id; +}; + + +RelativeDateEntry::RelativeDateEntry(GncOption& option) +{ + + auto renderer = gtk_cell_renderer_text_new(); + auto store = gtk_list_store_new(1, G_TYPE_STRING); + /* Add values to the list store, entry and tooltip */ + auto num = option.num_permissible_values(); + for (decltype(num) index = 0; index < num; ++index) + { + GtkTreeIter iter; + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, + option.permissible_value_name(index), -1); + } + + /* Create the new Combo with tooltip and add the store */ + m_entry = GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store))); + gtk_combo_box_set_active(GTK_COMBO_BOX(m_entry), 0); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(m_entry), renderer, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(m_entry), + renderer, "text", 0); + + g_object_unref(store); + + m_handler_id = g_signal_connect(G_OBJECT(m_entry), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), + &option); +} + +void +RelativeDateEntry::set_entry_from_option(GncOption& option) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(m_entry), option.get_value()); +} + +void +RelativeDateEntry::set_option_from_entry(GncOption& option) +{ + option.set_value(gtk_combo_box_get_active(GTK_COMBO_BOX(m_entry))); +} + +void +RelativeDateEntry::block_signals(bool block) +{ + auto entry{G_OBJECT(GNC_DATE_EDIT(m_entry)->date_entry)}; + if (block) + g_signal_handler_block(m_entry, m_handler_id); + else + g_signal_handler_unblock(m_entry, m_handler_id); +} + +using AbsoluteDateEntryPtr = std::unique_ptr; +using RelativeDateEntryPtr = std::unique_ptr; + +class BothDateEntry : public GncDateEntry +{ +public: + BothDateEntry(GncOption& option); + ~BothDateEntry() = default; //The GncOptionGtkUIItem owns the widget + void set_entry_from_option(GncOption& option) override; + void set_option_from_entry(GncOption& option) override; + GtkWidget* get_widget() override { return m_widget; } + GtkWidget* get_entry() override; + void toggle_relative(bool use_absolute) override; + void block_signals(bool) override; +private: + GtkWidget* m_widget; + GtkWidget* m_abs_button; + AbsoluteDateEntryPtr m_abs_entry; + GtkWidget* m_rel_button; + RelativeDateEntryPtr m_rel_entry; + bool m_use_absolute = true; + unsigned long m_abs_hdlr; + unsigned long m_rel_hdlr; +}; + +static void date_set_absolute_cb(GtkWidget *widget, gpointer data1); +static void date_set_relative_cb(GtkWidget *widget, gpointer data1); + +BothDateEntry::BothDateEntry(GncOption& option) : + m_widget{gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5)}, + m_abs_button{gtk_radio_button_new(NULL)}, + m_abs_entry{std::make_unique(option)}, + m_rel_button{ + gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(m_abs_button))}, + m_rel_entry{std::make_unique(option)} +{ + gtk_box_set_homogeneous (GTK_BOX(m_widget), FALSE); + m_abs_hdlr = g_signal_connect(G_OBJECT(m_abs_button), "toggled", + G_CALLBACK(date_set_absolute_cb), &option); + m_rel_hdlr = g_signal_connect(G_OBJECT(m_rel_button), "toggled", + G_CALLBACK(date_set_relative_cb), &option); + + gtk_box_pack_start(GTK_BOX(m_widget), + m_abs_button, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(m_widget), + m_abs_entry->get_entry(), FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(m_widget), + m_rel_button, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(m_widget), + m_rel_entry->get_entry(), FALSE, FALSE, 0); + +} + +GtkWidget* +BothDateEntry::get_entry() +{ + return m_use_absolute ? m_abs_entry->get_entry() : m_rel_entry->get_entry(); +} + +void +BothDateEntry::toggle_relative(bool use_absolute) +{ + m_use_absolute = use_absolute; +} + +void +BothDateEntry::set_entry_from_option(GncOption& option) +{ + m_use_absolute = + option.get_value() == RelativeDatePeriod::ABSOLUTE; + if (m_use_absolute) + m_abs_entry->set_entry_from_option(option); + else + m_rel_entry->set_entry_from_option(option); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_rel_button), + !m_use_absolute); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_abs_button), + m_use_absolute); +} + +void +BothDateEntry::set_option_from_entry(GncOption& option) +{ + if (m_use_absolute) + m_abs_entry->set_option_from_entry(option); + else + m_rel_entry->set_option_from_entry(option); +} + +void +BothDateEntry::block_signals(bool block) +{ + if (block) + { + g_signal_handler_block(m_abs_button, m_abs_hdlr); + g_signal_handler_block(m_rel_button, m_rel_hdlr); + } + else + { + g_signal_handler_unblock(m_abs_button, m_abs_hdlr); + g_signal_handler_unblock(m_rel_button, m_rel_hdlr); + } + m_abs_entry->block_signals(block); + m_rel_entry->block_signals(block); +} + +class GncOptionDateUIItem : public GncOptionGtkUIItem +{ +public: + GncOptionDateUIItem(GncDateEntryPtr entry, GncOptionUIType type) : + GncOptionGtkUIItem{nullptr, type}, m_entry{std::move(entry)} { } + ~GncOptionDateUIItem() = default; + void clear_ui_item() override { m_entry = nullptr; } + void set_ui_item_from_option(GncOption& option) noexcept override + { + if (m_entry) + m_entry->set_entry_from_option(option); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + if (m_entry) + m_entry->set_option_from_entry(option); + } + GtkWidget* const get_widget() const override + { + return m_entry->get_widget(); + } + GncDateEntry* get_entry() { return m_entry.get(); } +private: + GncDateEntryPtr m_entry; +}; + +static void +date_set_absolute_cb(GtkWidget *widget, gpointer data1) +{ + GncOption* option = static_cast(data1); + auto ui_item = option->get_ui_item(); + if (auto date_ui = dynamic_cast(ui_item)) + { + const_cast(date_ui)->get_entry()->toggle_relative(true); + gnc_option_changed_option_cb(widget, option); + } +} + +static void +date_set_relative_cb(GtkWidget *widget, gpointer data1) +{ + GncOption* option = static_cast(data1); + auto ui_item = option->get_ui_item(); + if (auto date_ui = dynamic_cast(ui_item)) + { + const_cast(date_ui)->get_entry()->toggle_relative(false); + gnc_option_changed_option_cb(widget, option); + } +} + +static GtkWidget* +create_date_option_widget(GncOption& option, GtkGrid *page_box, + GtkLabel *name_label, char *documentation, + /* Return values */ + GtkWidget **enclosing, bool *packed) +{ + auto grid_row = GPOINTER_TO_INT(g_object_get_data + (G_OBJECT(page_box), "options-grid-row")); + auto type = option.get_ui_type(); + switch (type) + { + case GncOptionUIType::DATE_ABSOLUTE: + option.set_ui_item(std::make_unique(std::make_unique(option), type)); + break; + case GncOptionUIType::DATE_RELATIVE: + option.set_ui_item(std::make_unique(std::make_unique(option), type)); + break; + case GncOptionUIType::DATE_BOTH: + option.set_ui_item(std::make_unique(std::make_unique(option), type)); + break; + default: + PERR("Attempted to create date option widget with wrong UI type %d", + static_cast(type)); + return nullptr; + break; + } + + auto widget{option_get_gtk_widget(&option)}; + if (type == GncOptionUIType::DATE_RELATIVE) + { + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + } + else + { + *enclosing = gtk_frame_new (NULL); + g_object_set (G_OBJECT(widget), "margin", 3, NULL); + + gtk_container_add (GTK_CONTAINER(*enclosing), widget); + } + + gtk_widget_set_halign (GTK_WIDGET(*enclosing), GTK_ALIGN_START); + + /* Pack option widget into an extra eventbox because otherwise the + "documentation" tooltip is not displayed. */ + auto eventbox = gtk_event_box_new(); + gtk_container_add (GTK_CONTAINER (eventbox), *enclosing); + + gtk_grid_attach (GTK_GRID(page_box), eventbox, 1, grid_row, 1, 1); + *packed = TRUE; + + gtk_widget_set_tooltip_text (eventbox, documentation); + + auto ui_item{dynamic_cast(option.get_ui_item())}; + if (auto date_ui{ui_item ? ui_item->get_entry() : nullptr}) + { + date_ui->block_signals(true); + date_ui->set_entry_from_option(option); + date_ui->block_signals(false); + } + + gtk_widget_show_all(*enclosing); + return widget; +} + +template<> GtkWidget* +create_option_widget(GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + return create_date_option_widget(option, page_box, name_label, + documentation, enclosing, packed); +} + +template<> GtkWidget* +create_option_widget(GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + return create_date_option_widget(option, page_box, name_label, + documentation, enclosing, packed); +} + +template<> GtkWidget* +create_option_widget(GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + return create_date_option_widget(option, page_box, name_label, + documentation, enclosing, packed); +} + +using GncOptionAccountList = std::vector; + +static void +account_select_all_cb(GtkWidget *widget, gpointer data) +{ + GncOption* option = static_cast(data); + GncTreeViewAccount *tree_view; + GtkTreeSelection *selection; + + tree_view = GNC_TREE_VIEW_ACCOUNT(option_get_gtk_widget (option)); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view)); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); + gtk_tree_selection_select_all(selection); + gnc_option_changed_widget_cb(widget, option); +} + +static void +account_clear_all_cb(GtkWidget *widget, gpointer data) +{ + GncOption* option = static_cast(data); + GncTreeViewAccount *tree_view; + GtkTreeSelection *selection; + + tree_view = GNC_TREE_VIEW_ACCOUNT(option_get_gtk_widget (option)); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); + gtk_tree_selection_unselect_all(selection); + gnc_option_changed_widget_cb(widget, option); +} + +static void +account_select_children_cb(GtkWidget *widget, gpointer data) +{ + GncOption* option = static_cast(data); + GncTreeViewAccount *tree_view; + GList *acct_list = NULL, *acct_iter = NULL; + + tree_view = GNC_TREE_VIEW_ACCOUNT(option_get_gtk_widget (option)); + acct_list = gnc_tree_view_account_get_selected_accounts (tree_view); + + for (acct_iter = acct_list; acct_iter; acct_iter = acct_iter->next) + gnc_tree_view_account_select_subaccounts (tree_view, static_cast(acct_iter->data)); + + g_list_free (acct_list); +} + +static void +account_set_default_cb(GtkWidget* widget, gpointer data) +{ + GncOption* option = static_cast(data); + account_clear_all_cb(widget, data); + option->set_value(option->get_default_value()); + option->set_ui_item_from_option(); +} + +static void +show_hidden_toggled_cb(GtkWidget *widget, GncOption* option) +{ + if (option->get_ui_type() != GncOptionUIType::ACCOUNT_LIST && + option->get_ui_type() != GncOptionUIType::ACCOUNT_SEL) + return; + + auto tree_view = GNC_TREE_VIEW_ACCOUNT(option_get_gtk_widget(option)); + AccountViewInfo avi; + gnc_tree_view_account_get_view_info (tree_view, &avi); + avi.show_hidden = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + gnc_tree_view_account_set_view_info (tree_view, &avi); + gnc_option_changed_widget_cb(widget, option); +} + +class GncGtkAccountListUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkAccountListUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::ACCOUNT_LIST} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GNC_TREE_VIEW_ACCOUNT(get_widget())}; + GList *acc_list = nullptr; + const GncOptionAccountList& accounts = + option.get_value(); + auto book{gnc_get_current_book()}; + for (auto guid : accounts) + { + auto account{xaccAccountLookup(&guid, book)}; + acc_list = g_list_prepend(acc_list, account); + } + acc_list = g_list_reverse(acc_list); + gnc_tree_view_account_set_selected_accounts(widget, acc_list, TRUE); + g_list_free(acc_list); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GNC_TREE_VIEW_ACCOUNT(get_widget())}; + auto acc_list = gnc_tree_view_account_get_selected_accounts(widget); + GncOptionAccountList acc_vec; + acc_vec.reserve(g_list_length(acc_list)); + for (auto node = acc_list; node; node = g_list_next(node)) + { + auto guid{qof_entity_get_guid(node->data)}; + acc_vec.push_back(*guid); + } + g_list_free(acc_list); + option.set_value(acc_vec); + } +}; + +static GtkWidget* +create_account_widget(GncOption& option, char *name) +{ + bool multiple_selection; + GtkWidget *scroll_win; + GtkWidget *button; + GtkWidget *frame; + GtkWidget *tree; + GtkWidget *vbox; + GtkWidget *bbox; + GList *acct_type_list; + GtkTreeSelection *selection; + + multiple_selection = option.is_multiselect(); + acct_type_list = option.account_type_list(); + + frame = gtk_frame_new(name); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_set_homogeneous (GTK_BOX (vbox), FALSE); + + gtk_container_add(GTK_CONTAINER(frame), vbox); + + tree = GTK_WIDGET(gnc_tree_view_account_new (FALSE)); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(tree), FALSE); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree)); + if (multiple_selection) + gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); + else + gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + + if (acct_type_list) + { + GList *node; + AccountViewInfo avi; + int i; + + gnc_tree_view_account_get_view_info (GNC_TREE_VIEW_ACCOUNT (tree), &avi); + + for (i = 0; i < NUM_ACCOUNT_TYPES; i++) + avi.include_type[i] = FALSE; + avi.show_hidden = FALSE; + + for (node = acct_type_list; node; node = node->next) + { + GNCAccountType type = static_cast(GPOINTER_TO_INT (node->data)); + avi.include_type[type] = TRUE; + } + + gnc_tree_view_account_set_view_info (GNC_TREE_VIEW_ACCOUNT (tree), &avi); + g_list_free (acct_type_list); + } + else + { + AccountViewInfo avi; + int i; + + gnc_tree_view_account_get_view_info (GNC_TREE_VIEW_ACCOUNT (tree), &avi); + + for (i = 0; i < NUM_ACCOUNT_TYPES; i++) + avi.include_type[i] = TRUE; + avi.show_hidden = FALSE; + gnc_tree_view_account_set_view_info (GNC_TREE_VIEW_ACCOUNT (tree), &avi); + } + + scroll_win = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(scroll_win), 5); + + bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 10); + + option.set_ui_item(std::make_unique(tree)); + option.set_ui_item_from_option(); + + if (multiple_selection) + { + button = gtk_button_new_with_label(_("Select All")); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_tooltip_text(button, _("Select all accounts.")); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(account_select_all_cb), &option); + + button = gtk_button_new_with_label(_("Clear All")); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_tooltip_text(button, _("Clear the selection and unselect all accounts.")); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(account_clear_all_cb), &option); + + button = gtk_button_new_with_label(_("Select Children")); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_tooltip_text(button, _("Select all descendents of selected account.")); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(account_select_children_cb), &option); + } + + button = gtk_button_new_with_label(_("Select Default")); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_tooltip_text(button, _("Select the default account selection.")); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(account_set_default_cb), &option); + + gtk_widget_set_margin_start (GTK_WIDGET(bbox), 6); + gtk_widget_set_margin_end (GTK_WIDGET(bbox), 6); + + if (multiple_selection) + { + /* Put the "Show hidden" checkbox on a separate line since + the 4 buttons make the dialog too wide. */ + bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + } + + button = gtk_check_button_new_with_label(_("Show Hidden Accounts")); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_tooltip_text(button, _("Show accounts that have been marked hidden.")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(show_hidden_toggled_cb), &option); + + gtk_container_add(GTK_CONTAINER(scroll_win), tree); + return frame; +} + +static void +option_account_sel_changed_cb(GtkTreeSelection *sel, gpointer data) +{ + auto tree_view{gtk_tree_selection_get_tree_view(sel)}; + gnc_option_changed_widget_cb(GTK_WIDGET(tree_view), + static_cast(data)); +} + +template<> GtkWidget* +create_option_widget(GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + align_label (name_label); + + *enclosing = create_account_widget(option, NULL); + gtk_widget_set_vexpand (GTK_WIDGET(*enclosing), TRUE); + gtk_widget_set_hexpand (GTK_WIDGET(*enclosing), TRUE); + + gtk_widget_set_tooltip_text(*enclosing, documentation); + + auto grid_row = GPOINTER_TO_INT(g_object_get_data + (G_OBJECT(page_box), "options-grid-row")); + gtk_grid_attach (GTK_GRID(page_box), *enclosing, 1, grid_row, 1, 1); + *packed = TRUE; + + auto widget = option_get_gtk_widget(&option); + + auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(option_account_sel_changed_cb), &option); + + // gtk_clist_set_row_height(GTK_CLIST(value), 0); + // gtk_widget_set_size_request(value, -1, GTK_CLIST(value)->row_height * 10); + gtk_widget_show_all(*enclosing); + return widget; +} + +class GncGtkAccountSelUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkAccountSelUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::ACCOUNT_SEL} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GNC_ACCOUNT_SEL(get_widget())}; + auto instance{option.get_value()}; + if (instance) + gnc_account_sel_set_account(widget, const_cast(instance), FALSE); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GNC_ACCOUNT_SEL(get_widget())}; + option.set_value(qof_instance_cast((gnc_account_sel_get_account(widget)))); + } +}; + +template<> GtkWidget* +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + auto acct_type_list = option.account_type_list(); + auto widget = gnc_account_sel_new (); + gnc_account_sel_set_acct_filters(GNC_ACCOUNT_SEL(widget), + acct_type_list, NULL); + g_list_free(acct_type_list); + g_signal_connect(widget, "account_sel_changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + + +// gnc_account_sel doesn't emit a changed signal + option.set_ui_item(std::make_unique(widget)); + option.set_ui_item_from_option(); + + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +static void +list_changed_cb(GtkTreeSelection *selection, GncOption* option) +{ + GtkTreeView *view = GTK_TREE_VIEW(option_get_gtk_widget (option)); + gnc_option_changed_widget_cb(GTK_WIDGET(view), option); +} + +static void +list_select_all_cb(GtkWidget *widget, gpointer data) +{ + GncOption* option = static_cast(data); + GtkTreeView *view; + GtkTreeSelection *selection; + + view = GTK_TREE_VIEW(option_get_gtk_widget(option)); + selection = gtk_tree_view_get_selection(view); + gtk_tree_selection_select_all(selection); + gnc_option_changed_widget_cb(GTK_WIDGET(view), option); +} + +static void +list_clear_all_cb(GtkWidget *widget, gpointer data) +{ + GncOption* option = static_cast(data); + GtkTreeView *view; + GtkTreeSelection *selection; + + view = GTK_TREE_VIEW(option_get_gtk_widget(option)); + selection = gtk_tree_view_get_selection(view); + gtk_tree_selection_unselect_all(selection); + gnc_option_changed_widget_cb(GTK_WIDGET(view), option); +} + +static void +list_set_default_cb(GtkWidget *widget, gpointer data) +{ + GncOption* option = static_cast(data); + list_clear_all_cb(widget, data); + option->set_value(option->get_default_value()); + option->set_ui_item_from_option(); +} + +class GncGtkListUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkListUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::LIST} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GTK_TREE_VIEW(get_widget())}; + auto selection{gtk_tree_view_get_selection(widget)}; + gtk_tree_selection_unselect_all(selection); + for (auto index : option.get_value()) + { + auto path{gtk_tree_path_new_from_indices(index, -1)}; + gtk_tree_selection_select_path(selection, path); + gtk_tree_path_free(path); + } + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GTK_TREE_VIEW(get_widget())}; + auto selection{gtk_tree_view_get_selection(widget)}; + auto rows{option.num_permissible_values()}; + GncMultichoiceOptionIndexVec vec; + for (size_t row = 0; row < rows; ++row) + { + auto path{gtk_tree_path_new_from_indices(row, -1)}; + auto selected{gtk_tree_selection_path_is_selected(selection, path)}; + gtk_tree_path_free(path); + if (selected) + vec.push_back(row); + } + option.set_value(vec); + } +}; + +static GtkWidget * +create_list_widget(GncOption& option, char *name) +{ + auto frame = gtk_frame_new(name); + auto hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE); + gtk_container_add(GTK_CONTAINER(frame), hbox); + + auto store = gtk_list_store_new(1, G_TYPE_STRING); + auto view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store))); + g_object_unref(store); + auto renderer = gtk_cell_renderer_text_new(); + auto column = gtk_tree_view_column_new_with_attributes("", renderer, + "text", 0, + NULL); + gtk_tree_view_append_column(view, column); + gtk_tree_view_set_headers_visible(view, FALSE); + + auto num_values = option.num_permissible_values(); + for (decltype(num_values) i = 0; i < num_values; i++) + { + GtkTreeIter iter; + auto raw_string = option.permissible_value_name(i); + auto string = (raw_string && *raw_string) ? _(raw_string) : ""; + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, string ? string : "", -1); + } + + option.set_ui_item(std::make_unique(GTK_WIDGET(view))); + option.set_ui_item_from_option(); + + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(view), FALSE, FALSE, 0); + + auto selection = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + g_signal_connect(selection, "changed", + G_CALLBACK(list_changed_cb), &option); + + auto bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD); + gtk_box_pack_end(GTK_BOX(hbox), bbox, FALSE, FALSE, 0); + + auto button = gtk_button_new_with_label(_("Select All")); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_tooltip_text(button, _("Select all entries.")); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(list_select_all_cb), &option); + + button = gtk_button_new_with_label(_("Clear All")); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_tooltip_text(button, _("Clear the selection and unselect all entries.")); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(list_clear_all_cb), &option); + + button = gtk_button_new_with_label(_("Select Default")); + gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); + gtk_widget_set_tooltip_text(button, _("Select the default selection.")); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(list_set_default_cb), &option); + + g_object_set (G_OBJECT(hbox), "margin", 3, NULL); + + option.set_ui_item(std::make_unique(GTK_WIDGET(view))); + option.set_ui_item_from_option(); + + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(view), FALSE, FALSE, 0); + + return frame; +} + +template<> GtkWidget * +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + auto grid_row = GPOINTER_TO_INT(g_object_get_data + (G_OBJECT(page_box), "options-grid-row")); + + *enclosing = create_list_widget(option, NULL); + auto value = option_get_gtk_widget(&option); + + align_label (name_label); + + /* Pack option widget into an extra eventbox because otherwise the + "documentation" tooltip is not displayed. */ + auto eventbox = gtk_event_box_new(); + gtk_container_add (GTK_CONTAINER (eventbox), *enclosing); + + gtk_grid_attach (GTK_GRID(page_box), eventbox, 1, grid_row, 1, 1); + *packed = TRUE; + + gtk_widget_set_tooltip_text(eventbox, documentation); + + gtk_widget_show(*enclosing); + return value; +} + +class GncGtkNumberRangeUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkNumberRangeUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::NUMBER_RANGE} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + gtk_spin_button_set_value(GTK_SPIN_BUTTON(get_widget()), + option.get_value()); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + option.set_value(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget()))); + } +}; + +/* New spin button configured with the values provided by the passed-in + * GncOption, which had better contain a GncOptionRangeValue. + * + * Also used to set up the pixel spinner in the plot_size control. + */ +static GtkSpinButton* +create_range_spinner(GncOption& option) +{ + gdouble lower_bound = G_MINDOUBLE; + gdouble upper_bound = G_MAXDOUBLE; + gdouble step_size = 1.0; + + option.get_limits(upper_bound, lower_bound, step_size); + auto adj = GTK_ADJUSTMENT(gtk_adjustment_new(lower_bound, lower_bound, + upper_bound, step_size, + step_size * 5.0, + 0)); + + size_t num_decimals = 0; + for (auto steps = step_size; steps < 1; steps *= 10) + ++num_decimals; + auto widget = gtk_spin_button_new(adj, step_size, num_decimals); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(widget), TRUE); + + size_t num_digits = 0; + for (auto bigger = MAX(ABS(lower_bound), ABS(upper_bound)); + bigger >= 1; bigger /= 10.0) + ++num_digits; + num_digits += num_decimals; + gtk_entry_set_width_chars(GTK_ENTRY(widget), num_digits); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), + (upper_bound / 2)); //default + return GTK_SPIN_BUTTON(widget); +} + +template<> GtkWidget * +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + GtkAdjustment *adj; + size_t num_decimals = 0; + + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + + auto widget = create_range_spinner(option); + option.set_ui_item(std::make_unique(GTK_WIDGET(widget))); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + + gtk_box_pack_start(GTK_BOX(*enclosing), GTK_WIDGET(widget), + FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return GTK_WIDGET(widget); +} + +class GncGtkColorUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkColorUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::COLOR} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + GdkRGBA color; + /* gdk_rgba_parse uses pango_color_parse for hex color strings instead + * of pango_color_parse_with_alpha and that fails if the string length + * is 8. + */ + auto value{option.get_value().substr(0,6)}; + auto rgba_str{g_strdup_printf("#%s", value.c_str())}; + if (gdk_rgba_parse(&color, rgba_str)) + { + auto color_button = GTK_COLOR_CHOOSER(get_widget()); + gtk_color_chooser_set_rgba(color_button, &color); + } + g_free(rgba_str); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + GdkRGBA color; + auto color_button = GTK_COLOR_CHOOSER(get_widget()); + gtk_color_chooser_get_rgba(color_button, &color); + auto rgba_str = g_strdup_printf("%2x%2x%2x%2x", + (uint8_t)(color.red * 255), + (uint8_t)(color.green * 255), + (uint8_t)(color.blue * 255), + (uint8_t)(color.alpha * 255)); + auto rgb_str = g_strdup_printf("%2x%2x%2x", + (uint8_t)(color.red * 255), + (uint8_t)(color.green * 255), + (uint8_t)(color.blue * 255)); +// Hello World uses an old HTML4 attribute that doesn't understand alpha. + option.set_value(std::string{rgb_str}); + g_free(rgba_str); + } +}; + +template<> GtkWidget * +create_option_widget (GncOption& option, GtkGrid *page_box, + GtkLabel *name_label, char *documentation, + /* Return values */ + GtkWidget **enclosing, bool *packed) +{ + bool use_alpha; + + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + + auto widget = gtk_color_button_new(); + gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(widget), TRUE); + + option.set_ui_item(std::make_unique(widget)); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(widget), "color-set", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +class GncGtkFontUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkFontUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::FONT} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + GtkFontButton *font_button = GTK_FONT_BUTTON(get_widget()); + gtk_font_button_set_font_name(font_button, + option.get_value().c_str()); + + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + GtkFontButton *font_button = GTK_FONT_BUTTON(get_widget()); + option.set_value(std::string{gtk_font_button_get_font_name(font_button)}); + } +}; + +template<> GtkWidget * +create_option_widget (GncOption& option, GtkGrid *page_box, + GtkLabel *name_label, char *documentation, + /* Return values */ + GtkWidget **enclosing, bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + auto widget = gtk_font_button_new(); + g_object_set(G_OBJECT(widget), + "use-font", TRUE, + "show-style", TRUE, + "show-size", TRUE, + (char *)NULL); + + option.set_ui_item(std::make_unique(widget)); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(widget), "font-set", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +static void +update_preview_cb (GtkFileChooser *chooser, void* data) +{ + g_return_if_fail(chooser != NULL); + + ENTER("chooser %p", chooser); + auto filename = gtk_file_chooser_get_preview_filename(chooser); + DEBUG("chooser preview name is %s.", filename ? filename : "(null)"); + if (filename == NULL) + { + filename = g_strdup(static_cast(g_object_get_data(G_OBJECT(chooser), LAST_SELECTION))); + DEBUG("using last selection of %s", filename ? filename : "(null)"); + if (filename == NULL) + { + LEAVE("no usable name"); + return; + } + } + + auto image = GTK_IMAGE(gtk_file_chooser_get_preview_widget(chooser)); + auto pixbuf = gdk_pixbuf_new_from_file_at_size(filename, 128, 128, NULL); + g_free(filename); + auto have_preview = (pixbuf != NULL); + + gtk_image_set_from_pixbuf(image, pixbuf); + if (pixbuf) + g_object_unref(pixbuf); + + gtk_file_chooser_set_preview_widget_active(chooser, have_preview); + LEAVE("preview visible is %d", have_preview); +} + +static void +change_image_cb (GtkFileChooser *chooser, void* data) +{ + auto filename{gtk_file_chooser_get_preview_filename(chooser)}; + if (!filename) + return; + g_object_set_data_full(G_OBJECT(chooser), LAST_SELECTION, filename, g_free); +} + +class GncGtkPixmapUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkPixmapUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::PIXMAP} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto string{option.get_value()}; + if (!string.empty()) + { + DEBUG("string = %s", string.c_str()); + auto chooser{GTK_FILE_CHOOSER(get_widget())}; + gtk_file_chooser_select_filename(chooser, string.c_str()); + auto filename{gtk_file_chooser_get_filename(chooser)}; + g_object_set_data_full(G_OBJECT(chooser), LAST_SELECTION, + g_strdup(string.c_str()), g_free); + DEBUG("Set %s, retrieved %s", string.c_str(), + filename ? filename : "(null)"); + update_preview_cb(chooser, &option); + } + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(get_widget())); + DEBUG("filename %s", string ? string : "(null)"); + if (string) + { + option.set_value(std::string{string}); + g_free(string); + } + } +}; + +template<> GtkWidget * +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + + auto button = gtk_button_new_with_label(_("Clear")); + gtk_widget_set_tooltip_text(button, _("Clear any selected image file.")); + + auto widget = gtk_file_chooser_button_new(_("Select image"), + GTK_FILE_CHOOSER_ACTION_OPEN); + gtk_widget_set_tooltip_text(widget, _("Select an image file.")); + g_object_set(G_OBJECT(widget), + "width-chars", 30, + "preview-widget", gtk_image_new(), + (char *)NULL); + + option.set_ui_item(std::make_unique(widget)); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT (widget), "selection-changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + g_signal_connect(G_OBJECT (widget), "selection-changed", + G_CALLBACK(change_image_cb), &option); + g_signal_connect(G_OBJECT (widget), "update-preview", + G_CALLBACK(update_preview_cb), &option); + g_signal_connect_swapped(G_OBJECT (button), "clicked", + G_CALLBACK(gtk_file_chooser_unselect_all), widget); + + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(*enclosing), button, FALSE, FALSE, 0); + + gtk_widget_show(widget); + gtk_widget_show(*enclosing); + + return widget; +} + +static void +radiobutton_set_cb(GtkWidget *w, gpointer data) +{ + GncOption* option = static_cast(data); + gpointer _current, _new_value; + gint current, new_value; + + auto widget = option_get_gtk_widget(option); + + _current = g_object_get_data(G_OBJECT(widget), "gnc_radiobutton_index"); + current = GPOINTER_TO_INT (_current); + + _new_value = g_object_get_data (G_OBJECT(w), "gnc_radiobutton_index"); + new_value = GPOINTER_TO_INT (_new_value); + + if (current == new_value) + return; + + g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index", + GINT_TO_POINTER(new_value)); + gnc_option_changed_widget_cb(widget, option); +} + +class GncGtkRadioButtonUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkRadioButtonUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::RADIOBUTTON} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto index{option.get_value()}; + auto list{gtk_container_get_children(GTK_CONTAINER(get_widget()))}; + auto box{GTK_WIDGET(list->data)}; + g_list_free(list); + + list = gtk_container_get_children(GTK_CONTAINER(box)); + auto node{g_list_nth(list, index)}; + GtkButton* button{}; + if (node) + { + button = GTK_BUTTON(node->data); + } + else + { + PERR("Invalid Radio Button Selection %lu", index); + g_list_free(list); + return; + } + g_list_free(list); + auto val{g_object_get_data (G_OBJECT (button), + "gnc_radiobutton_index")}; + g_return_if_fail (GPOINTER_TO_UINT (val) == index); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto index{g_object_get_data(G_OBJECT(get_widget()), + "gnc_radiobutton_index")}; + option.set_value(GPOINTER_TO_INT(index)); + } +}; + +static GtkWidget * +create_radiobutton_widget(char *name, GncOption& option) +{ + GtkWidget *frame, *box; + GtkWidget *widget = NULL; + + auto num_values{option.num_permissible_values()}; + + g_return_val_if_fail(num_values >= 0, NULL); + + /* Create our button frame */ + frame = gtk_frame_new (name); + + /* Create the button box */ + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); + gtk_box_set_homogeneous (GTK_BOX (box), FALSE); + gtk_container_add (GTK_CONTAINER (frame), box); + + option.set_ui_item(std::make_unique(frame)); + option.set_ui_item_from_option(); + + /* Iterate over the options and create a radio button for each one */ + for (decltype(num_values) i = 0; i < num_values; i++) + { + auto label = option.permissible_value_name(i); + + widget = + gtk_radio_button_new_with_label_from_widget (widget ? + GTK_RADIO_BUTTON (widget) : + NULL, + label && *label ? _(label) : ""); + g_object_set_data (G_OBJECT (widget), "gnc_radiobutton_index", + GINT_TO_POINTER (i)); + g_signal_connect(G_OBJECT(widget), "toggled", + G_CALLBACK(radiobutton_set_cb), &option); + gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0); + } + + return frame; +} + +template<> GtkWidget * +create_option_widget (GncOption& option, GtkGrid *page_box, + GtkLabel *name_label, char *documentation, + /* Return values */ + GtkWidget **enclosing, bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + + align_label (name_label); + + auto widget = create_radiobutton_widget(NULL, option); + + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +class GncGtkDateFormatUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkDateFormatUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::DATE_FORMAT} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widget{GNC_DATE_FORMAT(get_widget())}; + gnc_date_format_set_custom(widget, + option.get_value().c_str()); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GNC_DATE_FORMAT(get_widget())}; + option.set_value(std::string{gnc_date_format_get_custom(widget)}); + } +}; + + +template<> GtkWidget * +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + *enclosing = gnc_date_format_new_without_label (); + align_label (name_label); + + option.set_ui_item(std::make_unique(*enclosing)); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(*enclosing), "format_changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + gtk_widget_show_all(*enclosing); + return *enclosing; +} + +static void +gnc_plot_size_option_set_select_method(GncOption& option, bool set_buttons) +{ + GList* widget_list; + GtkWidget *px_widget, *p_widget; + GtkWidget *widget; + + widget = option_get_gtk_widget(&option); + + widget_list = gtk_container_get_children(GTK_CONTAINER(widget)); + // px_button item 0 + px_widget = static_cast(g_list_nth_data(widget_list, 1)); + // p_button item 2 + p_widget = static_cast(g_list_nth_data(widget_list, 3)); + g_list_free(widget_list); + + if (set_buttons) + { + gtk_widget_set_sensitive(px_widget, TRUE); + gtk_widget_set_sensitive(p_widget, FALSE); + } + else + { + gtk_widget_set_sensitive(p_widget, TRUE); + gtk_widget_set_sensitive(px_widget, FALSE); + } +} + +static void +gnc_rd_option_px_set_cb(GtkWidget *widget, GncOption* option) +{ + option->set_alternate(true); + gnc_option_changed_option_cb(widget, option); +} + +static void +gnc_rd_option_p_set_cb(GtkWidget *widget, GncOption* option) +{ + option->set_alternate(false); + gnc_option_changed_option_cb(widget, option); +} + +class GncGtkPlotSizeUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkPlotSizeUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::PLOT_SIZE} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto widgets{gtk_container_get_children(GTK_CONTAINER(get_widget()))}; + GtkWidget *button{}, *spin{}; + if (option.is_alternate()) + { + button = GTK_WIDGET(g_list_nth_data(widgets, 2)); + spin = GTK_WIDGET(g_list_nth_data(widgets, 3)); + } + else + { + button = GTK_WIDGET(g_list_nth_data(widgets, 2)); + spin = GTK_WIDGET(g_list_nth_data(widgets, 3)); + } + gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), + option.get_value()); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widgets{gtk_container_get_children(GTK_CONTAINER(get_widget()))}; + auto px_button{GTK_BUTTON(g_list_nth_data(widgets, 0))}; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(px_button))) + { + option.set_alternate(false); + auto spin{g_list_nth_data(widgets, 1)}; + option.set_value(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget()))); + } + else + { + option.set_alternate(true); + auto spin{g_list_nth_data(widgets, 3)}; + option.set_value(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget()))); + } + } +}; + + +template<> GtkWidget * +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + GtkWidget *value_percent; + GtkWidget *px_butt, *p_butt; + GtkWidget *hbox; + GtkAdjustment *adj_percent; + + *enclosing = gtk_frame_new(NULL); + gtk_widget_set_halign (GTK_WIDGET(*enclosing), GTK_ALIGN_START); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE); + g_object_set (G_OBJECT(hbox), "margin", 3, NULL); + + auto value_px = create_range_spinner(option); + + adj_percent = GTK_ADJUSTMENT(gtk_adjustment_new(1, 10, 100, 1, 5.0, 0)); + value_percent = gtk_spin_button_new(adj_percent, 1, 0); + gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(value_percent), TRUE); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(value_percent), 100); //default + gtk_entry_set_width_chars(GTK_ENTRY(value_percent), 3); + gtk_widget_set_sensitive(value_percent, FALSE); + + + px_butt = gtk_radio_button_new_with_label(NULL, _("Pixels")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(px_butt), TRUE); + + + p_butt = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(px_butt), _("Percent")); + + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(px_butt), FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(value_px), FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(p_butt), FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(value_percent), + FALSE, FALSE, 0); + + option.set_ui_item(std::make_unique(static_cast(hbox))); + option.set_ui_item_from_option(); + + g_signal_connect(G_OBJECT(value_px), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + g_signal_connect(G_OBJECT(value_percent), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + g_signal_connect(G_OBJECT(px_butt), "toggled", + G_CALLBACK(gnc_rd_option_px_set_cb), &option); + g_signal_connect(G_OBJECT(p_butt), "toggled", + G_CALLBACK(gnc_rd_option_p_set_cb), &option); + + gtk_container_add(GTK_CONTAINER(*enclosing), hbox); + gtk_widget_show_all(*enclosing); + return hbox; +} + +static GtkWidget * +create_budget_widget(GncOption& option) +{ + GtkTreeModel *tm; + GtkComboBox *cb; + GtkCellRenderer *cr; + + tm = gnc_tree_model_budget_new(gnc_get_current_book()); + cb = GTK_COMBO_BOX(gtk_combo_box_new_with_model(tm)); + g_object_unref(tm); + cr = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cb), cr, TRUE); + + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(cb), cr, "text", + BUDGET_NAME_COLUMN, NULL); + return GTK_WIDGET(cb); +} + +class GncGtkBudgetUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkBudgetUIItem(GtkWidget* widget) : + GncOptionGtkUIItem{widget, GncOptionUIType::BUDGET} {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + GtkTreeIter iter; + auto widget{GTK_COMBO_BOX(get_widget())}; + auto instance{option.get_value()}; + if (instance) + { + auto tree_model{gtk_combo_box_get_model(widget)}; + if (gnc_tree_model_budget_get_iter_for_budget(tree_model, &iter, + GNC_BUDGET(instance))) + gtk_combo_box_set_active_iter(widget, &iter); + } + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + GtkTreeIter iter; + auto widget{GTK_COMBO_BOX(get_widget())}; + gtk_combo_box_get_active_iter(widget, &iter); + auto tree_model{gtk_combo_box_get_model(widget)}; + auto budget{gnc_tree_model_budget_get_budget(tree_model, &iter)}; + option.set_value(qof_instance_cast(budget)); + } +}; + +template<> GtkWidget * +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + + auto widget{create_budget_widget(option)}; + + option.set_ui_item(std::make_unique(widget)); + option.set_ui_item_from_option(); + + /* Maybe connect destroy handler for tree model here? */ + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), &option); + + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +static void +gnc_options_ui_factory_initialize(void) +{ + GncOptionUIFactory::set_func(GncOptionUIType::BOOLEAN, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::STRING, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::TEXT, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::CURRENCY, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::COMMODITY, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::MULTICHOICE, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::DATE_ABSOLUTE, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::DATE_RELATIVE, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::DATE_BOTH, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::ACCOUNT_LIST, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::ACCOUNT_SEL, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::LIST, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::NUMBER_RANGE, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::COLOR, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::FONT, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::PLOT_SIZE, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::BUDGET, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::PIXMAP, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::RADIOBUTTON, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::DATE_FORMAT, + create_option_widget); + + +} + +void +gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb) +{ + if (!odb) return; + auto num_split_action = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, + GNC_PREF_NUM_SOURCE); + if (num_split_action) + { + auto option{odb->find_option(OPTION_SECTION_ACCOUNTS, + OPTION_NAME_NUM_FIELD_SOURCE)}; + auto num_source_button{option_get_gtk_widget(option)}; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (num_source_button), + num_split_action); + } +} + diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h deleted file mode 100644 index 3000717348..0000000000 --- a/gnucash/gnome-utils/dialog-options.h +++ /dev/null @@ -1,116 +0,0 @@ -/********************************************************************\ - * dialog-options.h -- GNOME option handling * - * Copyright (C) 1998-2000 Linas Vepstas * - * * - * This program 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 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * -\********************************************************************/ - -#ifndef OPTIONS_DIALOG_H -#define OPTIONS_DIALOG_H - -#include -#include "option-util.h" -#include - -/** A simple wrapper that casts the gpointer result of - * gnc_option_get_widget() already into a GtkWidget*. */ -GtkWidget *gnc_option_get_gtk_widget (GNCOption *option); - -typedef struct gnc_option_win GNCOptionWin; - -typedef void (* GNCOptionWinCallback)(GNCOptionWin *, gpointer data); - -GNCOptionWin * gnc_options_dialog_new_modal (gboolean modal, gchar *title, - const char *component_class, - GtkWindow *parent); -GNCOptionWin * gnc_options_dialog_new (gchar *title, GtkWindow *parent); -GNCOptionWin * gnc_options_dialog_new_w_dialog (gchar *title, GtkWidget *dialog); -void gnc_options_dialog_destroy (GNCOptionWin * win); -void gnc_options_register_stocks (void); - -GtkWidget * gnc_options_dialog_widget (GNCOptionWin * win); -GtkWidget * gnc_options_page_list (GNCOptionWin * win); -GtkWidget * gnc_options_dialog_notebook (GNCOptionWin * win); - -void gnc_options_dialog_changed (GNCOptionWin *win); - -void gnc_option_changed_widget_cb (GtkWidget *widget, GNCOption *option); -void gnc_option_changed_option_cb (GtkWidget *dummy, GNCOption *option); - -void gnc_options_dialog_set_apply_cb (GNCOptionWin * win, - GNCOptionWinCallback thunk, - gpointer cb_data); -void gnc_options_dialog_set_help_cb (GNCOptionWin * win, - GNCOptionWinCallback thunk, - gpointer cb_data); -void gnc_options_dialog_set_close_cb (GNCOptionWin * win, - GNCOptionWinCallback thunk, - gpointer cb_data); - -void gnc_options_dialog_set_global_help_cb (GNCOptionWinCallback thunk, - gpointer cb_data); - -void gnc_options_dialog_build_contents (GNCOptionWin *win, - GNCOptionDB *odb); - -void gnc_options_dialog_build_contents_full (GNCOptionWin *win, - GNCOptionDB *odb, - gboolean show_dialog); - -/* Both apply_cb and close_cb should be scheme functions with 0 arguments. - * References to these functions will be held until the close_cb is called - */ -void gnc_options_dialog_set_scm_callbacks (GNCOptionWin *win, - SCM apply_cb, - SCM close_cb); - -/*****************************************************************/ -/* Option Registration */ - -/* Function to set the UI widget based upon the option */ -typedef GtkWidget * -(*GNCOptionUISetWidget) (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed); - -/* Function to set the UI Value for a particular option */ -typedef gboolean -(*GNCOptionUISetValue) (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value); - -/* Function to get the UI Value for a particular option */ -typedef SCM -(*GNCOptionUIGetValue) (GNCOption *option, GtkWidget *widget); - - -typedef struct gnc_option_def -{ - const char * option_name; - GNCOptionUISetWidget set_widget; - GNCOptionUISetValue set_value; - GNCOptionUIGetValue get_value; -} GNCOptionDef_t; - - -/* Register a new option type in the UI */ -void gnc_options_ui_initialize (void); -void gnc_options_ui_register_option (GNCOptionDef_t *option); -GNCOptionDef_t * gnc_options_ui_get_option (const char *option_name); - -#endif /* OPTIONS_DIALOG_H */ diff --git a/gnucash/gnome-utils/dialog-options.hpp b/gnucash/gnome-utils/dialog-options.hpp new file mode 100644 index 0000000000..71c02c39d3 --- /dev/null +++ b/gnucash/gnome-utils/dialog-options.hpp @@ -0,0 +1,200 @@ +/********************************************************************\ + * dialog-options.hpp -- GNOME option handling * + * Copyright (C) 1998-2000 Linas Vepstas * + * Copyright (c) 2006 David Hampton * + * Copyright (c) 2011 Robert Fewell * + * Copyright 2019-2021 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @addtogroup GUI + @{ */ +/** @addtogroup GuiOptions Options Dialog + @{ */ + +#ifndef GNC_DIALOG_OPTIONS_HPP_ +#define GNC_DIALOG_OPTIONS_HPP_ + +#include + +#include +#include + +/** @fn WidgetCreateFunc + * Function pointer for per-option-type GtkWidget constructors. + * @param option The option to create an element for. + * @param page_box The option dialog page's layout grid + * @param name_label A GtkLabel to attach to the widget + * @param documentation The string to use for the tooltip. + * @param enclosing The parent widget + * @param packed Whether the widget will be packed into an eventbox. + * @return pointer to the widget. + */ + +typedef GtkWidget* (*WidgetCreateFunc)(GncOption&, GtkGrid*, GtkLabel*, char*, + GtkWidget**, bool*); +/** @class GncOptionUIFactory + * Factory class that keeps track of which GncOptionValueType needs which + * WidgetCreateFunc and calls the appropriate one when required. + */ +class GncOptionUIFactory +{ +public: +/** Register a WidgetCreateFunc + * @param type The UI type + * @param func The function to register + */ + static void set_func(GncOptionUIType type, WidgetCreateFunc func); +/** Create a widget + * @param option The option for which to create the widget + * @param page The Option dialog page in which to insert the widget + * @param name The label to attach to the widget + * @param description The text for the widget's tooltip + * @param enclosing The widget's parent + * @param packed Whether the widget will be packed into an eventbox. + * @return pointer to the created widget. + */ + static GtkWidget* create(GncOption&, GtkGrid*, GtkLabel*, char*, + GtkWidget**, bool*); +private: + static std::vector s_registry; + static bool s_initialized; +}; + +/** class GncOptionGtkUIItem + * Gtk-specific Interface class for Option Widget + */ +class GncOptionGtkUIItem : public GncOptionUIItem +{ +public: + GncOptionGtkUIItem(GtkWidget* widget, GncOptionUIType type); + GncOptionGtkUIItem(const GncOptionGtkUIItem& item); + GncOptionGtkUIItem(GncOptionGtkUIItem&&) = default; + virtual ~GncOptionGtkUIItem() override; +/** Control wether the widget is sensitive */ + virtual void set_selectable(bool) const noexcept override; +/** Clear the data from the widget. */ + void clear_ui_item() override; + void set_widget(GtkWidget* widget); + virtual GtkWidget* const get_widget() const { return m_widget; } + static WidgetCreateFunc option_widget_factory(GncOption& option, + GtkGrid* page, + GtkLabel* name, + char* description, + GtkWidget** enclosing, + bool* packed); +private: + GtkWidget* m_widget; +}; + +template GtkWidget* +create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**, + bool*); + +/** Templated cast to convert various QofInstance subtype ptrs into QofInstance* + * to placate the C++ type system. QofInstance is a GObject hierarchy so the + * usual C++ type substitution doesn't work. + */ +template inline const QofInstance* +qof_instance_cast(Instance inst) +{ + static_assert(std::is_pointer_v, "Pointers Only!"); + return reinterpret_cast(inst); +} +class GncOptionsDialog; + +typedef void (* GncOptionsDialogCallback)(GncOptionsDialog*, void* data); + +class GncOptionsDialog +{ + GtkWidget * m_window; + GtkWidget * m_notebook; + GtkWidget * m_page_list_view; + GtkWidget * m_page_list; + GtkButton * m_help_button; + GtkButton * m_cancel_button; + GtkButton * m_apply_button; + GtkButton * m_ok_button; + + bool toplevel; + + GncOptionsDialogCallback m_apply_cb; + gpointer m_apply_cb_data; + + GncOptionsDialogCallback m_help_cb; + gpointer m_help_cb_data; + + GncOptionsDialogCallback m_close_cb; + gpointer m_close_cb_data; + + /* Hold onto this for a complete reset */ + GncOptionDB* m_option_db; + + /* Hold on to this to unregister the right class */ + const char* m_component_class; + + /* widget being destroyed */ + bool m_destroying{false}; + +public: + GncOptionsDialog(const char* title, GtkWindow* parent) : + GncOptionsDialog(false, title, nullptr, parent) {} + GncOptionsDialog(bool modal, const char* title, const char* component_class, + GtkWindow* parent); + GncOptionsDialog(const GncOptionsDialog&) = default; + GncOptionsDialog(GncOptionsDialog&&) = default; + ~GncOptionsDialog(); + + GtkWidget* get_widget() const noexcept { return m_window; } + GtkWidget* get_page_list() const noexcept { return m_page_list; } + GtkWidget* get_page_list_view() const noexcept { return m_page_list_view; } + GtkWidget* get_notebook() const noexcept { return m_notebook; } + GncOptionDB* get_option_db() noexcept { return m_option_db; } + inline void build_contents(GncOptionDB* odb){ + build_contents(odb, true); } + void build_contents(GncOptionDB* odb, bool show_dialog); + void set_sensitive(bool sensitive) noexcept; + void changed() noexcept; + void set_apply_cb(GncOptionsDialogCallback, void* cb_data) noexcept; + void call_apply_cb() noexcept; + void set_help_cb(GncOptionsDialogCallback, void* cb_data) noexcept; + void call_help_cb() noexcept; + void set_close_cb(GncOptionsDialogCallback, void* cb_data) noexcept; + void call_close_cb() noexcept; + void set_book_help_cb() noexcept; + void call_book_help_cb() noexcept; + void set_style_sheet_help_cb() noexcept; + void call_style_sheet_help_cb() noexcept; +}; + +void gnc_option_changed_widget_cb (GtkWidget *widget, GncOption *option); +void gnc_option_changed_option_cb (GtkWidget *dummy, GncOption *option); + +/** + * Set the initial values of new book options to values specified in user + * preferences. + + * Nothing to do with GncOptionsDialog, but it depends on Gtk and s used in + * both assistant-hierarchy and gnc-main-window. + * @param odb: The book's options database. + */ +void gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb); + +#endif // GNC_DIALOG_OPTIONS_HPP_ +/** @} + @} */ diff --git a/gnucash/gnome-utils/dialog-utils.c b/gnucash/gnome-utils/dialog-utils.c index b717bf344d..e19f51209b 100644 --- a/gnucash/gnome-utils/dialog-utils.c +++ b/gnucash/gnome-utils/dialog-utils.c @@ -821,51 +821,6 @@ gnc_new_book_option_display (GtkWidget *parent) return TRUE; } -/* This function returns a widget for selecting a cost policy - */ -GtkWidget * -gnc_cost_policy_select_new (void) -{ - GtkWidget *cost_policy_widget = NULL; - GList *list_of_policies = NULL; - - list_of_policies = gnc_get_valid_policy_list(); - - g_return_val_if_fail(g_list_length (list_of_policies) >= 0, NULL); - if (list_of_policies) - { - GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); - GtkTreeIter iter; - GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); - const char *description; - GList *l = NULL; - - /* Add values to the list store, entry and tooltip */ - for (l = list_of_policies; l != NULL; l = l->next) - { - GNCPolicy *pcy = l->data; - description = PolicyGetDescription (pcy); - - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, - 0, (description && *description) ? _(description) : "", - -1); - } - g_list_free (list_of_policies); - /* Create the new Combo with the store */ - cost_policy_widget = gtk_combo_box_new_with_model (GTK_TREE_MODEL(store)); - - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(cost_policy_widget), renderer, TRUE); - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(cost_policy_widget), - renderer, "text", 0); - g_object_unref (store); - } - return cost_policy_widget; -} - -/* This function returns a string for the CSS 'gnc-class-negative-numbers' class, - * the returned string must be freed - */ gchar* gnc_get_negative_color (void) { diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c index 2c3cfac024..9219ff32d9 100644 --- a/gnucash/gnome-utils/gnc-gnome-utils.c +++ b/gnucash/gnome-utils/gnc-gnome-utils.c @@ -41,7 +41,6 @@ #include "gnc-window.h" #include "gnc-icons.h" #include "dialog-doclink-utils.h" -#include "dialog-options.h" #include "dialog-commodity.h" #include "dialog-totd.h" #include "gnc-ui-util.h" @@ -73,13 +72,10 @@ const gchar *msg_no_help_reason = /* Translators: URI of missing help files */ const gchar *msg_no_help_location = N_("Expected location"); -static void gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat); - void gnc_gnome_utils_init (void) { gnc_component_manager_init (); - gnc_options_ui_initialize (); scm_init_sw_gnome_utils_module(); scm_c_use_module ("sw_gnome_utils"); @@ -87,63 +83,6 @@ gnc_gnome_utils_init (void) } -static void -gnc_global_options_help_cb (GNCOptionWin *win, gpointer dat) -{ - gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_GLOBPREFS); -} - -static void -gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat) -{ - gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_BOOK_OPTIONS); -} - -void -gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win) -{ - gnc_options_dialog_set_help_cb(win, - (GNCOptionWinCallback)gnc_book_options_help_cb, - NULL); -} - -void -gnc_options_dialog_set_new_book_option_values (GNCOptionDB *odb) -{ - GNCOption *num_source_option; - GtkWidget *num_source_is_split_action_button; - gboolean num_source_is_split_action; - - if (!odb) return; - num_source_is_split_action = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, - GNC_PREF_NUM_SOURCE); - if (num_source_is_split_action) - { - num_source_option = gnc_option_db_get_option_by_name(odb, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_NUM_FIELD_SOURCE); - num_source_is_split_action_button = - gnc_option_get_gtk_widget (num_source_option); - gtk_toggle_button_set_active - (GTK_TOGGLE_BUTTON (num_source_is_split_action_button), - num_source_is_split_action); - } -} - -static void -gnc_style_sheet_options_help_cb (GNCOptionWin *win, gpointer dat) -{ - gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_STYLE_SHEET); -} - -void -gnc_options_dialog_set_style_sheet_options_help_cb (GNCOptionWin *win) -{ - gnc_options_dialog_set_help_cb (win, - (GNCOptionWinCallback)gnc_style_sheet_options_help_cb, - NULL); -} - /* gnc_configure_date_format * sets dateFormat to the current value on the scheme side * @@ -772,8 +711,6 @@ gnc_gui_init(void) gnc_file_set_shutdown_callback (gnc_shutdown); - gnc_options_dialog_set_global_help_cb (gnc_global_options_help_cb, NULL); - main_window = gnc_main_window_new (); // Bug#350993: // gtk_widget_show (GTK_WIDGET (main_window)); diff --git a/gnucash/gnome-utils/gnc-gnome-utils.h b/gnucash/gnome-utils/gnc-gnome-utils.h index f7f45b819d..471d67189a 100644 --- a/gnucash/gnome-utils/gnc-gnome-utils.h +++ b/gnucash/gnome-utils/gnc-gnome-utils.h @@ -36,7 +36,6 @@ #define GNC_GNOME_UTILS_H #include -#include "dialog-options.h" /** Initialize the gnome-utils library * Should be run once before using any gnome-utils features. @@ -65,21 +64,6 @@ void gnc_gnome_help (GtkWindow *parent, const char *file_name, */ void gnc_launch_doclink (GtkWindow *parent, const char *uri); -/** Set the help callback to 'gnc_book_options_help_cb' to open a help browser - * and point it to the Book Options link in the Help file. - */ -void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win); - -/** Set the initial values of new book options to values specified in user - * preferences. - */ -void gnc_options_dialog_set_new_book_option_values (GNCOptionDB *odb); - -/** Set the help callback to 'gnc_style_sheet_options_help_cb' to open a help browser - * and point it to the Style Sheet link in the Help file. - */ -void gnc_options_dialog_set_style_sheet_options_help_cb (GNCOptionWin *win); - /** Given a file name, find and load the requested pixmap. This * routine will display an error message if it can't find the file or * load the pixmap. diff --git a/gnucash/gnome-utils/gnc-gobject-utils.h b/gnucash/gnome-utils/gnc-gobject-utils.h index e84e06d57d..c0fd9cff9c 100644 --- a/gnucash/gnome-utils/gnc-gobject-utils.h +++ b/gnucash/gnome-utils/gnc-gobject-utils.h @@ -128,7 +128,7 @@ void gnc_gobject_tracking_dump (void); #define _GNC_DEFINE_TYPE_EXTENDED_BEGIN(TypeName, type_name, TYPE_PARENT, flags) \ \ -static void type_name##_init (TypeName *self, void *class); \ +static void type_name##_init (TypeName *self, void *klass); \ static void type_name##_class_init (TypeName##Class *klass); \ static gpointer type_name##_parent_class = NULL; \ static gint TypeName##_private_offset; \ diff --git a/gnucash/gnome-utils/gnc-icons.c b/gnucash/gnome-utils/gnc-icons.c index 1cbe2483f2..b4e155fa0c 100644 --- a/gnucash/gnome-utils/gnc-icons.c +++ b/gnucash/gnome-utils/gnc-icons.c @@ -33,6 +33,7 @@ #include "gnc-filepath-utils.h" #include "gnc-gnome-utils.h" #include "gnc-path.h" +#include // For define GNC_MOD_GUI static QofLogModule log_module = GNC_MOD_GUI; diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.cpp similarity index 89% rename from gnucash/gnome-utils/gnc-main-window.c rename to gnucash/gnome-utils/gnc-main-window.cpp index fdff025c10..c61de37306 100644 --- a/gnucash/gnome-utils/gnc-main-window.c +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -32,18 +32,21 @@ @author Copyright (C) 2003 Jan Arne Petersen @author Copyright (C) 2003,2005,2006 David Hampton */ -#include - +#include #include #include #include #include +extern "C" +{ +#include + + #include "gnc-plugin.h" #include "gnc-plugin-manager.h" #include "gnc-main-window.h" -#include "dialog-options.h" #include "dialog-preferences.h" #include "dialog-reset-warnings.h" #include "dialog-transfer.h" @@ -71,9 +74,7 @@ #include "gnc-warnings.h" #include "gnc-window.h" #include "gnc-prefs.h" -#include "option-util.h" -// +JSLED -//#include "gnc-html.h" +#include #include "gnc-autosave.h" #include "print-session.h" #ifdef MAC_INTEGRATION @@ -84,6 +85,8 @@ # include # include // for stat(2) #endif +} +#include "dialog-options.hpp" /** Names of signals generated by the main window. */ enum @@ -116,17 +119,30 @@ enum #define DIALOG_BOOK_OPTIONS_CM_CLASS "dialog-book-options" +/** + * Processes selected options in the Book Options dialog: checks book_currency + * and use_split_action_for_num to see if features kvp should be set. To be used + * where ever a new book situation requires book option selection (e.g., not + * just in Book Options dialog opened from main window but also in new-file + * assistant). + * + * @param GncOptionDB * options. + * + * @return TRUE if gnc_gui_refresh_all should be called; otherwise FALSE. + **/ +extern gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options); + /* Static Globals *******************************************************/ /** The debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_GUI; /** A pointer to the parent class of an embedded window. */ -static GObjectClass *parent_class = NULL; +static GObjectClass *parent_class = nullptr; /** An identifier that indicates a "main" window. */ static GQuark window_type = 0; /** A list of all extant main windows. This is for convenience as the * same information can be obtained from the object tracking code. */ -static GList *active_windows = NULL; +static GList *active_windows = nullptr; /** Count down timer for the save changes dialog. If the timer reaches zero * any changes will be saved and the save dialog closed automatically */ static guint secs_to_save = 0; @@ -271,27 +287,27 @@ static GtkActionEntry gnc_menu_actions [] = { /* Toplevel */ - { "FileAction", NULL, N_("_File"), NULL, NULL, NULL, }, - { "EditAction", NULL, N_("_Edit"), NULL, NULL, NULL }, - { "ViewAction", NULL, N_("_View"), NULL, NULL, NULL }, - { "ActionsAction", NULL, N_("_Actions"), NULL, NULL, NULL }, - { "TransactionAction", NULL, N_("Tra_nsaction"), NULL, NULL, NULL }, - { "ReportsAction", NULL, N_("_Reports"), NULL, NULL, NULL }, - { "ToolsAction", NULL, N_("_Tools"), NULL, NULL, NULL }, - { "ExtensionsAction", NULL, N_("E_xtensions"), NULL, NULL, NULL }, - { "WindowsAction", NULL, N_("_Windows"), NULL, NULL, NULL }, - { "HelpAction", NULL, N_("_Help"), NULL, NULL, NULL }, + { "FileAction", nullptr, N_("_File"), nullptr, nullptr, nullptr, }, + { "EditAction", nullptr, N_("_Edit"), nullptr, nullptr, nullptr }, + { "ViewAction", nullptr, N_("_View"), nullptr, nullptr, nullptr }, + { "ActionsAction", nullptr, N_("_Actions"), nullptr, nullptr, nullptr }, + { "TransactionAction", nullptr, N_("Tra_nsaction"), nullptr, nullptr, nullptr }, + { "ReportsAction", nullptr, N_("_Reports"), nullptr, nullptr, nullptr }, + { "ToolsAction", nullptr, N_("_Tools"), nullptr, nullptr, nullptr }, + { "ExtensionsAction", nullptr, N_("E_xtensions"), nullptr, nullptr, nullptr }, + { "WindowsAction", nullptr, N_("_Windows"), nullptr, nullptr, nullptr }, + { "HelpAction", nullptr, N_("_Help"), nullptr, nullptr, nullptr }, /* File menu */ - { "FileImportAction", NULL, N_("_Import"), NULL, NULL, NULL }, - { "FileExportAction", NULL, N_("_Export"), NULL, NULL, NULL }, + { "FileImportAction", nullptr, N_("_Import"), nullptr, nullptr, nullptr }, + { "FileExportAction", nullptr, N_("_Export"), nullptr, nullptr, nullptr }, { "FilePrintAction", "document-print", N_("_Print..."), "p", - N_("Print the currently active page"), NULL + N_("Print the currently active page"), nullptr }, #ifndef GTK_STOCK_PAGE_SETUP -# define GTK_STOCK_PAGE_SETUP NULL +# define GTK_STOCK_PAGE_SETUP nullptr #endif { "FilePageSetupAction", "document-page-setup", N_("Pa_ge Setup..."), "p", @@ -332,7 +348,7 @@ static GtkActionEntry gnc_menu_actions [] = G_CALLBACK (gnc_main_window_cmd_edit_paste) }, { - "EditPreferencesAction", "preferences-system", N_("Pr_eferences"), NULL, + "EditPreferencesAction", "preferences-system", N_("Pr_eferences"), nullptr, N_("Edit the global preferences of GnuCash"), G_CALLBACK (gnc_main_window_cmd_edit_preferences) }, @@ -340,12 +356,12 @@ static GtkActionEntry gnc_menu_actions [] = /* View menu */ { - "ViewSortByAction", NULL, N_("_Sort By..."), NULL, - N_("Select sorting criteria for this page view"), NULL + "ViewSortByAction", nullptr, N_("_Sort By..."), nullptr, + N_("Select sorting criteria for this page view"), nullptr }, { - "ViewFilterByAction", NULL, N_("_Filter By..."), NULL, - N_("Select the account types that should be displayed."), NULL + "ViewFilterByAction", nullptr, N_("_Filter By..."), nullptr, + N_("Select the account types that should be displayed."), nullptr }, { "ViewRefreshAction", "view-refresh", N_("_Refresh"), "r", @@ -355,14 +371,14 @@ static GtkActionEntry gnc_menu_actions [] = /* Actions menu */ - { "ScrubMenuAction", NULL, N_("_Check & Repair"), NULL, NULL, NULL }, + { "ScrubMenuAction", nullptr, N_("_Check & Repair"), nullptr, nullptr, nullptr }, { - "ActionsForgetWarningsAction", NULL, N_("Reset _Warnings..."), NULL, + "ActionsForgetWarningsAction", nullptr, N_("Reset _Warnings..."), nullptr, N_("Reset the state of all warning messages so they will be shown again."), G_CALLBACK (gnc_main_window_cmd_actions_reset_warnings) }, { - "ActionsRenamePageAction", NULL, N_("Re_name Page"), NULL, + "ActionsRenamePageAction", nullptr, N_("Re_name Page"), nullptr, N_("Rename this page."), G_CALLBACK (gnc_main_window_cmd_actions_rename_page) }, @@ -370,12 +386,12 @@ static GtkActionEntry gnc_menu_actions [] = /* Windows menu */ { - "WindowNewAction", NULL, N_("_New Window"), NULL, + "WindowNewAction", nullptr, N_("_New Window"), nullptr, N_("Open a new top-level GnuCash window."), G_CALLBACK (gnc_main_window_cmd_window_new) }, { - "WindowMovePageAction", NULL, N_("New Window with _Page"), NULL, + "WindowMovePageAction", nullptr, N_("New Window with _Page"), nullptr, N_("Move the current page to a new top-level GnuCash window."), G_CALLBACK (gnc_main_window_cmd_window_move_page) }, @@ -393,7 +409,7 @@ static GtkActionEntry gnc_menu_actions [] = G_CALLBACK (gnc_main_window_cmd_help_contents) }, { - "HelpAboutAction", "help-about", N_("_About"), NULL, + "HelpAboutAction", "help-about", N_("_About"), nullptr, N_("About GnuCash"), G_CALLBACK (gnc_main_window_cmd_help_about) }, @@ -406,17 +422,17 @@ static guint gnc_menu_n_actions = G_N_ELEMENTS (gnc_menu_actions); static GtkToggleActionEntry toggle_actions [] = { { - "ViewToolbarAction", NULL, N_("_Toolbar"), NULL, + "ViewToolbarAction", nullptr, N_("_Toolbar"), nullptr, N_("Show/hide the toolbar on this window"), G_CALLBACK (gnc_main_window_cmd_view_toolbar), TRUE }, { - "ViewSummaryAction", NULL, N_("Su_mmary Bar"), NULL, + "ViewSummaryAction", nullptr, N_("Su_mmary Bar"), nullptr, N_("Show/hide the summary bar on this window"), G_CALLBACK (gnc_main_window_cmd_view_summary), TRUE }, { - "ViewStatusbarAction", NULL, N_("Stat_us Bar"), NULL, + "ViewStatusbarAction", nullptr, N_("Stat_us Bar"), nullptr, N_("Show/hide the status bar on this window"), G_CALLBACK (gnc_main_window_cmd_view_statusbar), TRUE }, @@ -429,20 +445,20 @@ static guint n_toggle_actions = G_N_ELEMENTS (toggle_actions); * code. */ static GtkRadioActionEntry radio_entries [] = { - { "Window0Action", NULL, N_("Window _1"), NULL, NULL, 0 }, - { "Window1Action", NULL, N_("Window _2"), NULL, NULL, 1 }, - { "Window2Action", NULL, N_("Window _3"), NULL, NULL, 2 }, - { "Window3Action", NULL, N_("Window _4"), NULL, NULL, 3 }, - { "Window4Action", NULL, N_("Window _5"), NULL, NULL, 4 }, - { "Window5Action", NULL, N_("Window _6"), NULL, NULL, 5 }, - { "Window6Action", NULL, N_("Window _7"), NULL, NULL, 6 }, - { "Window7Action", NULL, N_("Window _8"), NULL, NULL, 7 }, - { "Window8Action", NULL, N_("Window _9"), NULL, NULL, 8 }, - { "Window9Action", NULL, N_("Window _0"), NULL, NULL, 9 }, + { "Window0Action", nullptr, N_("Window _1"), nullptr, nullptr, 0 }, + { "Window1Action", nullptr, N_("Window _2"), nullptr, nullptr, 1 }, + { "Window2Action", nullptr, N_("Window _3"), nullptr, nullptr, 2 }, + { "Window3Action", nullptr, N_("Window _4"), nullptr, nullptr, 3 }, + { "Window4Action", nullptr, N_("Window _5"), nullptr, nullptr, 4 }, + { "Window5Action", nullptr, N_("Window _6"), nullptr, nullptr, 5 }, + { "Window6Action", nullptr, N_("Window _7"), nullptr, nullptr, 6 }, + { "Window7Action", nullptr, N_("Window _8"), nullptr, nullptr, 7 }, + { "Window8Action", nullptr, N_("Window _9"), nullptr, nullptr, 8 }, + { "Window9Action", nullptr, N_("Window _0"), nullptr, nullptr, 9 }, }; /** The number of radio actions provided by the main window. */ -static guint n_radio_entries = G_N_ELEMENTS (radio_entries); +static gsize n_radio_entries = G_N_ELEMENTS (radio_entries); #endif /** These are the "important" actions provided by the main window. @@ -451,7 +467,7 @@ static guint n_radio_entries = G_N_ELEMENTS (radio_entries); static const gchar *gnc_menu_important_actions[] = { "FileCloseAction", - NULL, + nullptr, }; @@ -462,7 +478,7 @@ static const gchar *gnc_menu_important_actions[] = static const gchar *always_insensitive_actions[] = { "FilePrintAction", - NULL + nullptr }; @@ -472,7 +488,7 @@ static const gchar *always_insensitive_actions[] = static const gchar *initially_insensitive_actions[] = { "FileCloseAction", - NULL + nullptr }; @@ -484,7 +500,7 @@ static const gchar *always_hidden_actions[] = { "ViewSortByAction", "ViewFilterByAction", - NULL + nullptr }; @@ -493,7 +509,7 @@ static const gchar *always_hidden_actions[] = static const gchar *immutable_page_actions[] = { "FileCloseAction", - NULL + nullptr }; @@ -502,7 +518,7 @@ static const gchar *immutable_page_actions[] = static const gchar *multiple_page_actions[] = { "WindowMovePageAction", - NULL + nullptr }; @@ -547,19 +563,14 @@ gnc_main_window_is_restoring_pages (GncMainWindow *window) void gnc_main_window_foreach_page (GncMainWindowPageFunc fn, gpointer user_data) { - GncMainWindowPrivate *priv; - GncMainWindow *window; - GncPluginPage *page; - GList *w, *p; - ENTER(" "); - for (w = active_windows; w; w = g_list_next(w)) + for (auto w = active_windows; w; w = g_list_next(w)) { - window = w->data; - priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - for (p = priv->installed_pages; p; p = g_list_next(p)) + auto window{static_cast(w->data)}; + auto priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); + for (auto p = priv->installed_pages; p; p = g_list_next(p)) { - page = p->data; + auto page{static_cast(p->data)}; fn(page, user_data); } } @@ -584,9 +595,9 @@ gnc_main_window_restore_page (GncMainWindow *window, { GncMainWindowPrivate *priv; GncPluginPage *page; - gchar *page_group, *page_type = NULL, *name = NULL; + gchar *page_group, *page_type = nullptr, *name = nullptr; const gchar *class_type; - GError *error = NULL; + GError *error = nullptr; ENTER("window %p, data %p (key file %p, window %d, page start %d, page num %d)", window, data, data->key_file, data->window_num, data->page_offset, @@ -605,7 +616,8 @@ gnc_main_window_restore_page (GncMainWindow *window, } /* See if the page already exists. */ - page = g_list_nth_data(priv->installed_pages, data->page_num); + page = static_cast(g_list_nth_data(priv->installed_pages, + data->page_num)); if (page) { class_type = GNC_PLUGIN_PAGE_GET_CLASS(page)->plugin_name; @@ -624,7 +636,7 @@ gnc_main_window_restore_page (GncMainWindow *window, if (page) { /* Does the page still need to be installed into the window? */ - if (page->window == NULL) + if (page->window == nullptr) { gnc_plugin_page_set_use_new_window(page, FALSE); gnc_main_window_open_page(window, page); @@ -675,8 +687,8 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da gsize length; gboolean max, visible, desired_visibility; gchar *window_group; - gint page_start, page_count, i; - GError *error = NULL; + gsize page_start, page_count, i; + GError *error = nullptr; /* Setup */ ENTER("window %p, data %p (key file %p, window %d)", @@ -731,7 +743,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da } /* Build a window if we don't already have one */ - if (window == NULL) + if (window == nullptr) { DEBUG("Window %d doesn't exist. Creating new window.", data->window_num); DEBUG("active_windows %p.", active_windows); @@ -750,7 +762,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da g_warning("error reading group %s key %s: %s", window_group, WINDOW_GEOMETRY, error->message); g_error_free(error); - error = NULL; + error = nullptr; } else if (length != 2) { @@ -772,7 +784,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da g_warning("error reading group %s key %s: %s", window_group, WINDOW_POSITION, error->message); g_error_free(error); - error = NULL; + error = nullptr; } else if (length != 2) { @@ -811,7 +823,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da g_warning("error reading group %s key %s: %s", window_group, WINDOW_MAXIMIZED, error->message); g_error_free(error); - error = NULL; + error = nullptr; } else if (max) { @@ -828,7 +840,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da g_warning("error reading group %s key %s: %s", window_group, TOOLBAR_VISIBLE, error->message); g_error_free(error); - error = NULL; + error = nullptr; } else if (visible != desired_visibility) { @@ -844,7 +856,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da g_warning("error reading group %s key %s: %s", window_group, TOOLBAR_VISIBLE, error->message); g_error_free(error); - error = NULL; + error = nullptr; } else if (visible != desired_visibility) { @@ -860,7 +872,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da g_warning("error reading group %s key %s: %s", window_group, TOOLBAR_VISIBLE, error->message); g_error_free(error); - error = NULL; + error = nullptr; } else if (visible != desired_visibility) { @@ -889,18 +901,18 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da g_warning("error reading group %s key %s: %s", window_group, WINDOW_PAGEORDER, error->message); g_error_free(error); - error = NULL; + error = nullptr; } else if (length != page_count) { - g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %d", + g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %" G_GSIZE_FORMAT, window_group, WINDOW_PAGEORDER, length, page_count); } else { /* Dump any list that might exist */ g_list_free(priv->usage_order); - priv->usage_order = NULL; + priv->usage_order = nullptr; /* Now rebuild the list from the key file. */ for (i = 0; i < length; i++) { @@ -934,9 +946,8 @@ void gnc_main_window_restore_all_windows(const GKeyFile *keyfile) { gint i, window_count; - GError *error = NULL; + GError *error = nullptr; GncMainWindowSaveData data; - GncMainWindow *window; /* We use the same struct for reading and for writing, so we cast away the const. */ @@ -954,14 +965,15 @@ gnc_main_window_restore_all_windows(const GKeyFile *keyfile) /* Restore all state information on the open windows. Window numbers in state file are 1-based. GList indices are 0-based. */ - gnc_set_busy_cursor (NULL, TRUE); + gnc_set_busy_cursor (nullptr, TRUE); for (i = 0; i < window_count; i++) { data.window_num = i; - window = g_list_nth_data(active_windows, i); + auto window{static_cast(g_list_nth_data(active_windows, + i))}; gnc_main_window_restore_window(window, &data); } - gnc_unset_busy_cursor (NULL); + gnc_unset_busy_cursor (nullptr); statusbar_notification_lastmodified(); } @@ -975,7 +987,7 @@ gnc_main_window_restore_default_state(GncMainWindow *window) * in the window. */ DEBUG("no saved state file"); if (!window) - window = g_list_nth_data(active_windows, 0); + window = static_cast(g_list_nth_data(active_windows, 0)); gtk_widget_show (GTK_WIDGET(window)); action = gnc_main_window_find_action(window, "ViewAccountTreeAction"); gtk_action_activate(action); @@ -1055,7 +1067,7 @@ gnc_main_window_save_window (GncMainWindow *window, GncMainWindowSaveData *data) /* Save page ordering within the notebook. Use +1 notation so the * numbers in the page order match the page sections, at least for * the one window case. */ - order = g_malloc(sizeof(gint) * num_pages); + order = static_cast(g_malloc(sizeof(gint) * num_pages)); for (i = 0; i < num_pages; i++) { gpointer page = g_list_nth_data(priv->usage_order, i); @@ -1141,7 +1153,7 @@ gnc_main_window_finish_pending (GncMainWindow *window) priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); for (item = priv->installed_pages; item; item = g_list_next(item)) { - if (!gnc_plugin_page_finish_pending(item->data)) + if (!gnc_plugin_page_finish_pending(static_cast(item->data))) { return FALSE; } @@ -1158,14 +1170,14 @@ gnc_main_window_all_finish_pending (void) windows = gnc_gobject_tracking_get_list(GNC_MAIN_WINDOW_NAME); for (item = windows; item; item = g_list_next(item)) { - if (!gnc_main_window_finish_pending(item->data)) + if (!gnc_main_window_finish_pending(static_cast(item->data))) { return FALSE; } } if (gnc_gui_refresh_suspended ()) { - gnc_warning_dialog (NULL, "%s", "An operation is still running, wait for it to complete before quitting."); + gnc_warning_dialog (nullptr, "%s", "An operation is still running, wait for it to complete before quitting."); return FALSE; } return TRUE; @@ -1191,7 +1203,7 @@ gnc_main_window_page_exists (GncPluginPage *page) for (walker = active_windows; walker; walker = g_list_next(walker)) { - window = walker->data; + auto window{static_cast(walker->data)}; priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); if (g_list_find(priv->installed_pages, page)) { @@ -1204,7 +1216,7 @@ gnc_main_window_page_exists (GncPluginPage *page) static gboolean auto_save_countdown (GtkWidget *dialog) { GtkWidget *label; - gchar *timeoutstr = NULL; + gchar *timeoutstr = nullptr; /* Stop count down if user closed the dialog since the last time we were called */ if (!GTK_IS_DIALOG (dialog)) @@ -1270,7 +1282,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window) filename = qof_session_get_url(session); if (!strlen (filename)) filename = _(""); - if ((tmp = strrchr(filename, '/')) != NULL) + if ((tmp = strrchr(filename, '/')) != nullptr) filename = tmp + 1; /* Remove any pending auto-save timeouts */ @@ -1283,7 +1295,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window) title, filename); oldest_change = qof_book_get_session_dirty_time(book); - minutes = (gnc_time (NULL) - oldest_change) / 60 + 1; + minutes = (gnc_time (nullptr) - oldest_change) / 60 + 1; hours = minutes / 60; minutes = minutes % 60; days = hours / 24; @@ -1309,7 +1321,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window) _("Close _Without Saving"), GTK_RESPONSE_CLOSE, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Save"), GTK_RESPONSE_APPLY, - NULL); + nullptr); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY); /* If requested by the user, add a timeout to the question to save automatically @@ -1317,7 +1329,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window) */ if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_EXPIRES)) { - gchar *timeoutstr = NULL; + gchar *timeoutstr = nullptr; secs_to_save = gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_WAIT_TIME); timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save); @@ -1327,7 +1339,7 @@ gnc_main_window_prompt_for_save (GtkWidget *window) msg_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG(dialog)); gtk_box_pack_end (GTK_BOX(msg_area), label, TRUE, TRUE, 0); - g_object_set (G_OBJECT (label), "xalign", 0.0, NULL); + g_object_set (G_OBJECT (label), "xalign", 0.0, nullptr); g_object_set_data (G_OBJECT (dialog), "count-down-label", label); g_timeout_add_seconds (1, (GSourceFunc)auto_save_countdown, dialog); @@ -1417,21 +1429,21 @@ gnc_main_window_quit(GncMainWindow *window) for (w = active_windows; w; w = next) { GncMainWindowPrivate *priv; - GncMainWindow *wind = w->data; + GncMainWindow *window = static_cast(w->data); next = g_list_next (w); - wind->window_quitting = TRUE; // set window_quitting on all windows + window->window_quitting = TRUE; //set window_quitting on all windows - priv = GNC_MAIN_WINDOW_GET_PRIVATE(wind); + priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); // if there are no pages destroy window if (priv->installed_pages == NULL) - gtk_widget_destroy (GTK_WIDGET(wind)); + gtk_widget_destroy (GTK_WIDGET(window)); } /* remove the preference callbacks from the main window */ gnc_main_window_remove_prefs (window); - g_timeout_add(250, gnc_main_window_timed_quit, NULL); + g_timeout_add(250, gnc_main_window_timed_quit, nullptr); return TRUE; } return FALSE; @@ -1570,10 +1582,10 @@ gnc_main_window_generate_title (GncMainWindow *window) GncPluginPage *page; QofBook *book; gboolean immutable; - gchar *filename = NULL; - const gchar *uri = NULL; + gchar *filename = nullptr; + const gchar *uri = nullptr; const gchar *dirty = ""; - const gchar *readonly_text = NULL; + const gchar *readonly_text = nullptr; gchar *readonly; gchar *title; @@ -1590,7 +1602,7 @@ gnc_main_window_generate_title (GncMainWindow *window) readonly_text = _("(read-only)"); } } - readonly = (readonly_text != NULL) + readonly = (readonly_text != nullptr) ? g_strdup_printf(" %s", readonly_text) : g_strdup(""); @@ -1666,7 +1678,7 @@ gnc_main_window_update_all_titles (void) { g_list_foreach(active_windows, (GFunc)gnc_main_window_update_title, - NULL); + nullptr); } static void @@ -1688,7 +1700,7 @@ gnc_main_window_attach_to_book (QofSession *session) g_return_if_fail(session); book = qof_session_get_book(session); - qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, NULL); + qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, nullptr); gnc_main_window_update_all_titles(); #ifndef MAC_INTEGRATION gnc_main_window_update_all_menu_items(); @@ -1702,7 +1714,7 @@ static guint gnc_statusbar_notification_messageid = 0; * statusbar by generate_statusbar_lastmodified_message. */ static gboolean statusbar_notification_off(gpointer user_data_unused) { - GncMainWindow *mainwindow = GNC_MAIN_WINDOW (gnc_ui_get_main_window (NULL)); + GncMainWindow *mainwindow = GNC_MAIN_WINDOW (gnc_ui_get_main_window (nullptr)); //g_warning("statusbar_notification_off\n"); if (gnc_statusbar_notification_messageid == 0) return FALSE; @@ -1725,8 +1737,8 @@ static gboolean statusbar_notification_off(gpointer user_data_unused) * data file. */ static gchar *generate_statusbar_lastmodified_message() { - gchar *message = NULL; - const gchar *uri = NULL; + gchar *message = nullptr; + const gchar *uri = nullptr; if (gnc_current_session_exist()) { @@ -1734,7 +1746,7 @@ static gchar *generate_statusbar_lastmodified_message() } if (!(uri && strlen (uri))) - return NULL; + return nullptr; else { if (gnc_uri_targets_local_fs (uri)) @@ -1750,25 +1762,29 @@ static gchar *generate_statusbar_lastmodified_message() if (info && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED)) { - guint64 modtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED); - - /* Translators: This is the date and time that is shown in - the status bar after opening a file: The date and time of - last modification. The string is a format string using - boost::date_time's format flags, see the boost docs for an - explanation of the modifiers. */ - char *time_string = gnc_print_time64 (modtime, - _("Last modified on %a, %b %d, %Y at %I:%M %p")); - //g_warning("got time %ld, str=%s\n", mtime, time_string); - /* Translators: This message appears in the status bar after opening the file. */ - message = g_strdup_printf(_("File %s opened. %s"), - filename, time_string); - free(time_string); - } - else - { - g_warning("Unable to read mtime for file %s\n", filepath); - // message is still NULL + // Access the mtime information through stat(2) + struct stat statbuf; + int r = stat(filepath, &statbuf); + if (r == 0) + { + /* Translators: This is the date and time that is shown in + the status bar after opening a file: The date and time of + last modification. The string is a format string using + boost::date_time's format flags, see the boost docs for an + explanation of the modifiers. */ + char *time_string = gnc_print_time64(statbuf.st_mtime, + _("Last modified on %a, %b %d, %Y at %I:%M %p")); + //g_warning("got time %ld, str=%s\n", mtime, time_string); + /* Translators: This message appears in the status bar after opening the file. */ + message = g_strdup_printf(_("File %s opened. %s"), + filename, time_string); + free(time_string); + } + else + { + g_warning("Unable to read mtime for file %s\n", filepath); + // message is still nullptr + } } g_free(filename); g_free(filepath); @@ -1786,11 +1802,11 @@ statusbar_notification_lastmodified() { // First look up the first GncMainWindow to set the statusbar there GList *iter; - GtkWidget *widget = NULL; + GtkWidget *widget = nullptr; for (iter = active_windows; iter && !(widget && GNC_IS_MAIN_WINDOW(widget)); iter = g_list_next(iter)) { - widget = iter->data; + widget = static_cast(iter->data); } if (widget && GNC_IS_MAIN_WINDOW(widget)) { @@ -1808,7 +1824,7 @@ statusbar_notification_lastmodified() #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL // Also register a timeout callback to remove that statusbar // notification again after 10 seconds - g_timeout_add(10 * 1000, statusbar_notification_off, NULL); // maybe not needed anyway? + g_timeout_add(10 * 1000, statusbar_notification_off, nullptr); // maybe not needed anyway? #endif } else @@ -1862,7 +1878,7 @@ gnc_main_window_update_one_menu_action (GncMainWindow *window, g_object_set(G_OBJECT(action), "label", data->label, "visible", data->visible, - (char *)NULL); + (char *)nullptr); LEAVE(" "); } @@ -1885,7 +1901,7 @@ gnc_main_window_update_radio_button (GncMainWindow *window) GtkAction *action, *first_action; GSList *action_list; gchar *action_name; - gint index; + gsize index; ENTER("window %p", window); @@ -1893,12 +1909,12 @@ gnc_main_window_update_radio_button (GncMainWindow *window) index = g_list_index(active_windows, window); if (index >= n_radio_entries) { - LEAVE("window %d, only %d actions", index, n_radio_entries); + LEAVE("window %" G_GSIZE_FORMAT ", only %" G_GSIZE_FORMAT " actions", index, n_radio_entries); return; } priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - action_name = g_strdup_printf("Window%dAction", index); + action_name = g_strdup_printf("Window%" G_GSIZE_FORMAT "Action", index); action = gtk_action_group_get_action(priv->action_group, action_name); /* Block the signal so as not to affect window ordering (top to @@ -1906,15 +1922,15 @@ gnc_main_window_update_radio_button (GncMainWindow *window) action_list = gtk_radio_action_get_group(GTK_RADIO_ACTION(action)); if (action_list) { - first_action = g_slist_last(action_list)->data; + first_action = static_cast(g_slist_last(action_list)->data); g_signal_handlers_block_by_func(G_OBJECT(first_action), - G_CALLBACK(gnc_main_window_cmd_window_raise), + (gpointer)gnc_main_window_cmd_window_raise, window); DEBUG("blocked signal on %p, set %p active, window %p", first_action, action, window); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); g_signal_handlers_unblock_by_func(G_OBJECT(first_action), - G_CALLBACK(gnc_main_window_cmd_window_raise), + (gpointer)gnc_main_window_cmd_window_raise, window); } g_free(action_name); @@ -1939,13 +1955,13 @@ gnc_main_window_update_menu_item (GncMainWindow *window) { struct menu_update data; gchar **strings, *title, *expanded; - gint index; + gsize index; ENTER("window %p", window); index = g_list_index(active_windows, window); if (index > n_radio_entries) { - LEAVE("skip window %d (only %d entries)", index, n_radio_entries); + LEAVE("skip window %" G_GSIZE_FORMAT " (only %" G_GSIZE_FORMAT " entries)", index, n_radio_entries); return; } @@ -1956,7 +1972,7 @@ gnc_main_window_update_menu_item (GncMainWindow *window) expanded = g_strjoinv("__", strings); if (index < 10) { - data.label = g_strdup_printf("_%d %s", (index + 1) % 10, expanded); + data.label = g_strdup_printf("_%" G_GSIZE_FORMAT " %s", (index + 1) % 10, expanded); g_free(expanded); } else @@ -1966,7 +1982,7 @@ gnc_main_window_update_menu_item (GncMainWindow *window) g_strfreev(strings); data.visible = TRUE; - data.action_name = g_strdup_printf("Window%dAction", index); + data.action_name = g_strdup_printf("Window%" G_GSIZE_FORMAT "Action", index); g_list_foreach(active_windows, (GFunc)gnc_main_window_update_one_menu_action, &data); @@ -1991,23 +2007,22 @@ gnc_main_window_update_all_menu_items (void) { struct menu_update data; gchar *label; - gint i; ENTER(""); /* First update the entries for all existing windows */ g_list_foreach(active_windows, (GFunc)gnc_main_window_update_menu_item, - NULL); + nullptr); g_list_foreach(active_windows, (GFunc)gnc_main_window_update_radio_button, - NULL); + nullptr); /* Now hide any entries that aren't being used. */ data.visible = FALSE; - for (i = g_list_length(active_windows); i < n_radio_entries; i++) + for (gsize i = g_list_length(active_windows); i < n_radio_entries; i++) { - data.action_name = g_strdup_printf("Window%dAction", i); - label = g_strdup_printf("Window _%d", (i - 1) % 10); + data.action_name = g_strdup_printf("Window%" G_GSIZE_FORMAT "Action", i); + label = g_strdup_printf("Window _%" G_GSIZE_FORMAT, (i - 1) % 10); data.label = gettext(label); g_list_foreach(active_windows, @@ -2036,11 +2051,9 @@ static void gnc_main_window_update_tab_close_one_page (GncPluginPage *page, gpointer user_data) { - gboolean *new_value = user_data; - GtkWidget * close_button; - + auto new_value{static_cast(user_data)}; ENTER("page %p, visible %d", page, *new_value); - close_button = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON); + auto close_button{static_cast(g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON))}; if (!close_button) { LEAVE("no close button"); @@ -2115,13 +2128,10 @@ gnc_main_window_update_tab_color_one_page (GncPluginPage *page, static void gnc_main_window_update_tab_color (gpointer gsettings, gchar *pref, gpointer user_data) { - GncMainWindowPrivate *priv; - GncMainWindow *window; - ENTER(" "); g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data)); - window = user_data; - priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); + auto window{static_cast(user_data)}; + auto priv{GNC_MAIN_WINDOW_GET_PRIVATE(window)}; if (g_strcmp0 (GNC_PREF_TAB_COLOR, pref) == 0) priv->show_color_tabs = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR); gnc_main_window_foreach_page (gnc_main_window_update_tab_color_one_page, window); @@ -2181,11 +2191,10 @@ static void gnc_main_window_update_tab_width_one_page (GncPluginPage *page, gpointer user_data) { - gint *new_value = user_data; - GtkWidget *label; + auto new_value{static_cast(user_data)}; ENTER("page %p, visible %d", page, *new_value); - label = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL); + auto label{static_cast(g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL))}; if (!label) { LEAVE("no label"); @@ -2238,7 +2247,7 @@ main_window_find_tab_items (GncMainWindow *window, ENTER("window %p, page %p, label_p %p, entry_p %p", window, page, label_p, entry_p); priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - *label_p = *entry_p = NULL; + *label_p = *entry_p = nullptr; if (!page->notebook_page) { @@ -2261,7 +2270,7 @@ main_window_find_tab_items (GncMainWindow *window, children = gtk_container_get_children(GTK_CONTAINER(tab_hbox)); for (tmp = children; tmp; tmp = g_list_next(tmp)) { - widget = tmp->data; + widget = static_cast(tmp->data); if (GTK_IS_LABEL(widget)) { *label_p = widget; @@ -2286,7 +2295,7 @@ main_window_find_tab_widget (GncMainWindow *window, ENTER("window %p, page %p, widget %p", window, page, widget_p); - *widget_p = NULL; + *widget_p = nullptr; if (!page->notebook_page) { @@ -2314,7 +2323,7 @@ main_window_update_page_name (GncPluginPage *page, ENTER(" "); - if ((name_in == NULL) || (*name_in == '\0')) + if ((name_in == nullptr) || (*name_in == '\0')) { LEAVE("no string"); return; @@ -2355,14 +2364,14 @@ main_window_update_page_name (GncPluginPage *page, /* Update Tooltip on notebook Tab */ if (old_page_long_name && old_page_name - && g_strrstr(old_page_long_name, old_page_name) != NULL) + && g_strrstr(old_page_long_name, old_page_name) != nullptr) { gchar *new_page_long_name; gint string_position; GtkWidget *tab_widget; string_position = strlen(old_page_long_name) - strlen(old_page_name); - new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL); + new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, nullptr); gnc_plugin_page_set_page_long_name(page, new_page_long_name); @@ -2398,7 +2407,7 @@ main_window_update_page_color (GncPluginPage *page, GncMainWindowPrivate *priv; GtkWidget *tab_widget; GdkRGBA tab_color; - gchar *color_string = NULL; + gchar *color_string = nullptr; gboolean want_color = FALSE; ENTER(" "); @@ -2413,7 +2422,7 @@ main_window_update_page_color (GncPluginPage *page, if (want_color) gnc_plugin_page_set_page_color(page, color_string); else - gnc_plugin_page_set_page_color(page, NULL); + gnc_plugin_page_set_page_color(page, nullptr); /* Update the notebook tab */ main_window_find_tab_widget (window, page, &tab_widget); @@ -2438,9 +2447,9 @@ main_window_update_page_color (GncPluginPage *page, stylectxt = gtk_widget_get_style_context (GTK_WIDGET (tab_widget)); col_str = gdk_rgba_to_string (&tab_color); - widget_css = g_strconcat ("*{\n background-color:", col_str, ";\n}\n", NULL); + widget_css = g_strconcat ("*{\n background-color:", col_str, ";\n}\n", nullptr); - gtk_css_provider_load_from_data (provider, widget_css, -1, NULL); + gtk_css_provider_load_from_data (provider, widget_css, -1, nullptr); gtk_style_context_add_provider (stylectxt, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_unref (provider); @@ -2498,7 +2507,7 @@ main_window_update_page_set_read_only_icon (GncPluginPage *page, /* For each, walk the list of container children to get image widget */ for (GList *child = children; child; child = g_list_next (child)) { - GtkWidget *widget = child->data; + GtkWidget *widget = static_cast(child->data); if (GTK_IS_IMAGE(widget)) image = widget; } @@ -2629,7 +2638,7 @@ gnc_main_window_class_init (GncMainWindowClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS(klass); - parent_class = g_type_class_peek_parent (klass); + parent_class = static_cast(g_type_class_peek_parent (klass)); window_type = g_quark_from_static_string ("gnc-main-window"); @@ -2654,7 +2663,7 @@ gnc_main_window_class_init (GncMainWindowClass *klass) G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GncMainWindowClass, page_added), - NULL, NULL, + nullptr, nullptr, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); @@ -2674,24 +2683,24 @@ gnc_main_window_class_init (GncMainWindowClass *klass) G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GncMainWindowClass, page_changed), - NULL, NULL, + nullptr, nullptr, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON, - gnc_main_window_update_tab_close, - NULL); + (gpointer)gnc_main_window_update_tab_close, + nullptr); gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH, - gnc_main_window_update_tab_width, - NULL); + (gpointer)gnc_main_window_update_tab_width, + nullptr); gnc_hook_add_dangler(HOOK_BOOK_SAVED, - (GFunc)gnc_main_window_update_all_titles, NULL, NULL); + (GFunc)gnc_main_window_update_all_titles, nullptr, nullptr); gnc_hook_add_dangler(HOOK_BOOK_OPENED, - (GFunc)gnc_main_window_attach_to_book, NULL, NULL); + (GFunc)gnc_main_window_attach_to_book, nullptr, nullptr); } @@ -2728,7 +2737,7 @@ gnc_main_window_init (GncMainWindow *window, void *data) gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR, - gnc_main_window_update_tab_color, + (gpointer)gnc_main_window_update_tab_color, window); gnc_main_window_setup_window (window); @@ -2750,10 +2759,10 @@ gnc_main_window_init (GncMainWindow *window, void *data) static void gnc_main_window_finalize (GObject *object) { - g_return_if_fail (object != NULL); + g_return_if_fail (object != nullptr); g_return_if_fail (GNC_IS_MAIN_WINDOW (object)); - if (active_windows == NULL) + if (active_windows == nullptr) { /* Oops. User killed last window and we didn't catch it. */ g_idle_add((GSourceFunc)gnc_shutdown, 0); @@ -2770,37 +2779,37 @@ gnc_main_window_remove_prefs (GncMainWindow *window) // remove the registered preference callbacks setup in this file. gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR, - gnc_main_window_update_tab_color, + (gpointer)gnc_main_window_update_tab_color, window); gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON, - gnc_main_window_update_tab_close, - NULL); + (gpointer)gnc_main_window_update_tab_close, + nullptr); gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH, - gnc_main_window_update_tab_width, - NULL); + (gpointer)gnc_main_window_update_tab_width, + nullptr); gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_TOP, - gnc_main_window_update_tab_position, + (gpointer)gnc_main_window_update_tab_position, window); gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM, - gnc_main_window_update_tab_position, + (gpointer)gnc_main_window_update_tab_position, window); gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT, - gnc_main_window_update_tab_position, + (gpointer)gnc_main_window_update_tab_position, window); gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT, - gnc_main_window_update_tab_position, + (gpointer)gnc_main_window_update_tab_position, window); // remove the registered negative color preference callback. @@ -2829,7 +2838,7 @@ gnc_main_window_destroy (GtkWidget *widget) GncPluginManager *manager; GList *plugins; - g_return_if_fail (widget != NULL); + g_return_if_fail (widget != nullptr); g_return_if_fail (GNC_IS_MAIN_WINDOW (widget)); window = GNC_MAIN_WINDOW (widget); @@ -2846,7 +2855,7 @@ gnc_main_window_destroy (GtkWidget *widget) gnc_main_window_close_page(priv->current_page); if (gnc_window_get_progressbar_window() == GNC_WINDOW(window)) - gnc_window_set_progressbar_window(NULL); + gnc_window_set_progressbar_window(nullptr); #ifndef MAC_INTEGRATION /* Update the "Windows" menu in all other windows */ gnc_main_window_update_all_menu_items(); @@ -2858,7 +2867,7 @@ gnc_main_window_destroy (GtkWidget *widget) priv->event_handler_id = 0; g_hash_table_destroy (priv->merged_actions_table); - priv->merged_actions_table = NULL; + priv->merged_actions_table = nullptr; /* GncPluginManager stuff */ manager = gnc_plugin_manager_get (); @@ -2927,13 +2936,10 @@ gnc_main_window_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer GncMainWindow * gnc_main_window_new (void) { - GncMainWindow *window; - GtkWindow *old_window; - - window = g_object_new (GNC_TYPE_MAIN_WINDOW, NULL); + auto window{static_cast(g_object_new (GNC_TYPE_MAIN_WINDOW, nullptr))}; gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); - old_window = gnc_ui_get_main_window (NULL); + auto old_window = gnc_ui_get_main_window (nullptr); if (old_window) { gint width, height; @@ -3065,9 +3071,9 @@ gnc_main_window_disconnect (GncMainWindow *window, /* Disconnect the callbacks */ g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page), - G_CALLBACK(gnc_main_window_popup_menu_cb), page); + (gpointer)gnc_main_window_popup_menu_cb, page); g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page), - G_CALLBACK(gnc_main_window_button_press_cb), page); + (gpointer)gnc_main_window_button_press_cb, page); // Remove the page_changed signal callback gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page)); @@ -3078,7 +3084,7 @@ gnc_main_window_disconnect (GncMainWindow *window, { gnc_plugin_page_unmerge_actions (page, window->ui_merge); gnc_plugin_page_unselected (page); - priv->current_page = NULL; + priv->current_page = nullptr; } /* Remove it from the list of pages in the window */ @@ -3089,10 +3095,10 @@ gnc_main_window_disconnect (GncMainWindow *window, notebook = GTK_NOTEBOOK (priv->notebook); if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_NEXT_RECENT)) { - new_page = g_list_nth_data (priv->usage_order, 0); + new_page = static_cast(g_list_nth_data (priv->usage_order, 0)); if (new_page) { - page_num = gtk_notebook_page_num(notebook, new_page->notebook_page); + page_num = gtk_notebook_page_num(notebook, new_page->notebook_page); gtk_notebook_set_current_page(notebook, page_num); /* This may have caused WebKit to schedule a timer interrupt which it sometimes forgets to cancel before deleting the object. See @@ -3113,14 +3119,14 @@ gnc_main_window_disconnect (GncMainWindow *window, * page is removed. The notebook doesn't generate a signal * for this, therefore the switch_page code in this file * never gets called to generate this signal. */ - gnc_main_window_switch_page(notebook, NULL, -1, window); - //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, NULL); + gnc_main_window_switch_page(notebook, nullptr, -1, window); + //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, nullptr); } gnc_plugin_page_removed (page); gtk_ui_manager_ensure_update (window->ui_merge); - gnc_window_set_status (GNC_WINDOW(window), page, NULL); + gnc_window_set_status (GNC_WINDOW(window), page, nullptr); } @@ -3150,7 +3156,7 @@ gnc_main_window_display_page (GncPluginPage *page) * exists in any window, then that window will be brought to the * front and the notebook switch to display the specified page. If * the page is new then it will be added to the specified window. If - * the window is NULL, the new page will be added to the first + * the window is nullptr, the new page will be added to the first * window. */ void @@ -3185,18 +3191,18 @@ gnc_main_window_open_page (GncMainWindow *window, { window = GNC_MAIN_WINDOW(tmp->data); priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - if (priv->installed_pages == NULL) + if (priv->installed_pages == nullptr) { break; } } - if (tmp == NULL) + if (tmp == nullptr) window = gnc_main_window_new (); gtk_widget_show(GTK_WIDGET(window)); } - else if ((window == NULL) && active_windows) + else if ((window == nullptr) && active_windows) { - window = active_windows->data; + window = static_cast(active_windows->data); } page->window = GTK_WIDGET(window); @@ -3225,7 +3231,7 @@ gnc_main_window_open_page (GncMainWindow *window, gtk_box_set_homogeneous (GTK_BOX (tab_hbox), FALSE); gtk_widget_show (tab_hbox); - if (icon != NULL) + if (icon != nullptr) { image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU); gtk_widget_show (image); @@ -3267,7 +3273,7 @@ gnc_main_window_open_page (GncMainWindow *window, gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE); close_image = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_MENU); gtk_widget_show(close_image); - gtk_widget_get_preferred_size (close_image, &requisition, NULL); + gtk_widget_get_preferred_size (close_image, &requisition, nullptr); gtk_widget_set_size_request(close_button, requisition.width + 4, requisition.height + 2); gtk_container_add(GTK_CONTAINER(close_button), close_image); @@ -3333,7 +3339,7 @@ gnc_main_window_close_page (GncPluginPage *page) /* If this isn't the last window, go ahead and destroy the window. */ priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - if (priv->installed_pages == NULL) + if (priv->installed_pages == nullptr) { if (window->window_quitting) { @@ -3387,7 +3393,7 @@ gnc_main_window_manual_merge_actions (GncMainWindow *window, MergedActionEntry *entry; g_return_if_fail (GNC_IS_MAIN_WINDOW (window)); - g_return_if_fail (group_name != NULL); + g_return_if_fail (group_name != nullptr); g_return_if_fail (GTK_IS_ACTION_GROUP(group)); g_return_if_fail (merge_id > 0); @@ -3418,18 +3424,17 @@ gnc_main_window_merge_actions (GncMainWindow *window, { GncMainWindowPrivate *priv; GncMainWindowActionData *data; - MergedActionEntry *entry; - GError *error = NULL; + GError *error = nullptr; gchar *pathname; g_return_if_fail (GNC_IS_MAIN_WINDOW (window)); - g_return_if_fail (group_name != NULL); - g_return_if_fail (actions != NULL); + g_return_if_fail (group_name != nullptr); + g_return_if_fail (actions != nullptr); g_return_if_fail (n_actions > 0); - g_return_if_fail (filename != NULL); + g_return_if_fail (filename != nullptr); pathname = gnc_filepath_locate_ui_file (filename); - if (pathname == NULL) + if (pathname == nullptr) return; data = g_new0 (GncMainWindowActionData, 1); @@ -3437,11 +3442,11 @@ gnc_main_window_merge_actions (GncMainWindow *window, data->data = user_data; priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - entry = g_new0 (MergedActionEntry, 1); + auto entry{static_cast(g_new0 (MergedActionEntry, 1))}; entry->action_group = gtk_action_group_new (group_name); gtk_action_group_set_translation_domain (entry->action_group, PROJECT_NAME); gtk_action_group_add_actions (entry->action_group, actions, n_actions, data); - if (toggle_actions != NULL && n_toggle_actions > 0) + if (toggle_actions != nullptr && n_toggle_actions > 0) { gtk_action_group_add_toggle_actions (entry->action_group, toggle_actions, n_toggle_actions, @@ -3476,17 +3481,16 @@ gnc_main_window_unmerge_actions (GncMainWindow *window, const gchar *group_name) { GncMainWindowPrivate *priv; - MergedActionEntry *entry; g_return_if_fail (GNC_IS_MAIN_WINDOW (window)); - g_return_if_fail (group_name != NULL); + g_return_if_fail (group_name != nullptr); priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - if (priv->merged_actions_table == NULL) + if (priv->merged_actions_table == nullptr) return; - entry = g_hash_table_lookup (priv->merged_actions_table, group_name); + auto entry{static_cast(g_hash_table_lookup (priv->merged_actions_table, group_name))}; - if (entry == NULL) + if (entry == nullptr) return; gtk_ui_manager_remove_action_group (window->ui_merge, entry->action_group); @@ -3524,7 +3528,7 @@ gnc_main_window_actions_updated (GncMainWindow *window) GtkAction * gnc_main_window_find_action (GncMainWindow *window, const gchar *name) { - GtkAction *action = NULL; + GtkAction *action = nullptr; const GList *groups, *tmp; groups = gtk_ui_manager_get_action_groups(window->ui_merge); @@ -3547,18 +3551,17 @@ gnc_main_window_get_action_group (GncMainWindow *window, const gchar *group_name) { GncMainWindowPrivate *priv; - MergedActionEntry *entry; - g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL); - g_return_val_if_fail (group_name != NULL, NULL); + g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), nullptr); + g_return_val_if_fail (group_name != nullptr, nullptr); priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - if (priv->merged_actions_table == NULL) - return NULL; - entry = g_hash_table_lookup (priv->merged_actions_table, group_name); + if (priv->merged_actions_table == nullptr) + return nullptr; + auto entry{static_cast(g_hash_table_lookup (priv->merged_actions_table, group_name))}; - if (entry == NULL) - return NULL; + if (entry == nullptr) + return nullptr; return entry->action_group; } @@ -3613,7 +3616,7 @@ gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean gboolean has_selection; has_selection = gtk_editable_get_selection_bounds - (GTK_EDITABLE (widget), NULL, NULL); + (GTK_EDITABLE (widget), nullptr, nullptr); can_copy = has_selection; can_cut = has_selection; @@ -3626,7 +3629,7 @@ gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget)); has_selection = gtk_text_buffer_get_selection_bounds - (text_buffer, NULL, NULL); + (text_buffer, nullptr, nullptr); can_copy = has_selection; can_cut = has_selection; @@ -3710,7 +3713,7 @@ gnc_main_window_window_menu (GncMainWindow *window) gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui.xml"); GncMainWindowPrivate *priv; #endif - GError *error = NULL; + GError *error = nullptr; g_assert(filename); merge_id = gtk_ui_manager_add_ui_from_file(window->ui_merge, filename, &error); @@ -3731,7 +3734,7 @@ static gboolean gnc_main_window_page_focus_in (GtkWidget *widget, GdkEvent *event, gpointer user_data) { - GncMainWindow *window = user_data; + auto window{static_cast(user_data)}; GncPluginPage *page = gnc_main_window_get_current_page (window); g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page); @@ -3746,7 +3749,7 @@ gnc_main_window_setup_window (GncMainWindow *window) guint merge_id; GncPluginManager *manager; GList *plugins; - GError *error = NULL; + GError *error = nullptr; gchar *filename; ENTER(" "); @@ -3772,7 +3775,7 @@ gnc_main_window_setup_window (GncMainWindow *window) g_object_set(G_OBJECT(priv->notebook), "scrollable", TRUE, "enable-popup", TRUE, - (char *)NULL); + (char *)nullptr); gtk_widget_show (priv->notebook); g_signal_connect (G_OBJECT (priv->notebook), "switch-page", G_CALLBACK (gnc_main_window_switch_page), window); @@ -3852,21 +3855,21 @@ gnc_main_window_setup_window (GncMainWindow *window) gnc_main_window_window_menu(window); gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_TOP, - gnc_main_window_update_tab_position, + (gpointer)gnc_main_window_update_tab_position, window); gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM, - gnc_main_window_update_tab_position, + (gpointer)gnc_main_window_update_tab_position, window); gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT, - gnc_main_window_update_tab_position, + (gpointer)gnc_main_window_update_tab_position, window); gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT, - gnc_main_window_update_tab_position, + (gpointer)gnc_main_window_update_tab_position, window); - gnc_main_window_update_tab_position(NULL, NULL, window); + gnc_main_window_update_tab_position(nullptr, nullptr, window); gnc_main_window_init_menu_updaters(window); @@ -3925,7 +3928,7 @@ gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window) static void gnc_quartz_set_menu(GncMainWindow* window) { - GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL); + auto theApp{static_cast(g_object_new(GTKOSX_TYPE_APPLICATION, nullptr))}; GtkWidget *menu; GtkWidget *item; @@ -4000,7 +4003,7 @@ gnc_main_window_add_widget (GtkUIManager *merge, * @param window A pointer to the window in question. * * @param action If known, a pointer to the "ViewSummaryBar" - * GtkToggleAction. If NULL, the function will look up this action. + * GtkToggleAction. If nullptr, the function will look up this action. * * @return TRUE if the summarybar should be visible. */ @@ -4010,10 +4013,10 @@ gnc_main_window_show_summarybar (GncMainWindow *window, GtkAction *action) GncMainWindowPrivate *priv; priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - if (action == NULL) + if (action == nullptr) action = gtk_action_group_get_action(priv->action_group, "ViewSummaryAction"); - if (action == NULL) + if (action == nullptr) return TRUE; return gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)); } @@ -4043,7 +4046,7 @@ gnc_main_window_switch_page (GtkNotebook *notebook, g_return_if_fail (GNC_IS_MAIN_WINDOW (window)); priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - if (priv->current_page != NULL) + if (priv->current_page != nullptr) { page = priv->current_page; gnc_plugin_page_unmerge_actions (page, window->ui_merge); @@ -4053,20 +4056,20 @@ gnc_main_window_switch_page (GtkNotebook *notebook, child = gtk_notebook_get_nth_page (notebook, pos); if (child) { - page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL); + page = static_cast(g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL)); } else { - page = NULL; + page = nullptr; } priv->current_page = page; - if (page != NULL) + if (page != nullptr) { /* Update the user interface (e.g. menus and toolbars */ gnc_plugin_page_merge_actions (page, window->ui_merge); - visible = gnc_main_window_show_summarybar(window, NULL); + visible = gnc_main_window_show_summarybar(window, nullptr); gnc_plugin_page_show_summarybar (page, visible); /* Allow page specific actions */ @@ -4115,7 +4118,7 @@ gnc_main_window_page_reordered (GtkNotebook *notebook, priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL); + page = static_cast(g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL)); if (!page) return; old_link = g_list_find (priv->installed_pages, page); @@ -4166,28 +4169,25 @@ gnc_main_window_cmd_page_setup (GtkAction *action, } gboolean -gnc_book_options_dialog_apply_helper(GNCOptionDB * options) +gnc_book_options_dialog_apply_helper(GncOptionDB * options) { QofBook *book = gnc_get_current_book (); gboolean use_split_action_for_num_before = qof_book_use_split_action_for_num_field (book); - gboolean use_book_currency_before = - gnc_book_use_book_currency (book); gint use_read_only_threshold_before = qof_book_get_num_days_autoreadonly (book); gboolean use_split_action_for_num_after; - gboolean use_book_currency_after; gint use_read_only_threshold_after; gboolean return_val = FALSE; - GList *results = NULL, *iter; + GList *results = nullptr, *iter; if (!options) return return_val; results = gnc_option_db_commit (options); for (iter = results; iter; iter = iter->next) { - GtkWidget *dialog = gtk_message_dialog_new(gnc_ui_get_main_window (NULL), - 0, + GtkWidget *dialog = gtk_message_dialog_new(gnc_ui_get_main_window (nullptr), + (GtkDialogFlags)0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", @@ -4201,7 +4201,6 @@ gnc_book_options_dialog_apply_helper(GNCOptionDB * options) qof_book_save_options (book, gnc_option_db_save, options, TRUE); use_split_action_for_num_after = qof_book_use_split_action_for_num_field (book); - use_book_currency_after = gnc_book_use_book_currency (book); // mark cached value as invalid so we get new value book->cached_num_days_autoreadonly_isvalid = FALSE; @@ -4213,11 +4212,6 @@ gnc_book_options_dialog_apply_helper(GNCOptionDB * options) use_split_action_for_num_after); return_val = TRUE; } - if (use_book_currency_before != use_book_currency_after) - { - gnc_book_option_book_currency_selected_cb (use_book_currency_after); - return_val = TRUE; - } if (use_read_only_threshold_before != use_read_only_threshold_after) return_val = TRUE; @@ -4226,10 +4220,10 @@ gnc_book_options_dialog_apply_helper(GNCOptionDB * options) } static void -gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin, +gnc_book_options_dialog_apply_cb(GncOptionsDialog * optionwin, gpointer user_data) { - GNCOptionDB * options = user_data; + auto options{static_cast(user_data)}; if (!options) return; @@ -4238,12 +4232,12 @@ gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin, } static void -gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin, +gnc_book_options_dialog_close_cb(GncOptionsDialog * optionwin, gpointer user_data) { - GNCOptionDB * options = user_data; + auto options{static_cast(user_data)}; - gnc_options_dialog_destroy(optionwin); + delete optionwin; gnc_option_db_destroy(options); } @@ -4266,37 +4260,16 @@ gnc_book_option_num_field_source_change_cb (gboolean num_action) gnc_resume_gui_refresh (); } -/** Calls gnc_book_option_book_currency_selected to initiate registered - * callbacks when currency accounting book option changes to book-currency so - * that registers/reports can update themselves; sets feature flag */ -void -gnc_book_option_book_currency_selected_cb (gboolean use_book_currency) -{ - gnc_suspend_gui_refresh (); - if (use_book_currency) - { - /* Set a feature flag in the book for use of book currency. This will - * prevent older GnuCash versions that don't support this feature from - * opening this file. */ - gnc_features_set_used (gnc_get_current_book(), - GNC_FEATURE_BOOK_CURRENCY); - } - gnc_book_option_book_currency_selected (use_book_currency); - gnc_resume_gui_refresh (); -} - static gboolean show_handler (const char *class_name, gint component_id, gpointer user_data, gpointer iter_data) { - GNCOptionWin *optwin = user_data; - GtkWidget *widget; + auto optwin{static_cast(user_data)}; if (!optwin) return(FALSE); - widget = gnc_options_dialog_widget(optwin); - + auto widget = optwin->get_widget(); gtk_window_present(GTK_WINDOW(widget)); return(TRUE); } @@ -4304,44 +4277,38 @@ show_handler (const char *class_name, gint component_id, GtkWidget * gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent) { - QofBook *book = gnc_get_current_book (); - GNCOptionDB *options; - GNCOptionWin *optionwin; + auto book = gnc_get_current_book (); - options = gnc_option_db_new_for_type (QOF_ID_BOOK); + auto options = gnc_option_db_new(); + gnc_option_db_book_options(options); qof_book_load_options (book, gnc_option_db_load, options); gnc_option_db_clean (options); /* Only allow one Book Options dialog if called from file->properties menu */ if (gnc_forall_gui_components(DIALOG_BOOK_OPTIONS_CM_CLASS, - show_handler, NULL)) + show_handler, nullptr)) { - return NULL; + return nullptr; } - optionwin = gnc_options_dialog_new_modal ( - modal, - (title ? title : _( "Book Options")), - DIALOG_BOOK_OPTIONS_CM_CLASS, parent); - gnc_options_dialog_build_contents (optionwin, options); - - gnc_options_dialog_set_book_options_help_cb (optionwin); - - gnc_options_dialog_set_apply_cb (optionwin, - gnc_book_options_dialog_apply_cb, - (gpointer)options); - gnc_options_dialog_set_close_cb (optionwin, - gnc_book_options_dialog_close_cb, - (gpointer)options); + auto optionwin = new GncOptionsDialog (modal, + (title ? title : _( "Book Options")), + DIALOG_BOOK_OPTIONS_CM_CLASS, parent); + optionwin->build_contents(options); + optionwin->set_book_help_cb(); + optionwin->set_apply_cb(gnc_book_options_dialog_apply_cb, + (gpointer)options); + optionwin->set_close_cb ( gnc_book_options_dialog_close_cb, + (gpointer)options); if (modal) gnc_options_dialog_set_new_book_option_values (options); - return gnc_options_dialog_widget (optionwin); + return optionwin->get_widget(); } static void gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window) { - gnc_book_options_dialog_cb (FALSE, NULL, GTK_WINDOW (window)); + gnc_book_options_dialog_cb (FALSE, nullptr, GTK_WINDOW (window)); } static void @@ -4417,13 +4384,15 @@ gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window) } else if (GTK_IS_TEXT_VIEW(widget)) { - GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget)); - GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget), - GDK_SELECTION_CLIPBOARD); - gboolean editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget)); - + auto text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget)); + auto clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer), + GDK_SELECTION_CLIPBOARD); if (clipboard) - gtk_text_buffer_paste_clipboard (text_buffer, clipboard, NULL, editable); + { + auto editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget)); + gtk_text_buffer_paste_clipboard (text_buffer, clipboard, nullptr, + editable); + } } } @@ -4501,7 +4470,8 @@ gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window) visible = gnc_main_window_show_summarybar(window, action); for (item = priv->installed_pages; item; item = g_list_next(item)) { - gnc_plugin_page_show_summarybar(item->data, visible); + gnc_plugin_page_show_summarybar(static_cast(item->data), + visible); } } @@ -4609,7 +4579,7 @@ gnc_main_window_cmd_window_raise (GtkAction *action, ENTER("action %p, current %p, window %p", action, current, old_window); value = gtk_radio_action_get_current_value(current); - new_window = g_list_nth_data(active_windows, value); + new_window = static_cast(g_list_nth_data(active_windows, value)); gtk_window_present(GTK_WINDOW(new_window)); /* revert the change in the radio group * impossible while handling "changed" (G_SIGNAL_NO_RECURSE) */ @@ -4636,17 +4606,17 @@ gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window) * @param partial The name of the file relative to the gnucash * specific shared data directory. * - * @return The text of the file or NULL. The caller is responsible + * @return The text of the file or nullptr. The caller is responsible * for freeing this string. */ static gchar * get_file (const gchar *partial) { - gchar *filename, *text = NULL; + gchar *filename, *text = nullptr; gsize length; filename = gnc_filepath_locate_doc_file(partial); - if (filename && g_file_get_contents(filename, &text, &length, NULL)) + if (filename && g_file_get_contents(filename, &text, &length, nullptr)) { if (length) { @@ -4656,7 +4626,7 @@ get_file (const gchar *partial) g_free(text); } g_free (filename); - return NULL; + return nullptr; } @@ -4666,7 +4636,7 @@ get_file (const gchar *partial) * @param partial The name of the file relative to the gnucash * specific shared data directory. * - * @return The text of the file as an array of strings, or NULL. The + * @return The text of the file as an array of strings, or nullptr. The * caller is responsible for freeing all the strings and the array. */ static gchar ** @@ -4676,7 +4646,7 @@ get_file_strsplit (const gchar *partial) text = get_file(partial); if (!text) - return NULL; + return nullptr; lines = g_strsplit_set(text, "\r\n", -1); g_free(text); @@ -4715,7 +4685,7 @@ gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window) GNC_ICON_APP, 128, GTK_ICON_LOOKUP_USE_BUILTIN, - NULL); + nullptr); gchar *version = g_strdup_printf ("%s: %s\n%s: %s\nFinance::Quote: %s", _("Version"), gnc_version(), _("Build ID"), gnc_build_id(), @@ -4739,7 +4709,7 @@ gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window) "version", version, "website", PACKAGE_URL, "website-label", _("Visit the GnuCash website."), - NULL); + nullptr); g_free(version); g_free(copyright); @@ -4751,7 +4721,7 @@ gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window) g_strfreev(authors); g_object_unref (logo); g_signal_connect (dialog, "activate-link", - G_CALLBACK (url_signal_cb), NULL); + G_CALLBACK (url_signal_cb), nullptr); /* Set dialog to resize. */ gtk_window_set_resizable(GTK_WINDOW (dialog), TRUE); @@ -4771,15 +4741,15 @@ gnc_main_window_show_all_windows(void) { GList *window_iter; #ifdef MAC_INTEGRATION - GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL); + auto theApp{static_cast(g_object_new(GTKOSX_TYPE_APPLICATION, nullptr))}; #endif - for (window_iter = active_windows; window_iter != NULL; window_iter = window_iter->next) + for (window_iter = active_windows; window_iter != nullptr; window_iter = window_iter->next) { gtk_widget_show(GTK_WIDGET(window_iter->data)); } #ifdef MAC_INTEGRATION g_signal_connect(theApp, "NSApplicationWillTerminate", - G_CALLBACK(gnc_quartz_shutdown), NULL); + G_CALLBACK(gnc_quartz_shutdown), nullptr); gtkosx_application_ready(theApp); g_object_unref (theApp); #endif @@ -4791,13 +4761,13 @@ gnc_ui_get_gtk_window (GtkWidget *widget) GtkWidget *toplevel; if (!widget) - return NULL; + return nullptr; toplevel = gtk_widget_get_toplevel (widget); if (toplevel && GTK_IS_WINDOW (toplevel)) return GTK_WINDOW (toplevel); else - return NULL; + return nullptr; } GtkWindow * @@ -4814,13 +4784,13 @@ gnc_ui_get_main_window (GtkWidget *widget) for (window = active_windows; window; window = window->next) if (gtk_window_is_active (GTK_WINDOW (window->data))) - return window->data; + return static_cast(window->data); for (window = active_windows; window; window = window->next) if (gtk_widget_get_mapped (GTK_WIDGET(window->data))) - return window->data; + return static_cast(window->data); - return NULL; + return nullptr; } @@ -4832,7 +4802,7 @@ gnc_ui_get_main_window (GtkWidget *widget) static GtkWindow * gnc_main_window_get_gtk_window (GncWindow *window) { - g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL); + g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), nullptr); return GTK_WINDOW(window); } @@ -4848,7 +4818,7 @@ gnc_main_window_get_statusbar (GncWindow *window_in) GncMainWindowPrivate *priv; GncMainWindow *window; - g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL); + g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), nullptr); window = GNC_MAIN_WINDOW(window_in); priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); @@ -4867,7 +4837,7 @@ gnc_main_window_get_progressbar (GncWindow *window_in) GncMainWindowPrivate *priv; GncMainWindow *window; - g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL); + g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), nullptr); window = GNC_MAIN_WINDOW(window_in); priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); @@ -4878,25 +4848,21 @@ gnc_main_window_get_progressbar (GncWindow *window_in) static void gnc_main_window_all_ui_set_sensitive (GncWindow *unused, gboolean sensitive) { - GncMainWindow *window; - GncMainWindowPrivate *priv; - GList *groupp, *groups, *winp, *tmp; - GtkWidget *close_button; - for (winp = active_windows; winp; winp = g_list_next(winp)) + for (auto winp = active_windows; winp; winp = g_list_next(winp)) { - window = winp->data; - priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); + auto window{static_cast(winp->data)}; + auto priv = GNC_MAIN_WINDOW_GET_PRIVATE(window); - groups = gtk_ui_manager_get_action_groups(window->ui_merge); - for (groupp = groups; groupp; groupp = g_list_next(groupp)) + auto groups = gtk_ui_manager_get_action_groups(window->ui_merge); + for (auto groupp = groups; groupp; groupp = g_list_next(groupp)) { gtk_action_group_set_sensitive(GTK_ACTION_GROUP(groupp->data), sensitive); } - for (tmp = priv->installed_pages; tmp; tmp = g_list_next(tmp)) + for (auto tmp = priv->installed_pages; tmp; tmp = g_list_next(tmp)) { - close_button = g_object_get_data(tmp->data, PLUGIN_PAGE_CLOSE_BUTTON); + auto close_button{static_cast(g_object_get_data(static_cast(tmp->data), PLUGIN_PAGE_CLOSE_BUTTON))}; if (!close_button) continue; gtk_widget_set_sensitive (close_button, sensitive); @@ -4954,7 +4920,7 @@ do_popup_menu(GncPluginPage *page, GdkEventButton *event) ENTER("page %p, event %p", page, event); ui_merge = gnc_plugin_page_get_ui_merge(page); - if (ui_merge == NULL) + if (ui_merge == nullptr) { LEAVE("no ui merge"); return; @@ -4990,7 +4956,7 @@ gnc_main_window_popup_menu_cb (GtkWidget *widget, GncPluginPage *page) { ENTER("widget %p, page %p", widget, page); - do_popup_menu(page, NULL); + do_popup_menu(page, nullptr); LEAVE(" "); return TRUE; } @@ -5024,12 +4990,9 @@ void gnc_main_window_all_action_set_sensitive (const gchar *action_name, gboolean sensitive) { - GList *tmp; - GtkAction *action; - - for (tmp = active_windows; tmp; tmp = g_list_next(tmp)) + for (auto tmp = active_windows; tmp; tmp = g_list_next(tmp)) { - action = gnc_main_window_find_action (tmp->data, action_name); + auto action{gnc_main_window_find_action (static_cast(tmp->data), action_name)}; gtk_action_set_sensitive (action, sensitive); } } diff --git a/gnucash/gnome-utils/gnc-main-window.h b/gnucash/gnome-utils/gnc-main-window.h index 305494e73b..6116a9c755 100644 --- a/gnucash/gnome-utils/gnc-main-window.h +++ b/gnucash/gnome-utils/gnc-main-window.h @@ -35,6 +35,10 @@ #ifndef __GNC_MAIN_WINDOW_H #define __GNC_MAIN_WINDOW_H +#ifdef __cplusplus +extern "C" +{ +#endif #include #include "gnc-plugin-page.h" @@ -450,12 +454,15 @@ GtkWidget *gnc_book_options_dialog_cb (gboolean modal, gchar *title, * just in Book Options dialog opened from main window but also in new-file * assistant). * - * @param GNCOptionDB * options. + * @param GncOptionDB * options. * * @return TRUE if gnc_gui_refresh_all should be called; otherwise FALSE. **/ -gboolean gnc_book_options_dialog_apply_helper(GNCOptionDB * options); +gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options); +#ifdef __cplusplus +} +#endif #endif /* __GNC_MAIN_WINDOW_H */ /** @} */ diff --git a/gnucash/gnome-utils/gnome-utils.i b/gnucash/gnome-utils/gnome-utils.i index c5c4fe3100..b9bcccf2f8 100644 --- a/gnucash/gnome-utils/gnome-utils.i +++ b/gnucash/gnome-utils/gnome-utils.i @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -49,13 +48,6 @@ SCM scm_init_sw_gnome_utils_module (void); %import "base-typemaps.i" -GNCOptionWin * gnc_options_dialog_new(gchar *title, GtkWindow* parent); -void gnc_options_dialog_destroy(GNCOptionWin * win); -void gnc_options_dialog_build_contents(GNCOptionWin *propertybox, - GNCOptionDB *odb); -void gnc_options_dialog_set_scm_callbacks (GNCOptionWin *win, - SCM apply_cb, SCM close_cb); - gboolean gnc_verify_dialog (GtkWindow *parent, gboolean yes_is_default, const gchar *format, ...); diff --git a/gnucash/gnome/CMakeLists.txt b/gnucash/gnome/CMakeLists.txt index ade0f0ce40..2aafc05305 100644 --- a/gnucash/gnome/CMakeLists.txt +++ b/gnucash/gnome/CMakeLists.txt @@ -27,7 +27,7 @@ set (gnc_gnome_noinst_HEADERS dialog-payment.h dialog-print-check.h dialog-progress.h - dialog-report-column-view.h + dialog-report-column-view.hpp dialog-report-style-sheet.h dialog-sx-editor.h dialog-sx-from-trans.h @@ -64,11 +64,11 @@ gnc_add_swig_guile_command (swig-gnome-c set (gnc_gnome_SOURCES assistant-acct-period.c - assistant-hierarchy.c + assistant-hierarchy.cpp assistant-loan.cpp assistant-stock-split.c assistant-stock-transaction.cpp - business-options-gnome.c + business-options-gnome.cpp business-urls.c business-gnome-utils.c dialog-doclink.c @@ -93,8 +93,8 @@ set (gnc_gnome_SOURCES dialog-price-edit-db.c dialog-print-check.c dialog-progress.c - dialog-report-column-view.c - dialog-report-style-sheet.c + dialog-report-column-view.cpp + dialog-report-style-sheet.cpp dialog-sx-editor.c dialog-sx-from-trans.c dialog-sx-since-last-run.c @@ -112,14 +112,14 @@ set (gnc_gnome_SOURCES gnc-plugin-page-invoice.c gnc-plugin-page-owner-tree.c gnc-plugin-page-register.c - gnc-plugin-page-report.c + gnc-plugin-page-report.cpp gnc-plugin-page-sx-list.c gnc-split-reg.c reconcile-view.c search-owner.c top-level.c window-reconcile.c - window-report.c + window-report.cpp window-autoclear.c ) diff --git a/gnucash/gnome/assistant-hierarchy.c b/gnucash/gnome/assistant-hierarchy.cpp similarity index 91% rename from gnucash/gnome/assistant-hierarchy.c rename to gnucash/gnome/assistant-hierarchy.cpp index 54873226de..5f60c10426 100644 --- a/gnucash/gnome/assistant-hierarchy.c +++ b/gnucash/gnome/assistant-hierarchy.cpp @@ -22,28 +22,28 @@ * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ +#include +#include +#include +#include + +extern "C" +{ #include #include -#include #if PLATFORM(WINDOWS) #include #endif -#include -#include -#include #include #include #include - #ifdef MAC_INTEGRATION #include #endif - #include "gnc-account-merge.h" #include "dialog-new-user.h" -#include "dialog-options.h" #include "dialog-utils.h" #include "dialog-file-access.h" #include "assistant-hierarchy.h" @@ -66,6 +66,10 @@ #include "gnc-plugin-page-account-tree.h" #include "gnc-engine.h" +} +#include +#include + static QofLogModule log_module = GNC_MOD_IMPORT; #define GNC_PREFS_GROUP "dialogs.new-hierarchy" @@ -119,8 +123,8 @@ typedef struct gboolean use_defaults; gboolean new_book; /* presumably only used for new book creation but we check*/ - GNCOptionDB *options; - GNCOptionWin *optionwin; + GncOptionDB *options; + GncOptionsDialog *optionwin; GncHierarchyAssistantFinishedCallback when_completed; @@ -154,7 +158,7 @@ delete_hierarchy_dialog (hierarchy_data *data) static void destroy_hash_helper (gpointer key, gpointer value, gpointer user_data) { - gnc_numeric *balance = value; + auto balance{static_cast(value)}; g_free (balance); } @@ -166,9 +170,9 @@ gnc_hierarchy_destroy_cb (GtkWidget *obj, hierarchy_data *data) hash = data->balance_hash; if (hash) { - g_hash_table_foreach (hash, destroy_hash_helper, NULL); + g_hash_table_foreach (hash, destroy_hash_helper, nullptr); g_hash_table_destroy (hash); - data->balance_hash = NULL; + data->balance_hash = nullptr; } g_free (data->gnc_accounts_dir); @@ -177,12 +181,10 @@ gnc_hierarchy_destroy_cb (GtkWidget *obj, hierarchy_data *data) static gnc_numeric get_final_balance (GHashTable *hash, Account *account) { - gnc_numeric *balance; - if (!hash || !account) return gnc_numeric_zero (); - balance = g_hash_table_lookup(hash, account); + auto balance{static_cast(g_hash_table_lookup(hash, account))}; if (balance) return *balance; return gnc_numeric_zero (); @@ -191,12 +193,10 @@ get_final_balance (GHashTable *hash, Account *account) static void set_final_balance (GHashTable *hash, Account *account, gnc_numeric in_balance) { - gnc_numeric *balance; - if (!hash || !account) return; - balance = g_hash_table_lookup (hash, account); + auto balance{static_cast(g_hash_table_lookup(hash, account))}; if (balance) { *balance = in_balance; @@ -218,7 +218,7 @@ mac_locale() NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSLocale* locale = [NSLocale currentLocale]; NSString* locale_str; - char *retval = NULL; + char *retval = nullptr; @try { locale_str =[[[locale objectForKey: NSLocaleLanguageCode] @@ -241,7 +241,7 @@ mac_locale() static gchar* gnc_get_ea_locale_dir(const char *top_dir) { - static gchar *default_locale = "C"; + static const char* default_locale = "C"; gchar *ret; gchar *locale; GStatBuf buf; @@ -274,11 +274,11 @@ gnc_get_ea_locale_dir(const char *top_dir) #elif defined MAC_INTEGRATION locale = mac_locale(); # else - locale = g_strdup(setlocale(LC_MESSAGES, NULL)); + locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); #endif i = strlen(locale); - ret = g_build_filename(top_dir, locale, (char *)NULL); + ret = g_build_filename(top_dir, locale, (char *)nullptr); while (g_stat(ret, &buf) != 0) { @@ -286,12 +286,12 @@ gnc_get_ea_locale_dir(const char *top_dir) if (i < 1) { g_free(ret); - ret = g_build_filename(top_dir, default_locale, (char *)NULL); + ret = g_build_filename(top_dir, default_locale, (char *)nullptr); break; } locale[i] = '\0'; g_free(ret); - ret = g_build_filename(top_dir, locale, (char *)NULL); + ret = g_build_filename(top_dir, locale, (char *)nullptr); } g_free(locale); @@ -313,8 +313,8 @@ region_combo_changed_cb (GtkComboBox *widget, hierarchy_data *data) GtkTreeModel *filter_model = gtk_combo_box_get_model (GTK_COMBO_BOX(data->region_combo)); GtkTreeModel *region_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(filter_model)); GtkTreeIter filter_iter, region_iter; - gchar *lang_reg = NULL; - gchar *account_path = NULL; + gchar *lang_reg = nullptr; + gchar *account_path = nullptr; if (gtk_combo_box_get_active_iter (widget, &filter_iter)) { @@ -335,13 +335,13 @@ region_combo_changed_cb (GtkComboBox *widget, hierarchy_data *data) /* Remove the old account tree */ if (data->category_accounts_tree) gtk_widget_destroy(GTK_WIDGET(data->category_accounts_tree)); - data->category_accounts_tree = NULL; + data->category_accounts_tree = nullptr; // clear the categories list store in prep for new load if (cat_list) gtk_list_store_clear (cat_list); - account_path = g_build_filename (data->gnc_accounts_dir, lang_reg, NULL); + account_path = g_build_filename (data->gnc_accounts_dir, lang_reg, nullptr); qof_event_suspend (); list = gnc_load_example_account_list (account_path); @@ -350,7 +350,7 @@ region_combo_changed_cb (GtkComboBox *widget, hierarchy_data *data) if (data->initial_category) { gtk_tree_row_reference_free (data->initial_category); - data->initial_category = NULL; + data->initial_category = nullptr; } // repopulate the category list @@ -360,7 +360,7 @@ region_combo_changed_cb (GtkComboBox *widget, hierarchy_data *data) { path = gtk_tree_row_reference_get_path (data->initial_category); gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(data->categories_tree), - path, NULL, TRUE, 0.5, 0.5); + path, nullptr, TRUE, 0.5, 0.5); } else path = gtk_tree_path_new_first (); @@ -393,8 +393,8 @@ region_combo_change_filter_cb (GtkComboBox *widget, hierarchy_data *data) GtkTreeModel *sort_model = gtk_combo_box_get_model (GTK_COMBO_BOX(data->language_combo)); GtkTreeModel *language_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT(sort_model)); GtkListStore *cat_list = GTK_LIST_STORE(gtk_tree_view_get_model (data->categories_tree)); - GtkTreeIter *iter = NULL; - gchar *language = NULL; + GtkTreeIter *iter = nullptr; + gchar *language = nullptr; gint count = 0; gboolean valid; @@ -408,7 +408,7 @@ region_combo_change_filter_cb (GtkComboBox *widget, hierarchy_data *data) // loop through the regions and filter any out that are not linked to language setting while (valid) { - gchar *region_test = NULL; + gchar *region_test = nullptr; gtk_tree_model_get (region_model, ®ion_iter, LANGUAGE_STRING, ®ion_test, -1); @@ -433,7 +433,7 @@ region_combo_change_filter_cb (GtkComboBox *widget, hierarchy_data *data) // if we only have a language or just one region activate it if (count == 1) { - gchar *region_label = NULL; + gchar *region_label = nullptr; GtkTreeIter filter_iter; gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER(filter_model), &filter_iter, @@ -476,10 +476,10 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir) { GtkListStore *language_store = gtk_list_store_new (1, G_TYPE_STRING); GtkListStore *region_store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN); - GtkTreeModel *filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL(region_store), NULL); + GtkTreeModel *filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL(region_store), nullptr); GtkTreeModel *sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(language_store)); GtkTreeIter language_iter, region_iter; - gchar *start_region = NULL; + gchar *start_region = nullptr; gboolean valid; // set sort order @@ -495,11 +495,11 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir) if (g_file_test (data->gnc_accounts_dir, G_FILE_TEST_IS_DIR)) { - GHashTable *testhash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - GDir *acct_dir = g_dir_open (data->gnc_accounts_dir, 0, NULL); + GHashTable *testhash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nullptr); + GDir *acct_dir = g_dir_open (data->gnc_accounts_dir, 0, nullptr); const gchar *name = "a"; - while (name != NULL) + while (name != nullptr) { name = g_dir_read_name (acct_dir); @@ -524,7 +524,7 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir) start_region = g_strdup (parts[0]); } // add the region part to the region model store - if (parts[1] != NULL) + if (parts[1] != nullptr) gtk_list_store_set (region_store, ®ion_iter, REGION_STRING, parts[1], -1); else gtk_list_store_set (region_store, ®ion_iter, REGION_STRING, "--", -1); @@ -545,12 +545,13 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir) lang_name = g_strdup (parts[0]); // see if language is in hash table so we only add it once. - if (g_hash_table_lookup (testhash, lang_name) == NULL) + if (g_hash_table_lookup (testhash, lang_name) == nullptr) { + static const char* t_str{"test"}; gtk_list_store_append (language_store, &language_iter); gtk_list_store_set (language_store, &language_iter, LANGUAGE_STRING, lang_name, -1); - g_hash_table_insert (testhash, g_strdup (lang_name), "test"); + g_hash_table_insert (testhash, g_strdup (lang_name), &t_str); } g_strfreev (parts); g_free (lang_name); @@ -564,7 +565,7 @@ update_language_region_combos (hierarchy_data *data, const gchar *locale_dir) valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(language_store), &language_iter); while (valid) { - gchar *language_test = NULL; + gchar *language_test = nullptr; gtk_tree_model_get (GTK_TREE_MODEL(language_store), &language_iter, LANGUAGE_STRING, &language_test, -1); @@ -676,8 +677,8 @@ add_one_category (GncExampleAccount *acc, GtkTreePath* path; gboolean use_defaults; - g_return_if_fail(acc != NULL); - g_return_if_fail(data != NULL); + g_return_if_fail(acc != nullptr); + g_return_if_fail(data != nullptr); view = data->categories_tree; store = GTK_LIST_STORE(gtk_tree_view_get_model(view)); @@ -755,11 +756,11 @@ account_categories_tree_view_prepare (hierarchy_data *data) data); renderer = gtk_cell_renderer_toggle_new (); - g_object_set (G_OBJECT (renderer), "activatable", TRUE, NULL); + g_object_set (G_OBJECT (renderer), "activatable", TRUE, nullptr); column = gtk_tree_view_column_new_with_attributes (_("Selected"), renderer, "active", COL_CHECKED, - NULL); + nullptr); gtk_tree_view_append_column (tree_view, column); gtk_tree_view_column_set_sort_column_id (column, COL_CHECKED); g_signal_connect (G_OBJECT (renderer), "toggled", @@ -771,7 +772,7 @@ account_categories_tree_view_prepare (hierarchy_data *data) column = gtk_tree_view_column_new_with_attributes (_("Account Types"), renderer, "text", COL_TITLE, - NULL); + nullptr); gtk_tree_view_append_column (tree_view, column); gtk_tree_view_column_set_sort_column_id (column, COL_TITLE); @@ -779,7 +780,7 @@ account_categories_tree_view_prepare (hierarchy_data *data) // column = gtk_tree_view_column_new_with_attributes (_("Description"), // renderer, // "text", COL_SHORT_DESCRIPTION, -// NULL); +// nullptr); // gtk_tree_view_append_column (tree_view, column); // gtk_tree_view_column_set_sort_column_id (column, COL_SHORT_DESCRIPTION); @@ -793,7 +794,7 @@ account_categories_tree_view_prepare (hierarchy_data *data) if (data->initial_category) { path = gtk_tree_row_reference_get_path (data->initial_category); - gtk_tree_view_scroll_to_cell (tree_view, path, NULL, TRUE, 0.5, 0.5); + gtk_tree_view_scroll_to_cell (tree_view, path, nullptr, TRUE, 0.5, 0.5); } else path = gtk_tree_path_new_first (); @@ -832,7 +833,7 @@ on_choose_account_categories_prepare (hierarchy_data *data) /* clear out the description/tree */ if (data->category_accounts_tree) gtk_widget_destroy(GTK_WIDGET(data->category_accounts_tree)); - data->category_accounts_tree = NULL; + data->category_accounts_tree = nullptr; buffer = gtk_text_view_get_buffer(data->category_description); gtk_text_buffer_set_text(buffer, "", -1); @@ -861,7 +862,7 @@ categories_tree_selection_changed (GtkTreeSelection *selection, /* Remove the old account tree */ if (data->category_accounts_tree) gtk_widget_destroy(GTK_WIDGET(data->category_accounts_tree)); - data->category_accounts_tree = NULL; + data->category_accounts_tree = nullptr; /* Add a new one if something selected */ if (gtk_tree_selection_get_selected (selection, &model, &iter)) @@ -911,7 +912,7 @@ select_helper (GtkListStore *store, g_return_val_if_fail(GTK_IS_LIST_STORE(store), FALSE); gtk_tree_model_get (GTK_TREE_MODEL(store), iter, COL_ACCOUNT, &gea, -1); - if ((gea != NULL) && !gea->exclude_from_select_all) + if ((gea != nullptr) && !gea->exclude_from_select_all) { gtk_list_store_set(store, iter, COL_CHECKED, GPOINTER_TO_INT(data), @@ -946,11 +947,11 @@ clear_all_clicked (GtkButton *button, static void delete_our_account_tree (hierarchy_data *data) { - if (data->our_account_tree != NULL) + if (data->our_account_tree != nullptr) { xaccAccountBeginEdit (data->our_account_tree); xaccAccountDestroy (data->our_account_tree); - data->our_account_tree = NULL; + data->our_account_tree = nullptr; } } @@ -976,7 +977,7 @@ struct add_group_data_struct static void add_groups_for_each (Account *toadd, gpointer data) { - struct add_group_data_struct *dadata = data; + auto dadata{static_cast(data)}; Account *foundact; foundact = gnc_account_lookup_by_name(dadata->to, xaccAccountGetName(toadd)); @@ -1015,7 +1016,7 @@ add_new_accounts_with_random_guids (Account *into, Account *from, { struct add_group_data_struct data; data.to = into; - data.parent = NULL; + data.parent = nullptr; data.com = com; gnc_account_foreach_child (from, add_groups_for_each, &data); @@ -1029,7 +1030,7 @@ hierarchy_merge_accounts (GSList *dalist, gnc_commodity *com) for (mark = dalist; mark; mark = mark->next) { - GncExampleAccount *xea = mark->data; + auto xea{static_cast(mark->data)}; add_new_accounts_with_random_guids (ret, xea->root, com); } @@ -1062,7 +1063,7 @@ accumulate_accounts (GtkListStore *store, static GSList * get_selected_account_list (GtkTreeView *tree_view) { - GSList *actlist = NULL; + GSList *actlist = nullptr; GtkTreeModel *model; model = gtk_tree_view_get_model (tree_view); @@ -1124,7 +1125,7 @@ balance_cell_data_func (GtkTreeViewColumn *tree_column, "text", string, "editable", allow_value, "sensitive", allow_value, - NULL); + nullptr); } static void @@ -1138,20 +1139,20 @@ balance_cell_edited (GtkCellRendererText *cell, gnc_numeric amount; hierarchy_data *data = (hierarchy_data *)user_data; - g_return_if_fail(data != NULL); + g_return_if_fail(data != nullptr); account = gnc_tree_view_account_get_selected_account(data->final_account_tree); - if (account == NULL) + if (account == nullptr) { g_critical("account is null"); return; } - error_loc = NULL; + error_loc = nullptr; if (!gnc_exp_parser_parse (new_text, &amount, &error_loc)) { amount = gnc_numeric_zero(); - g_object_set (G_OBJECT(cell), "text", "", NULL); + g_object_set (G_OBJECT(cell), "text", "", nullptr); } /* Bug#348364: Emulating price-cell, we need to ensure the denominator of * the amount is in the SCU of the account's commodity (so @@ -1163,7 +1164,7 @@ balance_cell_edited (GtkCellRendererText *cell, amount = gnc_numeric_convert(amount, account_cmdty_fraction, GNC_HOW_RND_ROUND_HALF_UP); } set_final_balance (data->balance_hash, account, amount); - qof_event_gen (QOF_INSTANCE(account), QOF_EVENT_MODIFY, NULL); + qof_event_gen (QOF_INSTANCE(account), QOF_EVENT_MODIFY, nullptr); } static void @@ -1211,7 +1212,7 @@ placeholder_cell_toggled (GtkCellRendererToggle *cell_renderer, GtkTreePath *treepath; hierarchy_data *data = (hierarchy_data *)user_data; - g_return_if_fail(data != NULL); + g_return_if_fail(data != nullptr); treepath = gtk_tree_path_new_from_string (path); @@ -1226,7 +1227,7 @@ placeholder_cell_toggled (GtkCellRendererToggle *cell_renderer, if (!state) { set_final_balance (data->balance_hash, account, gnc_numeric_zero()); - qof_event_gen (QOF_INSTANCE(account), QOF_EVENT_MODIFY, NULL); + qof_event_gen (QOF_INSTANCE(account), QOF_EVENT_MODIFY, nullptr); } gtk_tree_path_free (treepath); } @@ -1238,16 +1239,15 @@ use_existing_account_data_func(GtkTreeViewColumn *tree_column, GtkTreeIter *iter, gpointer user_data) { - Account *new_acct; Account *real_root; GncAccountMergeDisposition disposition; - char *to_user = "(error; unknown condition)"; + auto to_user{"(error; unknown condition)"}; g_return_if_fail (GTK_TREE_MODEL (tree_model)); - new_acct = gnc_tree_view_account_get_account_from_iter(tree_model, iter); - if (new_acct == NULL) + auto new_acct{static_cast(gnc_tree_view_account_get_account_from_iter(tree_model, iter))}; + if (!new_acct) { - g_object_set (G_OBJECT(cell), "text", "(null account)", NULL); + g_object_set (G_OBJECT(cell), "text", "(null account)", nullptr); return; } @@ -1263,7 +1263,7 @@ use_existing_account_data_func(GtkTreeViewColumn *tree_column, break; } - g_object_set(G_OBJECT(cell), "text", to_user, NULL); + g_object_set(G_OBJECT(cell), "text", to_user, nullptr); } void @@ -1287,7 +1287,7 @@ on_final_account_prepare (hierarchy_data *data) if (data->final_account_tree) { gtk_widget_destroy(GTK_WIDGET(data->final_account_tree)); - data->final_account_tree = NULL; + data->final_account_tree = nullptr; } delete_our_account_tree (data); @@ -1330,17 +1330,17 @@ on_final_account_prepare (hierarchy_data *data) g_object_set(G_OBJECT (renderer), "activatable", TRUE, "sensitive", TRUE, - NULL); + nullptr); g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (placeholder_cell_toggled), data); column = gtk_tree_view_column_new_with_attributes(_("Placeholder"), - renderer, NULL); + renderer, nullptr); gtk_tree_view_column_set_cell_data_func (column, renderer, placeholder_cell_data_func, - (gpointer)data, NULL); + (gpointer)data, nullptr); gnc_tree_view_append_column (GNC_TREE_VIEW(tree_view), column); } @@ -1349,16 +1349,16 @@ on_final_account_prepare (hierarchy_data *data) renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "xalign", 1.0, - (char *)NULL); + (char *)nullptr); g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (balance_cell_edited), data); column = gtk_tree_view_column_new_with_attributes (_("Opening Balance"), renderer, - NULL); + nullptr); gtk_tree_view_column_set_cell_data_func (column, renderer, balance_cell_data_func, - (gpointer)data, NULL); + (gpointer)data, nullptr); gnc_tree_view_append_column (GNC_TREE_VIEW(tree_view), column); } @@ -1368,16 +1368,16 @@ on_final_account_prepare (hierarchy_data *data) GList *renderers; column = gnc_tree_view_add_text_column(GNC_TREE_VIEW(tree_view), _("Use Existing"), - NULL, - NULL, + nullptr, + nullptr, "yes", GNC_TREE_VIEW_COLUMN_DATA_NONE, GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS, - NULL); + nullptr); renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column)); - g_object_set(G_OBJECT(renderer), "xalign", 1.0, (char*)NULL); + g_object_set(G_OBJECT(renderer), "xalign", 1.0, (char*)nullptr); gtk_tree_view_column_set_cell_data_func(column, GTK_CELL_RENDERER(renderers->data), - use_existing_account_data_func, (gpointer)data, NULL); + use_existing_account_data_func, (gpointer)data, nullptr); g_list_free(renderers); } @@ -1396,7 +1396,7 @@ on_cancel (GtkAssistant *gtkassistant, { gnc_suspend_gui_refresh (); if (data->new_book) - gnc_options_dialog_destroy (data->optionwin); + delete data->optionwin; delete_hierarchy_dialog (data); delete_our_account_tree (data); @@ -1414,7 +1414,8 @@ starting_balance_helper (Account *account, hierarchy_data *data) balance = gnc_numeric_neg(balance); if (!gnc_numeric_zero_p (balance) && gnc_commodity_is_currency (xaccAccountGetCommodity (account))) - gnc_account_create_opening_balance (account, balance, gnc_time (NULL), + gnc_account_create_opening_balance (account, balance, + gnc_time (nullptr), gnc_get_current_book ()); } @@ -1443,7 +1444,7 @@ on_finish (GtkAssistant *gtkassistant, gnc_suspend_gui_refresh (); if (data->new_book) - gnc_options_dialog_destroy (data->optionwin); + delete data->optionwin; account_trees_merge(gnc_get_current_root_account(), data->our_account_tree); @@ -1477,24 +1478,11 @@ on_select_currency_prepare (hierarchy_data *data) { gnc_book_options_dialog_apply_helper(data->options); - if (gnc_book_use_book_currency (gnc_get_current_book ())) - { - gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(data->currency_selector), - gnc_book_get_book_currency (gnc_get_current_book ())); - gtk_label_set_text (GTK_LABEL(data->currency_selector_label), - ( _("You selected a book currency and it will be used for\n" \ - "new accounts. Accounts in other currencies must be\n" \ - "added manually.") )); - gtk_widget_set_sensitive(data->currency_selector, FALSE); - } - else - { - gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(data->currency_selector), - gnc_default_currency()); - gtk_label_set_text (GTK_LABEL(data->currency_selector_label), - ( _("Please choose the currency to use for new accounts.") )); - gtk_widget_set_sensitive(data->currency_selector, TRUE); - } + gnc_currency_edit_set_currency (GNC_CURRENCY_EDIT(data->currency_selector), + gnc_default_currency()); + gtk_label_set_text (GTK_LABEL(data->currency_selector_label), + ( _("Please choose the currency to use for new accounts.") )); + gtk_widget_set_sensitive(data->currency_selector, TRUE); } } @@ -1515,39 +1503,39 @@ on_select_currency_prepare (hierarchy_data *data) * dialog to make it clean up after itself. */ static void -book_options_dialog_close_cb(GNCOptionWin * optionwin, - gpointer user_data) +book_options_dialog_close_cb(GncOptionsDialog *optionwin, + gpointer user_data) { - GNCOptionDB * options = user_data; + auto options{static_cast(user_data)}; - gnc_options_dialog_destroy(optionwin); + delete optionwin; gnc_option_db_destroy(options); } static void assistant_insert_book_options_page (hierarchy_data *data) { - GtkWidget *options, *parent; GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_box_set_homogeneous (GTK_BOX (vbox), FALSE); - data->options = gnc_option_db_new_for_type (QOF_ID_BOOK); + data->options = gnc_option_db_new(); + gnc_option_db_book_options(data->options); qof_book_load_options (gnc_get_current_book (), gnc_option_db_load, data->options); gnc_option_db_clean (data->options); /* The options dialog gets added to the notebook so it doesn't need a parent.*/ - data->optionwin = gnc_options_dialog_new_modal (TRUE, _("New Book Options"), - DIALOG_BOOK_OPTIONS_CM_CLASS, NULL); - gnc_options_dialog_build_contents_full (data->optionwin, data->options, FALSE); + data->optionwin = new GncOptionsDialog(true, _("New Book Options"), + DIALOG_BOOK_OPTIONS_CM_CLASS, + nullptr); + data->optionwin->build_contents(data->options, false); - gnc_options_dialog_set_close_cb (data->optionwin, - book_options_dialog_close_cb, - (gpointer)data->options); + data->optionwin->set_close_cb(book_options_dialog_close_cb, + (gpointer)data->options); gnc_options_dialog_set_new_book_option_values (data->options); - options = gnc_options_dialog_notebook (data->optionwin); - parent = gtk_widget_get_parent (options); + auto options = data->optionwin->get_notebook(); + auto parent = gtk_widget_get_parent (options); g_object_ref (options); gtk_container_remove (GTK_CONTAINER(parent), options); @@ -1634,12 +1622,12 @@ gnc_create_hierarchy_assistant (gboolean use_defaults, GncHierarchyAssistantFini /* Final Accounts Page */ data->final_account_tree_container = GTK_WIDGET(gtk_builder_get_object (builder, "final_account_tree_box")); - data->final_account_tree = NULL; + data->final_account_tree = nullptr; - data->balance_hash = g_hash_table_new(NULL, NULL); + data->balance_hash = g_hash_table_new(nullptr, nullptr); gnc_restore_window_size (GNC_PREFS_GROUP, - GTK_WINDOW(data->dialog), gnc_ui_get_main_window(NULL)); + GTK_WINDOW(data->dialog), gnc_ui_get_main_window(nullptr)); g_signal_connect (G_OBJECT(dialog), "destroy", G_CALLBACK (gnc_hierarchy_destroy_cb), data); @@ -1656,7 +1644,7 @@ gnc_create_hierarchy_assistant (gboolean use_defaults, GncHierarchyAssistantFini GtkWidget* gnc_ui_hierarchy_assistant(gboolean use_defaults) { - return gnc_create_hierarchy_assistant(use_defaults, NULL); + return gnc_create_hierarchy_assistant(use_defaults, nullptr); } GtkWidget* @@ -1670,7 +1658,7 @@ static void after_assistant(void) { qof_book_mark_session_dirty(gnc_get_current_book()); - gnc_ui_file_access_for_save_as (gnc_ui_get_main_window (NULL)); + gnc_ui_file_access_for_save_as (gnc_ui_get_main_window (nullptr)); } static void @@ -1686,5 +1674,6 @@ void gnc_ui_hierarchy_assistant_initialize (void) { gnc_hook_add_dangler(HOOK_NEW_BOOK, - (GFunc)gnc_ui_hierarchy_assistant_hook, NULL, NULL); + (GFunc)gnc_ui_hierarchy_assistant_hook, + nullptr, nullptr); } diff --git a/gnucash/gnome/assistant-hierarchy.h b/gnucash/gnome/assistant-hierarchy.h index 8f48bbb9e9..fc0d414756 100644 --- a/gnucash/gnome/assistant-hierarchy.h +++ b/gnucash/gnome/assistant-hierarchy.h @@ -29,6 +29,10 @@ * completes successfully. I.e., the new-user assistant can finish the GnuCash * New-User Experience, create an account plugin-page, &c. **/ +#ifdef __cplusplus +extern "C" +{ +#endif typedef void (*GncHierarchyAssistantFinishedCallback)(void); @@ -37,4 +41,7 @@ GtkWidget* gnc_ui_hierarchy_assistant_with_callback(gboolean use_defaults, GncHi void gnc_ui_hierarchy_assistant_initialize (void); +#ifdef __cplusplus +} +#endif #endif diff --git a/gnucash/gnome/business-options-gnome.c b/gnucash/gnome/business-options-gnome.c deleted file mode 100644 index afee518cae..0000000000 --- a/gnucash/gnome/business-options-gnome.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * business-options.c -- Initialize Business Options - * - * Written By: Derek Atkins - * Copyright (C) 2002,2006 Derek Atkins - * - * This program 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 2 of - * the License, or (at your option) any later version. - * - * This program 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, contact: - * - * Free Software Foundation Voice: +1-617-542-5942 - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 - * Boston, MA 02110-1301, USA gnu@gnu.org - */ - -#include - -#include -#include -#include "swig-runtime.h" -#include "guile-mappings.h" -#include - -#include "gnc-ui-util.h" -#include "dialog-utils.h" -#include "qof.h" -#include "option-util.h" -#include "gnc-general-search.h" - -#include "dialog-options.h" -#include "business-options-gnome.h" -#include "business-gnome-utils.h" -#include "dialog-invoice.h" - -#define FUNC_NAME G_STRFUNC - -static GtkWidget * -create_owner_widget (GNCOption *option, GncOwnerType type, GtkWidget *hbox) -{ - GtkWidget *widget; - GncOwner owner; - - switch (type) - { - case GNC_OWNER_CUSTOMER: - gncOwnerInitCustomer (&owner, NULL); - break; - case GNC_OWNER_VENDOR: - gncOwnerInitVendor (&owner, NULL); - break; - case GNC_OWNER_EMPLOYEE: - gncOwnerInitEmployee (&owner, NULL); - break; - case GNC_OWNER_JOB: - gncOwnerInitJob (&owner, NULL); - break; - default: - return NULL; - } - - widget = gnc_owner_select_create (NULL, hbox, - gnc_get_current_book (), &owner); - gnc_option_set_widget (option, widget); - - g_signal_connect (G_OBJECT (widget), "changed", - G_CALLBACK (gnc_option_changed_option_cb), option); - - return widget; -} - -static GtkWidget * -make_name_label (char *name) -{ - GtkWidget *label = gtk_label_new (name); - gnc_label_set_alignment (label, 1.0, 0.5); - - return label; -} - -/********************************************************************/ -/* "Owner" Option functions */ - - -static GncOwnerType -get_owner_type_from_option (GNCOption *option) -{ - SCM odata = gnc_option_get_option_data (option); - - /* The option data is enum-typed. It's just the enum value. */ - return (GncOwnerType) scm_to_int(odata); -} - - -/* Function to set the UI widget based upon the option */ -static GtkWidget * -owner_set_widget (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); - - value = create_owner_widget (option, get_owner_type_from_option (option), - *enclosing); - - gnc_option_set_ui_value (option, FALSE); - - gtk_widget_show_all (*enclosing); - return value; -} - -/* Function to set the UI Value for a particular option */ -static gboolean -owner_set_value (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GncOwner owner_def; - GncOwner *owner; - - if (!SWIG_IsPointer (value)) - scm_misc_error("business_options:owner_set_value", - "SCM is not a wrapped pointer.", value); - - owner = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncOwner"), 1, 0); - - /* XXX: should we verify that the owner type is correct? */ - if (!owner) - { - owner_def.type = get_owner_type_from_option (option); - owner_def.owner.undefined = NULL; - owner = &owner_def; - } - - widget = gnc_option_get_gtk_widget (option); - gnc_owner_set_owner (widget, owner); - return FALSE; -} - -/* Function to get the UI Value for a particular option */ -static SCM -owner_get_value (GNCOption *option, GtkWidget *widget) -{ - static GncOwner owner; /* XXX: might cause trouble? */ - GncOwnerType type; - - type = get_owner_type_from_option (option); - owner.type = type; - gnc_owner_get_owner (widget, &owner); - - return SWIG_NewPointerObj(&owner, SWIG_TypeQuery("_p__gncOwner"), 0); -} - - -/********************************************************************/ -/* "Customer" Option functions */ - - -/* Function to set the UI widget based upon the option */ -static GtkWidget * -customer_set_widget (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); - - value = create_owner_widget (option, GNC_OWNER_CUSTOMER, *enclosing); - - gnc_option_set_ui_value (option, FALSE); - - gtk_widget_show_all (*enclosing); - return value; -} - -/* Function to set the UI Value for a particular option */ -static gboolean -customer_set_value (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GncOwner owner; - GncCustomer *customer; - - if (!SWIG_IsPointer (value)) - scm_misc_error("business_options:customer_set_value", - "SCM is not a wrapped pointer.", value); - - customer = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncCustomer"), 1, 0); - gncOwnerInitCustomer (&owner, customer); - - widget = gnc_option_get_gtk_widget (option); - gnc_owner_set_owner (widget, &owner); - return FALSE; -} - -/* Function to get the UI Value for a particular option */ -static SCM -customer_get_value (GNCOption *option, GtkWidget *widget) -{ - GncOwner owner; - - gnc_owner_get_owner (widget, &owner); - return SWIG_NewPointerObj(owner.owner.undefined, - SWIG_TypeQuery("_p__gncCustomer"), 0); -} - - -/********************************************************************/ -/* "Vendor" Option functions */ - - -/* Function to set the UI widget based upon the option */ -static GtkWidget * -vendor_set_widget (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); - - value = create_owner_widget (option, GNC_OWNER_VENDOR, *enclosing); - - gnc_option_set_ui_value (option, FALSE); - - gtk_widget_show_all (*enclosing); - return value; -} - -/* Function to set the UI Value for a particular option */ -static gboolean -vendor_set_value (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GncOwner owner; - GncVendor *vendor; - - if (!SWIG_IsPointer (value)) - scm_misc_error("business_options:vendor_set_value", - "SCM is not a wrapped pointer.", value); - - vendor = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncVendor"), 1, 0); - gncOwnerInitVendor (&owner, vendor); - - widget = gnc_option_get_gtk_widget (option); - gnc_owner_set_owner (widget, &owner); - return FALSE; -} - -/* Function to get the UI Value for a particular option */ -static SCM -vendor_get_value (GNCOption *option, GtkWidget *widget) -{ - GncOwner owner; - - gnc_owner_get_owner (widget, &owner); - return SWIG_NewPointerObj(owner.owner.undefined, - SWIG_TypeQuery("_p__gncVendor"), 0); -} - -/********************************************************************/ -/* "Employee" Option functions */ - - -/* Function to set the UI widget based upon the option */ -static GtkWidget * -employee_set_widget (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); - - value = create_owner_widget (option, GNC_OWNER_EMPLOYEE, *enclosing); - - gnc_option_set_ui_value (option, FALSE); - - gtk_widget_show_all (*enclosing); - return value; -} - -/* Function to set the UI Value for a particular option */ -static gboolean -employee_set_value (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GncOwner owner; - GncEmployee *employee; - - if (!SWIG_IsPointer (value)) - scm_misc_error("business_options:employee_set_value", - "SCM is not a wrapped pointer.", value); - - employee = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncEmployee"), 1, 0); - gncOwnerInitEmployee (&owner, employee); - - widget = gnc_option_get_gtk_widget (option); - gnc_owner_set_owner (widget, &owner); - return FALSE; -} - -/* Function to get the UI Value for a particular option */ -static SCM -employee_get_value (GNCOption *option, GtkWidget *widget) -{ - GncOwner owner; - - gnc_owner_get_owner (widget, &owner); - - return SWIG_NewPointerObj(owner.owner.undefined, - SWIG_TypeQuery("_p__gncEmployee"), 0); -} - -/********************************************************************/ -/* "Invoice" Option functions */ - - -static GtkWidget * -create_invoice_widget (GNCOption *option, GtkWidget *hbox) -{ - GtkWidget *widget; - - /* No owner or starting invoice here, but that's okay. */ - widget = gnc_invoice_select_create (hbox, gnc_get_current_book(), - NULL, NULL, NULL); - - gnc_option_set_widget (option, widget); - g_signal_connect (G_OBJECT (widget), "changed", - G_CALLBACK (gnc_option_changed_option_cb), option); - - return widget; -} - -/* Function to set the UI widget based upon the option */ -static GtkWidget * -invoice_set_widget (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); - - value = create_invoice_widget (option, *enclosing); - - gnc_option_set_ui_value (option, FALSE); - - gtk_widget_show_all (*enclosing); - return value; -} - -/* Function to set the UI Value for a particular option */ -static gboolean -invoice_set_value (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GncInvoice *invoice; - - if (!SWIG_IsPointer (value)) - scm_misc_error("business_options:invoice_set_value", - "SCM is not a wrapped pointer.", value); - - invoice = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncInvoice"), 1, 0); - - widget = gnc_option_get_gtk_widget (option); - gnc_general_search_set_selected (GNC_GENERAL_SEARCH (widget), invoice); - return FALSE; -} - -/* Function to get the UI Value for a particular option */ -static SCM -invoice_get_value (GNCOption *option, GtkWidget *widget) -{ - GncInvoice *invoice; - - invoice = gnc_general_search_get_selected (GNC_GENERAL_SEARCH (widget)); - return SWIG_NewPointerObj(invoice, SWIG_TypeQuery("_p__gncInvoice"), 0); -} - - -/********************************************************************/ -/* "Tax Table" Option functions */ - - -static GtkWidget * -create_taxtable_widget (GNCOption *option, GtkWidget *hbox) -{ - GtkWidget *widget; - GtkBuilder *builder; - - builder = gtk_builder_new(); - gnc_builder_add_from_file (builder, "business-options-gnome.glade", "taxtable_store"); - gnc_builder_add_from_file (builder, "business-options-gnome.glade", "taxtable_menu"); - - widget = GTK_WIDGET (gtk_builder_get_object (builder, "taxtable_menu")); - gnc_taxtables_combo (GTK_COMBO_BOX(widget), gnc_get_current_book (), TRUE, NULL); - gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0); - - gnc_option_set_widget (option, widget); - - g_signal_connect (widget, "changed", - G_CALLBACK (gnc_option_changed_option_cb), option); - - g_object_unref(G_OBJECT(builder)); - return widget; -} - -/* Function to set the UI widget based upon the option */ -static GtkWidget * -taxtable_set_widget (GNCOption *option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, - /* Return values */ - GtkWidget **enclosing, gboolean *packed) -{ - GtkWidget *value; - - *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); - - value = create_taxtable_widget (option, *enclosing); - - gnc_option_set_ui_value (option, FALSE); - - gtk_widget_show_all (*enclosing); - return value; -} - -/* Function to set the UI Value for a particular option */ -static gboolean -taxtable_set_value (GNCOption *option, gboolean use_default, - GtkWidget *widget, SCM value) -{ - GncTaxTable *taxtable; - - if (!SWIG_IsPointer (value)) - scm_misc_error("business_options:taxtable_set_value", - "SCM is not a wrapped pointer.", value); - - taxtable = SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncTaxTable"), 1, 0); - - widget = gnc_option_get_gtk_widget (option); - gnc_simple_combo_set_value (GTK_COMBO_BOX(widget), taxtable); - return FALSE; -} - -/* Function to get the UI Value for a particular option */ -static SCM -taxtable_get_value (GNCOption *option, GtkWidget *widget) -{ - GncTaxTable *taxtable; - - taxtable = gnc_simple_combo_get_value (GTK_COMBO_BOX(widget)); - return SWIG_NewPointerObj(taxtable, SWIG_TypeQuery("_p__gncTaxTable"), 0); -} - - - - -void -gnc_business_options_gnome_initialize (void) -{ - int i; - static GNCOptionDef_t options[] = - { - { "owner", owner_set_widget, owner_set_value, owner_get_value }, - { - "customer", customer_set_widget, customer_set_value, - customer_get_value - }, - { "vendor", vendor_set_widget, vendor_set_value, vendor_get_value }, - { "employee", employee_set_widget, employee_set_value, employee_get_value }, - { "invoice", invoice_set_widget, invoice_set_value, invoice_get_value }, - { "taxtable", taxtable_set_widget, taxtable_set_value, taxtable_get_value }, - { NULL } - }; - - SWIG_GetModule(NULL); /* Work-around for SWIG bug. */ - for (i = 0; options[i].option_name; i++) - gnc_options_ui_register_option (&(options[i])); -} diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp new file mode 100644 index 0000000000..48144d9296 --- /dev/null +++ b/gnucash/gnome/business-options-gnome.cpp @@ -0,0 +1,224 @@ +/* + * business-options.c -- Initialize Business Options + * + * Written By: Derek Atkins + * Copyright (C) 2002,2006 Derek Atkins + * + * This program 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 2 of + * the License, or (at your option) any later version. + * + * This program 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, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +#include +#include +#include + +extern "C" +{ +#include + +#include "swig-runtime.h" +#include "guile-mappings.h" + +#include "gnc-ui-util.h" +#include "dialog-utils.h" +#include "qof.h" +#include "gnc-general-search.h" + +#include "business-options-gnome.h" +#include "business-gnome-utils.h" +#include "dialog-invoice.h" +} + +#include +#include +#include + +#include +#include +#define FUNC_NAME G_STRFUNC + + +static inline GncOwnerType +ui_type_to_owner_type(GncOptionUIType ui_type) +{ + if (ui_type == GncOptionUIType::CUSTOMER) + return GNC_OWNER_CUSTOMER; + if (ui_type == GncOptionUIType::VENDOR) + return GNC_OWNER_VENDOR; + if (ui_type == GncOptionUIType::EMPLOYEE) + return GNC_OWNER_EMPLOYEE; + + std::ostringstream oss; + oss << "UI type " << static_cast(ui_type) << " could not be converted to owner type\n"; + throw std::invalid_argument(oss.str()); +} + + +class GncGtkOwnerUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkOwnerUIItem(GtkWidget* widget, GncOptionUIType type) : + GncOptionGtkUIItem(widget, type) {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + GncOwner owner{}; + owner.type = ui_type_to_owner_type(option.get_ui_type()); + owner.owner.undefined = (void*)option.get_value(); + gnc_owner_set_owner(get_widget(), &owner); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + GncOwner owner{}; + gnc_owner_get_owner(get_widget(), &owner); + if (owner.type == ui_type_to_owner_type(option.get_ui_type())) + option.set_value(static_cast(owner.owner.undefined)); + } +}; + +template<> GtkWidget* +create_option_widget(GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + + GncOwner owner{}; + auto ui_type{option.get_ui_type()}; + owner.type = ui_type_to_owner_type(ui_type); + auto widget = gnc_owner_select_create(nullptr, *enclosing, + gnc_get_current_book(), + &owner); + + option.set_ui_item(std::make_unique(widget, ui_type)); + option.set_ui_item_from_option(); + g_signal_connect (G_OBJECT (widget), "changed", + G_CALLBACK (gnc_option_changed_widget_cb), &option); + gtk_widget_show_all(*enclosing); + return widget; +} + +class GncGtkInvoiceUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkInvoiceUIItem(GtkWidget* widget) : + GncOptionGtkUIItem(widget, GncOptionUIType::INVOICE) {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto instance{option.get_value()}; + if (instance) + gnc_general_search_set_selected(GNC_GENERAL_SEARCH(get_widget()), + GNC_INVOICE(instance)); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + option.set_value(qof_instance_cast(gnc_general_search_get_selected(GNC_GENERAL_SEARCH(get_widget())))); + } +}; + +template<> GtkWidget* +create_option_widget(GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + auto widget{gnc_invoice_select_create(*enclosing, gnc_get_current_book(), + nullptr, nullptr, nullptr)}; + + option.set_ui_item(std::make_unique(widget)); + option.set_ui_item_from_option(); + g_signal_connect(G_OBJECT (widget), "changed", + G_CALLBACK (gnc_option_changed_widget_cb), &option); + gtk_widget_show_all(*enclosing); + return widget; +} + +class GncGtkTaxTableUIItem : public GncOptionGtkUIItem +{ +public: + GncGtkTaxTableUIItem(GtkWidget* widget) : + GncOptionGtkUIItem(widget, GncOptionUIType::TAX_TABLE) {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + auto taxtable{option.get_value()}; + if (taxtable) + gnc_simple_combo_set_value(GTK_COMBO_BOX(get_widget()), + GNC_TAXTABLE(taxtable)); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto taxtable{gnc_simple_combo_get_value(GTK_COMBO_BOX(get_widget()))}; + option.set_value(qof_instance_cast(taxtable)); + } +}; + +template<> GtkWidget* +create_option_widget(GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); + constexpr const char* glade_file{"business-options-gnome.glade"}; + constexpr const char* glade_store{"taxtable_store"}; + constexpr const char* glade_menu{"taxtable_menu"}; + auto builder{gtk_builder_new()}; + gnc_builder_add_from_file(builder, glade_file, glade_store); + gnc_builder_add_from_file(builder, glade_file, glade_menu); + auto widget{GTK_WIDGET(gtk_builder_get_object(builder, glade_menu))}; + gnc_taxtables_combo(GTK_COMBO_BOX(widget), gnc_get_current_book(), TRUE, + nullptr); + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + option.set_ui_item(std::make_unique(widget)); + option.set_ui_item_from_option(); + g_object_unref(builder); // Needs to wait until after widget has been reffed. + g_signal_connect (G_OBJECT (widget), "changed", + G_CALLBACK (gnc_option_changed_widget_cb), &option); + + gtk_widget_show_all(*enclosing); + return widget; +} + +void +gnc_business_options_gnome_initialize(void) +{ + GncOptionUIFactory::set_func(GncOptionUIType::OWNER, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::CUSTOMER, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::VENDOR, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::EMPLOYEE, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::INVOICE, + create_option_widget); + GncOptionUIFactory::set_func(GncOptionUIType::TAX_TABLE, + create_option_widget); +} diff --git a/gnucash/gnome/business-options-gnome.h b/gnucash/gnome/business-options-gnome.h index 32db8740b3..fb454c0f83 100644 --- a/gnucash/gnome/business-options-gnome.h +++ b/gnucash/gnome/business-options-gnome.h @@ -1,5 +1,5 @@ /* - * business-options.h -- Initialize the Business Options + * business-options-gnome.h -- Initialize the Business Options * * Written By: Derek Atkins * Copyright (C) 2002 Derek Atkins @@ -21,10 +21,19 @@ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * Boston, MA 02110-1301, USA gnu@gnu.org */ +/** @addtogroup GUI + @{ */ +/** @addtogroup GuiOptions Options Dialog + @{ */ #ifndef GNC_BUSINESS_OPTIONS_H_ #define GNC_BUSINESS_OPTIONS_H_ +/** + * Set up the business and counters pages in the File Preferences dialog. + */ void gnc_business_options_gnome_initialize (void); #endif /* GNC_BUSINESS_OPTIONS_H_ */ +/** @} + @} */ diff --git a/gnucash/gnome/dialog-custom-report.c b/gnucash/gnome/dialog-custom-report.c index 4ca9937bfb..7f72ef563f 100644 --- a/gnucash/gnome/dialog-custom-report.c +++ b/gnucash/gnome/dialog-custom-report.c @@ -29,10 +29,8 @@ #include "swig-runtime.h" #include "dialog-custom-report.h" -#include "dialog-options.h" #include "dialog-utils.h" #include "gnc-main-window.h" -#include "option-util.h" #include "window-report.h" #include "guile-mappings.h" #include "gnc-guile-utils.h" diff --git a/gnucash/gnome/dialog-report-column-view.c b/gnucash/gnome/dialog-report-column-view.c deleted file mode 100644 index 88c5925fcc..0000000000 --- a/gnucash/gnome/dialog-report-column-view.c +++ /dev/null @@ -1,705 +0,0 @@ -/******************************************************************** - * dialog-report-column-view.c -- editor for column view of reports * - * Copyright (C) 2001 Bill Gribble * - * Copyright (c) 2006 David Hampton * - * * - * This program 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 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * - ********************************************************************/ - -#include - -#include -#include -#include -#include "swig-runtime.h" - -#include "dialog-report-column-view.h" -#include "dialog-options.h" -#include "dialog-utils.h" -#include "option-util.h" -#include "window-report.h" -#include "guile-mappings.h" -#include "gnc-guile-utils.h" -#include "gnc-report.h" -#include "gnc-ui.h" - -enum available_cols -{ - AVAILABLE_COL_NAME = 0, - AVAILABLE_COL_GUID, - NUM_AVAILABLE_COLS -}; - -enum contents_cols -{ - CONTENTS_COL_NAME = 0, - CONTENTS_COL_ROW, - CONTENTS_COL_REPORT_ROWS, - CONTENTS_COL_REPORT_COLS, - NUM_CONTENTS_COLS -}; - -struct gncp_column_view_edit -{ - GNCOptionWin * optwin; - GtkTreeView * available; - GtkTreeView * contents; - - SCM options; - SCM view; - GNCOptionDB * odb; - - SCM available_list; - SCM contents_list; - int contents_selected; - - GtkWidget *add_button; - GtkWidget *remove_button; - GtkWidget *up_button; - GtkWidget *down_button; - GtkWidget *size_button; -}; - -void gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data); -void gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data); -void gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data); -void gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data); -void gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data); - -static void -gnc_column_view_set_option(GNCOptionDB * odb, char * section, char * name, - SCM new_value) -{ - GNCOption * option = - gnc_option_db_get_option_by_name(odb, section, name); - - if (option) - { - gnc_option_db_set_option(odb, section, name, new_value); - - /* set_option doesn't do this */ - gnc_option_set_changed (option, TRUE); - } -} - -static void -gnc_column_view_edit_destroy(gnc_column_view_edit * view) -{ - gnc_options_dialog_destroy(view->optwin); - scm_gc_unprotect_object(view->options); - scm_gc_unprotect_object(view->view); - gnc_option_db_destroy(view->odb); - g_free(view); -} - -static void -update_available_lists(gnc_column_view_edit * view) -{ - SCM get_rpt_guids = scm_c_eval_string("gnc:all-report-template-guids"); - SCM template_menu_name = scm_c_eval_string("gnc:report-template-menu-name/report-guid"); - SCM rpt_guids = scm_call_0(get_rpt_guids); - SCM selection; - - gchar *name; - gchar *guid_str; - - GtkTreeModel *model; - GtkListStore *store; - GtkTreeIter iter; - GtkTreeSelection *tree_selection; - - /* Update the list of available reports (left selection box). */ - tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view->available)); - model = gtk_tree_view_get_model (GTK_TREE_VIEW(view->available)); - - if (gtk_tree_selection_get_selected(tree_selection, &model, &iter)) - { - gchar *guid_str; - gtk_tree_model_get(model, &iter, - AVAILABLE_COL_GUID, &guid_str, - -1); - selection = scm_from_utf8_string(guid_str); - g_free (guid_str); - } - else - selection = SCM_UNDEFINED; - - scm_gc_unprotect_object(view->available_list); - view->available_list = rpt_guids; - scm_gc_protect_object(view->available_list); - - store = GTK_LIST_STORE(model); - gtk_list_store_clear(store); - - if (scm_is_list(rpt_guids)) - { - for (int i = 0; !scm_is_null(rpt_guids); rpt_guids = SCM_CDR(rpt_guids), i++) - { - SCM rpt_guids_temp = SCM_CAR(rpt_guids); - - guid_str = scm_to_utf8_string (rpt_guids_temp); - name = gnc_scm_to_utf8_string (scm_call_2(template_menu_name, rpt_guids_temp, - SCM_BOOL_F)); - - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - AVAILABLE_COL_NAME, _(name), - AVAILABLE_COL_GUID, guid_str, - -1); - - if (scm_is_equal (rpt_guids_temp, selection)) - gtk_tree_selection_select_iter (tree_selection, &iter); - - g_free (name); - g_free (guid_str); - } - } -} - -static void -update_contents_lists(gnc_column_view_edit * view) -{ - SCM report_menu_name = scm_c_eval_string("gnc:report-menu-name"); - SCM contents = - gnc_option_db_lookup_option(view->odb, "__general", "report-list", - SCM_BOOL_F); - SCM this_report; - SCM selection; - gchar *name; - - GtkListStore *store; - GtkTreeIter iter; - GtkTreeSelection *tree_selection; - - /* Update the list of selected reports (right selection box). */ - tree_selection = gtk_tree_view_get_selection(view->contents); - - if (scm_is_list(view->contents_list) && !scm_is_null (view->contents_list)) - { - int row = view->contents_selected; - row = MIN (row, scm_ilength (view->contents_list) - 1); - selection = scm_list_ref (view->contents_list, scm_from_int (row)); - } - else - selection = SCM_UNDEFINED; - - scm_gc_unprotect_object(view->contents_list); - view->contents_list = contents; - scm_gc_protect_object(view->contents_list); - - store = GTK_LIST_STORE(gtk_tree_view_get_model(view->contents)); - gtk_list_store_clear(store); - - if (scm_is_list(contents)) - { - for (int i = 0; !scm_is_null(contents); contents = SCM_CDR(contents), i++) - { - SCM contents_temp = SCM_CAR(contents); - - int id = scm_to_int(SCM_CAAR(contents)); - - this_report = gnc_report_find(id); - name = gnc_scm_to_utf8_string (scm_call_1(report_menu_name, this_report)); - - gtk_list_store_append(store, &iter); - gtk_list_store_set - (store, &iter, - CONTENTS_COL_NAME, _(name), - CONTENTS_COL_ROW, i, - CONTENTS_COL_REPORT_COLS, scm_to_int(SCM_CADR(contents_temp)), - CONTENTS_COL_REPORT_ROWS, scm_to_int(SCM_CADDR(contents_temp)), - -1); - - if (scm_is_equal (contents_temp, selection)) - gtk_tree_selection_select_iter (tree_selection, &iter); - - g_free (name); - } - } -} - -static void -gnc_column_view_update_buttons_cb (GtkTreeSelection *selection, - gnc_column_view_edit *r) -{ - GtkTreeModel *model; - GtkTreeIter iter; - gboolean is_selected; - - /* compare treeviews to establish which selected treeview */ - if (gtk_tree_selection_get_tree_view (selection) == r->available) - { - /* available treeview */ - is_selected = gtk_tree_selection_get_selected (selection, &model, &iter); - gtk_widget_set_sensitive (r->add_button, is_selected); - return; - } - - /* contents treeview */ - is_selected = gtk_tree_selection_get_selected (selection, &model, &iter); - gtk_widget_set_sensitive (r->size_button, is_selected); - gtk_widget_set_sensitive (r->remove_button, is_selected); - - if (is_selected) - { - int len = scm_ilength (r->contents_list); - - gtk_tree_model_get (model, &iter, - CONTENTS_COL_ROW, &r->contents_selected, -1); - - if (len > 1) - { - gtk_widget_set_sensitive (r->up_button, TRUE); - gtk_widget_set_sensitive (r->down_button, TRUE); - - if (r->contents_selected == len -1) - gtk_widget_set_sensitive (r->down_button, FALSE); - - if (r->contents_selected == 0) - gtk_widget_set_sensitive (r->up_button, FALSE); - } - } - else - { - gtk_widget_set_sensitive (r->up_button, FALSE); - gtk_widget_set_sensitive (r->down_button, FALSE); - } -} - -static void -gnc_column_view_edit_apply_cb(GNCOptionWin * w, gpointer user_data) -{ - SCM dirty_report = scm_c_eval_string("gnc:report-set-dirty?!"); - gnc_column_view_edit * win = user_data; - GList *results = NULL, *iter; - - if (!win) return; - results = gnc_option_db_commit (win->odb); - for (iter = results; iter; iter = iter->next) - { - GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(gnc_options_dialog_widget(w)), - 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - "%s", - (char*)iter->data); - gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_destroy(dialog); - g_free (iter->data); - } - g_list_free (results); - - scm_call_2(dirty_report, win->view, SCM_BOOL_T); -} - -static void -gnc_column_view_edit_close_cb(GNCOptionWin * win, gpointer user_data) -{ - gnc_column_view_edit * r = user_data; - SCM set_editor = scm_c_eval_string("gnc:report-set-editor-widget!"); - - scm_call_2(set_editor, r->view, SCM_BOOL_F); - gnc_column_view_edit_destroy(r); -} - - -/******************************************************************** - * gnc_column_view_edit_options - * create the editor. - ********************************************************************/ - -GtkWidget * -gnc_column_view_edit_options(SCM options, SCM view) -{ - SCM get_editor = scm_c_eval_string("gnc:report-editor-widget"); - SCM ptr; - GtkWidget * editor; - GtkListStore *store; - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - GtkTreeSelection *selection; - - ptr = scm_call_1(get_editor, view); - if (ptr != SCM_BOOL_F) - { -#define FUNC_NAME "gtk_window_present" - GtkWindow * w = SWIG_MustGetPtr(ptr, SWIG_TypeQuery("_p_GtkWidget"), 1, 0); - gtk_window_present(w); -#undef FUNC_NAME - return NULL; - } - else - { - gnc_column_view_edit * r = g_new0(gnc_column_view_edit, 1); - GtkBuilder *builder; - - r->optwin = gnc_options_dialog_new (NULL, GTK_WINDOW(gnc_ui_get_main_window (NULL))); - - /* Hide the generic dialog page list. */ - gtk_widget_hide(gnc_options_page_list(r->optwin)); - - builder = gtk_builder_new(); - gnc_builder_add_from_file (builder, "dialog-report.glade", "view_contents_table"); - - editor = GTK_WIDGET(gtk_builder_get_object (builder, "view_contents_table")); - r->available = GTK_TREE_VIEW (gtk_builder_get_object (builder, "available_view")); - r->contents = GTK_TREE_VIEW (gtk_builder_get_object (builder, "contents_view")); - - r->add_button = GTK_WIDGET(gtk_builder_get_object (builder, "add_button1")); - r->remove_button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button1")); - r->up_button = GTK_WIDGET(gtk_builder_get_object (builder, "up_button1")); - r->down_button = GTK_WIDGET(gtk_builder_get_object (builder, "down_button1")); - r->size_button = GTK_WIDGET(gtk_builder_get_object (builder, "size_button1")); - - r->options = options; - r->view = view; - r->available_list = SCM_EOL; - r->contents_selected = 0; - r->contents_list = SCM_EOL; - r->odb = gnc_option_db_new(r->options); - - gnc_options_dialog_build_contents(r->optwin, r->odb); - - gtk_notebook_append_page(GTK_NOTEBOOK(gnc_options_dialog_notebook - (r->optwin)), - editor, - gtk_label_new(_("Contents"))); - - scm_gc_protect_object(r->options); - scm_gc_protect_object(r->view); - scm_gc_protect_object(r->available_list); - scm_gc_protect_object(r->contents_list); - - /* Build the 'available' view */ - store = gtk_list_store_new (NUM_AVAILABLE_COLS, G_TYPE_STRING, G_TYPE_STRING); - gtk_tree_view_set_model(r->available, GTK_TREE_MODEL(store)); - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), AVAILABLE_COL_NAME, GTK_SORT_ASCENDING); - g_object_unref(store); - - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes("", renderer, - "text", AVAILABLE_COL_NAME, - NULL); - gtk_tree_view_append_column(r->available, column); - - /* use the selection cb to update buttons */ - selection = gtk_tree_view_get_selection(r->available); - g_signal_connect(selection, "changed", - G_CALLBACK(gnc_column_view_update_buttons_cb), r); - - /* Build the 'contents' view */ - store = gtk_list_store_new (NUM_CONTENTS_COLS, G_TYPE_STRING, G_TYPE_INT, - G_TYPE_INT, G_TYPE_INT); - gtk_tree_view_set_model(r->contents, GTK_TREE_MODEL(store)); - g_object_unref(store); - - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Report"), renderer, - "text", CONTENTS_COL_NAME, - NULL); - gtk_tree_view_append_column(r->contents, column); - - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Rows"), renderer, - "text", CONTENTS_COL_REPORT_ROWS, - NULL); - gtk_tree_view_append_column(r->contents, column); - - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Cols"), renderer, - "text", CONTENTS_COL_REPORT_COLS, - NULL); - gtk_tree_view_append_column(r->contents, column); - - /* use the selection cb to update buttons */ - selection = gtk_tree_view_get_selection(r->contents); - g_signal_connect(selection, "changed", - G_CALLBACK(gnc_column_view_update_buttons_cb), r); - - update_available_lists(r); - update_contents_lists(r); - - gnc_options_dialog_set_apply_cb(r->optwin, - gnc_column_view_edit_apply_cb, r); - gnc_options_dialog_set_close_cb(r->optwin, - gnc_column_view_edit_close_cb, r); - - gtk_widget_show(gnc_options_dialog_widget(r->optwin)); - - gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, r); - - g_object_unref(G_OBJECT(builder)); - - return gnc_options_dialog_widget(r->optwin); - } -} - -void -gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data) -{ - gnc_column_view_edit * r = user_data; - SCM make_report = scm_c_eval_string("gnc:make-report"); - SCM mark_report = scm_c_eval_string("gnc:report-set-needs-save?!"); - SCM template_name; - SCM new_report; - SCM newlist = SCM_EOL; - SCM oldlist = r->contents_list; - int count; - int oldlength, id; - gchar *guid_str; - GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(r->available)); - GtkTreeModel *model; - GtkTreeIter iter; - - /* make sure there is a selected entry */ - if (gtk_tree_selection_get_selected(selection, &model, &iter)) - gtk_tree_model_get(model, &iter, - AVAILABLE_COL_GUID, &guid_str, -1); - else - return; - - if (scm_is_list(r->available_list)) - { - template_name = scm_from_utf8_string(guid_str); - - new_report = scm_call_1(make_report, template_name); - id = scm_to_int(new_report); - scm_call_2(mark_report, gnc_report_find(id), SCM_BOOL_T); - oldlength = scm_ilength(r->contents_list); - - if (oldlength > r->contents_selected) - { - for (count = 0; count < r->contents_selected; count++) - { - newlist = scm_cons(SCM_CAR(oldlist), newlist); - oldlist = SCM_CDR(oldlist); - } - newlist = scm_append - (scm_list_n (scm_reverse - (scm_cons - (scm_list_4 (new_report, - scm_from_int (1), - scm_from_int (1), - SCM_BOOL_F), - newlist)), - oldlist, - SCM_UNDEFINED)); - } - else - { - newlist = scm_append - (scm_list_n (oldlist, - scm_list_1 - (scm_list_4 (new_report, - scm_from_int (1), - scm_from_int (1), - SCM_BOOL_F)), - SCM_UNDEFINED)); - r->contents_selected = oldlength; - } - - scm_gc_unprotect_object(r->contents_list); - r->contents_list = newlist; - scm_gc_protect_object(r->contents_list); - - gnc_column_view_set_option(r->odb, "__general", "report-list", - r->contents_list); - gnc_options_dialog_changed (r->optwin); - } - g_free (guid_str); - update_contents_lists(r); -} - -void -gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data) -{ - gnc_column_view_edit * r = user_data; - SCM newlist = SCM_EOL; - SCM oldlist = r->contents_list; - int count; - int oldlength; - - if (scm_is_list(r->contents_list)) - { - oldlength = scm_ilength(r->contents_list); - if (oldlength > r->contents_selected) - { - for (count = 0; count < r->contents_selected; count++) - { - newlist = scm_cons(SCM_CAR(oldlist), newlist); - oldlist = SCM_CDR(oldlist); - } - if (count <= oldlength) - { - newlist = scm_append(scm_list_n (scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED)); - } - } - - if (r->contents_selected > 0 && oldlength == r->contents_selected + 1) - { - r->contents_selected --; - } - - scm_gc_unprotect_object(r->contents_list); - r->contents_list = newlist; - scm_gc_protect_object(r->contents_list); - - gnc_column_view_set_option(r->odb, "__general", "report-list", - r->contents_list); - - gnc_options_dialog_changed (r->optwin); - } - update_contents_lists(r); -} - -void -gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data) -{ - gnc_column_view_edit * r = user_data; - SCM oldlist = r->contents_list; - SCM newlist = SCM_EOL; - SCM temp; - int oldlength; - int count; - - oldlength = scm_ilength(r->contents_list); - if ((r->contents_selected > 0) && (oldlength > r->contents_selected)) - { - for (count = 1; count < r->contents_selected; count++) - { - newlist = scm_cons(SCM_CAR(oldlist), newlist); - oldlist = SCM_CDR(oldlist); - } - temp = SCM_CAR(oldlist); - oldlist = SCM_CDR(oldlist); - newlist = scm_cons(temp, scm_cons(SCM_CAR(oldlist), newlist)); - newlist = scm_append(scm_list_n (scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED)); - - scm_gc_unprotect_object(r->contents_list); - r->contents_list = newlist; - scm_gc_protect_object(r->contents_list); - - r->contents_selected = r->contents_selected - 1; - - gnc_column_view_set_option(r->odb, "__general", "report-list", - r->contents_list); - - gnc_options_dialog_changed (r->optwin); - - update_contents_lists(r); - } -} - -void -gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data) -{ - gnc_column_view_edit * r = user_data; - SCM oldlist = r->contents_list; - SCM newlist = SCM_EOL; - SCM temp; - int oldlength; - int count; - - oldlength = scm_ilength(r->contents_list); - if (oldlength > (r->contents_selected + 1)) - { - for (count = 0; count < r->contents_selected; count++) - { - newlist = scm_cons(SCM_CAR(oldlist), newlist); - oldlist = SCM_CDR(oldlist); - } - temp = SCM_CAR(oldlist); - oldlist = SCM_CDR(oldlist); - newlist = scm_cons(temp, scm_cons(SCM_CAR(oldlist), newlist)); - newlist = scm_append(scm_list_n (scm_reverse(newlist), SCM_CDR(oldlist), SCM_UNDEFINED)); - - scm_gc_unprotect_object(r->contents_list); - r->contents_list = newlist; - scm_gc_protect_object(r->contents_list); - - r->contents_selected = r->contents_selected + 1; - - gnc_column_view_set_option(r->odb, "__general", "report-list", - r->contents_list); - - gnc_options_dialog_changed (r->optwin); - - update_contents_lists(r); - } -} - -void -gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data) -{ - gnc_column_view_edit * r = user_data; - GtkWidget * rowspin; - GtkWidget * colspin; - GtkWidget * dlg; - GtkBuilder *builder; - SCM current; - int length; - int dlg_ret; - - builder = gtk_builder_new(); - gnc_builder_add_from_file (builder, "dialog-report.glade", "col_adjustment"); - gnc_builder_add_from_file (builder, "dialog-report.glade", "row_adjustment"); - gnc_builder_add_from_file (builder, "dialog-report.glade", "edit_report_size"); - dlg = GTK_WIDGET(gtk_builder_get_object (builder, "edit_report_size")); - - gtk_window_set_transient_for (GTK_WINDOW(dlg), - GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(button)))); - - /* get the spinner widgets */ - rowspin = GTK_WIDGET(gtk_builder_get_object (builder, "row_spin")); - colspin = GTK_WIDGET(gtk_builder_get_object (builder, "col_spin")); - - length = scm_ilength(r->contents_list); - if (length > r->contents_selected) - { - current = scm_list_ref(r->contents_list, - scm_from_int (r->contents_selected)); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(colspin), - (float)scm_to_int(SCM_CADR(current))); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(rowspin), - (float)scm_to_int(SCM_CADDR(current))); - - dlg_ret = gtk_dialog_run(GTK_DIALOG(dlg)); - gtk_widget_hide(dlg); - - if (dlg_ret == GTK_RESPONSE_OK) - { - current = scm_list_4 (SCM_CAR (current), - scm_from_int (gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(colspin))), - scm_from_int (gtk_spin_button_get_value_as_int - (GTK_SPIN_BUTTON(rowspin))), - SCM_BOOL_F); - scm_gc_unprotect_object(r->contents_list); - r->contents_list = scm_list_set_x(r->contents_list, - scm_from_int (r->contents_selected), - current); - scm_gc_protect_object(r->contents_list); - gnc_options_dialog_changed (r->optwin); - update_contents_lists(r); - } - - g_object_unref(G_OBJECT(builder)); - - gtk_widget_destroy(dlg); - } -} diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp new file mode 100644 index 0000000000..3a189219b1 --- /dev/null +++ b/gnucash/gnome/dialog-report-column-view.cpp @@ -0,0 +1,565 @@ +/******************************************************************** + * dialog-report-column-view.c -- editor for column view of reports * + * Copyright (C) 2001 Bill Gribble * + * Copyright (c) 2006 David Hampton * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + ********************************************************************/ + +#include +#include +#include +#include + +extern "C" +{ +#include + +#include "swig-runtime.h" + +#include "dialog-utils.h" +#include "window-report.h" +#include "guile-mappings.h" +#include "gnc-guile-utils.h" +#include "gnc-ui.h" +} + +#include "dialog-report-column-view.hpp" +#include +#include +#include + +enum available_cols +{ + AVAILABLE_COL_NAME = 0, + AVAILABLE_COL_GUID, + NUM_AVAILABLE_COLS +}; + +enum contents_cols +{ + CONTENTS_COL_NAME = 0, + CONTENTS_COL_ROW, + CONTENTS_COL_REPORT_ROWS, + CONTENTS_COL_REPORT_COLS, + NUM_CONTENTS_COLS +}; + +using StrVec = std::vector; + +struct gncp_column_view_edit +{ + GncOptionsDialog * optwin; + GtkTreeView * available; + GtkTreeView * contents; + + SCM view; + GncOptionDB * odb; + + StrVec available_list; + GncOptionReportPlacementVec contents_list; + int contents_selected; + + GtkWidget *add_button; + GtkWidget *remove_button; + GtkWidget *up_button; + GtkWidget *down_button; + GtkWidget *size_button; +}; + +/* Even though these aren't external nor used outside this file they must be + * declared this way to ensure that they're in the library's symbol table and + * aren't mangled. That's so that dlsym is able to find them when GtkBuilder + * needs to connect the signals to them. + */ +extern "C" +{ +void gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data); +void gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data); +void gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data); +void gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data); +void gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data); +} + +static void +gnc_column_view_set_option(GncOptionDB* odb, const char* section, + const char* name, const GncOptionReportPlacementVec& new_value) +{ + odb->find_option(section, name)->set_value(new_value); +} + +static void +gnc_column_view_edit_destroy(gnc_column_view_edit * view) +{ + delete view->optwin; + scm_gc_unprotect_object(view->view); + gnc_option_db_destroy(view->odb); + g_free(view); +} + +static StrVec +get_available_reports () +{ + StrVec sv; + auto scm_list{scm_call_0(scm_c_eval_string("gnc:all-report-template-guids"))}; + for (auto next{scm_list}; !scm_is_null(next); next = scm_cdr(next)) + sv.emplace_back(scm_to_utf8_string(scm_car(next))); + return sv; +} + +static void +update_available_lists(gnc_column_view_edit * view) +{ + SCM template_menu_name = scm_c_eval_string("gnc:report-template-menu-name/report-guid"); + std::string selection; + + + GtkTreeIter iter; + + /* Update the list of available reports (left selection box). */ + auto tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view->available)); + auto model = gtk_tree_view_get_model (GTK_TREE_VIEW(view->available)); + + if (gtk_tree_selection_get_selected(tree_selection, &model, &iter)) + { + gchar *guid_str; + gtk_tree_model_get(model, &iter, + AVAILABLE_COL_GUID, &guid_str, + -1); + selection = std::string(guid_str); + g_free (guid_str); + } + view->available_list = get_available_reports(); + + auto store = GTK_LIST_STORE(model); + gtk_list_store_clear(store); + + for (auto guid : view->available_list) + { + auto rpt_guid{scm_from_utf8_string(guid.c_str())}; + auto name = + gnc_scm_to_utf8_string (scm_call_2(template_menu_name, rpt_guid, SCM_BOOL_F)); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + AVAILABLE_COL_NAME, _(name), + AVAILABLE_COL_GUID, guid.c_str(), + -1); + + if (guid == selection) + gtk_tree_selection_select_iter (tree_selection, &iter); + + g_free (name); + } +} + +static void +update_contents_lists(gnc_column_view_edit * view) +{ + SCM report_menu_name = scm_c_eval_string("gnc:report-menu-name"); + auto contents{view->odb->find_option("__general", "report-list")->get_value()}; + GtkTreeIter iter; + GncOptionReportPlacement selection{0, 0, 0}; + + /* Update the list of selected reports (right selection box). */ + auto tree_selection = gtk_tree_view_get_selection(view->contents); + + view->contents_list = contents; + + if (!contents.empty() && static_cast(view->contents_selected) < contents.size()) + selection = contents[view->contents_selected]; + + auto store = GTK_LIST_STORE(gtk_tree_view_get_model(view->contents)); + gtk_list_store_clear(store); + + for (size_t i = 0; i < contents.size(); ++i) + { + auto [id, wide, high] = contents[i]; + auto this_report = gnc_report_find(id); + auto name = gnc_scm_to_utf8_string (scm_call_1(report_menu_name, this_report)); + + gtk_list_store_append(store, &iter); + gtk_list_store_set + (store, &iter, + CONTENTS_COL_NAME, _(name), + CONTENTS_COL_ROW, i, + CONTENTS_COL_REPORT_COLS, wide, + CONTENTS_COL_REPORT_ROWS, high, + -1); + + if (id == std::get<0>(selection)) + gtk_tree_selection_select_iter (tree_selection, &iter); + + g_free (name); + } +} + +static void +gnc_column_view_update_buttons_cb (GtkTreeSelection *selection, + gnc_column_view_edit *r) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean is_selected; + + /* compare treeviews to establish which selected treeview */ + if (gtk_tree_selection_get_tree_view (selection) == r->available) + { + /* available treeview */ + is_selected = gtk_tree_selection_get_selected (selection, &model, &iter); + gtk_widget_set_sensitive (r->add_button, is_selected); + return; + } + + /* contents treeview */ + is_selected = gtk_tree_selection_get_selected (selection, &model, &iter); + gtk_widget_set_sensitive (r->size_button, is_selected); + gtk_widget_set_sensitive (r->remove_button, is_selected); + + if (is_selected) + { + int len = r->contents_list.size(); + + gtk_tree_model_get (model, &iter, + CONTENTS_COL_ROW, &r->contents_selected, -1); + + if (len > 1) + { + gtk_widget_set_sensitive (r->up_button, TRUE); + gtk_widget_set_sensitive (r->down_button, TRUE); + + if (r->contents_selected == len -1) + gtk_widget_set_sensitive (r->down_button, FALSE); + + if (r->contents_selected == 0) + gtk_widget_set_sensitive (r->up_button, FALSE); + } + } + else + { + gtk_widget_set_sensitive (r->up_button, FALSE); + gtk_widget_set_sensitive (r->down_button, FALSE); + } +} + +static void +gnc_column_view_edit_apply_cb(GncOptionsDialog *dlg, gpointer user_data) +{ + SCM dirty_report = scm_c_eval_string("gnc:report-set-dirty?!"); + auto win{static_cast(user_data)}; + + if (!win) return; + auto results = gnc_option_db_commit (dlg->get_option_db()); + for (auto iter = results; iter; iter = iter->next) + { + GtkWidget *dialog = + gtk_message_dialog_new(GTK_WINDOW(dlg->get_widget()), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", + (char*)iter->data); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + g_free (iter->data); + } + g_list_free (results); + + scm_call_2(dirty_report, win->view, SCM_BOOL_T); +} + +static void +gnc_column_view_edit_close_cb(GncOptionsDialog *win, gpointer user_data) +{ + auto r{static_cast(user_data)}; + SCM set_editor = scm_c_eval_string("gnc:report-set-editor-widget!"); + + scm_call_2(set_editor, r->view, SCM_BOOL_F); + gnc_column_view_edit_destroy(r); +} + + +/******************************************************************** + * gnc_column_view_edit_options + * create the editor. + ********************************************************************/ + +GtkWidget * +gnc_column_view_edit_options(GncOptionDB* odb, SCM view) +{ + SCM get_editor = scm_c_eval_string("gnc:report-editor-widget"); + SCM ptr; + GtkWidget * editor; + GtkListStore *store; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + ptr = scm_call_1(get_editor, view); + if (ptr != SCM_BOOL_F) + { +#define FUNC_NAME "gtk_window_present" + auto w{static_cast(SWIG_MustGetPtr(ptr, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))}; + gtk_window_present(w); +#undef FUNC_NAME + return nullptr; + } + else + { + gnc_column_view_edit * r = g_new0(gnc_column_view_edit, 1); + GtkBuilder *builder; + + r->optwin = new GncOptionsDialog(nullptr, GTK_WINDOW(gnc_ui_get_main_window (nullptr))); + + /* Hide the generic dialog page list. */ + gtk_widget_hide(r->optwin->get_page_list()); + + builder = gtk_builder_new(); + gnc_builder_add_from_file (builder, "dialog-report.glade", "view_contents_table"); + + editor = GTK_WIDGET(gtk_builder_get_object (builder, "view_contents_table")); + r->available = GTK_TREE_VIEW (gtk_builder_get_object (builder, "available_view")); + r->contents = GTK_TREE_VIEW (gtk_builder_get_object (builder, "contents_view")); + + r->add_button = GTK_WIDGET(gtk_builder_get_object (builder, "add_button1")); + r->remove_button = GTK_WIDGET(gtk_builder_get_object (builder, "remove_button1")); + r->up_button = GTK_WIDGET(gtk_builder_get_object (builder, "up_button1")); + r->down_button = GTK_WIDGET(gtk_builder_get_object (builder, "down_button1")); + r->size_button = GTK_WIDGET(gtk_builder_get_object (builder, "size_button1")); + + r->view = view; + r->available_list.clear(); + r->contents_selected = 0; + r->contents_list.clear(); + r->odb = odb; + + r->optwin->build_contents(r->odb); + + gtk_notebook_append_page(GTK_NOTEBOOK(r->optwin->get_notebook()), + editor, + gtk_label_new(_("Contents"))); + + scm_gc_protect_object(r->view); + + /* Build the 'available' view */ + store = gtk_list_store_new (NUM_AVAILABLE_COLS, G_TYPE_STRING, G_TYPE_STRING); + gtk_tree_view_set_model(r->available, GTK_TREE_MODEL(store)); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), AVAILABLE_COL_NAME, GTK_SORT_ASCENDING); + g_object_unref(store); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("", renderer, + "text", AVAILABLE_COL_NAME, + nullptr); + gtk_tree_view_append_column(r->available, column); + + /* use the selection cb to update buttons */ + selection = gtk_tree_view_get_selection(r->available); + g_signal_connect(selection, "changed", + G_CALLBACK(gnc_column_view_update_buttons_cb), r); + + /* Build the 'contents' view */ + store = gtk_list_store_new (NUM_CONTENTS_COLS, G_TYPE_STRING, G_TYPE_INT, + G_TYPE_INT, G_TYPE_INT); + gtk_tree_view_set_model(r->contents, GTK_TREE_MODEL(store)); + g_object_unref(store); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Report"), renderer, + "text", CONTENTS_COL_NAME, + nullptr); + gtk_tree_view_append_column(r->contents, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Rows"), renderer, + "text", CONTENTS_COL_REPORT_ROWS, + nullptr); + gtk_tree_view_append_column(r->contents, column); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Cols"), renderer, + "text", CONTENTS_COL_REPORT_COLS, + nullptr); + gtk_tree_view_append_column(r->contents, column); + + /* use the selection cb to update buttons */ + selection = gtk_tree_view_get_selection(r->contents); + g_signal_connect(selection, "changed", + G_CALLBACK(gnc_column_view_update_buttons_cb), r); + + update_available_lists(r); + update_contents_lists(r); + + r->optwin->set_apply_cb(gnc_column_view_edit_apply_cb, r); + r->optwin->set_close_cb(gnc_column_view_edit_close_cb, r); + + gtk_widget_show(r->optwin->get_widget()); + + gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, r); + + g_object_unref(G_OBJECT(builder)); + + return r->optwin->get_widget(); + } +} + +void +gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data) +{ + auto r = static_cast(user_data); + SCM make_report = scm_c_eval_string("gnc:make-report"); + SCM mark_report = scm_c_eval_string("gnc:report-set-needs-save?!"); + gchar *guid_str; + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(r->available)); + GtkTreeModel *model; + GtkTreeIter iter; + + /* make sure there is a selected entry */ + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + gtk_tree_model_get(model, &iter, + AVAILABLE_COL_GUID, &guid_str, -1); + else + return; + + auto template_name = scm_from_utf8_string(guid_str); + + auto new_report = scm_call_1(make_report, template_name); + auto id = scm_to_int(new_report); + scm_call_2(mark_report, gnc_report_find(id), SCM_BOOL_T); + auto oldlength = r->contents_list.size(); + GncOptionReportPlacement new_rpt_placement{id, 1, 1}; + + if (oldlength > static_cast(r->contents_selected)) + r->contents_list.emplace(r->contents_list.begin() + r->contents_selected + 1, id, 1, 1); + else + { + r->contents_list.emplace_back(id, 1, 1); + r->contents_selected = oldlength; + } + + gnc_column_view_set_option(r->odb, "__general", "report-list", + r->contents_list); + g_free (guid_str); + r->optwin->changed(); + update_contents_lists(r); +} + +void +gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data) +{ + auto r = static_cast(user_data); + + r->contents_list.erase(r->contents_list.begin() + r->contents_selected); + if (r->contents_selected) + --r->contents_selected; + gnc_column_view_set_option(r->odb, "__general", "report-list", + r->contents_list); + + r->optwin->changed(); + update_contents_lists(r); +} + +static void +move_selected_item(gnc_column_view_edit* r, int increment) +{ + if (!r || !increment) + return; + + auto cur_sel{r->contents_list.begin() + r->contents_selected}; + auto move_to{cur_sel + increment}; + if (increment > 0) + std::reverse(cur_sel, move_to + 1); + else + std::reverse(move_to, cur_sel + 1); + r->contents_selected += increment; + + gnc_column_view_set_option(r->odb, "__general", "report-list", + r->contents_list); + r->optwin->changed(); + update_contents_lists(r); +} + +void +gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data) +{ + auto r = static_cast(user_data); + move_selected_item(r, -1); +} + +void +gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data) +{ + auto r = static_cast(user_data); + move_selected_item(r, 1); +} + +void +gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data) +{ + auto r = static_cast(user_data); + GtkWidget * rowspin; + GtkWidget * colspin; + GtkWidget * dlg; + GtkBuilder *builder; + SCM current; + int length; + int dlg_ret; + + builder = gtk_builder_new(); + gnc_builder_add_from_file (builder, "dialog-report.glade", "col_adjustment"); + gnc_builder_add_from_file (builder, "dialog-report.glade", "row_adjustment"); + gnc_builder_add_from_file (builder, "dialog-report.glade", "edit_report_size"); + dlg = GTK_WIDGET(gtk_builder_get_object (builder, "edit_report_size")); + + gtk_window_set_transient_for (GTK_WINDOW(dlg), + GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(button)))); + + /* get the spinner widgets */ + rowspin = GTK_WIDGET(gtk_builder_get_object (builder, "row_spin")); + colspin = GTK_WIDGET(gtk_builder_get_object (builder, "col_spin")); + + if (r->contents_list.size() > static_cast(r->contents_selected)) + { + auto [id, wide, high] = r->contents_list[r->contents_selected]; + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(colspin), + static_cast(wide)); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(rowspin), + static_cast(high)); + + dlg_ret = gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_hide(dlg); + + if (dlg_ret == GTK_RESPONSE_OK) + { + std::get<1>(r->contents_list[r->contents_selected]) = + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(colspin)); + std::get<2>(r->contents_list[r->contents_selected]) = + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rowspin)); + + gnc_column_view_set_option(r->odb, "__general", "report-list", + r->contents_list); + r->optwin->changed(); + update_contents_lists(r); + } + + g_object_unref(G_OBJECT(builder)); + + gtk_widget_destroy(dlg); + } +} diff --git a/gnucash/gnome/dialog-report-column-view.h b/gnucash/gnome/dialog-report-column-view.hpp similarity index 95% rename from gnucash/gnome/dialog-report-column-view.h rename to gnucash/gnome/dialog-report-column-view.hpp index 1b37b499ff..9d3843b831 100644 --- a/gnucash/gnome/dialog-report-column-view.h +++ b/gnucash/gnome/dialog-report-column-view.hpp @@ -24,10 +24,12 @@ #define GNC_DIALOG_COLUMN_VIEW_H #include +extern "C" +{ #include typedef struct gncp_column_view_edit gnc_column_view_edit; -GtkWidget * gnc_column_view_edit_options(SCM options, SCM view); - +GtkWidget * gnc_column_view_edit_options(GncOptionDB* odb, SCM view); +} #endif diff --git a/gnucash/gnome/dialog-report-style-sheet.c b/gnucash/gnome/dialog-report-style-sheet.cpp similarity index 88% rename from gnucash/gnome/dialog-report-style-sheet.c rename to gnucash/gnome/dialog-report-style-sheet.cpp index bf6fe65a04..a873541545 100644 --- a/gnucash/gnome/dialog-report-style-sheet.c +++ b/gnucash/gnome/dialog-report-style-sheet.cpp @@ -22,21 +22,27 @@ * Boston, MA 02110-1301, USA gnu@gnu.org * ********************************************************************/ -#include - +#include #include #include +extern "C" +{ +#include + #include "dialog-report-style-sheet.h" -#include "dialog-options.h" #include "dialog-utils.h" #include "gnc-component-manager.h" #include "gnc-session.h" #include "gnc-gtk-utils.h" #include "gnc-gnome-utils.h" #include "gnc-guile-utils.h" -#include "gnc-report.h" #include "gnc-ui.h" +#include +} +#include "gnc-report.h" +#include +#include #define DIALOG_STYLE_SHEETS_CM_CLASS "style-sheets-dialog" #define GNC_PREFS_GROUP "dialogs.style-sheet" @@ -55,10 +61,10 @@ struct _stylesheetdialog typedef struct ss_info { - GNCOptionWin * odialog; - GNCOptionDB * odb; - SCM stylesheet; - GtkTreeRowReference * row_ref; + GncOptionsDialog * odialog; + GncOptionDB * odb; + SCM stylesheet; + GtkTreeRowReference *row_ref; } ss_info; enum @@ -68,13 +74,14 @@ enum COLUMN_DIALOG, N_COLUMNS }; - +extern "C" // So that gtk_builder_connect_full can find them. +{ void gnc_style_sheet_select_dialog_new_cb (GtkWidget *widget, gpointer user_data); void gnc_style_sheet_select_dialog_edit_cb (GtkWidget *widget, gpointer user_data); void gnc_style_sheet_select_dialog_delete_cb (GtkWidget *widget, gpointer user_data); void gnc_style_sheet_select_dialog_close_cb (GtkWidget *widget, gpointer user_data); void gnc_style_sheet_select_dialog_destroy_cb (GtkWidget *widget, gpointer user_data); - +} /************************************************************ * Style Sheet Edit Dialog (I.E. an options dialog) * ************************************************************/ @@ -82,9 +89,9 @@ void gnc_style_sheet_select_dialog_destroy_cb (GtkWidget *widget, gpointer user_ static void dirty_same_stylesheet (gpointer key, gpointer val, gpointer data) { - SCM dirty_ss = data; + auto dirty_ss{static_cast(data)}; SCM rep_ss = NULL; - SCM report = val; + auto report{static_cast(val)}; SCM func = NULL; func = scm_c_eval_string ("gnc:report-stylesheet"); @@ -103,7 +110,7 @@ dirty_same_stylesheet (gpointer key, gpointer val, gpointer data) } static void -gnc_style_sheet_options_apply_cb (GNCOptionWin * propertybox, +gnc_style_sheet_options_apply_cb (GncOptionsDialog * propertybox, gpointer user_data) { ss_info * ssi = (ss_info *)user_data; @@ -118,30 +125,30 @@ gnc_style_sheet_options_apply_cb (GNCOptionWin * propertybox, results = gnc_option_db_commit (ssi->odb); for (iter = results; iter; iter = iter->next) { - GtkWidget *dialog = gtk_message_dialog_new (NULL, - 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - "%s", - (char*)iter->data); - gtk_dialog_run (GTK_DIALOG(dialog)); - gtk_widget_destroy (dialog); + GtkWidget *dialog = gtk_message_dialog_new(nullptr, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", + (char*)iter->data); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); g_free (iter->data); } g_list_free (results); } static void -gnc_style_sheet_options_close_cb (GNCOptionWin * propertybox, +gnc_style_sheet_options_close_cb (GncOptionsDialog *opt_dialog, gpointer user_data) { - ss_info * ssi = user_data; - GtkTreeIter iter; + auto ssi{static_cast(user_data)}; if (gtk_tree_row_reference_valid (ssi->row_ref)) { - StyleSheetDialog * ss = gnc_style_sheet_dialog; - GtkTreePath *path = gtk_tree_row_reference_get_path (ssi->row_ref); + auto ss = gnc_style_sheet_dialog; + auto path = gtk_tree_row_reference_get_path (ssi->row_ref); + GtkTreeIter iter; if (gtk_tree_model_get_iter (GTK_TREE_MODEL(ss->list_store), &iter, path)) gtk_list_store_set (ss->list_store, &iter, COLUMN_DIALOG, NULL, @@ -149,7 +156,7 @@ gnc_style_sheet_options_close_cb (GNCOptionWin * propertybox, gtk_tree_path_free (path); } gtk_tree_row_reference_free (ssi->row_ref); - gnc_options_dialog_destroy (ssi->odialog); + delete ssi->odialog; gnc_option_db_destroy (ssi->odb); scm_gc_unprotect_object (ssi->stylesheet); g_free (ssi); @@ -163,34 +170,26 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss, { SCM get_options = scm_c_eval_string ("gnc:html-style-sheet-options"); - SCM scm_options = scm_call_1 (get_options, sheet_info); + SCM scm_dispatch = scm_call_1 (get_options, sheet_info); ss_info * ssinfo = g_new0 (ss_info, 1); - GtkWidget * window; gchar * title; GtkWindow * parent = GTK_WINDOW(gtk_widget_get_toplevel (GTK_WIDGET(ss->list_view))); - title = g_strdup_printf (_("HTML Style Sheet Properties: %s"), name); - ssinfo->odialog = gnc_options_dialog_new (title, parent); - ssinfo->odb = gnc_option_db_new (scm_options); + title = g_strdup_printf(_("HTML Style Sheet Properties: %s"), name); + ssinfo->odialog = new GncOptionsDialog(title, parent); + ssinfo->odb = gnc_get_optiondb_from_dispatcher(scm_dispatch); ssinfo->stylesheet = sheet_info; ssinfo->row_ref = row_ref; g_free (title); scm_gc_protect_object (ssinfo->stylesheet); - g_object_ref (gnc_options_dialog_widget (ssinfo->odialog)); + g_object_ref (ssinfo->odialog->get_widget()); - gnc_options_dialog_build_contents (ssinfo->odialog, - ssinfo->odb); + ssinfo->odialog->build_contents(ssinfo->odb); - gnc_options_dialog_set_style_sheet_options_help_cb (ssinfo->odialog); - - gnc_options_dialog_set_apply_cb (ssinfo->odialog, - gnc_style_sheet_options_apply_cb, - ssinfo); - gnc_options_dialog_set_close_cb (ssinfo->odialog, - gnc_style_sheet_options_close_cb, - ssinfo); - window = gnc_options_dialog_widget (ssinfo->odialog); + ssinfo->odialog->set_apply_cb(gnc_style_sheet_options_apply_cb, ssinfo); + ssinfo->odialog->set_close_cb(gnc_style_sheet_options_close_cb, ssinfo); + auto window = ssinfo->odialog->get_widget(); gtk_window_set_transient_for (GTK_WINDOW(window), GTK_WINDOW(gnc_style_sheet_dialog->toplevel)); gtk_window_set_destroy_with_parent (GTK_WINDOW(window), TRUE); @@ -260,9 +259,9 @@ gnc_style_sheet_new (StyleSheetDialog * ssd) if (dialog_retval == GTK_RESPONSE_OK) { gint choice = gtk_combo_box_get_active (GTK_COMBO_BOX(template_combo)); - const char *template_str = g_list_nth_data (template_names, choice); - const char *name_str = gtk_entry_get_text (GTK_ENTRY(name_entry)); - if (name_str && strlen (name_str) == 0) + auto template_str{static_cast(g_list_nth_data (template_names, choice))}; + const char *name_str = gtk_entry_get_text(GTK_ENTRY(name_entry)); + if (name_str && strlen(name_str) == 0) { /* If the name is empty, we display an error dialog but * refuse to create the new style sheet. */ @@ -442,8 +441,7 @@ gnc_style_sheet_select_dialog_delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) { - StyleSheetDialog *ss = (StyleSheetDialog *)user_data; - // this cb allows the window size to be saved on closing with the X + auto ss{static_cast(user_data)}; gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(ss->toplevel)); return FALSE; } diff --git a/gnucash/gnome/dialog-report-style-sheet.h b/gnucash/gnome/dialog-report-style-sheet.h index fc1905c57f..b0a0cfa20f 100644 --- a/gnucash/gnome/dialog-report-style-sheet.h +++ b/gnucash/gnome/dialog-report-style-sheet.h @@ -23,9 +23,14 @@ #ifndef GNC_DIALOG_STYLE_SHEET_H #define GNC_DIALOG_STYLE_SHEET_H - +#ifdef __cplusplus +extern "C" +{ +#endif typedef struct _stylesheetdialog StyleSheetDialog; -void gnc_style_sheet_dialog_open (GtkWindow *parent); - +void gnc_style_sheet_dialog_open(GtkWindow *parent); +#ifdef __cplusplus +} +#endif #endif diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c index 55ccae223a..2b601f9445 100644 --- a/gnucash/gnome/gnc-budget-view.c +++ b/gnucash/gnome/gnc-budget-view.c @@ -49,7 +49,6 @@ #include "gnc-budget.h" #include "gnc-features.h" -#include "dialog-options.h" #include "dialog-utils.h" #include "gnc-gnome-utils.h" #include "gnc-gobject-utils.h" @@ -61,7 +60,6 @@ #include "gnc-tree-view-account.h" #include "gnc-ui.h" #include "gnc-ui-util.h" -#include "option-util.h" #include "gnc-main-window.h" #include "gnc-component-manager.h" #include "gnc-state.h" diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c index 456dad2ee1..4a9494b21b 100644 --- a/gnucash/gnome/gnc-plugin-page-budget.c +++ b/gnucash/gnome/gnc-plugin-page-budget.c @@ -44,13 +44,13 @@ #include "swig-runtime.h" #include "libguile.h" +#include #include "gnc-plugin-page-register.h" #include "gnc-plugin-page-report.h" #include "gnc-budget.h" #include "gnc-features.h" -#include "dialog-options.h" #include "dialog-utils.h" #include "gnc-gnome-utils.h" #include "misc-gnome-utils.h" @@ -65,7 +65,6 @@ #include "gnc-ui.h" #include "gnc-ui-util.h" #include "gnc-window.h" -#include "option-util.h" #include "gnc-main-window.h" #include "gnc-component-manager.h" diff --git a/gnucash/gnome/gnc-plugin-page-report.c b/gnucash/gnome/gnc-plugin-page-report.cpp similarity index 90% rename from gnucash/gnome/gnc-plugin-page-report.c rename to gnucash/gnome/gnc-plugin-page-report.cpp index e8a37b0460..23e98f52dc 100644 --- a/gnucash/gnome/gnc-plugin-page-report.c +++ b/gnucash/gnome/gnc-plugin-page-report.cpp @@ -36,13 +36,15 @@ @author Copyright (C) 2004 Joshua Sled @author Copyright (C) 2005 David Hampton */ - -#include - +#include #include #include #include -#include + +extern "C" +{ +#include + #include #include @@ -63,18 +65,21 @@ #include "gnc-plugin-page-report.h" #include "gnc-plugin-file-history.h" #include "gnc-prefs.h" -#include "gnc-report.h" #include "gnc-session.h" #include "gnc-ui-util.h" #include "gnc-ui.h" #include "gnc-window.h" -#include "option-util.h" #include "window-report.h" #include "swig-runtime.h" #include "guile-mappings.h" -#include "business-options.h" #include "gnc-icons.h" #include "print-session.h" +} + + +#include +#include +#include /* NW: you can add GNC_MOD_REPORT to gnc-engine.h or simply define it locally. Any unique string with @@ -82,13 +87,13 @@ a gnucash- prefix will do. Then just set a log level with qof_log_set_level().*/ static QofLogModule log_module = GNC_MOD_GUI; -static GObjectClass *parent_class = NULL; +static GObjectClass *parent_class = nullptr; // A static GHashTable to record the usage count for each printer // output name. FIXME: Currently this isn't cleaned up at program // shutdown because there isn't a place to easily insert a finalize() // function for this. Oh well. -static GHashTable *static_report_printnames = NULL; +static GHashTable *static_report_printnames = nullptr; // Property-id values. enum @@ -106,15 +111,15 @@ typedef struct GncPluginPageReportPrivate /// The report which this Page is satisfying SCM cur_report; /// The Option DB for this report. - GNCOptionDB *cur_odb; - SCM option_change_cb_id; + GncOptionDB *cur_odb; + size_t option_change_cb_id = 0; /* initial_report is special; it's the one that's saved and * restored. The name_change_callback only gets called when * the initial_report name is changed. */ SCM initial_report; - GNCOptionDB * initial_odb; - SCM name_change_cb_id; + GncOptionDB * initial_odb; + size_t name_change_cb_id; /* keep a list of edited reports so that we can destroy them when * the window is closed. */ @@ -273,7 +278,7 @@ gnc_plugin_page_report_class_init (GncPluginPageReportClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GncPluginPageClass *gnc_plugin_page_class = GNC_PLUGIN_PAGE_CLASS(klass); - parent_class = g_type_class_peek_parent (klass); + parent_class = static_cast(g_type_class_peek_parent (klass)); object_class->constructor = gnc_plugin_page_report_constructor; object_class->finalize = gnc_plugin_page_report_finalize; @@ -294,29 +299,19 @@ gnc_plugin_page_report_class_init (GncPluginPageReportClass *klass) gnc_plugin_page_class->focus_page_function = gnc_plugin_page_report_focus_widget; // create the "reportId" property + auto paramspec{static_cast(G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)}; g_object_class_install_property( object_class, PROP_REPORT_ID, g_param_spec_int( "report-id", _("The numeric ID of the report."), _("The numeric ID of the report."), - -1, G_MAXINT, -1, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE ) ); - - /* JSLED: report-selected? - plugin_page_signals[ACCOUNT_SELECTED] = - g_signal_new ("account_selected", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GncPluginPageReportClass, account_selected), - NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - */ + -1, G_MAXINT, -1, + paramspec)); // Also initialize the report name usage count table if (!static_report_printnames) static_report_printnames = g_hash_table_new_full(g_str_hash, - g_str_equal, g_free, NULL); + g_str_equal, g_free, nullptr); } static void @@ -353,8 +348,8 @@ gnc_plugin_page_report_load_uri (GncPluginPage *page) URLType type; char * id_name; char * child_name; - char * url_location = NULL; - char * url_label = NULL; + char * url_location = nullptr; + char * url_label = nullptr; report = GNC_PLUGIN_PAGE_REPORT(page); priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); @@ -363,7 +358,7 @@ gnc_plugin_page_report_load_uri (GncPluginPage *page) DEBUG( "Load uri id=%d", priv->reportId ); id_name = g_strdup_printf("id=%d", priv->reportId ); - child_name = gnc_build_url( URL_TYPE_REPORT, id_name, NULL ); + child_name = gnc_build_url( URL_TYPE_REPORT, id_name, nullptr ); type = gnc_html_parse_url( priv->html, child_name, &url_location, &url_label); DEBUG( "passing id_name=[%s] child_name=[%s] type=[%s], location=[%s], label=[%s]", id_name, child_name ? child_name : "(null)", @@ -388,8 +383,8 @@ gnc_plugin_page_report_load_uri (GncPluginPage *page) gnc_plugin_page_report_set_progressbar( page, FALSE ); - // this resets the window for the progressbar to NULL - gnc_window_set_progressbar_window( NULL ); + // this resets the window for the progressbar to nullptr + gnc_window_set_progressbar_window( nullptr ); } /* used to capture Ctrl+Alt+PgUp/Down for tab selection */ @@ -443,8 +438,8 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page ) URLType type; char * id_name; char * child_name; - char * url_location = NULL; - char * url_label = NULL; + char * url_location = nullptr; + char * url_label = nullptr; ENTER("page %p", page); @@ -458,7 +453,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page ) report = GNC_PLUGIN_PAGE_REPORT(page); priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); - topLvl = gnc_ui_get_main_window (NULL); + topLvl = gnc_ui_get_main_window (nullptr); // priv->html = gnc_html_new( topLvl ); priv->html = gnc_html_factory_create_html(); gnc_html_set_parent( priv->html, topLvl ); @@ -468,7 +463,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page ) gnc_plugin_page_report_history_destroy_cb, (gpointer)priv); - priv->container = GTK_CONTAINER(gtk_frame_new(NULL)); + priv->container = GTK_CONTAINER(gtk_frame_new(nullptr)); gtk_frame_set_shadow_type(GTK_FRAME(priv->container), GTK_SHADOW_NONE); // Set the name for this widget so it can be easily manipulated with css @@ -478,7 +473,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page ) gnc_html_get_widget(priv->html)); priv->component_manager_id = - gnc_register_gui_component(WINDOW_REPORT_CM_CLASS, NULL, + gnc_register_gui_component(WINDOW_REPORT_CM_CLASS, nullptr, close_handler, page); gnc_gui_component_set_session(priv->component_manager_id, gnc_get_current_session()); @@ -489,7 +484,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page ) /* We need to call the load call back so the report appears to have been run so it will get saved properly if the report is not realized in session */ id_name = g_strdup_printf("id=%d", priv->reportId ); - child_name = gnc_build_url( URL_TYPE_REPORT, id_name, NULL ); + child_name = gnc_build_url( URL_TYPE_REPORT, id_name, nullptr ); type = gnc_html_parse_url( priv->html, child_name, &url_location, &url_label); gnc_plugin_page_report_load_cb (priv->html, type, id_name, url_label, report); @@ -503,7 +498,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page ) g_signal_connect (G_OBJECT(page), "inserted", G_CALLBACK(gnc_plugin_page_inserted_cb), - NULL); + nullptr); // used to capture Ctrl+Alt+PgUp/Down for tab selection webview = gnc_html_get_webview (priv->html); @@ -556,9 +551,9 @@ gnc_plugin_page_report_setup( GncPluginPage *ppage ) priv->cur_report = SCM_BOOL_F; priv->initial_report = SCM_BOOL_F; priv->edited_reports = SCM_EOL; - priv->name_change_cb_id = SCM_BOOL_F; + priv->name_change_cb_id = 0; - g_object_get( ppage, "report-id", &report_id, NULL ); + g_object_get( ppage, "report-id", &report_id, nullptr ); PINFO("report-id: %d\n", report_id); @@ -593,7 +588,6 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, GncPluginPageReport *report = GNC_PLUGIN_PAGE_REPORT(data); GncPluginPageReportPrivate *priv; int report_id; - SCM get_options = scm_c_eval_string("gnc:report-options"); SCM set_needs_save = scm_c_eval_string("gnc:report-set-needs-save?!"); SCM inst_report; @@ -649,20 +643,19 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, DEBUG("calling set_needs_save for report with id=%d", report_id); scm_call_2(set_needs_save, inst_report, SCM_BOOL_T); - priv->initial_odb = gnc_option_db_new(scm_call_1(get_options, inst_report)); + priv->initial_odb = gnc_get_report_optiondb(inst_report); + priv->name_change_cb_id = - gnc_option_db_register_change_callback(priv->initial_odb, - gnc_plugin_page_report_refresh, - priv, - "General", "Report name"); + priv->initial_odb->register_callback( + gnc_plugin_page_report_refresh, priv); + } - if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != NULL)) + if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != nullptr)) { - gnc_option_db_unregister_change_callback_id(priv->cur_odb, - priv->option_change_cb_id); - gnc_option_db_destroy(priv->cur_odb); - priv->cur_odb = NULL; + priv->cur_odb->unregister_callback(priv->option_change_cb_id); + priv->option_change_cb_id = 0; + priv->cur_odb = nullptr; } if (priv->cur_report != SCM_BOOL_F) @@ -670,11 +663,11 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, priv->cur_report = inst_report; scm_gc_protect_object(priv->cur_report); - priv->cur_odb = gnc_option_db_new(scm_call_1(get_options, inst_report)); + priv->cur_odb = gnc_get_report_optiondb(inst_report); + priv->option_change_cb_id = - gnc_option_db_register_change_callback(priv->cur_odb, - gnc_plugin_page_report_option_change_cb, - report, NULL, NULL); + priv->cur_odb->register_callback( + gnc_plugin_page_report_option_change_cb, report); if (gnc_html_history_forward_p(gnc_html_get_history(priv->html))) { @@ -731,8 +724,9 @@ gnc_plugin_page_report_option_change_cb(gpointer data) /* Update the page (i.e. the notebook tab and window title) */ old_name = gnc_plugin_page_get_page_name(GNC_PLUGIN_PAGE(report)); - new_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General", - "Report name", NULL); + new_name = g_strdup(gnc_option_db_lookup_string_value(priv->cur_odb, + "General", + "Report name")); if (strcmp(old_name, new_name) != 0) { /* Bug 727130, 760711 - remove only the non-printable @@ -760,8 +754,8 @@ gnc_plugin_page_report_option_change_cb(gpointer data) gnc_plugin_page_report_set_progressbar( page, FALSE ); - // this resets the window for the progressbar to NULL - gnc_window_set_progressbar_window( NULL ); + // this resets the window for the progressbar to nullptr + gnc_window_set_progressbar_window( nullptr ); priv->reloading = FALSE; } @@ -801,7 +795,7 @@ gnc_plugin_page_report_refresh(gpointer data) { // FIXME? DEBUG( "report-refresh called" ); - // something like ... gnc_plugin_page_report_redraw( NULL, (GncPluginPageReportPrivate*)data ); + // something like ... gnc_plugin_page_report_redraw( nullptr, (GncPluginPageReportPrivate*)data ); return; } @@ -861,8 +855,8 @@ gnc_plugin_page_report_save_page (GncPluginPage *plugin_page, gchar *text, *key_name; g_return_if_fail (GNC_IS_PLUGIN_PAGE_REPORT(plugin_page)); - g_return_if_fail (key_file != NULL); - g_return_if_fail (group_name != NULL); + g_return_if_fail (key_file != nullptr); + g_return_if_fail (group_name != nullptr); ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file, group_name); @@ -935,14 +929,14 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window, GncPluginPage *page; gchar **keys; gsize i, num_keys; - GError *error = NULL; + GError *error = nullptr; gchar *option_string; gint report_id; SCM scm_id, final_id = SCM_BOOL_F; SCM report; - g_return_val_if_fail(key_file, NULL); - g_return_val_if_fail(group_name, NULL); + g_return_val_if_fail(key_file, nullptr); + g_return_val_if_fail(group_name, nullptr); ENTER("key_file %p, group_name %s", key_file, group_name); keys = g_key_file_get_keys(key_file, group_name, &num_keys, &error); @@ -952,7 +946,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window, group_name, error->message); g_error_free(error); LEAVE("no keys"); - return NULL; + return nullptr; } for (i = 0; i < num_keys; i++) @@ -968,7 +962,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window, g_error_free(error); g_strfreev (keys); LEAVE("bad value"); - return NULL; + return nullptr; } scm_id = scm_eval_string(scm_from_utf8_string(option_string)); g_free(option_string); @@ -977,7 +971,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window, { DEBUG("report id not an integer for key %s", keys[i]); g_strfreev (keys); - return NULL; + return nullptr; } if (final_id == SCM_BOOL_F) @@ -993,7 +987,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window, if (final_id == SCM_BOOL_F) { LEAVE("report not specified"); - return NULL; + return nullptr; } report_id = scm_to_int(final_id); @@ -1001,7 +995,7 @@ gnc_plugin_page_report_recreate_page (GtkWidget *window, if (!report) { LEAVE("report doesn't exist"); - return NULL; + return nullptr; } page = gnc_plugin_page_report_new( report_id ); @@ -1028,25 +1022,30 @@ gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name) const gchar *old_name; g_return_if_fail(GNC_IS_PLUGIN_PAGE_REPORT(page)); - g_return_if_fail(name != NULL); + g_return_if_fail(name != nullptr); ENTER("page %p, name %s", page, name); priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(page); - /* Is this a redundant call? */ - old_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General", - "Report name", NULL); - DEBUG("Comparing old name '%s' to new name '%s'", - old_name ? old_name : "(null)", name); - if (old_name && (strcmp(old_name, name) == 0)) + if (priv->cur_odb) { - LEAVE("no change"); - return; - } - /* Store the new name for the report. */ - gnc_option_db_set_string_option(priv->cur_odb, "General", - "Report name", name); + /* Is this a redundant call? */ + old_name = gnc_option_db_lookup_string_value(priv->cur_odb, "General", + "Report name"); + DEBUG("Comparing old name '%s' to new name '%s'", + old_name ? old_name : "(null)", name); + if (old_name && (strcmp(old_name, name) == 0)) + { + LEAVE("no change"); + return; + } + + /* Store the new name for the report. */ + gnc_option_db_set_string_value(priv->cur_odb, "General", + "Report name", name); + + } /* Have to manually call the option change hook. */ gnc_plugin_page_report_option_change_cb(page); @@ -1100,10 +1099,8 @@ gnc_plugin_page_report_destroy(GncPluginPageReportPrivate * priv) scm_call_2(set_editor, SCM_CAR(edited), SCM_BOOL_F); if (editor != SCM_BOOL_F) { - GtkWidget *w = NULL; #define FUNC_NAME "gtk_widget_destroy" - w = SWIG_MustGetPtr(editor, - SWIG_TypeQuery("_p_GtkWidget"), 1, 0); + auto w{static_cast(SWIG_MustGetPtr(editor, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))}; #undef FUNC_NAME gtk_widget_destroy(GTK_WIDGET(w)); } @@ -1111,17 +1108,15 @@ gnc_plugin_page_report_destroy(GncPluginPageReportPrivate * priv) if (priv->initial_odb) { - gnc_option_db_unregister_change_callback_id(priv->initial_odb, - priv->name_change_cb_id); - +//Remove this if there's a double-free gnc_option_db_destroy(priv->initial_odb); - priv->initial_odb = NULL; + priv->initial_odb = nullptr; } gnc_html_destroy(priv->html); - priv->container = NULL; - priv->html = NULL; + priv->container = nullptr; + priv->html = nullptr; if (priv->cur_report != SCM_BOOL_F) scm_gc_unprotect_object(priv->cur_report); @@ -1142,12 +1137,12 @@ static action_toolbar_labels toolbar_labels[] = to be used as toolbar button label. */ { "ReportSaveAsAction", N_("Save Config As...") }, { "FilePrintPDFAction", N_("Make Pdf") }, - { NULL, NULL }, + { nullptr, nullptr }, }; static const gchar *initially_insensitive_actions[] = { - NULL + nullptr }; static void @@ -1162,14 +1157,13 @@ gnc_plugin_page_report_constructor(GType this_type, guint n_properties, GObjectC GncPluginPageReportClass *our_class; GObjectClass *parent_class; gint reportId = -42; - int i; our_class = GNC_PLUGIN_PAGE_REPORT_CLASS ( g_type_class_peek (GNC_TYPE_PLUGIN_PAGE_REPORT)); parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (our_class)); obj = parent_class->constructor(this_type, n_properties, properties); - for (i = 0; i < n_properties; i++) + for (decltype(n_properties) i = 0; i < n_properties; i++) { GObjectConstructParam prop = properties[i]; if (strcmp(prop.pspec->name, "report-id") == 0) @@ -1207,7 +1201,7 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report G_CALLBACK(gnc_plugin_page_report_print_cb) }, { - "FilePrintPDFAction", GNC_ICON_PDF_EXPORT, N_("Export as P_DF..."), NULL, + "FilePrintPDFAction", GNC_ICON_PDF_EXPORT, N_("Export as P_DF..."), nullptr, N_("Export the current report as a PDF document"), G_CALLBACK(gnc_plugin_page_report_exportpdf_cb) }, @@ -1215,7 +1209,7 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report { "EditCutAction", "edit-cut", N_("Cu_t"), "X", N_("Cut the current selection and copy it to clipboard"), - NULL + nullptr }, { "EditCopyAction", "edit-copy", N_("_Copy"), "C", @@ -1225,7 +1219,7 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report { "EditPasteAction", "edit-paste", N_("_Paste"), "V", N_("Paste the clipboard content at the cursor position"), - NULL + nullptr }, { "ViewRefreshAction", "view-refresh", N_("_Refresh"), "r", @@ -1241,33 +1235,33 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report report_saveas_str, G_CALLBACK(gnc_plugin_page_report_save_as_cb) }, { - "ReportExportAction", "go-next", N_("Export _Report"), NULL, + "ReportExportAction", "go-next", N_("Export _Report"), nullptr, N_("Export HTML-formatted report to file"), G_CALLBACK(gnc_plugin_page_report_export_cb) }, { - "ReportOptionsAction", "document-properties", N_("_Report Options"), NULL, + "ReportOptionsAction", "document-properties", N_("_Report Options"), nullptr, N_("Edit report options"), G_CALLBACK(gnc_plugin_page_report_options_cb) }, { - "ReportBackAction", "go-previous", N_("Back"), NULL, + "ReportBackAction", "go-previous", N_("Back"), nullptr, N_("Move back one step in the history"), G_CALLBACK(gnc_plugin_page_report_back_cb) }, { - "ReportForwAction", "go-next", N_("Forward"), NULL, + "ReportForwAction", "go-next", N_("Forward"), nullptr, N_("Move forward one step in the history"), G_CALLBACK(gnc_plugin_page_report_forw_cb) }, { - "ReportReloadAction", "view-refresh", N_("Reload"), NULL, + "ReportReloadAction", "view-refresh", N_("Reload"), nullptr, N_("Reload the current page"), G_CALLBACK(gnc_plugin_page_report_reload_cb) }, { - "ReportStopAction", "process-stop", N_("Stop"), NULL, + "ReportStopAction", "process-stop", N_("Stop"), nullptr, N_("Cancel outstanding HTML requests"), G_CALLBACK(gnc_plugin_page_report_stop_cb) }, @@ -1289,7 +1283,7 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report "page-uri", "default:", "ui-description", "gnc-plugin-page-report-ui.xml", "use-new-window", use_new, - NULL); + nullptr); g_free(name); /* change me when the system supports multiple books */ @@ -1316,11 +1310,9 @@ gnc_plugin_page_report_constr_init(GncPluginPageReport *plugin_page, gint report GncPluginPage* gnc_plugin_page_report_new( int reportId ) { - GncPluginPageReport *plugin_page; - DEBUG( "report id = %d", reportId ); - plugin_page = g_object_new( GNC_TYPE_PLUGIN_PAGE_REPORT, - "report-id", reportId, NULL ); + auto plugin_page{g_object_new(GNC_TYPE_PLUGIN_PAGE_REPORT, "report-id", + reportId, nullptr)}; DEBUG( "plugin_page: %p", plugin_page ); DEBUG( "set %d on page %p", reportId, plugin_page ); return GNC_PLUGIN_PAGE( plugin_page ); @@ -1356,8 +1348,7 @@ gnc_plugin_page_report_raise_editor(SCM report) SCM get_editor = scm_c_eval_string("gnc:report-editor-widget"); SCM editor = scm_call_1(get_editor, report); #define FUNC_NAME "gtk_window_present" - GtkWidget *w = SWIG_MustGetPtr(editor, - SWIG_TypeQuery("_p_GtkWidget"), 1, 0); + auto w{static_cast(SWIG_MustGetPtr(editor, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))}; #undef FUNC_NAME gtk_window_present(GTK_WINDOW(w)); } @@ -1397,7 +1388,7 @@ static void gnc_plugin_page_report_forw_cb( GtkAction *action, GncPluginPageReport *report ) { GncPluginPageReportPrivate *priv; - gnc_html_history_node * node = NULL; + gnc_html_history_node * node = nullptr; DEBUG( "forw" ); priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); @@ -1464,8 +1455,8 @@ gnc_plugin_page_report_reload_cb( GtkAction *action, GncPluginPageReport *report gnc_plugin_page_report_set_progressbar( page, FALSE ); - // this resets the window for the progressbar to NULL - gnc_window_set_progressbar_window( NULL ); + // this resets the window for the progressbar to nullptr + gnc_window_set_progressbar_window( nullptr ); priv->reloading = FALSE; } @@ -1483,7 +1474,7 @@ gnc_plugin_page_report_stop_cb( GtkAction *action, GncPluginPageReport *report ) static SCM gnc_get_export_type_choice (SCM export_types, GtkWindow *parent) { - GList * choices = NULL; + GList * choices = nullptr; gboolean bad = FALSE; GList * node; int choice; @@ -1526,7 +1517,7 @@ gnc_get_export_type_choice (SCM export_types, GtkWindow *parent) choice = gnc_choose_radio_option_dialog (GTK_WIDGET (parent), _("Choose export format"), _("Choose the export format for this report:"), - NULL, 0, choices); + nullptr, 0, choices); } else choice = -1; @@ -1568,21 +1559,21 @@ gnc_get_export_filename (SCM choice, GtkWindow *parent) title = g_strdup_printf (_("Save %s To File"), type); default_dir = gnc_get_default_directory(GNC_PREFS_GROUP_REPORT); - filepath = gnc_file_dialog (parent, title, NULL, default_dir, + filepath = gnc_file_dialog (parent, title, nullptr, default_dir, GNC_FILE_DIALOG_EXPORT); - if (filepath != NULL) // test for cancel pressed + if (filepath != nullptr) // test for cancel pressed { /* Try to test for extension on file name, add if missing */ - if (g_strrstr(filepath, ".") == NULL) - filepath = g_strconcat(filepath, ".", g_ascii_strdown(type, strlen(type)), NULL); + if (g_strrstr(filepath, ".") == nullptr) + filepath = g_strconcat(filepath, ".", g_ascii_strdown(type, strlen(type)), nullptr); } g_free (type); g_free (title); g_free (default_dir); if (!filepath) - return NULL; + return nullptr; default_dir = g_path_get_dirname(filepath); gnc_set_default_directory (GNC_PREFS_GROUP_REPORT, default_dir); @@ -1598,7 +1589,7 @@ gnc_get_export_filename (SCM choice, GtkWindow *parent) gnc_error_dialog (parent, format, strerror(errno)); g_free(filepath); - return NULL; + return nullptr; } /* Check for a file that isn't a regular file. */ @@ -1608,7 +1599,7 @@ gnc_get_export_filename (SCM choice, GtkWindow *parent) gnc_error_dialog (parent, "%s", message); g_free(filepath); - return NULL; + return nullptr; } if (rc == 0) @@ -1619,7 +1610,7 @@ gnc_get_export_filename (SCM choice, GtkWindow *parent) if (!gnc_verify_dialog (parent, FALSE, format, filepath)) { g_free(filepath); - return NULL; + return nullptr; } } @@ -1728,8 +1719,10 @@ gnc_plugin_page_report_export_cb( GtkAction *action, GncPluginPageReport *report SCM get_export_error = scm_c_eval_string ("gnc:html-document-export-error"); if (scm_is_false (scm_call_1 (query_result, document))) - gnc_error_dialog (parent, _("This report must be upgraded to \ -return a document object with export-string or export-error.")); + gnc_error_dialog (parent, "%s", + _("This report must be upgraded to return a " + "document object with export-string or " + "export-error.")); else { SCM export_string = scm_call_1 (get_export_string, document); @@ -1737,7 +1730,7 @@ return a document object with export-string or export-error.")); if (scm_is_string (export_string)) { - GError *err = NULL; + GError *err = nullptr; gchar *exported = scm_to_utf8_string (export_string); if (!g_file_set_contents (filepath, exported, -1, &err)) gnc_error_dialog (parent, "Error during export: %s", err->message); @@ -1752,8 +1745,10 @@ return a document object with export-string or export-error.")); g_free (str); } else - gnc_error_dialog (parent, _("This report must be upgraded to \ -return a document object with export-string or export-error.")); + gnc_error_dialog (parent, "%s", + _("This report must be upgraded to return a " + "document object with export-string or " + "export-error.")); } result = TRUE; } @@ -1785,11 +1780,13 @@ gnc_plugin_page_report_options_cb( GtkAction *action, GncPluginPageReport *repor gnc_plugin_page_report_add_edited_report(priv, priv->cur_report); } -static GncInvoice *lookup_invoice(GncPluginPageReportPrivate *priv) +static GncInvoice* +lookup_invoice(GncPluginPageReportPrivate *priv) { - g_assert(priv); - return gnc_option_db_lookup_invoice_option(priv->cur_odb, "General", - "Invoice Number", NULL); + const QofInstance* opt_val = + gnc_option_db_lookup_qofinstance_value(priv->cur_odb, "General", + "Invoice Number"); + return GNC_INVOICE(opt_val); } #define GNC_PREFS_GROUP_REPORT_PDFEXPORT GNC_PREFS_GROUP_GENERAL_REPORT ".pdf-export" @@ -1798,8 +1795,8 @@ static GncInvoice *lookup_invoice(GncPluginPageReportPrivate *priv) static gchar *report_create_jobname(GncPluginPageReportPrivate *priv) { - gchar *job_name = NULL; - gchar *report_name = NULL; + gchar *job_name = nullptr; + gchar *report_name = nullptr; const gchar *report_number = ""; gchar *job_date; const gchar *default_jobname = N_("GnuCash-Report"); @@ -1823,7 +1820,7 @@ static gchar *report_create_jobname(GncPluginPageReportPrivate *priv) date_format_string = qof_date_format_get_string (date_format_here); - job_date = gnc_print_time64 (gnc_time (NULL), date_format_string); + job_date = gnc_print_time64 (gnc_time (nullptr), date_format_string); g_free (format_code); } @@ -1844,8 +1841,9 @@ static gchar *report_create_jobname(GncPluginPageReportPrivate *priv) * so I added yet another hack below for this. cstim. */ GncInvoice *invoice; - report_name = gnc_option_db_lookup_string_option(priv->cur_odb, "General", - "Report name", NULL); + report_name = g_strdup(gnc_option_db_lookup_string_value(priv->cur_odb, + "General", + "Report name")); if (!report_name) report_name = g_strdup (_(default_jobname)); if (g_strcmp0(report_name, _("Printable Invoice")) == 0 @@ -1901,7 +1899,7 @@ static gchar *report_create_jobname(GncPluginPageReportPrivate *priv) // Lookup the existing usage count value = g_hash_table_lookup(static_report_printnames, job_name); - already_found = (value != NULL); + already_found = (value != nullptr); if (!value) { value = GINT_TO_POINTER(0); @@ -1949,7 +1947,7 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep GncPluginPageReportPrivate *priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); gchar *job_name = report_create_jobname(priv); GncInvoice *invoice; - GncOwner *owner = NULL; + GncOwner *owner = nullptr; // Do we have an invoice report? invoice = lookup_invoice(priv); @@ -1960,21 +1958,18 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep if (owner) { QofInstance *inst = qofOwnerGetOwner (owner); - gchar *dirname = NULL; - qof_instance_get (inst, "export-pdf-dir", &dirname, NULL); + gchar *dirname = nullptr; + qof_instance_get (inst, "export-pdf-dir", &dirname, nullptr); // Yes. In the kvp, look up the key for the Export-PDF output // directory. If it exists, prepend this to the job name so that // we can export to PDF. - if (dirname) - { - if (g_file_test (dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) - { - gchar *tmp = g_build_filename (dirname, job_name, NULL); - g_free (job_name); - job_name = tmp; - } - g_free (dirname); - } + if (dirname && g_file_test(dirname, + (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) + { + gchar *tmp = g_build_filename(dirname, job_name, nullptr); + g_free(job_name); + job_name = tmp; + } } } @@ -2000,7 +1995,8 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep const char* dirname = gtk_print_settings_get(print_settings, GNC_GTK_PRINT_SETTINGS_EXPORT_DIR); // Only store the directory if it exists. - if (g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) + if (g_file_test(dirname, + (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { QofInstance *inst = qofOwnerGetOwner(owner); gncOwnerBeginEdit(owner); diff --git a/gnucash/gnome/gnc-plugin-page-report.h b/gnucash/gnome/gnc-plugin-page-report.h index 55da7a6529..46ba3ca507 100644 --- a/gnucash/gnome/gnc-plugin-page-report.h +++ b/gnucash/gnome/gnc-plugin-page-report.h @@ -34,6 +34,11 @@ #ifndef __GNC_PLUGIN_PAGE_REPORT_H #define __GNC_PLUGIN_PAGE_REPORT_H +#ifdef __cplusplus +extern "C" +{ +#endif + #include #include "gnc-plugin.h" @@ -85,7 +90,9 @@ void gnc_main_window_open_report (int report_id, GncMainWindow *window); void gnc_main_window_open_report_url (const char * url, GncMainWindow *window); G_END_DECLS - +#ifdef __cplusplus +} +#endif #endif /* __GNC_PLUGIN_PAGE_REPORT_H */ /** @} */ /** @} */ diff --git a/gnucash/gnome/top-level.c b/gnucash/gnome/top-level.c index e907354aaf..19c8b32f72 100644 --- a/gnucash/gnome/top-level.c +++ b/gnucash/gnome/top-level.c @@ -37,7 +37,6 @@ #include "dialog-commodity.h" #include "dialog-invoice.h" #include "dialog-preferences.h" -#include "dialog-options.h" #include "dialog-sx-editor.h" #include "dialog-transfer.h" #include "dialog-totd.h" diff --git a/gnucash/gnome/window-report.c b/gnucash/gnome/window-report.cpp similarity index 65% rename from gnucash/gnome/window-report.c rename to gnucash/gnome/window-report.cpp index 31df07d5b2..5580bad58b 100644 --- a/gnucash/gnome/window-report.c +++ b/gnucash/gnome/window-report.cpp @@ -24,25 +24,28 @@ * Boston, MA 02110-1301, USA gnu@gnu.org * * * ********************************************************************/ +#include +#include +#include +extern "C" +{ #include -#include #include -#include #include #include "swig-runtime.h" -#include "dialog-options.h" -#include "dialog-report-column-view.h" #include "gnc-guile-utils.h" -#include "gnc-report.h" #include "gnc-ui.h" -#include "option-util.h" #include "window-report.h" #include "guile-mappings.h" #include "gnc-plugin-page-report.h" +} +#include "gnc-report.h" +#include "dialog-options.hpp" +#include "dialog-report-column-view.hpp" /******************************************************************** * @@ -51,9 +54,9 @@ void reportWindow(int report_id, GtkWindow *parent) { - gnc_set_busy_cursor (NULL, TRUE); + gnc_set_busy_cursor (nullptr, TRUE); gnc_main_window_open_report(report_id, GNC_MAIN_WINDOW(parent)); - gnc_unset_busy_cursor (NULL); + gnc_unset_busy_cursor (nullptr); } /******************************************************************** @@ -62,31 +65,29 @@ reportWindow(int report_id, GtkWindow *parent) struct report_default_params_data { - GNCOptionWin * win; - GNCOptionDB * db; - SCM scm_options; + GncOptionsDialog * win; + GncOptionDB * odb; SCM cur_report; }; static void -gnc_options_dialog_apply_cb(GNCOptionWin * propertybox, +gnc_options_dialog_apply_cb(GncOptionsDialog *opt_dialog, gpointer user_data) { SCM dirty_report = scm_c_eval_string("gnc:report-set-dirty?!"); - struct report_default_params_data * win = user_data; - GList *results = NULL, *iter; + auto win{static_cast(user_data)}; if (!win) return; - results = gnc_option_db_commit (win->db); - for (iter = results; iter; iter = iter->next) + auto results = gnc_option_db_commit (win->odb); + for (auto iter = results; iter; iter = iter->next) { - GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW (win->win), - 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - "%s", - (char*)iter->data); + auto dialog = gtk_message_dialog_new(GTK_WINDOW (win->win), + static_cast(0), + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", + (char*)iter->data); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); g_free (iter->data); @@ -97,35 +98,33 @@ gnc_options_dialog_apply_cb(GNCOptionWin * propertybox, } static void -gnc_options_dialog_help_cb(GNCOptionWin * propertybox, +gnc_options_dialog_help_cb(GncOptionsDialog *opt_dialog, gpointer user_data) { - GtkWidget *dialog, *parent; - struct report_default_params_data * prm = user_data; + auto prm{static_cast(user_data)}; - parent = gnc_options_dialog_widget(prm->win); - dialog = gtk_message_dialog_new(GTK_WINDOW(parent), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_INFO, - GTK_BUTTONS_OK, - "%s", - _("Set the report options you want using this dialog.")); + auto parent = prm->win->get_widget(); + auto dialog = gtk_message_dialog_new(GTK_WINDOW(parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "%s", + _("Set the report options you want using this dialog.")); g_signal_connect(G_OBJECT(dialog), "response", - (GCallback)gtk_widget_destroy, NULL); + (GCallback)gtk_widget_destroy, nullptr); gtk_widget_show(dialog); } static void -gnc_options_dialog_close_cb(GNCOptionWin * propertybox, +gnc_options_dialog_close_cb(GncOptionsDialog *opt_dialog, gpointer user_data) { - struct report_default_params_data * win = user_data; + auto win{static_cast(user_data)}; SCM set_editor = scm_c_eval_string("gnc:report-set-editor-widget!"); scm_call_2(set_editor, win->cur_report, SCM_BOOL_F); - gnc_options_dialog_destroy(win->win); - gnc_option_db_destroy(win->db); - scm_gc_unprotect_object(win->scm_options); + delete win->win; + gnc_option_db_destroy(win->odb); g_free(win); } @@ -137,8 +136,7 @@ gnc_report_raise_editor(SCM report) if (editor != SCM_BOOL_F) { #define FUNC_NAME "gnc-report-raise-editor" - GtkWidget *w = SWIG_MustGetPtr(editor, - SWIG_TypeQuery("_p_GtkWidget"), 1, 0); + auto w{static_cast(SWIG_MustGetPtr(editor, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))}; #undef FUNC_NAME gtk_window_present(GTK_WINDOW(w)); return TRUE; @@ -149,7 +147,7 @@ gnc_report_raise_editor(SCM report) GtkWidget * -gnc_report_window_default_params_editor(SCM options, SCM report, +gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report, GtkWindow *parent) { SCM get_report_type = scm_c_eval_string("gnc:report-type"); @@ -157,18 +155,17 @@ gnc_report_window_default_params_editor(SCM options, SCM report, SCM get_template_name = scm_c_eval_string("gnc:report-template-name"); SCM ptr; - const gchar *title = NULL; + const gchar *title = nullptr; if (gnc_report_raise_editor (report)) - return NULL; + return nullptr; else { struct report_default_params_data * prm = g_new0(struct report_default_params_data, 1); - prm->scm_options = options; + prm->odb = odb; prm->cur_report = report; - prm->db = gnc_option_db_new(prm->scm_options); /* Get the title of the report's template. */ ptr = scm_call_1(get_report_type, report); @@ -184,26 +181,18 @@ gnc_report_window_default_params_editor(SCM options, SCM report, } /* Don't forget to translate the window title */ - prm->win = gnc_options_dialog_new((gchar*) (title && *title ? _(title) : ""), parent); + prm->win = new GncOptionsDialog((gchar*) (title && *title ? _(title) : ""), parent); g_free ((gpointer *) title); - scm_gc_protect_object(prm->scm_options); scm_gc_protect_object(prm->cur_report); - gnc_options_dialog_build_contents(prm->win, prm->db); - gnc_option_db_clean(prm->db); + prm->win->build_contents(prm->odb); - gnc_options_dialog_set_apply_cb(prm->win, - gnc_options_dialog_apply_cb, - (gpointer)prm); - gnc_options_dialog_set_help_cb(prm->win, - gnc_options_dialog_help_cb, - (gpointer)prm); - gnc_options_dialog_set_close_cb(prm->win, - gnc_options_dialog_close_cb, - (gpointer)prm); - return gnc_options_dialog_widget(prm->win); + prm->win->set_apply_cb(gnc_options_dialog_apply_cb, (gpointer)prm); + prm->win->set_help_cb(gnc_options_dialog_help_cb, (gpointer)prm); + prm->win->set_close_cb(gnc_options_dialog_close_cb, (gpointer)prm); + return prm->win->get_widget(); } } @@ -211,19 +200,18 @@ gboolean gnc_report_edit_options(SCM report, GtkWindow *parent) { SCM set_editor = scm_c_eval_string("gnc:report-set-editor-widget!"); - SCM get_options = scm_c_eval_string("gnc:report-options"); SCM get_report_type = scm_c_eval_string("gnc:report-type"); SCM ptr; - SCM options; - GtkWidget *options_widget = NULL; + GncOptionDB* odb; + GtkWidget *options_widget = nullptr; /* If the options editor widget already exists we simply raise it */ if (gnc_report_raise_editor (report)) return TRUE; /* Check if this report has options to edit */ - options = scm_call_1(get_options, report); - if (options == SCM_BOOL_F) + odb = gnc_get_report_optiondb(report); + if (!odb) { gnc_warning_dialog (parent, "%s", _("There are no options for this report.")); @@ -236,9 +224,9 @@ gnc_report_edit_options(SCM report, GtkWindow *parent) { gchar *rpt_type = gnc_scm_to_utf8_string (ptr); if (g_strcmp0 (rpt_type, "d8ba4a2e89e8479ca9f6eccdeb164588") == 0) - options_widget = gnc_column_view_edit_options (options, report); + options_widget = gnc_column_view_edit_options (odb, report); else - options_widget = gnc_report_window_default_params_editor (options, report, parent); + options_widget = gnc_report_window_default_params_editor (odb, report, parent); g_free (rpt_type); } @@ -250,3 +238,11 @@ gnc_report_edit_options(SCM report, GtkWindow *parent) return TRUE; } + +GncOptionDB* +gnc_get_report_optiondb(SCM report_instance) +{ + SCM get_report_options = scm_c_eval_string("gnc:report-options"); + auto scm_dispatch{scm_call_1(get_report_options, report_instance)}; + return gnc_get_optiondb_from_dispatcher(scm_dispatch); +} diff --git a/gnucash/gnome/window-report.h b/gnucash/gnome/window-report.h index e51bbc0a1d..e41cea70f8 100644 --- a/gnucash/gnome/window-report.h +++ b/gnucash/gnome/window-report.h @@ -24,16 +24,20 @@ #define GNC_REPORT_WINDOW_H #include - +#ifdef __cplusplus +extern "C" +{ +#endif //#include "gnc-html.h" #include "qof.h" +#include typedef struct gnc_report_window_s gnc_report_window; /** PROTOTYPES ******************************************************/ // scm-exposed -GtkWidget * gnc_report_window_default_params_editor(SCM options, SCM report, GtkWindow *parent); +GtkWidget * gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report, GtkWindow *parent); // called from multiple places // [gnome-business/dialog-invoice.c;gnome/window-register.c]; and @@ -41,4 +45,8 @@ GtkWidget * gnc_report_window_default_params_editor(SCM options, SCM report, Gtk void reportWindow(int id, GtkWindow *parent); gboolean gnc_report_edit_options(SCM report, GtkWindow *parent); +#ifdef __cplusplus +} +GncOptionDB* gnc_get_report_optiondb(SCM report_instance); +#endif #endif diff --git a/gnucash/gnucash-commands.cpp b/gnucash/gnucash-commands.cpp index 3a5edb8f36..6460987847 100644 --- a/gnucash/gnucash-commands.cpp +++ b/gnucash/gnucash-commands.cpp @@ -37,7 +37,6 @@ extern "C" { #include #include #include -#include #include #include } @@ -45,6 +44,7 @@ extern "C" { #include #include #include +#include namespace bl = boost::locale; diff --git a/gnucash/gnucash-core-app.cpp b/gnucash/gnucash-core-app.cpp index 3bbcc5f751..5bde70c1a8 100644 --- a/gnucash/gnucash-core-app.cpp +++ b/gnucash/gnucash-core-app.cpp @@ -42,7 +42,6 @@ extern "C" { #include #include #include -#include #include #include #include "gnucash-locale-platform.h" @@ -53,6 +52,7 @@ extern "C" { #include #include #include +#include namespace bl = boost::locale; diff --git a/gnucash/gnucash.cpp b/gnucash/gnucash.cpp index 1be769bfbb..70ed0c269c 100644 --- a/gnucash/gnucash.cpp +++ b/gnucash/gnucash.cpp @@ -33,12 +33,15 @@ #include "gnucash-core-app.hpp" extern "C" { +#include #include #include +#include // For define GNC_MOD_GUI #include #include #include #include +#include #include #include #include @@ -51,7 +54,6 @@ extern "C" { #include #include #include -#include #include #include #include @@ -65,6 +67,7 @@ extern "C" { #include #endif #include +#include #include namespace bl = boost::locale; diff --git a/gnucash/html/gnc-html-extras.h b/gnucash/html/gnc-html-extras.h index 0bcb4c60e7..8bcda07004 100644 --- a/gnucash/html/gnc-html-extras.h +++ b/gnucash/html/gnc-html-extras.h @@ -27,7 +27,7 @@ // gnc-html.i file. The full gnc-html.h file can't be parsed because of the new // use of GObject -typedef gchar* URLType; +typedef const char* URLType; #define URL_TYPE_FILE "file" #define URL_TYPE_JUMP "jump" diff --git a/gnucash/html/gnc-html-history.h b/gnucash/html/gnc-html-history.h index 95429e3f25..e4fdfd31df 100644 --- a/gnucash/html/gnc-html-history.h +++ b/gnucash/html/gnc-html-history.h @@ -30,7 +30,7 @@ typedef struct _gnc_html_history gnc_html_history; struct _gnc_html_history_node { - URLType type; + char* type; gchar * location; gchar * label; }; diff --git a/gnucash/html/gnc-html-webkit2.c b/gnucash/html/gnc-html-webkit2.c index a892a4cb90..3ab0f0ad5b 100644 --- a/gnucash/html/gnc-html-webkit2.c +++ b/gnucash/html/gnc-html-webkit2.c @@ -587,8 +587,8 @@ perform_navigation_policy (WebKitWebView *web_view, GncHtml *self) { WebKitURIRequest *req = NULL; - const gchar* uri; // Can't init it here. - gchar *scheme = NULL, *location = NULL, *label = NULL; + const gchar* uri, *scheme; // Can't init it here. + gchar *location = NULL, *label = NULL; gboolean ignore = FALSE; WebKitNavigationAction *action = webkit_navigation_policy_decision_get_navigation_action (decision); diff --git a/gnucash/html/gnc-html.c b/gnucash/html/gnc-html.c index 4565bdaad2..edeb5df43b 100644 --- a/gnucash/html/gnc-html.c +++ b/gnucash/html/gnc-html.c @@ -359,7 +359,7 @@ gnc_html_show_url( GncHtml* self, URLType type, const gchar* location, const gchar* label, gboolean new_window_hint ) { - URLType lc_type = NULL; + char* lc_type = NULL; g_return_if_fail( self != NULL ); g_return_if_fail( GNC_IS_HTML(self) ); @@ -629,7 +629,7 @@ gnc_html_set_parent( GncHtml* self, GtkWindow* parent ) gboolean gnc_html_register_urltype( URLType type, const char *protocol ) { - URLType lc_type = NULL; + char* lc_type = NULL; char *lc_proto = NULL; if (!gnc_html_type_to_proto_hash) @@ -697,7 +697,7 @@ gnc_html_initialize( void ) gchar* gnc_build_url( URLType type, const gchar* location, const gchar* label ) { - URLType lc_type = NULL; + char* lc_type = NULL; char * type_name; DEBUG(" "); @@ -931,7 +931,7 @@ gnc_html_register_stream_handler( URLType url_type, GncHTMLStreamCB hand ) gnc_html_unregister_stream_handler( url_type ); if ( hand != NULL ) { - URLType lc_type = g_ascii_strdown (url_type, -1); + char* lc_type = g_ascii_strdown (url_type, -1); g_hash_table_insert( gnc_html_stream_handlers, lc_type, hand ); } } @@ -939,7 +939,7 @@ gnc_html_register_stream_handler( URLType url_type, GncHTMLStreamCB hand ) void gnc_html_unregister_stream_handler( URLType url_type ) { - URLType lc_type = g_ascii_strdown (url_type, -1); + char* lc_type = g_ascii_strdown (url_type, -1); g_hash_table_remove( gnc_html_stream_handlers, lc_type ); g_free(lc_type); } @@ -957,7 +957,7 @@ gnc_html_register_url_handler( URLType url_type, GncHTMLUrlCB hand ) gnc_html_unregister_url_handler( url_type ); if ( hand != NULL ) { - URLType lc_type = g_ascii_strdown (url_type, -1); + char* lc_type = g_ascii_strdown (url_type, -1); g_hash_table_insert( gnc_html_url_handlers, lc_type, hand ); } } @@ -965,7 +965,7 @@ gnc_html_register_url_handler( URLType url_type, GncHTMLUrlCB hand ) void gnc_html_unregister_url_handler( URLType url_type ) { - URLType lc_type = g_ascii_strdown (url_type, -1); + char* lc_type = g_ascii_strdown (url_type, -1); g_hash_table_remove( gnc_html_url_handlers, lc_type ); g_free(lc_type); } diff --git a/gnucash/html/gnc-html.h b/gnucash/html/gnc-html.h index 9e3733d6ac..26bee8044b 100644 --- a/gnucash/html/gnc-html.h +++ b/gnucash/html/gnc-html.h @@ -294,4 +294,5 @@ void gnc_html_unregister_url_handler( URLType url_type ); const gchar* gnc_html_get_embedded_param( gpointer eb, const gchar* param_name ); +G_END_DECLS #endif diff --git a/gnucash/html/gnc-html.i b/gnucash/html/gnc-html.i index 573cf81cfb..3f9b3a84f5 100644 --- a/gnucash/html/gnc-html.i +++ b/gnucash/html/gnc-html.i @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -34,6 +33,9 @@ #include %} #if defined(SWIGGUILE) + +%typemap (freearg) const char* "if (must_free$argnum && $1) SWIG_free((char*)$1);"; + %{ #include "guile-mappings.h" diff --git a/gnucash/report/CMakeLists.txt b/gnucash/report/CMakeLists.txt index 454d88a2de..afb1a40cc1 100644 --- a/gnucash/report/CMakeLists.txt +++ b/gnucash/report/CMakeLists.txt @@ -14,7 +14,7 @@ gnc_add_swig_guile_command (swig-report-c ) set (report_SOURCES - gnc-report.c + gnc-report.cpp ) add_library (gnc-report diff --git a/gnucash/report/gnc-report.c b/gnucash/report/gnc-report.cpp similarity index 91% rename from gnucash/report/gnc-report.c rename to gnucash/report/gnc-report.cpp index 1fb932e09e..544f01139d 100644 --- a/gnucash/report/gnc-report.c +++ b/gnucash/report/gnc-report.cpp @@ -30,19 +30,22 @@ #include #include #include +extern "C" +{ #include #include -#include "gfec.h" #include #include #include -#include "gnc-filepath-utils.h" -#include "gnc-guile-utils.h" +#include +#include +#include +#include +} #include "gnc-report.h" -#include "gnc-engine.h" -extern SCM scm_init_sw_report_module(void); +extern "C" SCM scm_init_sw_report_module(void); static QofLogModule log_module = GNC_MOD_GUI; @@ -130,11 +133,11 @@ gnc_report_remove_by_id(gint id) SCM gnc_report_find(gint id) { - gpointer report = NULL; + SCM report = nullptr; if (reports) { - report = g_hash_table_lookup(reports, &id); + report = static_cast(g_hash_table_lookup(reports, &id)); } if (!report) @@ -303,9 +306,6 @@ gnc_saved_reports_write_internal (const gchar *file, const gchar *contents, gboo { gboolean success = TRUE; gint fd; -#ifndef __MINGW32__ - extern int errno; -#endif ssize_t written; gint length; gint flags = O_WRONLY | O_CREAT | (overwrite ? O_TRUNC : O_APPEND); @@ -393,3 +393,27 @@ gnc_saved_reports_write_to_file (const gchar* report_def, gboolean overwrite) return success; } + +GncOptionDB* +gnc_get_optiondb_from_dispatcher(SCM dispatcher) +{ + SCM get_options = scm_c_eval_string("gnc:options-get"); + if (dispatcher == SCM_BOOL_F) + return nullptr; + auto scm_ptr{scm_call_1(get_options, dispatcher)}; + auto smob{!scm_is_null(scm_ptr) && SCM_INSTANCEP(scm_ptr) && + scm_is_true(scm_slot_exists_p(scm_ptr, SCM_EOL)) ? + scm_slot_ref(scm_ptr, SCM_EOL) : (scm_ptr)}; + + void *c_ptr{nullptr}; + if (!SCM_NULLP(smob)) + { + if (SCM_POINTER_P(smob)) + c_ptr = SCM_POINTER_VALUE(smob); + else + c_ptr = reinterpret_cast(SCM_CELL_WORD_1(smob)); + } + auto u_ptr{static_cast*>(c_ptr)}; + return u_ptr->get(); +} + diff --git a/gnucash/report/gnc-report.h b/gnucash/report/gnc-report.h index b0e83dca21..5e2a40ff76 100644 --- a/gnucash/report/gnc-report.h +++ b/gnucash/report/gnc-report.h @@ -27,6 +27,11 @@ #include #include +#ifdef __cplusplus +#include +extern "C" +{ +#endif #define SAVED_REPORTS_FILE "saved-reports-2.8" #define SAVED_REPORTS_FILE_OLD_REV "saved-reports-2.4" @@ -35,34 +40,54 @@ * * Should be called once before using any of its features. */ -void gnc_report_init (void); +void gnc_report_init(void); -gboolean gnc_run_report_with_error_handling (gint report_id, - gchar **data, - gchar **errmsg); -gboolean gnc_run_report_id_string_with_error_handling (const char * id_string, - char **data, - gchar **errmsg); +gboolean gnc_run_report_with_error_handling(gint report_id, + gchar** data, + gchar** errmsg); + +gboolean gnc_run_report_id_string_with_error_handling(const char* id_string, + char** data, + gchar** errmsg); /** * @param report The SCM version of the report. * @return a caller-owned copy of the name of the report, or NULL if report * is invalid. **/ -gchar* gnc_report_name( SCM report ); +gchar* gnc_report_name(SCM report); /* returns #f if the report id cannot be found */ SCM gnc_report_find(gint id); + void gnc_report_remove_by_id(gint id); + gint gnc_report_add(SCM report); void gnc_reports_flush_global(void); -GHashTable *gnc_reports_get_global(void); + +GHashTable* gnc_reports_get_global(void); gchar* gnc_get_default_report_font_family(void); -gboolean gnc_saved_reports_backup (void); -gboolean gnc_saved_reports_write_to_file (const gchar* report_def, gboolean overwrite); +gboolean gnc_saved_reports_backup(void); +gboolean gnc_saved_reports_write_to_file(const gchar* report_def, gboolean overwrite); + +#ifdef __cplusplus +} //extern "C" +/** + * Obtain a GncOptionDB* from Scheme + * + * When report or stylesheet options are generated in Scheme the GncObjectDB is + * wrapped in a std::unique_ptr and then in a Guile SMOB by SWIG. The GUI code + * needs a reference to the GncObjectDB and we don't want to introduce swig + * library dependencies. + * + * @param dispatch The scheme dispatch function returned by gnc:new-options + * @return GncOptiondDB* Do not free this pointer! + */ +GncOptionDB* gnc_get_optiondb_from_dispatcher(SCM dispatcher); +#endif #endif diff --git a/gnucash/report/html-utilities.scm b/gnucash/report/html-utilities.scm index ec256ff2e7..8b2c62be6c 100644 --- a/gnucash/report/html-utilities.scm +++ b/gnucash/report/html-utilities.scm @@ -80,10 +80,12 @@ (list))) (define (gnc:register-guid type guid) - (gnc-build-url URL-TYPE-REGISTER (string-append type guid) "")) + (and guid (gnc-build-url URL-TYPE-REGISTER (string-append type guid) ""))) (define (gnc:account-anchor-text acct) - (gnc:register-guid "acct-guid=" (gncAccountGetGUID acct))) + (if acct + (gnc:register-guid "acct-guid=" (gncAccountGetGUID acct)) + (format #t "No Account!"))) (define (gnc:split-anchor-text split) (gnc:register-guid "split-guid=" (gncSplitGetGUID split))) diff --git a/gnucash/report/report-core.scm b/gnucash/report/report-core.scm index 52505a0a52..036ffe0d81 100644 --- a/gnucash/report/report-core.scm +++ b/gnucash/report/report-core.scm @@ -375,14 +375,7 @@ not found."))) (let ((options (if (null? rest) (gnc:report-template-new-options template) (car rest)))) - (gnc:report-set-options! r options) - (gnc:options-register-callback - #f #f - (lambda () - (gnc:report-set-dirty?! r #t) - (let ((cb (gnc:report-template-options-changed-cb template))) - (if cb (cb r)))) - options)) + (gnc:report-set-options! r options)) (gnc:report-set-id! r (gnc-report-add r)) (gnc:report-id r))) diff --git a/gnucash/report/reports/standard/account-summary.scm b/gnucash/report/reports/standard/account-summary.scm index 96b8c273f7..be0d77088b 100644 --- a/gnucash/report/reports/standard/account-summary.scm +++ b/gnucash/report/reports/standard/account-summary.scm @@ -143,23 +143,19 @@ (define (accsum-options-generator sx? reportname) (let* ((options (gnc:new-options)) - (add-option - (lambda (new-option) - (gnc:register-option options new-option)))) + (odb (gnc:options-get options))) - (add-option - (gnc:make-string-option + (gnc-register-string-option odb gnc:pagename-general optname-report-title - "a" opthelp-report-title (G_ reportname))) - (add-option - (gnc:make-string-option + "a" opthelp-report-title (G_ reportname)) + (gnc-register-string-option odb gnc:pagename-general optname-party-name - "b" opthelp-party-name "")) + "b" opthelp-party-name "") ;; this should default to company name in (gnc-get-current-book) ;; does anyone know the function to get the company name?? ;; date at which to report balance - (if sx? + (if sx? (gnc:options-add-date-interval! options gnc:pagename-general optname-from-date optname-to-date "c") @@ -167,98 +163,83 @@ options gnc:pagename-general optname-date "c")) ;; accounts to work on - (add-option - (gnc:make-account-list-option + (gnc-register-account-list-option odb gnc:pagename-accounts optname-accounts "a" opthelp-accounts - (lambda () - (gnc:filter-accountlist-type - (list ACCT-TYPE-BANK ACCT-TYPE-CASH ACCT-TYPE-CREDIT - ACCT-TYPE-ASSET ACCT-TYPE-LIABILITY - ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY - ACCT-TYPE-PAYABLE ACCT-TYPE-RECEIVABLE - ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE) - (gnc-account-get-descendants-sorted (gnc-get-current-root-account)))) - #f #t)) + (gnc:filter-accountlist-type + (list ACCT-TYPE-BANK ACCT-TYPE-CASH ACCT-TYPE-CREDIT + ACCT-TYPE-ASSET ACCT-TYPE-LIABILITY + ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY + ACCT-TYPE-PAYABLE ACCT-TYPE-RECEIVABLE + ACCT-TYPE-EQUITY ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE) + (gnc-account-get-descendants-sorted (gnc-get-current-root-account)))) - (gnc:options-add-account-levels! + (gnc:options-add-account-levels! options gnc:pagename-accounts optname-depth-limit "b" opthelp-depth-limit 3) - (add-option - (gnc:make-multichoice-option + (gnc-register-multichoice-option odb gnc:pagename-accounts optname-bottom-behavior - "c" opthelp-bottom-behavior 'summarize + "c" opthelp-bottom-behavior "summarize" (list - (vector 'summarize (N_ "Recursive Balance")) - (vector 'flatten (N_ "Raise Accounts")) - (vector 'truncate (N_ "Omit Accounts"))))) + (vector "summarize" (N_ "Recursive Balance")) + (vector "flatten" (N_ "Raise Accounts")) + (vector "truncate" (N_ "Omit Accounts")))) ;; all about currencies - (gnc:options-add-currency! + (gnc:options-add-currency! options pagename-commodities optname-report-commodity "a") - (gnc:options-add-price-source! + (gnc:options-add-price-source! options pagename-commodities optname-price-source "b" 'pricedb-nearest) - (add-option - (gnc:make-simple-boolean-option + (gnc-register-simple-boolean-option odb pagename-commodities optname-show-foreign - "c" opthelp-show-foreign #t)) + "c" opthelp-show-foreign #t) - (add-option - (gnc:make-simple-boolean-option + (gnc-register-simple-boolean-option odb pagename-commodities optname-show-rates - "d" opthelp-show-rates #f)) + "d" opthelp-show-rates #f) ;; what to show for zero-balance accounts - (add-option - (gnc:make-simple-boolean-option + (gnc-register-simple-boolean-option odb gnc:pagename-display optname-show-zb-accts - "a" opthelp-show-zb-accts #t)) - (add-option - (gnc:make-simple-boolean-option + "a" opthelp-show-zb-accts #t) + (gnc-register-simple-boolean-option odb gnc:pagename-display optname-omit-zb-bals - "b" opthelp-omit-zb-bals #f)) + "b" opthelp-omit-zb-bals #f) ;; what to show for non-leaf accounts - (gnc:options-add-subtotal-view! + (gnc:options-add-subtotal-view! options gnc:pagename-display optname-parent-balance-mode optname-parent-total-mode "c") ;; some detailed formatting options - (add-option - (gnc:make-simple-boolean-option + (gnc-register-simple-boolean-option odb gnc:pagename-display optname-account-links - "e" opthelp-account-links #t)) - (add-option - (gnc:make-simple-boolean-option + "e" opthelp-account-links #t) + (gnc-register-simple-boolean-option odb gnc:pagename-display optname-use-rules - "f" opthelp-use-rules #f)) + "f" opthelp-use-rules #f) - (add-option - (gnc:make-simple-boolean-option + (gnc-register-simple-boolean-option odb gnc:pagename-display optname-show-bals - "g" opthelp-show-bals #t)) - (add-option - (gnc:make-simple-boolean-option + "g" opthelp-show-bals #t) + (gnc-register-simple-boolean-option odb gnc:pagename-display optname-show-code - "h" opthelp-show-code #t)) - (add-option - (gnc:make-simple-boolean-option + "h" opthelp-show-code #t) + (gnc-register-simple-boolean-option odb gnc:pagename-display optname-show-desc - "i" opthelp-show-desc #f)) - (add-option - (gnc:make-simple-boolean-option + "i" opthelp-show-desc #f) + (gnc-register-simple-boolean-option odb gnc:pagename-display optname-show-type - "j" opthelp-show-type #f)) - (add-option - (gnc:make-simple-boolean-option + "j" opthelp-show-type #f) + (gnc-register-simple-boolean-option odb gnc:pagename-display optname-show-notes - "k" opthelp-show-notes #f)) + "k" opthelp-show-notes #f) ;; Set the general page as default option tab (gnc:options-set-default-section options gnc:pagename-display) diff --git a/gnucash/report/reports/standard/test/test-equity-statement.scm b/gnucash/report/reports/standard/test/test-equity-statement.scm index 637b63deea..7e4646d83f 100644 --- a/gnucash/report/reports/standard/test/test-equity-statement.scm +++ b/gnucash/report/reports/standard/test/test-equity-statement.scm @@ -51,8 +51,8 @@ (gnc:options->sxml uuid options "test-equity-statement" test-title)) (define (test-equity-statement) - (let* ((options (gnc:make-report-options uuid)) - (account-alist (create-test-data)) + (let* ((account-alist (create-test-data)) + (options (gnc:make-report-options uuid)) (gbp-bank (assoc-ref account-alist "GBP Bank")) (usd-bank (assoc-ref account-alist "Bank")) (expense (assoc-ref account-alist "Expenses")) diff --git a/gnucash/report/reports/standard/test/test-portfolios.scm b/gnucash/report/reports/standard/test/test-portfolios.scm index 298d072e0b..e89329a5fc 100644 --- a/gnucash/report/reports/standard/test/test-portfolios.scm +++ b/gnucash/report/reports/standard/test/test-portfolios.scm @@ -97,8 +97,8 @@ (define (advanced-tests) (test-group-with-cleanup "advanced-portfolio-tests" - (let ((account-alist (create-stock-test-data)) - (options (gnc:make-report-options advanced-uuid))) + (let* ((account-alist (create-stock-test-data)) + (options (gnc:make-report-options advanced-uuid))) (let ((sxml (options->sxml advanced-uuid options "basic average"))) (test-equal "advanced: average basis" '("AAPL" "AAPL" "NASDAQ" "42.00" "$6.0000" "$484.88" "$252.00" "$800.00" diff --git a/gnucash/report/reports/standard/test/test-register.scm b/gnucash/report/reports/standard/test/test-register.scm index 9805e5f6ce..59809cb266 100644 --- a/gnucash/report/reports/standard/test/test-register.scm +++ b/gnucash/report/reports/standard/test/test-register.scm @@ -51,8 +51,8 @@ (gnc:options->sxml uuid options "test-register" test-title)) (define (test-register) - (let* ((options (gnc:make-report-options uuid)) - (account-alist (create-test-data)) + (let* ((account-alist (create-test-data)) + (options (gnc:make-report-options uuid)) (bank (cdr (assoc "Bank" account-alist)))) (gnc-commodity-set-user-symbol diff --git a/gnucash/report/reports/standard/test/test-stress-options.scm b/gnucash/report/reports/standard/test/test-stress-options.scm index 9a19517857..e0f7d8c748 100644 --- a/gnucash/report/reports/standard/test/test-stress-options.scm +++ b/gnucash/report/reports/standard/test/test-stress-options.scm @@ -11,6 +11,7 @@ (use-modules (gnucash reports)) (use-modules (tests test-report-extras)) (use-modules (srfi srfi-9)) +(use-modules (srfi srfi-26)) (use-modules (srfi srfi-64)) (use-modules (srfi srfi-98)) (use-modules (tests srfi64-extras)) @@ -68,7 +69,8 @@ (gnc:options-for-each (lambda (option) (when (case (gnc:option-type option) - ((multichoice) (pair? (cdr (gnc:option-data option)))) + ((multichoice) + (> (GncOption-num-permissible-values option) 1)) ((boolean) #t) (else #f)) (set! report-options-tested @@ -76,8 +78,9 @@ (gnc:option-section option) (gnc:option-name option) (case (gnc:option-type option) - ((multichoice) (map (lambda (d) (vector-ref d 0)) - (gnc:option-data option))) + ((multichoice) + (map (cut GncOption-permissible-value option <>) + (iota (GncOption-num-permissible-values option)))) ((boolean) (list #t #f)))) report-options-tested)))) options) diff --git a/gnucash/report/reports/standard/test/test-trial-balance.scm b/gnucash/report/reports/standard/test/test-trial-balance.scm index a6c0a21517..dd1ed98f5b 100644 --- a/gnucash/report/reports/standard/test/test-trial-balance.scm +++ b/gnucash/report/reports/standard/test/test-trial-balance.scm @@ -51,8 +51,8 @@ (gnc:options->sxml uuid options "test-trial-balance" test-title)) (define (test-trial-balance) - (let* ((options (gnc:make-report-options uuid)) - (account-alist (create-test-data)) + (let* ((account-alist (create-test-data)) + (options (gnc:make-report-options uuid)) (gbp-bank (assoc-ref account-alist "GBP Bank")) (usd-bank (assoc-ref account-alist "Bank")) (expense (assoc-ref account-alist "Expenses")) diff --git a/gnucash/report/reports/standard/view-column.scm b/gnucash/report/reports/standard/view-column.scm index b84d536b9d..352d59317a 100644 --- a/gnucash/report/reports/standard/view-column.scm +++ b/gnucash/report/reports/standard/view-column.scm @@ -42,8 +42,7 @@ (gnc:register-option options opt)))) ;; the report-list is edited by a special add-on page for the ;; options editor. - (opt-register - (gnc:make-internal-option "__general" "report-list" '())) + (gnc-register-report-placement-option (gnc:options-get options) "__general" "report-list") (opt-register (gnc:make-number-range-option @@ -53,18 +52,6 @@ options)) -(define (make-child-options-callback view child) - (let* ((view-opts (gnc:report-options view)) - (child-opts (gnc:report-options child)) - (id - (gnc:options-register-callback - #f #f - (lambda () - (gnc:report-set-dirty?! child #t) - (gnc:options-touch view-opts)) - child-opts))) - id)) - (define (render-view report) (let* ((view-doc (gnc:make-html-document)) (options (gnc:report-options report)) @@ -80,17 +67,6 @@ (current-width 0) (current-row-num 0)) - ;; make sure each subreport has an option change callback that - ;; pings the parent - (let loop ((reports reports) (new-reports '())) - (match reports - (() (gnc:option-set-value report-opt (reverse new-reports))) - (((child rowspan colspan callback) . rest) - (let ((callback (or callback - (make-child-options-callback - report (gnc-report-find child))))) - (loop rest (cons (list child rowspan colspan callback) new-reports)))))) - ;; we really would rather do something smart here with the ;; report's cached text if possible. For the moment, we'll have ;; to rerun every report, every time... FIXME @@ -106,7 +82,6 @@ (let* ((subreport (gnc-report-find (car report-info))) (colspan (cadr report-info)) (rowspan (caddr report-info)) - (opt-callback (cadddr report-info)) (toplevel-cell (gnc:make-html-table-cell/size rowspan colspan)) (report-table (gnc:make-html-table)) (contents-cell (gnc:make-html-table-cell))) diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt index 2b27989bce..e7b77fcb1c 100644 --- a/libgnucash/app-utils/CMakeLists.txt +++ b/libgnucash/app-utils/CMakeLists.txt @@ -11,11 +11,13 @@ set (app_utils_noinst_HEADERS calculation/finproto.h calculation/fin_spl_protos.h calculation/fin_static_proto.h + gnc-option-date.hpp + gnc-option-impl.hpp + gnc-optiondb-impl.hpp ) set (app_utils_HEADERS QuickFill.h - business-options.h file-utils.h gfec.h gnc-basic-gobject.h @@ -28,19 +30,25 @@ set (app_utils_HEADERS gnc-gsettings.h gnc-help-utils.h gnc-helpers.h + gnc-option.hpp + gnc-optiondb.h + gnc-optiondb.hpp gnc-prefs-utils.h - gnc-state.h + gnc-state.h gnc-sx-instance-model.h gnc-ui-util.h gnc-ui-balances.h - option-util.h ) # Command to generate the swig-app-utils-guile.c wrapper file -gnc_add_swig_guile_command (swig-apputils-guile-c - SWIG_APP_UTILS_GUILE_C swig-app-utils-guile.c - ${CMAKE_CURRENT_SOURCE_DIR}/app-utils.i "" +set(SWIG_ARGS "-c++" "-procdoc" "sw-gnc-option-doc" "-procdocformat" "plain") +gnc_add_swig_guile_command (swig-apputils-guile-cpp #target + SWIG_APP_UTILS_GUILE_CPP swig-app-utils-guile.cpp #outvar, output + ${CMAKE_CURRENT_SOURCE_DIR}/app-utils.i #input + "${CMAKE_SOURCE_DIR}/bindings;${CMAKE_SOURCE_DIR}/bindings/guile" #includes + ${CMAKE_CURRENT_SOURCE_DIR}/gnc-optiondb.i #additional dependencies ) +unset(SWIG_ARGS) # Command to generate the swig-app-utils-python.c wrapper file gnc_add_swig_python_command (swig-app-utils-python @@ -52,7 +60,6 @@ gnc_add_swig_python_command (swig-app-utils-python set (app_utils_SOURCES calculation/expression_parser.c calculation/fin.c - business-options.c QuickFill.c file-utils.c gfec.c @@ -64,12 +71,15 @@ set (app_utils_SOURCES gnc-exp-parser.c gnc-gsettings.cpp gnc-helpers.c + gnc-option-date.cpp + gnc-option.cpp + gnc-option-impl.cpp + gnc-optiondb.cpp gnc-prefs-utils.c gnc-sx-instance-model.c gnc-state.c gnc-ui-util.c gnc-ui-balances.c - option-util.c ) set_source_files_properties (${app_utils_SOURCES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H}) @@ -84,6 +94,7 @@ set(app_utils_ALL_LIBRARIES gnc-engine gnc-locale-tax gnucash-guile + ${GLIB_LDFLAGS} ${GIO_LDFLAGS} ${LIBXML2_LDFLAGS} ${LIBXSLT_LDFLAGS} @@ -105,7 +116,7 @@ if (WIN32) endif() -add_library (gnc-app-utils ${app_utils_ALL_SOURCES} ${SWIG_APP_UTILS_GUILE_C}) +add_library (gnc-app-utils ${app_utils_ALL_SOURCES} ${SWIG_APP_UTILS_GUILE_CPP}) target_link_libraries(gnc-app-utils ${app_utils_ALL_LIBRARIES}) target_include_directories (gnc-app-utils @@ -178,14 +189,6 @@ set (app_utils_SCHEME_1a options.scm ) -set (app_utils_SCHEME_1b - business-options.scm -) - -set (app_utils_SCHEME_1c - business-prefs.scm -) - set (app_utils_SCHEME_2 app-utils.scm ) @@ -207,22 +210,10 @@ gnc_add_scheme_targets(scm-app-utils-1a DEPENDS "scm-app-utils-1" MAKE_LINKS) -gnc_add_scheme_targets(scm-app-utils-1b - SOURCES "${app_utils_SCHEME_1b}" - OUTPUT_DIR "gnucash/app-utils" - DEPENDS "scm-app-utils-1a" - MAKE_LINKS) - -gnc_add_scheme_targets(scm-bus-prefs - SOURCES "${app_utils_SCHEME_1c}" - OUTPUT_DIR "gnucash/app-utils" - DEPENDS "scm-app-utils-1b" - MAKE_LINKS) - gnc_add_scheme_targets(scm-app-utils-2 SOURCES "${app_utils_SCHEME_2}" OUTPUT_DIR "gnucash" - DEPENDS "scm-bus-prefs" + DEPENDS "scm-app-utils-1a" MAKE_LINKS) add_custom_target(scm-app-utils ALL DEPENDS scm-app-utils-2 scm-app-utils-1) diff --git a/libgnucash/app-utils/app-utils.i b/libgnucash/app-utils/app-utils.i index 31d5066b20..6b77ffa4fd 100644 --- a/libgnucash/app-utils/app-utils.i +++ b/libgnucash/app-utils/app-utils.i @@ -21,8 +21,12 @@ %module sw_app_utils %{ /* Includes the header in the wrapper code */ +#include +#ifdef __cplusplus +extern "C" +{ +#endif #include -#include #include #include #include @@ -33,14 +37,22 @@ #include #include "gnc-engine-guile.h" +#ifdef __cplusplus +} +#endif %} -#if defined(SWIGGUILE) +#if defined(SWIGGUILE) //Always C++ %{ +extern "C" +{ #include "guile-mappings.h" SCM scm_init_sw_app_utils_module (void); +} %} + +%include "gnc-optiondb.i" #endif #if defined(SWIGPYTHON) @@ -53,9 +65,6 @@ PyObject* SWIG_init (void); %import "base-typemaps.i" -typedef void (*GNCOptionChangeCallback) (gpointer user_data); -typedef int GNCOptionDBHandle; - void gnc_prefs_init(); QofBook * gnc_get_current_book (void); @@ -64,11 +73,6 @@ const gchar * gnc_get_current_book_tax_name (void); const gchar * gnc_get_current_book_tax_type (void); Account * gnc_get_current_root_account (void); -GNCOptionDB * gnc_option_db_new(SCM guile_options); -void gnc_option_db_destroy(GNCOptionDB *odb); - -void gnc_option_db_set_option_selectable_by_name(SCM guile_option, - const char *section, const char *name, gboolean selectable); #if defined(SWIGGUILE) %typemap(out) GncCommodityList * { @@ -76,7 +80,7 @@ void gnc_option_db_set_option_selectable_by_name(SCM guile_option, GList *node; for (node = $1; node; node = node->next) - list = scm_cons(gnc_quoteinfo2scm(node->data), list); + list = scm_cons(gnc_quoteinfo2scm(static_cast(node->data)), list); $result = scm_reverse(list); } @@ -91,10 +95,6 @@ gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table * table); gnc_commodity * gnc_default_currency (void); gnc_commodity * gnc_default_report_currency (void); -void gncp_option_invoke_callback(GNCOptionChangeCallback callback, void *data); -void gnc_option_db_register_option(GNCOptionDBHandle handle, - SCM guile_option); - GNCPrintAmountInfo gnc_default_print_info (gboolean use_symbol); GNCPrintAmountInfo gnc_account_print_info (const Account *account, gboolean use_symbol); @@ -119,8 +119,6 @@ gnc_numeric gnc_convert_from_euro(const gnc_commodity * currency, time64 gnc_accounting_period_fiscal_start(void); time64 gnc_accounting_period_fiscal_end(void); -void gnc_register_kvp_option_generator(QofIdType id_type, SCM generator); - %typemap(out) GHashTable * { SCM table = scm_c_make_hash_table (g_hash_table_size($1) + 17); GHashTableIter iter; diff --git a/libgnucash/app-utils/app-utils.scm b/libgnucash/app-utils/app-utils.scm index b9f218a367..be5c5d9db7 100644 --- a/libgnucash/app-utils/app-utils.scm +++ b/libgnucash/app-utils/app-utils.scm @@ -28,11 +28,9 @@ (load-and-reexport (sw_app_utils) (gnucash app-utils date-utilities) - (gnucash app-utils business-options) - (gnucash app-utils business-prefs) (gnucash app-utils options) (gnucash app-utils c-interface)) - +;; gw-engine-spec.scm (re-export HOOK-SAVE-OPTIONS) (export gnc:get-debit-string) diff --git a/libgnucash/app-utils/business-options.c b/libgnucash/app-utils/business-options.c deleted file mode 100644 index 2eb18a4163..0000000000 --- a/libgnucash/app-utils/business-options.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * business-options.c -- Non-GUI Option Utilities for GNC Business Objects - * - * Written By: Derek Atkins - * Copyright (C) 2003 Derek Atkins - * - * This program 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 2 of - * the License, or (at your option) any later version. - * - * This program 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, contact: - * - * Free Software Foundation Voice: +1-617-542-5942 - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 - * Boston, MA 02110-1301, USA gnu@gnu.org - */ - -#include - -#include "business-options.h" -#include "swig-runtime.h" -#include "guile-mappings.h" - -#define FUNC_NAME G_STRFUNC - -#define LOOKUP_OPTION(fcn) \ - GNCOption *option; \ - SCM getter; \ - SCM value; \ - \ - option = gnc_option_db_get_option_by_name (odb, section, name); \ - \ - if (option == NULL) \ - return default_value; \ - \ - getter = gnc_option_getter (option); \ - if (getter == SCM_UNDEFINED) \ - return default_value; \ - \ - value = scm_call_0 (getter); \ - if (value == SCM_BOOL_F) \ - return NULL; \ - SWIG_GetModule(NULL); /* Work-around for SWIG bug. */ \ - if (!SWIG_IsPointer(value)) \ - scm_misc_error(fcn, "SCM is not a wrapped pointer.", value) - -GncTaxTable* -gnc_option_db_lookup_taxtable_option(GNCOptionDB *odb, - const char *section, - const char *name, - GncTaxTable * default_value) -{ - LOOKUP_OPTION("gnc_option_db_lookup_taxtable_option"); - return SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncTaxTable"), 1, 0); -} - -GncInvoice* -gnc_option_db_lookup_invoice_option(GNCOptionDB *odb, - const char *section, - const char *name, - GncInvoice * default_value) -{ - LOOKUP_OPTION("gnc_option_db_lookup_invoice_option"); - return SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncInvoice"), 1, 0); -} - -GncCustomer* -gnc_option_db_lookup_customer_option(GNCOptionDB *odb, - const char *section, - const char *name, - GncCustomer * default_value) -{ - LOOKUP_OPTION("gnc_option_db_lookup_customer_option"); - return SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncCustomer"), 1, 0); -} - -GncVendor* -gnc_option_db_lookup_vendor_option(GNCOptionDB *odb, - const char *section, - const char *name, - GncVendor * default_value) -{ - LOOKUP_OPTION("gnc_option_db_lookup_vendor_option"); - return SWIG_MustGetPtr(value, SWIG_TypeQuery("_p__gncVendor"), 1, 0); -} diff --git a/libgnucash/app-utils/business-options.h b/libgnucash/app-utils/business-options.h deleted file mode 100644 index 285e3e9a43..0000000000 --- a/libgnucash/app-utils/business-options.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * business-options.h -- non-GUI Option Utilities for GNC Business Objects - * - * Written By: Derek Atkins - * Copyright (C) 2003 Derek Atkins - * - * This program 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 2 of - * the License, or (at your option) any later version. - * - * This program 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, contact: - * - * Free Software Foundation Voice: +1-617-542-5942 - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 - * Boston, MA 02110-1301, USA gnu@gnu.org - */ - -#ifndef GNC_BUSINESS_OPTIONS_UTILS_H_ -#define GNC_BUSINESS_OPTIONS_UTILS_H_ - -#include "option-util.h" -#include "gncTaxTable.h" -#include "gncInvoice.h" -#include "gncCustomer.h" -#include "gncVendor.h" - - -GncTaxTable* gnc_option_db_lookup_taxtable_option(GNCOptionDB *odb, - const char *section, - const char *name, - GncTaxTable * default_value); - -GncInvoice* gnc_option_db_lookup_invoice_option(GNCOptionDB *odb, - const char *section, - const char *name, - GncInvoice * default_value); - -GncCustomer* gnc_option_db_lookup_customer_option(GNCOptionDB *odb, - const char *section, - const char *name, - GncCustomer * default_value); - -GncVendor* gnc_option_db_lookup_vendor_option(GNCOptionDB *odb, - const char *section, - const char *name, - GncVendor * default_value); - - -#endif /* GNC_BUSINESS_OPTIONS_UTILS_H_ */ diff --git a/libgnucash/app-utils/business-options.scm b/libgnucash/app-utils/business-options.scm deleted file mode 100644 index 96a4e4087e..0000000000 --- a/libgnucash/app-utils/business-options.scm +++ /dev/null @@ -1,386 +0,0 @@ -;; Scheme code for supporting options for the business modules -;; -;; Created by: Derek Atkins -;; -;; This program 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 2 of -;; the License, or (at your option) any later version. -;; -;; This program 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, contact: -;; -;; Free Software Foundation Voice: +1-617-542-5942 -;; 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 -;; Boston, MA 02110-1301, USA gnu@gnu.org - - -;; Internally, values are always a guid. Externally, both guids and -;; invoice pointers may be used to set the value of the option. The -;; option always returns a single invoice pointer. - -(define-module (gnucash app-utils business-options)) - -(eval-when (compile load eval expand) - (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module")) - -(use-modules (gnucash core-utils)) -(use-modules (gnucash engine)) -(use-modules (gnucash utilities)) -(use-modules (gnucash app-utils options)) -(use-modules (sw_app_utils)) - -(export gnc:*business-label*) -(export gnc:*company-name*) -(export gnc:*company-addy*) -(export gnc:*company-id*) -(export gnc:*company-phone*) -(export gnc:*company-fax*) -(export gnc:*company-url*) -(export gnc:*company-email*) -(export gnc:*company-contact*) -(export gnc:*fancy-date-label*) -(export gnc:*fancy-date-format*) -(export gnc:*tax-label*) -(export gnc:*tax-nr-label*) -(export gnc:company-info) -(export gnc:fancy-date-info) -(export gnc:*option-section-budgeting*) -(export gnc:*option-name-auto-readonly-days*) -(export gnc:*option-name-num-field-source*) -(export gnc:*kvp-option-path*) -(export gnc:options-fancy-date) -(export gnc:*option-name-default-budget*) - -(define gnc:*kvp-option-path* (list KVP-OPTION-PATH)) -(define gnc:*option-name-auto-readonly-days* OPTION-NAME-AUTO-READONLY-DAYS) -(define gnc:*option-name-num-field-source* OPTION-NAME-NUM-FIELD-SOURCE) - -(define gnc:*option-section-budgeting* OPTION-SECTION-BUDGETING) -(define gnc:*option-name-default-budget* OPTION-NAME-DEFAULT-BUDGET) - -(define gnc:*business-label* (N_ "Business")) -(define gnc:*company-name* (N_ "Company Name")) -(define gnc:*company-addy* (N_ "Company Address")) -(define gnc:*company-id* (N_ "Company ID")) -(define gnc:*company-phone* (N_ "Company Phone Number")) -(define gnc:*company-fax* (N_ "Company Fax Number")) -(define gnc:*company-url* (N_ "Company Website URL")) -(define gnc:*company-email* (N_ "Company Email Address")) -(define gnc:*company-contact* (N_ "Company Contact Person")) -(define gnc:*fancy-date-label* (N_ "Fancy Date Format")) -(define gnc:*fancy-date-format* (N_ "custom")) -(define gnc:*tax-label* (N_ "Tax")) -(define gnc:*tax-nr-label* (N_ "Tax Number")) - - -(define (gnc:options-fancy-date book) - (let ((date-format (gnc:fancy-date-info book gnc:*fancy-date-format*))) - (if (boolean? date-format) ;; date-format does not exist - (qof-date-format-get-string (qof-date-format-get)) - date-format))) - -(define (gnc:company-info book key) - ;; Access company info from key-value pairs for current book - (gnc:option-get-value book gnc:*business-label* key)) - -(define (gnc:fancy-date-info book key) - ;; Access fancy date info from key-value pairs for current book - (gnc:option-get-value book gnc:*business-label* (list gnc:*fancy-date-label* key))) - -(define (gnc:make-invoice-option - section - name - sort-tag - documentation-string - default-getter - value-validator) - - (define (convert-to-guid item) - (if (string? item) - item - (gncInvoiceReturnGUID item))) - - (define (convert-to-invoice item) - (if (string? item) - (gncInvoiceLookupFlip item (gnc-get-current-book)) - item)) - - (let* ((option (convert-to-guid (default-getter))) - (option-set #f) - (getter (lambda () (convert-to-invoice - (if option-set - option - (default-getter))))) - (value->string (lambda () - (string-append - "'" (gnc:value->string (if option-set option #f))))) - (validator - (if (not value-validator) - (lambda (invoice) (list #t invoice)) - (lambda (invoice) - (value-validator (convert-to-invoice invoice)))))) - (gnc:make-option - section name sort-tag 'invoice documentation-string getter - (lambda (invoice) ;; setter - (if (null? invoice) (set! invoice (default-getter))) - (set! invoice (convert-to-invoice invoice)) - (let* ((result (validator invoice)) - (valid (car result)) - (value (cadr result))) - (if valid - (begin - (set! option (convert-to-guid value)) - (set! option-set #t)) - (gnc:error "Illegal invoice value set")))) - (lambda () (convert-to-invoice (default-getter))) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b option p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (begin - (set! option v) - (set! option-set #t))))) - validator - #f #f #f #f))) - -;; Internally, values are always a guid. Externally, both guids and -;; customer pointers may be used to set the value of the option. The -;; option always returns a single customer pointer. -(define (gnc:make-owner-option - section - name - sort-tag - documentation-string - default-getter - value-validator - owner-type) - - (let ((option-value (gncOwnerNew))) - - (define (convert-to-pair item) - (if (pair? item) - item - (cons (gncOwnerGetType item) - (gncOwnerReturnGUID item)))) - - (define (convert-to-owner pair) - (if (pair? pair) - (let ((type (car pair))) - (cond - ((eqv? type GNC-OWNER-CUSTOMER) - (gncOwnerInitCustomer - option-value - (gncCustomerLookupFlip (cdr pair) (gnc-get-current-book))) - option-value) - - ((eqv? type GNC-OWNER-VENDOR) - (gncOwnerInitVendor - option-value - (gncVendorLookupFlip (cdr pair) (gnc-get-current-book))) - option-value) - - ((eqv? type GNC-OWNER-EMPLOYEE) - (gncOwnerInitEmployee - option-value - (gncEmployeeLookupFlip (cdr pair) (gnc-get-current-book))) - option-value) - - ((eqv? type GNC-OWNER-JOB) - (gncOwnerInitJob - option-value - (gncJobLookupFlip (cdr pair) (gnc-get-current-book))) - option-value) - - (else '()))) - pair)) - - (let* ((option (convert-to-pair (default-getter))) - (option-set #f) - (getter (lambda () (convert-to-owner - (if option-set - option - (default-getter))))) - (value->string (lambda () - (string-append - "'" (gnc:value->string - (if option-set option #f))))) - (validator - (if (not value-validator) - (lambda (owner) - (let ((type (if (pair? owner) - (car owner) - (gncOwnerGetType owner)))) - (if (equal? type owner-type) - (list #t owner) - (list #f "Owner-Type Mismatch")))) - (lambda (owner) - (value-validator (convert-to-owner owner)))))) - - (gnc:make-option - section name sort-tag 'owner documentation-string getter - (lambda (owner) - (if (null? owner) (set! owner (default-getter))) - (set! owner (convert-to-owner owner)) - (let* ((result (validator owner)) - (valid (car result)) - (value (cadr result))) - (if valid - (begin - (set! option (convert-to-pair value)) - (set! option-set #t)) - (gnc:error "Illegal owner value set")))) - (lambda () (convert-to-owner (default-getter))) - (gnc:restore-form-generator value->string) - (lambda (b p) - (qof-book-set-option b (symbol->string (car option)) - (append p '("type"))) - (qof-book-set-option b (cdr option) - (append p '("value")))) - (lambda (b p) - (let ((t (qof-book-get-option b (append p '("type")))) - (v (qof-book-get-option b (append p '("value"))))) - (if (and t v (string? t) (string? v)) - (begin - (set! option (cons (string->symbol t) v)) - (set! option-set #t))))) - validator - owner-type #f #f #f)))) - - -;; Internally, values are always a guid. Externally, both guids and -;; taxtable pointers may be used to set the value of the option. The -;; option always returns a single taxtable pointer. - -(define (gnc:make-taxtable-option - section - name - sort-tag - documentation-string - default-getter - value-validator) - - (define (convert-to-guid item) - (if (string? item) - item - (gncTaxTableReturnGUID item))) - - (define (convert-to-taxtable item) - (if (string? item) - (gncTaxTableLookupFlip item (gnc-get-current-book)) - item)) - - (let* ((option (convert-to-guid (default-getter))) - (option-set #f) - (getter (lambda () (convert-to-taxtable - (if option-set - option - (default-getter))))) - (value->string (lambda () - (string-append - "'" (gnc:value->string (if option-set option #f))))) - (validator - (if (not value-validator) - (lambda (taxtable) (list #t taxtable)) - (lambda (taxtable) - (value-validator (convert-to-taxtable taxtable)))))) - (gnc:make-option - section name sort-tag 'taxtable documentation-string getter - (lambda (taxtable) - (if (null? taxtable) (set! taxtable (default-getter))) - (set! taxtable (convert-to-taxtable taxtable)) - (let* ((result (validator taxtable)) - (valid (car result)) - (value (cadr result))) - (if valid - (begin - (set! option (convert-to-guid value)) - (set! option-set #t)) - (gnc:error "Illegal taxtable value set")))) - (lambda () (convert-to-taxtable (default-getter))) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b option p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (begin - (set! option v) - (set! option-set #t))))) - validator - #f #f #f #f))) - -;; This defines an option to set a counter value. This is a slightly -;; different kind of option: Unlike all other options, the values edited -;; by this option are not saved in the "options"/
kvm slot, but -;; in the "counters" slot. This is mostly due to backwards compatibility -;; and partly because counters are a bit different from other options -;; anyway. -;; -;; This is implemented by overriding the scm->kvp and kvp->scm methods -;; to ignore the kvp path passed and replace it with a hardcoded -;; "counters". -(define (gnc:make-counter-option - section - name - key - sort-tag - documentation-string - default-value) - (let ((option (gnc:make-number-range-option - section name sort-tag documentation-string - default-value 0 999999999 0 1))) - (gnc:set-option-scm->kvp - option - (lambda (b p) - (qof-book-set-option - b (inexact->exact ((gnc:option-getter option))) - (list "counters" key)))) - (gnc:set-option-kvp->scm - option - (lambda (b p) - (let ((v (qof-book-get-option b (list "counters" key)))) - (if (and v (integer? v)) - ((gnc:option-setter option) v))))) - option)) - -;; This defines an option to set a counter format, which has the same -;; exception as gnc:make-counter-option above. -;; Note this function uses a hack to make sure there never is a default value -;; (default-value is set to #f and value subsequently set to whatever was passed as default-value) -;; This hack was introduced to fix https://bugs.gnucash.org/show_bug.cgi?id=687504 -(define (gnc:make-counter-format-option - section - name - key - sort-tag - documentation-string - default-value) - (let ((option (gnc:make-string-option - section name sort-tag documentation-string #f))) - (gnc:option-set-value option default-value) - (gnc:set-option-scm->kvp - option - (lambda (b p) - (let ((value ((gnc:option-getter option))) - (path (string-concatenate (list "counter_formats/" key)))) - (qof-book-set-string-option b path value)))) - (gnc:set-option-kvp->scm - option - (lambda (b p) - (let* ((path (string-concatenate (list "counter_formats/" key))) - (v (qof-book-get-string-option b path))) - (if (and v (string? v)) - ((gnc:option-setter option) v))))) - option)) - -(export gnc:make-invoice-option) -(export gnc:make-owner-option) -(export gnc:make-taxtable-option) -(export gnc:make-counter-option) -(export gnc:make-counter-format-option) diff --git a/libgnucash/app-utils/business-prefs.scm b/libgnucash/app-utils/business-prefs.scm deleted file mode 100644 index 9e50ae3c12..0000000000 --- a/libgnucash/app-utils/business-prefs.scm +++ /dev/null @@ -1,199 +0,0 @@ -;; Business Preferences -;; -;; Created by: Derek Atkins -;; -;; This program 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 2 of -;; the License, or (at your option) any later version. -;; -;; This program 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, contact: -;; -;; Free Software Foundation Voice: +1-617-542-5942 -;; 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 -;; Boston, MA 02110-1301, USA gnu@gnu.org - -(define-module (gnucash app-utils business-prefs)) - -(eval-when (compile load eval expand) - (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module")) - -(use-modules (sw_app_utils)) -(use-modules (gnucash core-utils)) -(use-modules (gnucash engine)) -(use-modules (gnucash app-utils options)) -(use-modules (gnucash app-utils business-options)) - -(define gnc:*option-section-counters* (N_ "Counters")) - -;; This defines all available counter types to show options for. This a -;; list that contains a sublist for each counter type, containing: The -;; (untranslated) counter name, the format label, the previous number -;; label, the format help text and the previous number help text. -(define counter-types - (list (list "gncCustomer" (N_ "Customer number format") - (N_ "Customer number") - (N_ "The format string to use for generating customer numbers. This is a printf-style format string.") - (N_ "The previous customer number generated. This number will be incremented to generate the next customer number.")) - (list "gncEmployee" (N_ "Employee number format") - (N_ "Employee number") - (N_ "The format string to use for generating employee numbers. This is a printf-style format string.") - (N_ "The previous employee number generated. This number will be incremented to generate the next employee number.")) - (list "gncInvoice" (N_ "Invoice number format") - (N_ "Invoice number") - (N_ "The format string to use for generating invoice numbers. This is a printf-style format string.") - (N_ "The previous invoice number generated. This number will be incremented to generate the next invoice number.")) - (list "gncBill" (N_ "Bill number format") - (N_ "Bill number") - (N_ "The format string to use for generating bill numbers. This is a printf-style format string.") - (N_ "The previous bill number generated. This number will be incremented to generate the next bill number.")) - (list "gncExpVoucher" (N_ "Expense voucher number format") - (N_ "Expense voucher number") - (N_ "The format string to use for generating expense voucher numbers. This is a printf-style format string.") - (N_ "The previous expense voucher number generated. This number will be incremented to generate the next voucher number.")) - (list "gncJob" (N_ "Job number format") - (N_ "Job number") - (N_ "The format string to use for generating job numbers. This is a printf-style format string.") - (N_ "The previous job number generated. This number will be incremented to generate the next job number.")) - (list "gncOrder" (N_ "Order number format") - (N_ "Order number") - (N_ "The format string to use for generating order numbers. This is a printf-style format string.") - (N_ "The previous order number generated. This number will be incremented to generate the next order number.")) - (list "gncVendor" (N_ "Vendor number format") - (N_ "Vendor number") - (N_ "The format string to use for generating vendor numbers. This is a printf-style format string.") - (N_ "The previous vendor number generated. This number will be incremented to generate the next vendor number.")) -)) - -(define (book-options-generator options) - (define (reg-option new-option) - (gnc:register-option options new-option)) - - (reg-option - (gnc:make-string-option - gnc:*business-label* gnc:*company-name* - "a" (N_ "The name of your business.") "")) - - (reg-option - (gnc:make-text-option - gnc:*business-label* gnc:*company-addy* - "b1" (N_ "The address of your business.") "")) - - (reg-option - (gnc:make-string-option - gnc:*business-label* gnc:*company-contact* - "b2" (N_ "The contact person to print on invoices.") "")) - - (reg-option - (gnc:make-string-option - gnc:*business-label* gnc:*company-phone* - "c1" (N_ "The phone number of your business.") "")) - - (reg-option - (gnc:make-string-option - gnc:*business-label* gnc:*company-fax* - "c2" (N_ "The fax number of your business.") "")) - - (reg-option - (gnc:make-string-option - gnc:*business-label* gnc:*company-email* - "c3" (N_ "The email address of your business.") "")) - - (reg-option - (gnc:make-string-option - gnc:*business-label* gnc:*company-url* - "c4" (N_ "The URL address of your website.") "")) - - (reg-option - (gnc:make-string-option - gnc:*business-label* gnc:*company-id* - "c5" (N_ "The ID for your company (eg 'Tax-ID: 00-000000).") - "")) - - (reg-option - (gnc:make-taxtable-option - gnc:*business-label* (N_ "Default Customer TaxTable") - "e" (N_ "The default tax table to apply to customers.") - (lambda () '()) #f)) - - (reg-option - (gnc:make-taxtable-option - gnc:*business-label* (N_ "Default Vendor TaxTable") - "f" (N_ "The default tax table to apply to vendors.") - (lambda () '()) #f)) - - (reg-option - (gnc:make-dateformat-option - gnc:*business-label* gnc:*fancy-date-label* - "g" (N_ "The default date format used for fancy printed dates.") - #f)) - - ;; Accounts tab - - (reg-option - (gnc:make-number-range-option - gnc:*option-section-accounts* gnc:*option-name-auto-readonly-days* - "a" (N_ "Choose the number of days after which transactions will be read-only and cannot be edited anymore. This threshold is marked by a red line in the account register windows. If zero, all transactions can be edited and none are read-only.") - 0 ;; default - 0 ;; lower bound - 3650 ;; upper bound - 0 ;; number of decimals - 1 ;; step size - )) - - (reg-option - (gnc:make-simple-boolean-option - gnc:*option-section-accounts* gnc:*option-name-num-field-source* - "b" (N_ "Check to have split action field used in registers for 'Num' field in place of transaction number; transaction number shown as 'T-Num' on second line of register. Has corresponding effect on business features, reporting and imports/exports.") - #f)) - - (reg-option - (gnc:make-simple-boolean-option - gnc:*option-section-accounts* gnc:*option-name-trading-accounts* - "a" (N_ "Check to have trading accounts used for transactions involving more than one currency or commodity.") - #f)) - - ;; Budgeting Tab - - (reg-option - (gnc:make-budget-option - gnc:*option-section-budgeting* gnc:*option-name-default-budget* - "a" (N_ "Budget to be used when none has been otherwise specified."))) - - ;; Tax Tab - (reg-option - (gnc:make-string-option - gnc:*tax-label* gnc:*tax-nr-label* - "a" (N_ "The electronic tax number of your business") "")) - - ;; Counters Tab - (for-each - (lambda (vals) - ;; Unpack the list of strings for this counter type - (let* ((key (car vals)) - (format-label (cadr vals)) - (number-label (caddr vals)) - (format-description (cadddr vals)) - (number-description (cadddr (cdr vals)))) - ;; For each counter-type we create an option for the last used - ;; number and the format string to use. - (reg-option - (gnc:make-counter-option - gnc:*option-section-counters* number-label key - (string-append key "a") number-description 0)) - - (reg-option - (gnc:make-counter-format-option - gnc:*option-section-counters* format-label key - (string-append key "b") format-description "")))) - ;; Make counter and format option for each defined counter - counter-types)) - - -(gnc-register-kvp-option-generator QOF-ID-BOOK-SCM book-options-generator) diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp new file mode 100644 index 0000000000..3d2a3e141f --- /dev/null +++ b/libgnucash/app-utils/gnc-option-date.cpp @@ -0,0 +1,572 @@ +/********************************************************************\ + * gnc-option-date.cpp -- Relative Dates for options * + * Copyright (C) 2020 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#include "gnc-option-date.hpp" +#include +#include +#include +#include +#include + +extern "C" +{ +#include +} + +#define N_(string) string //So that xgettext will find it + +enum RelativeDateType +{ + ABSOLUTE, + LAST, + NEXT, + START, + END +}; + +enum RelativeDateOffset +{ + NONE, + WEEK, + MONTH, + QUARTER, + THREE, + SIX, + YEAR +}; + +struct GncRelativeDate +{ + RelativeDatePeriod m_period; + RelativeDateType m_type; + RelativeDateOffset m_offset; + const char* m_storage; + const char* m_display; + const char* m_description; +}; + + +/* The fixed values and strings for date periods. Accessor functions will use + * the RelativeDatePeriod as an index so any changes need to be reflected in the + * RelativeDatePeriod enum class in gnc-option-date.hpp and vice-versa. + * + * The double curly braces are actually correct and required for a std::array + * initializer list. + */ +static const std::array reldates +{{ + { + RelativeDatePeriod::TODAY, + RelativeDateType::LAST, + RelativeDateOffset::NONE, + "today", + N_("Today"), + N_("The current date.") + }, + { + RelativeDatePeriod::ONE_WEEK_AGO, + RelativeDateType::LAST, + RelativeDateOffset::WEEK, + "one-week-ago", + N_("One Week Ago"), + N_("One Week Ago.") + }, + { + RelativeDatePeriod::ONE_WEEK_AHEAD, + RelativeDateType::NEXT, + RelativeDateOffset::WEEK, + "one-week-ahead", + N_("One Week Ahead"), + N_("One Week Ahead.") + }, + { + RelativeDatePeriod::ONE_MONTH_AGO, + RelativeDateType::LAST, + RelativeDateOffset::MONTH, + "one-month-ago", + N_("One Month Ago"), + N_("One Month Ago.") + }, + { + RelativeDatePeriod::ONE_MONTH_AHEAD, + RelativeDateType::NEXT, + RelativeDateOffset::MONTH, + "one-month-ahead", + N_("One Month Ahead"), + N_("One Month Ahead.") + }, + { + RelativeDatePeriod::THREE_MONTHS_AGO, + RelativeDateType::LAST, + RelativeDateOffset::THREE, + "three-months-ago", + N_("Three Months Ago"), + N_("Three Months Ago.") + }, + { + RelativeDatePeriod::THREE_MONTHS_AHEAD, + RelativeDateType::NEXT, + RelativeDateOffset::THREE, + "three-months-ahead", + N_("Three Months Ahead"), + N_("Three Months Ahead.") + }, + { + RelativeDatePeriod::SIX_MONTHS_AGO, + RelativeDateType::LAST, + RelativeDateOffset::SIX, + "six-months-ago", + N_("Six Months Ago"), + N_("Six Months Ago.") + }, + { + RelativeDatePeriod::SIX_MONTHS_AHEAD, + RelativeDateType::NEXT, + RelativeDateOffset::SIX, + "six-months-ahead", + N_("Six Months Ahead"), + N_("Six Months Ahead.") + }, + { + RelativeDatePeriod::ONE_YEAR_AGO, + RelativeDateType::LAST, + RelativeDateOffset::YEAR, + "one-year-ago", + N_("One Year Ago"), + N_("One Year Ago.") + }, + { + RelativeDatePeriod::ONE_YEAR_AHEAD, + RelativeDateType::NEXT, + RelativeDateOffset::YEAR, + "one-year-ahead", + N_("One Year Ahead"), + N_("One Year Ahead.") + }, + { + RelativeDatePeriod::START_THIS_MONTH, + RelativeDateType::START, + RelativeDateOffset::MONTH, + "start-this-month", + N_("Start of this month"), + N_("First day of the current month.") + }, + { + RelativeDatePeriod::END_THIS_MONTH, + RelativeDateType::END, + RelativeDateOffset::MONTH, + "end-this-month", + N_("End of this month"), + N_("Last day of the current month.") + }, + { + RelativeDatePeriod::START_PREV_MONTH, + RelativeDateType::START, + RelativeDateOffset::MONTH, + "start-prev-month", + N_("Start of previous month"), + N_("First day of the previous month.") + }, + { + RelativeDatePeriod::END_PREV_MONTH, + RelativeDateType::END, + RelativeDateOffset::MONTH, + "end-prev-month", + N_("End of previous month"), + N_("Last day of previous month.") + }, + { + RelativeDatePeriod::START_NEXT_MONTH, + RelativeDateType::START, + RelativeDateOffset::MONTH, + "start-next-month", + N_("Start of next month"), + N_("First day of the next month.") + }, + { + RelativeDatePeriod::END_NEXT_MONTH, + RelativeDateType::END, + RelativeDateOffset::MONTH, + "end-next-month", + N_("End of next month"), + N_("Last day of next month.") + }, + { + RelativeDatePeriod::START_CURRENT_QUARTER, + RelativeDateType::START, + RelativeDateOffset::QUARTER, + "start-current-quarter", + N_("Start of current quarter"), + N_("First day of the current quarterly accounting period.") + }, + { + RelativeDatePeriod::END_CURRENT_QUARTER, + RelativeDateType::END, + RelativeDateOffset::QUARTER, + "end-current-quarter", + N_("End of current quarter"), + N_("Last day of the current quarterly accounting period.") + }, + { + RelativeDatePeriod::START_PREV_QUARTER, + RelativeDateType::START, + RelativeDateOffset::QUARTER, + "start-prev-quarter", + N_("Start of previous quarter"), + N_("First day of the previous quarterly accounting period.") + }, + { + RelativeDatePeriod::END_PREV_QUARTER, + RelativeDateType::END, + RelativeDateOffset::QUARTER, + "end-prev-quarter", + N_("End of previous quarter"), + N_("Last day of previous quarterly accounting period.") + }, + { + RelativeDatePeriod::START_NEXT_QUARTER, + RelativeDateType::START, + RelativeDateOffset::QUARTER, + "start-next-quarter", + N_("Start of next quarter"), + N_("First day of the next quarterly accounting period.") + }, + { + RelativeDatePeriod::END_NEXT_QUARTER, + RelativeDateType::END, + RelativeDateOffset::QUARTER, + "end-next-quarter", + N_("End of next quarter"), + N_("Last day of next quarterly accounting period.") + }, + { + RelativeDatePeriod::START_CAL_YEAR, + RelativeDateType::START, + RelativeDateOffset::YEAR, + "start-cal-year", + N_("Start of this year"), + N_("First day of the current calendar year.") + }, + { + RelativeDatePeriod::END_CAL_YEAR, + RelativeDateType::END, + RelativeDateOffset::YEAR, + "end-cal-year", + N_("End of this year"), + N_("Last day of the current calendar year.") + }, + { + RelativeDatePeriod::START_PREV_YEAR, + RelativeDateType::START, + RelativeDateOffset::YEAR, + "start-prev-year", + N_("Start of previous year"), + N_("First day of the previous calendar year.") + }, + { + RelativeDatePeriod::END_PREV_YEAR, + RelativeDateType::END, + RelativeDateOffset::YEAR, + "end-prev-year", + N_("End of previous year"), + N_("Last day of the previous calendar year.") + }, + { + RelativeDatePeriod::START_NEXT_YEAR, + RelativeDateType::START, + RelativeDateOffset::YEAR, + "start-next-year", + N_("Start of next year"), + N_("First day of the next calendar year.") + }, + { + RelativeDatePeriod::END_NEXT_YEAR, + RelativeDateType::END, + RelativeDateOffset::YEAR, + "end-next-year", + N_("End of next year"), + N_("Last day of the next calendar year.") + }, + { + RelativeDatePeriod::START_ACCOUNTING_PERIOD, + RelativeDateType::START, + RelativeDateOffset::YEAR, + "start-prev-fin-year", + N_("Start of accounting period"), + N_("First day of the accounting period, as set in the global preferences.") + }, + { + RelativeDatePeriod::END_ACCOUNTING_PERIOD, + RelativeDateType::END, + RelativeDateOffset::YEAR, + "end-prev-fin-year", + N_("End of accounting period"), + N_("Last day of the accounting period, as set in the global preferences.") + } + }}; + +static const GncRelativeDate& +checked_reldate(RelativeDatePeriod per) +{ + assert (reldates[static_cast(per)].m_period == per); + return reldates[static_cast(per)]; +} + +bool +gnc_relative_date_is_single(RelativeDatePeriod per) +{ + if (per == RelativeDatePeriod::ABSOLUTE) + return false; + auto reldate = checked_reldate(per); + return reldate.m_type == RelativeDateType::LAST || + reldate.m_type == RelativeDateType::NEXT; +} + +bool +gnc_relative_date_is_starting(RelativeDatePeriod per) +{ + if (per == RelativeDatePeriod::ABSOLUTE) + return false; + return checked_reldate(per).m_type == RelativeDateType::START; +} + +bool +gnc_relative_date_is_ending(RelativeDatePeriod per) +{ + if (per == RelativeDatePeriod::ABSOLUTE) + return false; + return checked_reldate(per).m_type == RelativeDateType::END; +} + +const char* +gnc_relative_date_storage_string(RelativeDatePeriod per) +{ + if (per == RelativeDatePeriod::ABSOLUTE) + return nullptr; + return checked_reldate(per).m_storage; +} + +const char* +gnc_relative_date_display_string(RelativeDatePeriod per) +{ + if (per == RelativeDatePeriod::ABSOLUTE) + return nullptr; + return checked_reldate(per).m_display; +} +const char* +gnc_relative_date_description(RelativeDatePeriod per) +{ + if (per == RelativeDatePeriod::ABSOLUTE) + return nullptr; + return checked_reldate(per).m_description; +} + +RelativeDatePeriod +gnc_relative_date_from_storage_string(const char* str) +{ + auto per = std::find_if(reldates.begin(), reldates.end(), + [str](auto rel) -> bool + { + return strcmp(str, rel.m_storage) == 0; + }); + return per != reldates.end() ? per->m_period : RelativeDatePeriod::ABSOLUTE; +} + +static bool +reldate_is_prev(RelativeDatePeriod per) +{ + auto rdate{checked_reldate(per)}; + return per == RelativeDatePeriod::START_PREV_YEAR || + per == RelativeDatePeriod::END_PREV_YEAR || + per == RelativeDatePeriod::START_PREV_QUARTER || + per == RelativeDatePeriod::END_PREV_QUARTER || + per == RelativeDatePeriod::START_PREV_MONTH || + per == RelativeDatePeriod::END_PREV_MONTH || + rdate.m_type == LAST; +} + +static bool +reldate_is_next(RelativeDatePeriod per) +{ + auto rdate{checked_reldate(per)}; + return per == RelativeDatePeriod::START_NEXT_YEAR || + per == RelativeDatePeriod::END_NEXT_YEAR || + per == RelativeDatePeriod::START_NEXT_QUARTER || + per == RelativeDatePeriod::END_NEXT_QUARTER || + per == RelativeDatePeriod::START_NEXT_MONTH || + per == RelativeDatePeriod::END_NEXT_MONTH || + rdate.m_type == NEXT; +} + +static RelativeDateOffset +reldate_offset(RelativeDatePeriod per) +{ + return checked_reldate(per).m_offset; +} + +static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* Normalize the modified struct tm computed in gnc_relative_date_to_time64 + * before setting the time and perhaps beginning/end of the month. Using the + * gnc_date API would involve multiple conversions to and from struct tm. +*/ +static void +normalize_reldate_tm(struct tm& now) +{ + auto factor{abs(now.tm_mon) / 12}; + now.tm_mon /= factor > 0 ? factor : 1; + now.tm_year += now.tm_mon < 0 ? -factor: factor; + + auto days = [](auto month, int year) + { + auto mon{month % 12 + (month < 0 ? 12 : 0)}; + auto num_days{days_in_month[mon]}; + //Leap year check. + if (mon == 1 && year % 4 == 0 && !(year % 100 == 0 && (year + 1900) % 400 != 0)) + ++num_days; + return num_days; + }; + + while (now.tm_mday < 1) + now.tm_mday += days(--now.tm_mon, now.tm_year); + + while (now.tm_mday > days(now.tm_mon, now.tm_year)) + now.tm_mday -= days(now.tm_mon++, now.tm_year); + + while (now.tm_mon < 0) + { + now.tm_mon += 12; + --now.tm_year; + } + while (now.tm_mon > 11) + { + now.tm_mon -= 12; + ++now.tm_year; + } + + /* This would happen only if we moved from Feb 29 in a leap year while + * adjusting the months so we don't need to worry about adjusting the year + * again. + */ + if (now.tm_mday > days_in_month[now.tm_mon]) + now.tm_mday -= days_in_month[now.tm_mon++]; +} + +static void +reldate_set_day_and_time(struct tm& now, RelativeDateType type) +{ + if (type == RelativeDateType::START) + { + gnc_tm_set_day_start(&now); + now.tm_mday = 1; + } + else if (type == RelativeDateType::END) + { + now.tm_mday = gnc_date_get_last_mday(now.tm_mon, now.tm_year + 1900); + gnc_tm_set_day_end(&now); + } + // Do nothing for LAST and NEXT. +}; + +time64 +gnc_relative_date_to_time64(RelativeDatePeriod period) +{ + if (period == RelativeDatePeriod::TODAY) + return static_cast(GncDateTime()); + if (period == RelativeDatePeriod::START_ACCOUNTING_PERIOD) + return gnc_accounting_period_fiscal_start(); + if (period == RelativeDatePeriod::END_ACCOUNTING_PERIOD) + return gnc_accounting_period_fiscal_end(); + + GncDateTime now_t; + if (period == RelativeDatePeriod::TODAY) + return static_cast(now_t); + auto now{static_cast(now_t)}; + auto acct_per{static_cast(GncDateTime(gnc_accounting_period_fiscal_start()))}; + + if (acct_per.tm_mon == now.tm_mon && acct_per.tm_mday == now.tm_mday) + { + //No set accounting period, use the calendar year + acct_per.tm_mon = 0; + acct_per.tm_mday = 0; + } + + switch(reldate_offset(period)) + { + case RelativeDateOffset::NONE: +// Report on today so nothing to do + break; + case RelativeDateOffset::YEAR: + if (reldate_is_prev(period)) + --now.tm_year; + else if (reldate_is_next(period)) + ++now.tm_year; + if (gnc_relative_date_is_starting(period)) + now.tm_mon = 0; + else if (gnc_relative_date_is_ending(period)) + now.tm_mon = 11; + break; + case RelativeDateOffset::SIX: + if (reldate_is_prev(period)) + now.tm_mon -= 6; + else if (reldate_is_next(period)) + now.tm_mon += 6; + break; + case RelativeDateOffset::QUARTER: + { + auto delta = (now.tm_mon > acct_per.tm_mon ? + now.tm_mon - acct_per.tm_mon : + acct_per.tm_mon - now.tm_mon) % 3; + now.tm_mon = now.tm_mon - delta; + } + [[fallthrough]]; + case RelativeDateOffset::THREE: + if (reldate_is_prev(period)) + now.tm_mon -= 3; + else if (reldate_is_next(period)) + now.tm_mon += 3; + if (gnc_relative_date_is_ending(period)) + now.tm_mon += 2; + break; + case RelativeDateOffset::MONTH: + if (reldate_is_prev(period)) + --now.tm_mon; + else if (reldate_is_next(period)) + ++now.tm_mon; + break; + case RelativeDateOffset::WEEK: + if (reldate_is_prev(period)) + now.tm_mday -= 7; + else if (reldate_is_next(period)) + now.tm_mday += 7; + } + normalize_reldate_tm(now); + reldate_set_day_and_time(now, checked_reldate(period).m_type); + return static_cast(GncDateTime(now)); +} + +std::ostream& +operator<<(std::ostream& ostr, RelativeDatePeriod per) +{ + ostr << "'reldate . " << gnc_relative_date_display_string(per); + return ostr; +} diff --git a/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp new file mode 100644 index 0000000000..b87ea4d08a --- /dev/null +++ b/libgnucash/app-utils/gnc-option-date.hpp @@ -0,0 +1,189 @@ +/********************************************************************\ + * gnc-option-date.hpp -- Relative dates for options * + * Copyright (C) 2020 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ +/** @addtogroup Engine + @{ */ +/** @addtogroup Options + @{ */ +/** @file gnc-option-date.hpp + @brief Relative date enumeration and manipulation functions. + @author Copyright 2019-2021 John Ralls +*/ +#ifndef GNC_OPTION_DATE_HPP_ +#define GNC_OPTION_DATE_HPP_ + +extern "C" +{ +#include +} + +#include +#include +/** + * Reporting periods relative to the current date. + * + * The original design allowed custom RelativeDatePeriods, but that facility is + * unused so we'll go with compiled-in enums. + */ +enum class RelativeDatePeriod : int +{ + ABSOLUTE = -1, + TODAY, + ONE_WEEK_AGO, + ONE_WEEK_AHEAD, + ONE_MONTH_AGO, + ONE_MONTH_AHEAD, + THREE_MONTHS_AGO, + THREE_MONTHS_AHEAD, + SIX_MONTHS_AGO, + SIX_MONTHS_AHEAD, + ONE_YEAR_AGO, + ONE_YEAR_AHEAD, + START_THIS_MONTH, + END_THIS_MONTH, + START_PREV_MONTH, + END_PREV_MONTH, + START_NEXT_MONTH, + END_NEXT_MONTH, + START_CURRENT_QUARTER, + END_CURRENT_QUARTER, + START_PREV_QUARTER, + END_PREV_QUARTER, + START_NEXT_QUARTER, + END_NEXT_QUARTER, + START_CAL_YEAR, + END_CAL_YEAR, + START_PREV_YEAR, + END_PREV_YEAR, + START_NEXT_YEAR, + END_NEXT_YEAR, + START_ACCOUNTING_PERIOD, + END_ACCOUNTING_PERIOD, +}; + +constexpr unsigned relative_date_periods = + static_cast(RelativeDatePeriod::END_ACCOUNTING_PERIOD) + 2; + +using RelativeDatePeriodVec = std::vector; + +/** + * Report whether the relative date represents a period offset to today's date + * rather than the beginning or end of a date range. For example ONE_MONTH_AGO + * will be made concrete as the same day as today in the previous month. + * + * @param period The Relative Date Period to check. + * @return true if the date is stand-alone. + */ +bool gnc_relative_date_is_single(RelativeDatePeriod); + +/** + * Report whether the relative date represents the beginning of a date + * range. For example START_LAST_MONTH is the beginning of a range. + * + * @param period The Relative Date Period to check. + * @return true if the date is the beginning of a date range + */ +bool gnc_relative_date_is_starting(RelativeDatePeriod); + +/** + * Report whether the relative date represents the end of a date range. For + * example END_LAST_MONTH is the end of a range. + * + * @param period The Relative Date Period to check. + * @return true if the date is the end of a date range. + */ +bool gnc_relative_date_is_ending(RelativeDatePeriod); + +/** + * Provide the string representation of a relative date for persisting the + * value. This string is not localizable. + * + * @param period The relative date period. + * @return A constant string or nullptr if the period is ABSOLUTE. The string's + * lifetime will be that of the Relative Date Period. It must not be freed and + * should be copied if the period might be destroyed before the using code is + * finished. + */ +const char* gnc_relative_date_storage_string(RelativeDatePeriod); + +/** + * Provide the string representation of a relative date for displaying + * value to a user. This string is localizable. + * + * @param period The relative date period. + * @return A constant string or nullptr if the period is ABSOLUTE. The string's + * lifetime will be that of the Relative Date Period. It must not be freed and + * should be copied if the period might be destroyed before the using code is + * finished. + */ +const char* gnc_relative_date_display_string(RelativeDatePeriod); + +/** + * Provide the description of a relative date. This string is localizable. + * + * @param period The relative date period. + * @return A constant string or nullptr if the period is ABSOLUTE. The string's + * lifetime will be that of the Relative Date Period. It must not be freed and + * should be copied if the period might be destroyed before the using code is + * finished. + */ +const char* gnc_relative_date_description(RelativeDatePeriod); + +/** + * Convert a relative date storage string back to a RelativeDatePeriod value. + * + * @param A string representation obtained from + * gnc_relative_date_storage_string. + * @return A RelativeDatePeriod value. + */ +RelativeDatePeriod gnc_relative_date_from_storage_string(const char*); + +/** + * Convert a RelativeDatePeriod value to a concrete time64 by applying the value + * to the current time. + * For example if it is now 3:15:42 PM local time 3 June, calling this with a + * period RelativeDatePeriod::ONE_WEEK_AHEAD will return a time64 representing + * 3:15:42 PM local time 10 June of this year. Times for START periods are + * changed to midnight local time and for END periods to 23:59:59 local time so + * for example if the period is instead RelativeDatePeriod::START_THIS_MONTH the + * time64 will represent 00:00:00 1 June and if it is + * RelativeDatePeriod::END_THIS_MONTH the time64 will be for 23:59:59 30 June, + * both in the current time zone. + * + * @param period The relative date period to use to calculate the concrete date. + * @return a time64. + */ +time64 gnc_relative_date_to_time64(RelativeDatePeriod); + +/** + * Add the display string to the provided std::ostream. + * + * @param stream the std::ostream to which to write the period value + * @param period the period value to write + * @return A reference to stream so that the operator can be chained. + */ +std::ostream& operator<<(std::ostream&, const RelativeDatePeriod); + +#endif //GNC_OPTION_DATE_HPP_ + +/** @} + @} */ diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp new file mode 100644 index 0000000000..5a6f0e9c74 --- /dev/null +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -0,0 +1,986 @@ +/********************************************************************\ + * gnc-option-impl.cpp -- Application options system * + * Copyright (C) 2019 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +//#include "options.h" +#include "gnc-option-impl.hpp" +#include +#include +#include +#include +#include + +extern "C" +{ +#include "gnc-accounting-period.h" +#include "gnc-ui-util.h" +} + +static const QofLogModule log_module{"gnc.options"}; + +const std::string GncOptionMultichoiceValue::c_empty_string{""}; +const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"}; + +using GncItem = std::pair; + +static GncItem +make_gnc_item(const QofInstance* inst) +{ + if (!inst) + return std::make_pair("", guid_new_return()); + auto type{qof_collection_get_type(qof_instance_get_collection(inst))}; + auto guid{qof_instance_get_guid(inst)}; + return std::make_pair(std::move(type), std::move(*const_cast(guid))); +} + +static const QofInstance* +qof_instance_from_gnc_item(const GncItem& item) +{ + auto [type, guid] = item; + auto book{gnc_get_current_book()}; + auto coll{qof_book_get_collection(book, type)}; + return static_cast(qof_collection_lookup_entity(coll, &guid)); +} + +static bool +operator!=(const GncItem& left, const GncItem& right) +{ + auto [ltype, lguid]{left}; + auto [rtype, rguid]{right}; + return strcmp(rtype, ltype) && !guid_equal(&rguid, &lguid); +} + +GncOptionQofInstanceValue::GncOptionQofInstanceValue( + const char* section, const char* name, + const char* key, const char* doc_string, + const QofInstance* value, GncOptionUIType ui_type) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type(ui_type), m_value{}, + m_default_value{} { + m_value = make_gnc_item(value); + m_default_value = make_gnc_item(value); +} + +GncOptionQofInstanceValue::GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from) : + OptionClassifier{from.m_section, from.m_name, from.m_sort_tag, + from.m_doc_string}, + m_ui_type(from.get_ui_type()), m_value{from.get_item()}, + m_default_value{from.get_default_item()} +{ +} +void +GncOptionQofInstanceValue::set_value(const QofInstance* new_value) +{ + m_value = make_gnc_item(new_value); +} + +void +GncOptionQofInstanceValue::set_default_value(const QofInstance *new_value) +{ + m_value = m_default_value = make_gnc_item(new_value); + +} + +const QofInstance* +GncOptionQofInstanceValue::get_value() const +{ + return qof_instance_from_gnc_item(m_value); +} + +const QofInstance* +GncOptionQofInstanceValue::get_default_value() const +{ + return qof_instance_from_gnc_item(m_default_value); +} + +void +GncOptionQofInstanceValue::reset_default_value() +{ + m_value = m_default_value; +} + +bool +GncOptionQofInstanceValue::is_changed() const noexcept +{ + return m_value != m_default_value; +} + +bool +GncOptionQofInstanceValue::deserialize(const std::string& str) noexcept +{ + QofInstance* inst{}; + // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic + try { + auto guid{static_cast(gnc::GUID::from_string(str))}; + inst = qof_instance_from_guid(&guid, m_ui_type); + if (inst) + { + m_value = make_gnc_item(inst); + return true; + } + } + catch (const gnc::guid_syntax_exception& err) + { + PWARN("Failed to convert %s to a GUID", str.c_str()); + } + return false; +} + +std::string +GncOptionQofInstanceValue::serialize() const noexcept +{ + auto inst{get_value()}; + std::string retval; + if (GNC_IS_COMMODITY(inst)) + { + auto commodity{GNC_COMMODITY(inst)}; + if (!gnc_commodity_is_currency(commodity)) + { + auto name_space{gnc_commodity_get_namespace(GNC_COMMODITY(inst))}; + if (name_space && *name_space != '\0') + { + retval = name_space; + retval += ":"; + } + } + retval += gnc_commodity_get_mnemonic(GNC_COMMODITY(inst)); + return retval; + } + else + { + gnc::GUID guid{m_value.second}; + retval = guid.to_string(); + } + return retval; +} + +static gnc_commodity* +gnc_commodity_from_namespace_and_mnemonic(std::string_view name_space, + std::string_view mnemonic) +{ + auto book{gnc_get_current_book()}; + auto table = gnc_commodity_table_get_table(book); + return gnc_commodity_table_lookup(table, name_space.data(), + mnemonic.data()); +} + +gnc_commodity* +GncOptionCommodityValue::get_value() const +{ + return gnc_commodity_from_namespace_and_mnemonic(m_namespace, m_mnemonic); +} + +gnc_commodity* +GncOptionCommodityValue::get_default_value() const +{ + return gnc_commodity_from_namespace_and_mnemonic(m_default_namespace, + m_default_mnemonic); +} + +void +GncOptionCommodityValue::set_value(gnc_commodity* value) +{ + if (!validate(value)) + throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set."); + m_mnemonic = gnc_commodity_get_mnemonic(value); + m_namespace = gnc_commodity_get_namespace(value); +} + +void +GncOptionCommodityValue::set_default_value(gnc_commodity* value) +{ + if (!validate(value)) + throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set."); + m_mnemonic = m_default_mnemonic = gnc_commodity_get_mnemonic(value); + m_namespace = m_default_namespace = gnc_commodity_get_namespace(value); +} + +void +GncOptionCommodityValue::reset_default_value() +{ + m_mnemonic = m_default_mnemonic; + m_namespace = m_default_namespace; +} + +bool +GncOptionCommodityValue::is_changed() const noexcept +{ + return m_namespace == m_default_namespace && m_mnemonic == m_default_mnemonic; +} + +bool +GncOptionCommodityValue::validate(gnc_commodity* comm) const noexcept +{ + if (!GNC_IS_COMMODITY(comm)) + return false; + if (m_is_currency && !gnc_commodity_is_currency(comm)) + return false; + return true; +} + +std::string +GncOptionCommodityValue::serialize() const noexcept +{ + if (m_is_currency) + return m_mnemonic; + else + return m_namespace + ":" + m_mnemonic; +} + +bool +GncOptionCommodityValue::deserialize(const std::string& str) noexcept +{ + auto sep{str.find(":")}; + gnc_commodity* comm{}; + std::string mnemonic, name_space; + if (sep != std::string::npos) + { + name_space = str.substr(0, sep); + mnemonic = str.substr(sep + 1, -1); + } + else + { + name_space = "CURRENCY"; + mnemonic = str; + } + comm = gnc_commodity_from_namespace_and_mnemonic(name_space, mnemonic); + if (!validate(comm)) + return false; + m_namespace = std::move(name_space); + m_mnemonic = std::move(mnemonic); + return true; +} + +bool +GncOptionAccountListValue::validate(const GncOptionAccountList& values) const +{ + if (values.empty()) + return true; + if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) && + values.size() != 1) + { + std::cerr << "GncOptionAccountListValue::validate: Multiple values for a non-multiselect option." << std::endl; + return false; + } + if (m_allowed.empty()) + return true; + auto book{gnc_get_current_book()}; + for(auto& guid : values) + { + if (std::find(m_allowed.begin(), m_allowed.end(), + xaccAccountGetType(xaccAccountLookup(&guid, book))) == m_allowed.end()) + { + std::cerr << "GncOptionAccountListValue::validate: Account " << gnc::GUID(guid).to_string() << " is not of an allowed type" << std::endl; + return false; } + } + return true; +} + +GncOptionAccountList +GncOptionAccountListValue::get_value() const +{ + return !m_value.empty() ? m_value : get_default_value(); +} + +GncOptionAccountList +GncOptionAccountListValue::get_default_value() const +{ + if (!m_default_value.empty()) + return m_default_value; + + /* If no default has been set and there's an allowed set then find the first + * account that matches one of the allowed account types. + */ + GncOptionAccountList retval{}; + if (m_allowed.empty()) + return retval; + + auto root{gnc_get_current_root_account()}; + auto account_list{gnc_account_get_descendants_sorted(root)}; + if (!account_list) + return retval; + + auto book{gnc_get_current_book()}; + for (auto node = account_list; node; node = g_list_next (node)) + { + if (std::find(m_allowed.begin(), m_allowed.end(), + xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end()) + { + retval.push_back(*qof_entity_get_guid(GNC_ACCOUNT(node->data))); + break; + } + } + g_list_free(account_list); + return retval; +} + +static bool +operator==(const GncGUID& l, const GncGUID& r) +{ + return guid_equal(&l, &r); +} + +bool +GncOptionAccountListValue::is_changed() const noexcept +{ + return m_value != m_default_value; +} + + + +/** + * Create a GList of account types to pass to gnc_account_sel_set_acct_filters. + * gnc_account_sel_set_acct_filters copies the list so the intermediary caller + * is responsible for freeing the list. + * + * @return an allocated GList* or nullptr if the list is empty. + */ +GList* +GncOptionAccountListValue::account_type_list() const noexcept +{ + if (m_allowed.empty()) + return nullptr; + GList* retval{nullptr}; + for (auto type : m_allowed) + retval = g_list_prepend(retval, GINT_TO_POINTER(type)); + return g_list_reverse(retval); +} + +bool +GncOptionAccountSelValue::validate(const Account* value) const +{ + if (m_allowed.empty() || !value) + return true; + if (std::find(m_allowed.begin(), m_allowed.end(), + xaccAccountGetType(value)) == m_allowed.end()) + return false; + return true; +} + +const Account* +GncOptionAccountSelValue::get_value() const +{ + auto book{gnc_get_current_book()}; + return guid_equal(guid_null(), &m_value) ? get_default_value() : + xaccAccountLookup(&m_value, book); +} + +const Account* +GncOptionAccountSelValue::get_default_value() const +{ + + if (!guid_equal(guid_null(), &m_default_value)) + { + auto book{gnc_get_current_book()}; + return xaccAccountLookup(&m_default_value, book); + } + + /* If no default has been set and there's an allowed set then find the first + * account that matches one of the allowed account types. + */ + if (m_allowed.empty()) + return nullptr; + + const Account* retval{nullptr}; + auto root{gnc_get_current_root_account()}; + auto account_list{gnc_account_get_descendants_sorted(root)}; + if (!account_list) + return nullptr; + + for (auto node = account_list; node; node = g_list_next (node)) + if (std::find(m_allowed.begin(), m_allowed.end(), + xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end()) + { + retval = GNC_ACCOUNT(node->data); + break; + } + g_list_free(account_list); + return retval; +} + + +/** + * Create a GList of account types to pass to gnc_account_sel_set_acct_filters. + * gnc_account_sel_set_acct_filters copies the list so the intermediary caller + * is responsible for freeing the list. + * + * @return an allocated GList* or nullptr if the list is empty. + */ +GList* +GncOptionAccountSelValue::account_type_list() const noexcept +{ + if (m_allowed.empty()) + return nullptr; + GList* retval{nullptr}; + for (auto type : m_allowed) + retval = g_list_prepend(retval, GINT_TO_POINTER(type)); + return g_list_reverse(retval); +} + +bool +GncOptionDateValue::validate(RelativeDatePeriod value) { + if (m_period_set.empty()) + return true; // No restrictions + if (std::find(m_period_set.begin(), m_period_set.end(), + value) != m_period_set.end()) + return true; + return false; +} + +time64 +GncOptionDateValue::get_value() const noexcept +{ + if (m_period == RelativeDatePeriod::ABSOLUTE) + return m_date; + return gnc_relative_date_to_time64(m_period); +} + +time64 +GncOptionDateValue::get_default_value() const noexcept +{ + if (m_default_period == RelativeDatePeriod::ABSOLUTE) + return m_default_date; + return gnc_relative_date_to_time64(m_default_period); +} + +/* Use asserts for pre- and post-conditions to deliberately crash if they're not + * met as the program design should prevent that from happening. + */ +size_t +GncOptionDateValue::get_period_index() const noexcept +{ + assert (m_period != RelativeDatePeriod::ABSOLUTE); + assert(!m_period_set.empty()); + auto item{std::find(m_period_set.begin(), m_period_set.end(), m_period)}; + assert(item != m_period_set.end()); + return item - m_period_set.begin(); +} + +size_t +GncOptionDateValue::get_default_period_index() const noexcept +{ + assert(m_period != RelativeDatePeriod::ABSOLUTE); + assert(!m_period_set.empty()); + auto item{std::find(m_period_set.begin(), m_period_set.end(), + m_default_period)}; + assert (item != m_period_set.end()); + return item - m_period_set.begin(); +} + +void +GncOptionDateValue::set_value(size_t index) noexcept +{ + assert(!m_period_set.empty()); + assert(index < m_period_set.size()); + m_date = INT64_MAX; + m_period = m_period_set[index]; +} + +size_t +GncOptionDateValue::permissible_value_index(const char* key) const noexcept +{ + auto index = std::find_if(m_period_set.begin(), m_period_set.end(), + [key](auto period) -> bool { + return strcmp(gnc_relative_date_display_string(period), + key) == 0; + }); + return index != m_period_set.end() ? index - m_period_set.begin() : 0; +} + +static const char* date_type_str[] {"absolute", "relative"}; + +std::ostream& +GncOptionDateValue::out_stream(std::ostream& oss) const noexcept +{ + if (m_period == RelativeDatePeriod::ABSOLUTE) + oss << date_type_str[0] << " . " << m_date; + else + oss << date_type_str[1] << " . " << + gnc_relative_date_storage_string(m_period); + return oss; +} + +std::istream& +GncOptionDateValue::in_stream(std::istream& iss) +{ + char type_str[10]; //The length of both "absolute" and "relative" plus 1. + iss.getline(type_str, sizeof(type_str), '.'); + if(!iss) + throw std::invalid_argument("Date Type separator missing"); + /* strcmp is safe, istream::getline null terminates the buffer. */ + if (strcmp(type_str, "absolute ") == 0) + { + time64 time; + iss >> time; + set_value(time); + if (iss.get() != ')') + iss.unget(); + } + else if (strcmp(type_str, "relative ") == 0) + { + std::string period_str; + iss >> period_str; + if (period_str.back() == ')') + period_str.pop_back(); + auto period = gnc_relative_date_from_storage_string(period_str.c_str()); + if (period == RelativeDatePeriod::ABSOLUTE) + { + std::string err{"Unknown period string in date option: '"}; + err += period_str; + err += "'"; + throw std::invalid_argument(err); + } + + set_value(period); + } + else + { + std::string err{"Unknown date type string in date option: '"}; + err += type_str; + err += "'"; + throw std::invalid_argument{err}; + } + return iss; +} + +QofInstance* +qof_instance_from_guid(GncGUID* guid, GncOptionUIType type) +{ + QofIdType qof_type; + switch(type) + { + case GncOptionUIType::BUDGET: + qof_type = "Budget"; + break; + case GncOptionUIType::JOB: + qof_type = "gncJob"; + break; + case GncOptionUIType::CUSTOMER: + qof_type = "gncCustomer"; + break; + case GncOptionUIType::VENDOR: + qof_type = "gncVendor"; + break; + case GncOptionUIType::EMPLOYEE: + qof_type = "gncEmployee"; + break; + case GncOptionUIType::INVOICE: + qof_type = "gncInvoice"; + break; + case GncOptionUIType::TAX_TABLE: + qof_type = "gncTaxtable"; + break; + case GncOptionUIType::ACCOUNT_LIST: + case GncOptionUIType::ACCOUNT_SEL: + default: + qof_type = "Account"; + break; + } + auto book{gnc_get_current_book()}; + auto col{qof_book_get_collection(book, qof_type)}; + return QOF_INSTANCE(qof_collection_lookup_entity(col, guid)); +} + +QofInstance* +qof_instance_from_string(const std::string& str, GncOptionUIType type) +{ + QofInstance* retval{nullptr}; + try { + auto guid{static_cast(gnc::GUID::from_string(str))}; + retval = qof_instance_from_guid(&guid, type); + } + catch (const gnc::guid_syntax_exception& err) + { + PWARN("Failed to convert %s to a GUID", str.c_str()); + } + return retval; +} + +std::string +qof_instance_to_string(const QofInstance* inst) +{ + std::string retval; + gnc::GUID guid{*qof_instance_get_guid(inst)}; + retval = guid.to_string(); + return retval; +} + +template void +GncOptionValue::set_value(ValueType new_value) +{ + m_value = new_value; +} + +template void +GncOptionValue::set_default_value(ValueType new_value) +{ + m_value = m_default_value = new_value; +} + +template void +GncOptionValue::reset_default_value() +{ + m_value = m_default_value; +} + +/* Missing on purpose: QofQuery because for current usage it's serialized with + * gnc_query2scm. The future is to replace QofQuery with SQL queries so there's + * not much point to spending the time to create a std::string serialization for + * them. + */ +template std::string +GncOptionValue::serialize() const noexcept +{ + static const std::string no_value{"No Value"}; + if constexpr(std::is_same_v) + return m_value ? qof_instance_to_string(m_value) : no_value; + if constexpr(std::is_same_v) + { + if (!m_value) + return no_value; + auto guid{qof_instance_to_string(qofOwnerGetOwner(m_value))}; + auto type{qofOwnerGetType(m_value)}; + std::ostringstream ostr{}; + ostr << type << " " << guid; + return ostr.str(); + } + if constexpr(std::is_same_v) + { + std::ostringstream ostr{}; + ostr << "'("; + std::for_each(m_value.begin(), m_value.end(), + [&ostr](auto rp){ + auto [id, wide, high] = rp; + ostr << "(" << id << " " << wide << " " << high << " #f) "; + }); + ostr << ")"; + return ostr.str(); + } + else if constexpr(is_same_decayed_v) + return m_value; + else if constexpr(is_same_decayed_v) + return m_value ? "True" : "False"; + else if constexpr(std::is_arithmetic_v) + return std::to_string(m_value); + else + return "Serialization not implemented"; +} + +template bool +GncOptionValue::deserialize(const std::string& str) noexcept +{ + if constexpr(std::is_same_v) + set_value(qof_instance_from_string(str, get_ui_type())); + if constexpr(std::is_same_v) + { + std::istringstream istr{str}; + std::string type, guid; + istr >> type >> guid; + auto inst{qof_instance_from_string(guid, get_ui_type())}; + qofOwnerSetEntity(const_cast(m_value), inst); + } + if constexpr(std::is_same_v) + { + std::istringstream istr{str}; + GncOptionReportPlacementVec rpv; + while (istr) + { + uint32_t id, wide, high; + istr >> id >> wide >> high; + rpv.emplace_back(id, wide, high); + } + set_value(rpv); + } + else if constexpr(is_same_decayed_v) + set_value(str); + else if constexpr(is_same_decayed_v) + set_value(str == "True"); + else if constexpr(is_same_decayed_v) + set_value(stoi(str)); + else if constexpr(is_same_decayed_v) + set_value(stoll(str)); + else if constexpr(is_same_decayed_v) + set_value(stod(str)); + else + return false; + return true; +} + +std::string +GncOptionAccountListValue::serialize() const noexcept +{ + static const std::string no_value{"No Value"}; + std::string retval; + bool first = true; + if (m_value.empty()) + return no_value; + for (auto val : m_value) + { + if (!first) + retval += " "; + first = false; + retval += guid_to_string(&val); + } + return retval; +} + +bool +GncOptionAccountListValue::deserialize(const std::string& str) noexcept +{ + if (str.empty() || str.size() < GUID_ENCODING_LENGTH) + return false; + m_value.clear(); + m_value.reserve(str.size() / GUID_ENCODING_LENGTH); + bool first = true; + size_t pos{}; + while (pos + GUID_ENCODING_LENGTH < str.size()) + { + if (!first) + ++pos; + first = false; + GncGUID guid{}; + string_to_guid(str.substr(pos, pos + GUID_ENCODING_LENGTH).c_str(), &guid); + m_value.push_back(guid); + pos += GUID_ENCODING_LENGTH; + } + return true; +} + +std::string +GncOptionAccountSelValue::serialize() const noexcept +{ + static const std::string no_value{"No Value"}; + return guid_equal(guid_null(), &m_value) ? no_value : guid_to_string(&m_value); +} + +bool +GncOptionAccountSelValue::deserialize(const std::string& str) noexcept +{ + set_value(reinterpret_cast(qof_instance_from_string(str, get_ui_type()))); + return true; +} + +std::string +GncOptionMultichoiceValue::serialize() const noexcept +{ + static const std::string no_value{"No Value"}; + std::string retval; + bool first = true; + if (m_value.empty()) + return no_value; + for (auto index : m_value) + { + if (!first) + retval += " "; + first = false; + retval += std::get<0>(m_choices[index]); + } + return retval; +} + +bool +GncOptionMultichoiceValue::deserialize(const std::string& str) noexcept +{ + static const auto size_t_max = std::numeric_limits::max(); + if (str.empty()) + + return false; + size_t pos{}; + while (pos < str.size()) + { + auto endpos{str.find(' ', pos)}; + if (endpos == std::string::npos) + endpos = str.size(); + //need a null-terminated char* to pass to permissible_value_index + auto index{permissible_value_index(str.substr(pos, endpos).c_str())}; + if (index == size_t_max) + return false; + m_value.push_back(index); + pos = endpos + 1; + } + return true; +} + +template std::string +GncOptionRangeValue::serialize() const noexcept +{ + if constexpr (std::is_arithmetic_v) + return std::to_string(m_value); + return ""; +} + +template bool +GncOptionRangeValue::deserialize(const std::string& str) noexcept +{ + if constexpr(is_same_decayed_v) + set_value(stoi(str)); + else if constexpr(is_same_decayed_v) + set_value(stod(str)); + return true; +} + +std::string +GncOptionDateValue::serialize() const noexcept +{ + std::string retval{"("}; + if (m_period == RelativeDatePeriod::ABSOLUTE) + { + retval += date_type_str[0]; + retval += " . "; + retval += std::to_string(m_date); + } + else + { + retval += date_type_str[1]; + retval += " . "; + retval += gnc_relative_date_storage_string(m_period); + } + retval += ")"; + return retval; +} + +bool +GncOptionDateValue::deserialize(const std::string& str) noexcept +{ + //The length of both "absolute" and "relative". + static constexpr size_t date_type_len{9}; + // date_type_len plus the length of " . ". + static constexpr size_t date_value_pos{12}; + auto type_str{str.substr(0, date_type_len)}; + auto period_str{str.substr(date_value_pos)}; + if (type_str == "absolute") + { + // Need a cast to disambiguate from time64. + set_value(static_cast(std::stoll(period_str))); + return true; + } + else if (type_str == "relative ") + { + auto period = gnc_relative_date_from_storage_string(period_str.c_str()); + if (period == RelativeDatePeriod::ABSOLUTE) + { + PWARN("Unknown period string in date option: '%s'", + period_str.c_str()); + return false; + } + + set_value(period); + return true; + } + else + { + PWARN("Unknown date type string in date option: '%s'", + type_str.c_str()); + return false; + } +} + +std::istream& +operator>> (std::istream& iss, GncOptionCommodityValue& opt) +{ + std::string instr; + iss >> instr; + if (!opt.deserialize(instr)) + throw std::invalid_argument("Invalid commodity string in stream."); + return iss; +} + +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template GncOptionValue::GncOptionValue(const GncOptionValue&); +template void GncOptionValue::set_value(bool); +template void GncOptionValue::set_value(int); +template void GncOptionValue::set_value(int64_t); +template void GncOptionValue::set_value(double); +template void GncOptionValue::set_value(char*); +template void GncOptionValue::set_value(const char*); +template void GncOptionValue::set_value(std::string); +template void GncOptionValue::set_value(const QofQuery*); +template void GncOptionValue::set_value(const GncOwner*); +template void GncOptionValue::set_value(RelativeDatePeriod); +template void GncOptionValue::set_value(size_t); +template void GncOptionValue::set_value(GncOptionAccountList); +template void GncOptionValue::set_value(GncMultichoiceOptionIndexVec); +template void GncOptionValue::set_value(GncOptionReportPlacementVec); +template void GncOptionValue::set_default_value(bool); +template void GncOptionValue::set_default_value(int); +template void GncOptionValue::set_default_value(int64_t); +template void GncOptionValue::set_default_value(double); +template void GncOptionValue::set_default_value(char*); +template void GncOptionValue::set_default_value(const char*); +template void GncOptionValue::set_default_value(std::string); +template void GncOptionValue::set_default_value(const QofQuery*); +template void GncOptionValue::set_default_value(const GncOwner*); +template void GncOptionValue::set_default_value(RelativeDatePeriod); +template void GncOptionValue::set_default_value(size_t); +template void GncOptionValue::set_default_value(GncOptionAccountList); +template void GncOptionValue::set_default_value(GncMultichoiceOptionIndexVec); +template void GncOptionValue::set_default_value(GncOptionReportPlacementVec); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template void GncOptionValue::reset_default_value(); +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionValue::serialize() const noexcept; +template std::string GncOptionRangeValue::serialize() const noexcept; +template std::string GncOptionRangeValue::serialize() const noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionRangeValue::deserialize(const std::string&) noexcept; +template bool GncOptionRangeValue::deserialize(const std::string&) noexcept; diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp new file mode 100644 index 0000000000..d126e3ea7a --- /dev/null +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -0,0 +1,1078 @@ +/********************************************************************\ + * gnc-option-impl.hpp -- Application options system * + * Copyright (C) 2019 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @addtogroup Engine + @{ */ +/** @addtogroup Options + @{ */ +/** @file gnc-option-impl.hpp + @brief Implementation templates and specializtions for GncOption values. + Objecte created by these templates are wrapped by the GncOption variant. + @author Copyright 2019-2021 John Ralls +*/ +#ifndef GNC_OPTION_IMPL_HPP_ +#define GNC_OPTION_IMPL_HPP_ + +#include "gnc-option.hpp" +extern "C" +{ +#include +#include +#include +#include +#include +} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gnc-option-uitype.hpp" + + +#ifndef SWIG +size_t constexpr classifier_size_max{50}; +size_t constexpr sort_tag_size_max{10}; +#endif + +/** @struct OptionClassifier + * This class is the parent of all option implmentations. It contains the + * elements that the optiondb uses to retrieve option values and that the + * options dialog determines on which tab to place the option, in what order, + * and what string to display as a tooltip. + */ +struct OptionClassifier +{ + std::string m_section; + std::string m_name; + std::string m_sort_tag; +// std::type_info m_kvp_type; + std::string m_doc_string; +}; + + +#ifndef SWIG +auto constexpr size_t_max = std::numeric_limits::max(); +#endif + +/** @class GncOptionValue + * The generic option-value class. Most option types can use this template. + */ +template +class GncOptionValue : public OptionClassifier +{ +public: + GncOptionValue(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, + GncOptionUIType ui_type = GncOptionUIType::INTERNAL) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type(ui_type), m_value{value}, m_default_value{value} { } + GncOptionValue(const GncOptionValue& from) : + OptionClassifier{from.m_section, from.m_name, from.m_sort_tag, + from.m_doc_string}, + m_ui_type(from.get_ui_type()), m_value{from.get_value()}, + m_default_value{from.get_default_value()}{} + GncOptionValue(GncOptionValue&&) = default; + GncOptionValue& operator=(const GncOptionValue&) = default; + GncOptionValue& operator=(GncOptionValue&&) = default; + ~GncOptionValue() = default; + ValueType get_value() const { return m_value; } + ValueType get_default_value() const { return m_default_value; } + void set_value(ValueType new_value); + void set_default_value(ValueType new_value); + void reset_default_value(); + bool is_changed() const noexcept { return m_value != m_default_value; } + GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } + void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } + std::string serialize() const noexcept; + bool deserialize(const std::string& str) noexcept; +private: + GncOptionUIType m_ui_type; + ValueType m_value; + ValueType m_default_value; +}; + +using GncItem = std::pair; + +class GncOptionQofInstanceValue: public OptionClassifier { +public: + GncOptionQofInstanceValue( + const char* section, const char* name, + const char* key, const char* doc_string, + const QofInstance* value, + GncOptionUIType ui_type = GncOptionUIType::INTERNAL); + GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from); + GncOptionQofInstanceValue(GncOptionQofInstanceValue&&) = default; + GncOptionQofInstanceValue& operator=(GncOptionQofInstanceValue&&) = default; + ~GncOptionQofInstanceValue() = default; + const QofInstance* get_value() const; + const QofInstance* get_default_value() const; + GncItem get_item() const { return m_value; } + GncItem get_default_item() const { return m_default_value; } + void set_value(const QofInstance* new_value); + void set_default_value(const QofInstance* new_value); + void reset_default_value(); + bool is_changed() const noexcept; + GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } + void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } + std::string serialize() const noexcept; + bool deserialize(const std::string& str) noexcept; +private: + GncOptionUIType m_ui_type; + GncItem m_value; + GncItem m_default_value; +}; + +/** class GncOptionCommodityValue + * Commodities are stored with their namespace and mnemonic instead of their gncGIUD + * so that they can be correctly retrieved even if they're deleted and recreated. + * Additionally if GncOptionCommodityValue is created with GncOptionUIType::CURRENCY + * it will throw std::invalid_argument if one attempts to set a value that isn't a + * currency. + */ + +class GncOptionCommodityValue : public OptionClassifier +{ +public: + GncOptionCommodityValue() = delete; + GncOptionCommodityValue(const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity* value, + GncOptionUIType ui_type = GncOptionUIType::COMMODITY) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type{ui_type}, m_is_currency{ui_type == GncOptionUIType::CURRENCY}, + m_namespace{gnc_commodity_get_namespace(value)}, + m_mnemonic{gnc_commodity_get_mnemonic(value)}, + m_default_namespace{gnc_commodity_get_namespace(value)}, + m_default_mnemonic{gnc_commodity_get_mnemonic(value)} + { + if (!validate(value)) + throw std::invalid_argument("Attempt to create GncOptionCommodityValue with currency UIType and non-currency value."); + } + GncOptionCommodityValue(const GncOptionCommodityValue&) = default; + GncOptionCommodityValue(GncOptionCommodityValue&&) = default; + GncOptionCommodityValue& operator=(const GncOptionCommodityValue&) = default; + GncOptionCommodityValue& operator=(GncOptionCommodityValue&&) = default; + gnc_commodity* get_value() const; + gnc_commodity* get_default_value() const; + bool validate(gnc_commodity*) const noexcept; + void set_value(gnc_commodity* value); + void set_default_value(gnc_commodity* value); + void reset_default_value(); + bool is_changed() const noexcept; + GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } + void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } + std::string serialize() const noexcept; + bool deserialize(const std::string& str) noexcept; +private: + GncOptionUIType m_ui_type; + bool m_is_currency; + std::string m_namespace; + std::string m_mnemonic; + std::string m_default_namespace; + std::string m_default_mnemonic; +}; + +QofInstance* qof_instance_from_string(const std::string& str, + GncOptionUIType type); +QofInstance* qof_instance_from_guid(GncGUID*, GncOptionUIType type); +std::string qof_instance_to_string(const QofInstance* inst); + +template +struct is_QofInstanceValue +{ + static constexpr bool value = + std::is_same_v, GncOptionQofInstanceValue>; +}; + +template inline constexpr bool +is_QofInstanceValue_v = is_QofInstanceValue::value; + +template +struct is_QofQueryValue +{ + static constexpr bool value = + std::is_same_v, GncOptionValue>; +}; + +template inline constexpr bool +is_QofQueryValue_v = is_QofQueryValue::value; + +/* These will work when m_value is a built-in class; GnuCash class and container + * values will need specialization unless they happen to define operators << and + * >>. + * Note that SWIG 3.0.12 chokes on elaborate enable_if so just hide the + * following templates from SWIG. (Ignoring doesn't work because SWIG still has + * to parse the templates to figure out the symbols. + */ +#ifndef SWIG +template && + ! (is_QofInstanceValue_v || + is_RangeValue_v), int> = 0> +std::ostream& operator<<(std::ostream& oss, const OptType& opt) +{ + oss << opt.get_value(); + return oss; +} + +template<> inline std::ostream& +operator<< >(std::ostream& oss, + const GncOptionValue& opt) +{ + oss << (opt.get_value() ? "#t" : "#f"); + return oss; +} + +inline std::ostream& +operator<< (std::ostream& oss, const GncOptionCommodityValue& opt) +{ + oss << opt.serialize(); + return oss; +} + +template, int> = 0> +inline std::ostream& +operator<< (std::ostream& oss, const OptType& opt) +{ + auto value = opt.get_value(); + oss << qof_instance_to_string(value); + return oss; +} + +template && + !(is_QofInstanceValue_v || + is_RangeValue_v), int> = 0> +std::istream& operator>>(std::istream& iss, OptType& opt) +{ + std::decay_t value; + if constexpr (std::is_same_v, const _gncOwner*> || + std::is_same_v, const _QofQuery*>) + return iss; + else + { + iss >> value; + opt.set_value(value); + return iss; + } +} + +std::istream& operator>> (std::istream& iss, GncOptionCommodityValue& opt); + +template, int> = 0> +std::istream& +operator>> (std::istream& iss, OptType& opt) +{ + std::string instr; + iss >> instr; + opt.set_value(qof_instance_from_string(instr, opt.get_ui_type())); + return iss; +} + +template<> inline std::istream& +operator>> >(std::istream& iss, + GncOptionValue& opt) +{ + std::string instr; + iss >> instr; + opt.set_value(instr == "#t" ? true : false); + return iss; +} + +template<> inline std::istream& +operator>> >(std::istream& iss, + GncOptionValue& opt) +{ + uint32_t id, wide, high; + iss >> id >> wide >> high; + opt.set_value(GncOptionReportPlacementVec{{id, wide, high}}); + return iss; +} +#endif // SWIG + +/** @class GncOptionRangeValue + * Used for numeric ranges and plot sizes. + */ + +template +class GncOptionRangeValue : public OptionClassifier +{ +public: + GncOptionRangeValue(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, ValueType min, + ValueType max, ValueType step) : + OptionClassifier{section, name, key, doc_string}, + m_value{value >= min && value <= max ? value : min}, + m_default_value{value >= min && value <= max ? value : min}, + m_min{min}, m_max{max}, m_step{step} {} + + GncOptionRangeValue(const GncOptionRangeValue&) = default; + GncOptionRangeValue(GncOptionRangeValue&&) = default; + GncOptionRangeValue& operator=(const GncOptionRangeValue&) = default; + GncOptionRangeValue& operator=(GncOptionRangeValue&&) = default; + ValueType get_value() const { return m_value; } + ValueType get_default_value() const { return m_default_value; } + bool validate(ValueType value) { return value >= m_min && value <= m_max; } + void set_value(ValueType value) + { + if (this->validate(value)) + m_value = value; + else + throw std::invalid_argument("Validation failed, value not set."); + } + void set_default_value(ValueType value) + { + if (this->validate(value)) + m_value = m_default_value = value; + else + throw std::invalid_argument("Validation failed, value not set."); + } + void get_limits(ValueType& upper, ValueType& lower, ValueType& step) const noexcept + { + upper = m_max; + lower = m_min; + step = m_step; + } + void reset_default_value() { m_value = m_default_value; } + bool is_changed() const noexcept { return m_value != m_default_value; } + GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } + void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } + bool is_alternate() const noexcept { return m_alternate; } + void set_alternate(bool value) noexcept { + if (m_ui_type == GncOptionUIType::PLOT_SIZE) + m_alternate = value; + } + std::string serialize() const noexcept; + bool deserialize(const std::string& str) noexcept; +private: + GncOptionUIType m_ui_type = GncOptionUIType::NUMBER_RANGE; + ValueType m_value; + ValueType m_default_value; + ValueType m_min; + ValueType m_max; + ValueType m_step; + bool m_alternate = false; +}; + +template, int> = 0> +inline std::ostream& +operator<< (std::ostream& oss, const OptType& opt) +{ + if (opt.get_ui_type() == GncOptionUIType::PLOT_SIZE) + oss << (opt.is_alternate() ? "pixels" : "percent") << " "; + oss << opt.get_value(); + return oss; +} + +template, int> = 0> +inline std::istream& +operator>> (std::istream& iss, OptType& opt) +{ + if (opt.get_ui_type() == GncOptionUIType::PLOT_SIZE) + { + std::string alt; + iss >> alt; + opt.set_alternate(strncmp(alt.c_str(), "percent", + strlen("percent")) == 0); + } + if constexpr (std::is_same_v, + GncOptionRangeValue>) + { + double d; + iss >> d; + opt.set_value(d); + } + else + { + int i; + iss >> i; + opt.set_value(i); + } + return iss; +} + +using GncMultichoiceOptionEntry = std::tuple; +using GncMultichoiceOptionIndexVec = std::vector; +using GncMultichoiceOptionChoices = std::vector; + +/** @class GncOptionMultichoiceValue + * Multichoice options have a vector of valid options + * (GncMultichoiceOptionChoices) and validate the selection as being one of + * those values. The value is the index of the selected item in the vector. + + * GncMultichoiceOptionEntry is a tuple of two strings and a + * GncOptionMultichoiceKeyType value; the first string is the internal value of + * the option, the second is the display name that should be localized at the + * point of use (so mark it with N_() or (N_ ) when creating the multichoices) + * and the third is an enum value indicating whether the key should be + * interpreted as a Scheme symbol, a string, or a number. + * + * + */ + +class GncOptionMultichoiceValue : public OptionClassifier +{ +public: + GncOptionMultichoiceValue(const char* section, const char* name, + const char* key, const char* doc_string, + const char* value, + GncMultichoiceOptionChoices&& choices, + GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type{ui_type}, + m_value{}, m_default_value{}, m_choices{std::move(choices)} + { + if (value) + { + if (auto index = find_key(value); + index != size_t_max) + { + m_value.push_back(index); + m_default_value.push_back(index); + } + } + } + + GncOptionMultichoiceValue(const char* section, const char* name, + const char* key, const char* doc_string, + size_t index, + GncMultichoiceOptionChoices&& choices, + GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type{ui_type}, + m_value{}, m_default_value{}, m_choices{std::move(choices)} + { + if (index < m_choices.size()) + { + m_value.push_back(index); + m_default_value.push_back(index); + } + } + + GncOptionMultichoiceValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncMultichoiceOptionIndexVec&& indices, + GncMultichoiceOptionChoices&& choices, + GncOptionUIType ui_type = GncOptionUIType::LIST) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type{ui_type}, + m_value{indices}, m_default_value{std::move(indices)}, + m_choices{std::move(choices)} {} + GncOptionMultichoiceValue(const GncOptionMultichoiceValue&) = default; + GncOptionMultichoiceValue(GncOptionMultichoiceValue&&) = default; + GncOptionMultichoiceValue& operator=(const GncOptionMultichoiceValue&) = default; + GncOptionMultichoiceValue& operator=(GncOptionMultichoiceValue&&) = default; + + const std::string& get_value() const + { + auto vec{m_value.size() > 0 ? m_value : m_default_value}; + if (vec.size() == 0) + return c_empty_string; + if (vec.size() == 1) + return std::get<0>(m_choices.at(vec[0])); + else + return c_list_string; + + } + const std::string& get_default_value() const + { + if (m_default_value.size() == 1) + return std::get<0>(m_choices.at(m_default_value[0])); + else if (m_default_value.size() == 0) + return c_empty_string; + else + return c_list_string; + } + + size_t get_index() const + { + if (m_value.size() > 0) + return m_value[0]; + if (m_default_value.size() > 0) + return m_default_value[0]; + return 0; + } + const GncMultichoiceOptionIndexVec& get_multiple() const noexcept + { + return m_value; + } + const GncMultichoiceOptionIndexVec& get_default_multiple() const noexcept + { + return m_default_value; + } + bool validate(const std::string& value) const noexcept + { + auto index = find_key(value); + return index != size_t_max; + + } + bool validate(const GncMultichoiceOptionIndexVec& indexes) const noexcept + { + for (auto index : indexes) + if (index >= m_choices.size()) + return false; + return true; + + } + void set_value(const std::string& value) + { + auto index = find_key(value); + if (index != size_t_max) + { + m_value.clear(); + m_value.push_back(index); + } + else + throw std::invalid_argument("Value not a valid choice."); + + } + void set_value(size_t index) + { + if (index < m_choices.size()) + { + m_value.clear(); + m_value.push_back(index); + } + else + throw std::invalid_argument("Value not a valid choice."); + + } + void set_default_value(const std::string& value) + { + auto index = find_key(value); + if (index != size_t_max) + { + m_value.clear(); + m_value.push_back(index); + m_default_value.clear(); + m_default_value.push_back(index); + } + else + throw std::invalid_argument("Value not a valid choice."); + + } + void set_default_value(size_t index) + { + if (index < m_choices.size()) + { + m_value.clear(); + m_value.push_back(index); + m_default_value.clear(); + m_default_value.push_back(index); + } + else + throw std::invalid_argument("Value not a valid choice."); + + } + void set_multiple(const GncMultichoiceOptionIndexVec& indexes) + { + if (validate(indexes)) + m_value = indexes; + else + throw std::invalid_argument("One of the supplied indexes was out of range."); + } + void set_default_multiple(const GncMultichoiceOptionIndexVec& indexes) + { + if (validate(indexes)) + m_value = m_default_value = indexes; + else + throw std::invalid_argument("One of the supplied indexes was out of range."); + } + std::size_t num_permissible_values() const noexcept + { + return m_choices.size(); + } + std::size_t permissible_value_index(const char* key) const noexcept + { + return find_key(key); + } + const char* permissible_value(std::size_t index) const + { + return std::get<0>(m_choices.at(index)).c_str(); + } + const char* permissible_value_name(std::size_t index) const + { + return std::get<1>(m_choices.at(index)).c_str(); + } + void reset_default_value() { m_value = m_default_value; } + bool is_changed() const noexcept { return m_value != m_default_value; } + GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } + void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } + GncOptionMultichoiceKeyType get_keytype(unsigned i) const { return std::get<2>(m_choices.at(i)); } + std::string serialize() const noexcept; + bool deserialize(const std::string& str) noexcept; +private: + std::size_t find_key (const std::string& key) const noexcept + { + auto iter = std::find_if(m_choices.begin(), m_choices.end(), + [key](auto choice) { + return std::get<0>(choice) == key; }); + if (iter != m_choices.end()) + return iter - m_choices.begin(); + else + return size_t_max; + + } + GncOptionUIType m_ui_type; + GncMultichoiceOptionIndexVec m_value; + GncMultichoiceOptionIndexVec m_default_value; + GncMultichoiceOptionChoices m_choices; + static const std::string c_empty_string; + static const std::string c_list_string; +}; + +template<> inline std::ostream& +operator<< (std::ostream& oss, + const GncOptionMultichoiceValue& opt) +{ + auto vec{opt.get_multiple()}; + bool first{true}; + for (auto index : vec) + { + if (first) + first = false; + else + oss << " "; + oss << opt.permissible_value(index); + } + return oss; +} + +template<> inline std::istream& +operator>> (std::istream& iss, + GncOptionMultichoiceValue& opt) +{ + GncMultichoiceOptionIndexVec values; + while (true) + { + std::string str; + std::getline(iss, str, ' '); + if (!str.empty()) + { + auto index = opt.permissible_value_index(str.c_str()); + if (index != size_t_max) + values.push_back(index); + else + { + std::string err = str + " is not one of "; + err += opt.m_name; + err += "'s permissible values."; + throw std::invalid_argument(err); + } + } + else + break; + } + opt.set_multiple(values); + iss.clear(); + return iss; +} + + +using GncOptionAccountList = std::vector; + +using GncOptionAccountTypeList = std::vector; + +/** @class GncOptionAccountListValue + * + * Set one or more accounts on which to report, optionally restricted to certain + * account types. Many calls to make-account-list-option will pass a get-default + * function that retrieves all of the accounts of a list of types. + * + * Some reports (examples/daily-reports.scm and standard/ account-piechart.scm, + * advanced-portfolio.scm, category-barchart.scm, net-charts.scm, and + * portfolio.scm) also provide a validator that rejects accounts that don't meet + * an account-type criterion. + * + * There are two types of option, account-list which permits more than one + * account selection and account-sel, which doesn't. + * + + */ + +class GncOptionAccountListValue : public OptionClassifier +{ +public: + GncOptionAccountListValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, bool multi=true) : + OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type}, + m_value{}, m_default_value{}, m_allowed{}, m_multiselect{multi} {} + + GncOptionAccountListValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + const GncOptionAccountList& value, bool multi=true) : + OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type}, + m_value{value}, m_default_value{std::move(value)}, m_allowed{}, + m_multiselect{multi} {} + GncOptionAccountListValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + GncOptionAccountTypeList&& allowed, bool multi=true) : + OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type}, + m_value{}, m_default_value{}, m_allowed{std::move(allowed)}, + m_multiselect{multi} {} + GncOptionAccountListValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed, bool multi=true) : + OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type}, + m_value{}, m_default_value{}, m_allowed{std::move(allowed)}, + m_multiselect{multi} { + if (!validate(value)) + throw std::invalid_argument("Supplied Value not in allowed set."); + m_value = value; + m_default_value = std::move(value); + } + + /* These aren't const& because if m_default_value hasn't been set + * get_default_value finds the first account that matches the allowed types + * and returns a GncOptionAccountList containing it. That's a stack variable + * and must be returned by value. + */ + GncOptionAccountList get_value() const; + GncOptionAccountList get_default_value() const; + bool validate (const GncOptionAccountList& values) const; + void set_value (GncOptionAccountList values) { + if (validate(values)) + //throw! + m_value = values; + } + void set_default_value (GncOptionAccountList values) { + if (validate(values)) + //throw! + m_value = m_default_value = values; + } + GList* account_type_list() const noexcept; + void reset_default_value() { m_value = m_default_value; } + bool is_changed() const noexcept; + GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } + void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } + bool is_multiselect() const noexcept { return m_multiselect; } + std::string serialize() const noexcept; + bool deserialize(const std::string& str) noexcept; +private: + GncOptionUIType m_ui_type; + GncOptionAccountList m_value; + GncOptionAccountList m_default_value; + GncOptionAccountTypeList m_allowed; + bool m_multiselect; +}; + +template<> inline std::ostream& +operator<< (std::ostream& oss, + const GncOptionAccountListValue& opt) +{ + auto values{opt.get_value()}; + bool first = true; + for (auto value : values) + { + if (first) + first = false; + else + oss << " "; + oss << guid_to_string(&value); + } + return oss; +} + +template<> inline std::istream& +operator>> (std::istream& iss, + GncOptionAccountListValue& opt) +{ + GncOptionAccountList values; + while (true) + { + std::string str; + std::getline(iss, str, ' '); + if (!str.empty()) + { + auto guid{qof_entity_get_guid(qof_instance_from_string(str, opt.get_ui_type()))}; + values.push_back(*guid); + } + else + break; + } + opt.set_value(values); + iss.clear(); + return iss; +} + +/* @class GncOptionAccountSelValue + * Like GncOptionAccountListValue but contains only a single account. + */ + +class GncOptionAccountSelValue : public OptionClassifier +{ +public: + GncOptionAccountSelValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type) : + OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type}, + m_value{*guid_null()}, m_default_value{*guid_null()}, m_allowed{} {} + + GncOptionAccountSelValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + const Account* value) : + OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type}, + m_value{*qof_entity_get_guid(value)}, + m_default_value{*qof_entity_get_guid(value)}, m_allowed{} {} + GncOptionAccountSelValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + GncOptionAccountTypeList&& allowed) : + OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type}, + m_value{*guid_null()}, m_default_value{*guid_null()}, + m_allowed{std::move(allowed)} {} + GncOptionAccountSelValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + const Account* value, + GncOptionAccountTypeList&& allowed) : + OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type}, + m_value{*guid_null()}, m_default_value{*guid_null()}, m_allowed{std::move(allowed)} { + if (!validate(value)) + throw std::invalid_argument("Supplied Value not in allowed set."); + m_value = m_default_value = *qof_entity_get_guid(value); + } + + const Account* get_value() const; + const Account* get_default_value() const; + bool validate (const Account* value) const; + void set_value (const Account* value) { + if (validate(value)) + { + auto guid{qof_entity_get_guid(value)}; + m_value = *guid; + } + //else throw + } + void set_default_value (const Account* value) { + if (validate(value)) + { + auto guid{qof_entity_get_guid(value)}; + m_value = m_default_value = *guid; + } + //else throw + } + GList* account_type_list() const noexcept; + void reset_default_value() { m_value = m_default_value; } + bool is_changed() const noexcept { return !guid_equal(&m_value, &m_default_value); } + GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } + void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } + std::string serialize() const noexcept; + bool deserialize(const std::string& str) noexcept; +private: + GncOptionUIType m_ui_type; + GncGUID m_value; + GncGUID m_default_value; + GncOptionAccountTypeList m_allowed; +}; + +template<> inline std::ostream& +operator<< (std::ostream& oss, + const GncOptionAccountSelValue& opt) +{ + auto value{opt.get_value()}; + oss << qof_instance_to_string(QOF_INSTANCE(value)); + return oss; +} + +template<> inline std::istream& +operator>> (std::istream& iss, + GncOptionAccountSelValue& opt) +{ + Account* value{nullptr}; + std::string str; + std::getline(iss, str, ' '); + if (!str.empty()) + value = (Account*)qof_instance_from_string(str, opt.get_ui_type()); + opt.set_value(value); + iss.clear(); + return iss; +} + +/** @class GncOptionDateValue + * A legal date value is a pair of either a RelativeDatePeriod, the absolute + * flag and a time64, or for legacy purposes the absolute flag and a timespec. + */ +/* +gnc-date-option-show-time? -- option_data[1] +gnc-date-option-get-subtype -- option_data[0] +gnc-date-option-value-type m_value +gnc-date-option-absolute-time m_type == RelativeDatePeriod::ABSOLUTE +gnc-date-option-relative-time m_type != RelativeDatePeriod::ABSOLUTE + */ + +class GncOptionDateValue : public OptionClassifier +{ +public: + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX}, + m_period{RelativeDatePeriod::TODAY}, + m_default_period{RelativeDatePeriod::TODAY}, + m_period_set{} {} + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, time64 time) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type{ui_type}, m_date{time}, m_default_date{time}, + m_period{RelativeDatePeriod::ABSOLUTE}, + m_default_period{RelativeDatePeriod::ABSOLUTE}, + m_period_set{} {} + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + RelativeDatePeriod period) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX}, + m_period{period}, m_default_period{period}, + m_period_set{} {} + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + const RelativeDatePeriodVec& period_set) : + OptionClassifier{section, name, key, doc_string}, + m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX}, + m_period{period_set.back()}, + m_default_period{period_set.back()}, + m_period_set{period_set} {} + GncOptionDateValue(const GncOptionDateValue&) = default; + GncOptionDateValue(GncOptionDateValue&&) = default; + GncOptionDateValue& operator=(const GncOptionDateValue&) = default; + GncOptionDateValue& operator=(GncOptionDateValue&&) = default; + time64 get_value() const noexcept; + time64 get_default_value() const noexcept; + RelativeDatePeriod get_period() const noexcept { return m_period; } + RelativeDatePeriod get_default_period() const noexcept { return m_default_period; } + size_t get_period_index() const noexcept; + size_t get_default_period_index() const noexcept; + std::ostream& out_stream(std::ostream& oss) const noexcept; + std::istream& in_stream(std::istream& iss); + bool validate(RelativeDatePeriod value); + bool validate(time64 time) { + if (time > MINTIME && time < MAXTIME) + return true; + return false; + } + void set_value(RelativeDatePeriod value) { + if (validate(value)) + { + m_period = value; + m_date = INT64_MAX; + } + } + void set_value(time64 time) { + if (validate(time)) + { + m_period = RelativeDatePeriod::ABSOLUTE; + m_date = time; + } + } + void set_value(size_t index) noexcept; + void set_default_value(RelativeDatePeriod value) { + if (validate(value)) + { + m_period = m_default_period = value; + m_date = m_default_date = INT64_MAX; + } + } + void set_default_value(time64 time) { + if (validate(time)) + { + m_period = m_default_period = RelativeDatePeriod::ABSOLUTE; + m_date = m_default_date = time; + } + } + std::size_t num_permissible_values() const noexcept + { + return m_period_set.size(); + } + std::size_t permissible_value_index(const char* key) const noexcept; + const char* permissible_value(std::size_t index) const + { + return gnc_relative_date_storage_string(m_period_set.at(index)); + } + const char* permissible_value_name(std::size_t index) const + { + return gnc_relative_date_display_string(m_period_set.at(index)); + } + void reset_default_value() { + m_period = m_default_period; + m_date = m_default_date; + } + bool is_changed() const noexcept { return m_period != m_default_period && + m_date != m_default_date; } + GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } + void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } + const RelativeDatePeriodVec& get_period_set() const { return m_period_set; } + std::string serialize() const noexcept; + bool deserialize(const std::string& str) noexcept; +private: + GncOptionUIType m_ui_type; + time64 m_date; + time64 m_default_date; + RelativeDatePeriod m_period; + RelativeDatePeriod m_default_period; + RelativeDatePeriodVec m_period_set; +}; + +template<> inline std::ostream& +operator<< (std::ostream& oss, + const GncOptionDateValue& opt) +{ + return opt.out_stream(oss); +} + +template<> inline std::istream& +operator>> (std::istream& iss, + GncOptionDateValue& opt) +{ + return opt.in_stream(iss); +} + + +#endif //GNC_OPTION_IMPL_HPP_ +/**@} + @} */ diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp new file mode 100644 index 0000000000..a40381bba7 --- /dev/null +++ b/libgnucash/app-utils/gnc-option-ui.hpp @@ -0,0 +1,63 @@ +/********************************************************************\ + * gnc-option-ui.hpp -- UI association for GncOption * + * Copyright (C) 2019 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +#ifndef GNC_OPTION_UI_HPP_ +#define GNC_OPTION_UI_HPP_ + +#include +#include "gnc-option-uitype.hpp" + +class GncOption; +class GncOptionUIItem; +using GncOptionUIItemPtr = std::unique_ptr; + +/** + * Holds a pointer to the UI item which will control the option and an enum + * representing the type of the option for dispatch purposes; all of that + * happens in gnucash/gnome-utils/dialog-options and + * gnucash/gnome/business-option-gnome. + * + * This class takes no ownership responsibility, so calling code is responsible + * for ensuring that the UI_Item is alive. For convenience the public + * clear_ui_item function can be used as a weak_ptr's destruction callback to + * ensure that the ptr will be nulled if the ui_item is destroyed elsewhere. + */ + +class GncOptionUIItem +{ +public: + GncOptionUIItem(GncOptionUIType type) : m_type{type} {} + virtual ~GncOptionUIItem() = default; + GncOptionUIType get_ui_type() const noexcept { return m_type; } + virtual void set_dirty(bool status) noexcept { m_dirty = status; } + virtual bool get_dirty() const noexcept { return m_dirty; } + virtual void set_selectable(bool selectable) const noexcept = 0; + virtual void clear_ui_item() = 0; + virtual void set_ui_item_from_option(GncOption& option) noexcept = 0; + virtual void set_option_from_ui_item(GncOption& option) noexcept = 0; +private: + GncOptionUIType m_type; + bool m_dirty = false; +}; + +#endif //GNC_OPTION_UI_HPP__ diff --git a/libgnucash/app-utils/test/test-app-utils.c b/libgnucash/app-utils/gnc-option-uitype.hpp similarity index 52% rename from libgnucash/app-utils/test/test-app-utils.c rename to libgnucash/app-utils/gnc-option-uitype.hpp index 17289e77fc..83b0f93477 100644 --- a/libgnucash/app-utils/test/test-app-utils.c +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -1,6 +1,6 @@ -/******************************************************************** - * test-app-utils.c: GLib g_test test execution file. * - * Copyright 2013 John Ralls * +/********************************************************************\ + * gnc-option-uitype.hpp -- UI Control Enum for GncOption * + * Copyright (C) 2020 John Ralls * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * @@ -18,38 +18,59 @@ * Free Software Foundation Voice: +1-617-542-5942 * * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * + * * \********************************************************************/ +#ifndef GNC_OPTION_UITYPE_HPP__ +#define GNC_OPTION_UITYPE_HPP__ -#include -#include -#include -#include +/** @addtogroup Engine + @{ */ +/** @addtogroup Options + @{ */ +/** @file gnc-option-uitype.hpp + @brief OptionUITypes + @author Copyright 2019-2021 John Ralls +*/ -extern void test_suite_option_util (void); -extern void test_suite_gnc_ui_util (void); - -static void -guile_main (void *closure, int argc, char **argv) +/** @enum GncOptionUIType + * Used by GncOptionClassifier to indicate to dialog-options what control + * should be displayed for the option. + */ +enum class GncOptionUIType : unsigned int { - int retval; - scm_c_use_module("gnucash app-utils"); + INTERNAL, + BOOLEAN, + STRING, + TEXT, + CURRENCY, + COMMODITY, + MULTICHOICE, + DATE_ABSOLUTE, + DATE_RELATIVE, + DATE_BOTH, + ACCOUNT_LIST, + ACCOUNT_SEL, + LIST, + NUMBER_RANGE, + COLOR, + FONT, + PLOT_SIZE, + BUDGET, + PIXMAP, + RADIOBUTTON, + DATE_FORMAT, + OWNER, + CUSTOMER, + VENDOR, + EMPLOYEE, + INVOICE, + JOB, + TAX_TABLE, + QUERY, + REPORT_PLACEMENT, + MAX_VALUE, //Nake sure this one is always last +}; - test_suite_option_util (); - test_suite_gnc_ui_util (); - retval = g_test_run (); - - exit (retval); -} - -int -main (int argc, char *argv[]) -{ - qof_init (); /* Initialize the GObject system */ - qof_log_init_filename_special ("stderr"); /* Init the log system */ - g_test_init (&argc, &argv, NULL); /* initialize test program */ - //qof_log_set_level("gnc", G_LOG_LEVEL_DEBUG); - g_test_bug_base("https://bugs.gnucash.org/show_bug.cgi?id="); /* init the bugzilla URL */ - g_setenv ("GNC_UNINSTALLED", "1", TRUE); - scm_boot_guile (argc, argv, guile_main, NULL); - return 0; -} +#endif // GNC_OPTION_UITYPE_H__ +/** @} + @} */ diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp new file mode 100644 index 0000000000..c113a6db6f --- /dev/null +++ b/libgnucash/app-utils/gnc-option.cpp @@ -0,0 +1,546 @@ +/********************************************************************\ + * gnc-option.cpp -- Application options system * + * Copyright (C) 2020 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +#include "gnc-option.hpp" +#include "gnc-option-impl.hpp" +#include "gnc-option-uitype.hpp" +#include "gnc-option-ui.hpp" + +static const char* log_module{"gnc.app-utils.gnc-option"}; + +extern "C" +{ +#include +} + +template , + int>> +GncOption::GncOption(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, GncOptionUIType ui_type) : + m_option{std::make_unique( + std::in_place_type>, + section, name, key, doc_string, value, ui_type)} +{ +} + +template ValueType +GncOption::get_value() const +{ + return std::visit( + [](const auto option)->ValueType { + if constexpr (is_same_decayed_v) + return option.get_value(); + if constexpr (is_same_decayed_v) + { + if constexpr (is_same_decayed_v) + return option.get_period(); + if constexpr (std::is_same_v) + return option.get_period_index(); + return ValueType{}; + } + if constexpr (is_same_decayed_v) + { + if constexpr (std::is_same_v) + return option.get_index(); + if constexpr (is_same_decayed_v) + return option.get_multiple(); + } + return ValueType {}; + }, *m_option); +} + +template ValueType +GncOption::get_default_value() const +{ + return std::visit( + [](const auto option)->ValueType { + if constexpr (is_same_decayed_v) + return option.get_default_value(); + if constexpr (is_same_decayed_v) + { + if constexpr (is_same_decayed_v) + return option.get_default_period(); + if constexpr (std::is_same_v) + return option.get_default_period_index(); + return ValueType{}; + } + if constexpr + (is_same_decayed_v && + is_same_decayed_v) + return option.get_default_multiple(); + return ValueType {}; + }, *m_option); + +} + +template void +GncOption::set_value(ValueType value) +{ + std::visit( + [value](auto& option) { + if constexpr + (is_same_decayed_v || + (is_same_decayed_v && + (is_same_decayed_v || + std::is_same_v))) + option.set_value(value); + if constexpr (is_same_decayed_v) + { + if constexpr (is_same_decayed_v) + option.set_multiple(value); + else if constexpr + (std::is_same_v || + is_same_decayed_v || + std::is_same_v, char*>) + option.set_value(value); + } + }, *m_option); +} + +template void +GncOption::set_default_value(ValueType value) +{ + std::visit( + [value](auto& option) { + if constexpr + (is_same_decayed_v|| + (is_same_decayed_v && + (is_same_decayed_v || + std::is_same_v))) + option.set_default_value(value); + if constexpr (is_same_decayed_v) + { + if constexpr (is_same_decayed_v) + option.set_default_multiple(value); + else if constexpr + (std::is_same_v || + is_same_decayed_v || + std::is_same_v, char*>) + option.set_default_value(value); + } + }, *m_option); +} +void +GncOption::reset_default_value() +{ + std::visit([](auto& option) { option.reset_default_value(); }, *m_option); +} + +template void +GncOption::get_limits(ValueType& max, ValueType& min, ValueType& step) const noexcept +{ + std::visit([&max, &min, &step](const auto& option) { + if constexpr + (is_same_decayed_v>) + option.get_limits(max, min, step); + }, *m_option); +} + +const std::string& +GncOption::get_section() const +{ + return std::visit([](const auto& option)->const std::string& { + return option.m_section; + }, *m_option); +} + +const std::string& +GncOption::get_name() const +{ + return std::visit([](const auto& option)->const std::string& { + return option.m_name; + }, *m_option); +} + +const std::string& +GncOption::get_key() const +{ + return std::visit([](const auto& option)->const std::string& { + return option.m_sort_tag; + }, *m_option); +} + +const std::string& +GncOption::get_docstring() const +{ + return std::visit([](const auto& option)->const std::string& { + return option.m_doc_string; + }, *m_option); +} + +void +GncOption::set_ui_item(GncOptionUIItemPtr&& ui_item) +{ + + auto opt_ui_type = std::visit([](const auto& option)->GncOptionUIType { + return option.get_ui_type(); + }, *m_option); + + if (ui_item->get_ui_type() != opt_ui_type) + { + PERR("Setting option %s:%s UI element failed, mismatched UI types.", + get_section().c_str(), get_name().c_str()); + return; + } + + m_ui_item = std::move(ui_item); +} + +void +GncOption::set_ui_item_selectable(bool selectable) const noexcept +{ + if (m_ui_item) + m_ui_item->set_selectable(selectable); +} + +const GncOptionUIType +GncOption::get_ui_type() const +{ + return std::visit([](const auto& option)->GncOptionUIType { + return option.get_ui_type(); + }, *m_option); +} + +GncOptionUIItem* const +GncOption::get_ui_item() const +{ + return m_ui_item.get(); +} + +void +GncOption::set_ui_item_from_option() +{ + if (!m_ui_item) + return; + m_ui_item->set_ui_item_from_option(*this); +} + +void +GncOption::set_option_from_ui_item() +{ + if (!m_ui_item) + return; + m_ui_item->set_option_from_ui_item(*this); +} + +void +GncOption::make_internal() +{ + if (m_ui_item) + { + PERR("Option %s:%s has a UI Element, can't be INTERNAL.", + get_section().c_str(), get_name().c_str()); + return; + } + std::visit([](auto& option) { + option.make_internal(); + }, *m_option); +} + +bool +GncOption::is_changed() const noexcept +{ + return std::visit([](const auto& option)->bool { + return option.is_changed(); + }, *m_option); +} + +bool +GncOption::is_multiselect() const noexcept +{ + return std::visit( + [](const auto& option)->bool { + if constexpr (is_same_decayed_v) + return option.is_multiselect(); + else + return false; + }, *m_option); +} + +template bool +GncOption::validate(ValueType value) const +{ + return std::visit( + [value] (const auto& option) -> bool { + if constexpr ((is_same_decayed_v && + is_same_decayed_v) || + (is_same_decayed_v && + is_same_decayed_v) || + (is_same_decayed_v && + is_same_decayed_v)) + return option.validate(value); + else + return false; + }, *m_option); +} + +std::size_t +GncOption::num_permissible_values() const +{ + return std::visit( + [] (const auto& option) -> size_t { + if constexpr (is_same_decayed_v || + is_same_decayed_v) + return option.num_permissible_values(); + else + return size_t_max; + }, *m_option); +} + +std::size_t +GncOption::permissible_value_index(const char* value) const +{ + return std::visit( + [&value] (const auto& option) -> size_t { + if constexpr (is_same_decayed_v || + is_same_decayed_v) + return option.permissible_value_index(value); + else + return size_t_max; + }, *m_option); +} + +const char* +GncOption::permissible_value(std::size_t index) const +{ + return std::visit([index] (const auto& option) -> const char* { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue> || + std::is_same_v, + GncOptionDateValue>) + return option.permissible_value(index); + else + return ""; + }, *m_option); +} + +const char* +GncOption::permissible_value_name(std::size_t index) const +{ + return std::visit([index] (const auto& option) -> const char* { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue> || + std::is_same_v, + GncOptionDateValue>) + return option.permissible_value_name(index); + else + return ""; + }, *m_option); +} + +GList* +GncOption::account_type_list() const noexcept +{ + return std::visit([] (const auto& option) -> GList* { + if constexpr (std::is_same_v, + GncOptionAccountListValue>) + return option.account_type_list(); + else + return nullptr; + }, *m_option); +} + +bool +GncOption::is_alternate() const noexcept +{ + return std::visit([](auto& option) -> bool { + if constexpr(is_RangeValue_v) + return option.is_alternate(); + return false; + }, *m_option); +} + +void +GncOption::set_alternate(bool alt) noexcept +{ + std::visit([alt](auto& option) { + if constexpr(is_RangeValue_v) + option.set_alternate(alt); + }, *m_option); +} + +std::string +GncOption::serialize() const +{ + if (m_option->valueless_by_exception()) + return "Valueless Option"; + return std::visit([&](auto& option) -> std::string { + return option.serialize(); + }, *m_option); +} + +bool +GncOption::deserialize(const std::string& str) +{ + return std::visit([&str](auto& option) -> bool { + return option.deserialize(str); + }, *m_option); +} + +std::istream& +GncOption::in_stream(std::istream& iss) +{ + return std::visit([&iss](auto& option) -> std::istream& { + iss >> option; + return iss; + }, *m_option); +} + +/* We must instantiate all of the templates we need here because we don't expose + * the template implementation in the public header. + */ + + +template GncOption::GncOption(const char*, const char*, const char*, + const char*, bool, GncOptionUIType); +//template GncOption::GncOption(const char*, const char*, const char*, +// const char*, int, GncOptionUIType); +template GncOption::GncOption(const char*, const char*, const char*, + const char*, int64_t, GncOptionUIType); +//template GncOption::GncOption(const char*, const char*, const char*, +// const char*, const char*, GncOptionUIType); +//template GncOption::GncOption(const char*, const char*, const char*, +// const char*, double, GncOptionUIType); +template GncOption::GncOption(const char*, const char*, const char*, + const char*, std::string, GncOptionUIType); +template GncOption::GncOption(const char*, const char*, const char*, + const char*, const QofQuery*, GncOptionUIType); +template GncOption::GncOption(const char*, const char*, const char*, + const char*, const GncOwner*, GncOptionUIType); + +template bool GncOption::get_value() const; +template int GncOption::get_value() const; +template int64_t GncOption::get_value() const; +template double GncOption::get_value() const; +template size_t GncOption::get_value() const; +template const char* GncOption::get_value() const; +template std::string GncOption::get_value() const; +template const QofInstance* GncOption::get_value() const; +template gnc_commodity* GncOption::get_value() const; +template const Account* GncOption::get_value() const; +template RelativeDatePeriod GncOption::get_value() const; +template GncOptionAccountList GncOption::get_value() const; +template GncMultichoiceOptionIndexVec GncOption::get_value() const; +template GncOptionReportPlacementVec GncOption::get_value() const; + +template bool GncOption::get_default_value() const; +template int GncOption::get_default_value() const; +template int64_t GncOption::get_default_value() const; +template double GncOption::get_default_value() const; +template const char* GncOption::get_default_value() const; +template std::string GncOption::get_default_value() const; +template const QofInstance* GncOption::get_default_value() const; +template gnc_commodity* GncOption::get_default_value() const; +template const Account* GncOption::get_default_value() const; +template RelativeDatePeriod GncOption::get_default_value() const; +template GncOptionAccountList GncOption::get_default_value() const; +template GncMultichoiceOptionIndexVec GncOption::get_default_value() const; +template GncOptionReportPlacementVec GncOption::get_default_value() const; + +template void GncOption::set_value(bool); +template void GncOption::set_value(int); +template void GncOption::set_value(int64_t); +template void GncOption::set_value(double); +template void GncOption::set_value(char*); +template void GncOption::set_value(const char*); +template void GncOption::set_value(std::string); +template void GncOption::set_value(const QofInstance*); +template void GncOption::set_value(gnc_commodity*); +template void GncOption::set_value(const Account*); +template void GncOption::set_value(RelativeDatePeriod); +template void GncOption::set_value(size_t); +template void GncOption::set_value(GncOptionAccountList); +template void GncOption::set_value(GncMultichoiceOptionIndexVec); +template void GncOption::set_value(GncOptionReportPlacementVec); + +template void GncOption::set_default_value(bool); +template void GncOption::set_default_value(int); +template void GncOption::set_default_value(int64_t); +template void GncOption::set_default_value(double); +template void GncOption::set_default_value(char*); +template void GncOption::set_default_value(const char*); +template void GncOption::set_default_value(std::string); +template void GncOption::set_default_value(const QofInstance*); +template void GncOption::set_default_value(const Account*); +template void GncOption::set_default_value(RelativeDatePeriod); +template void GncOption::set_default_value(size_t); +template void GncOption::set_default_value(GncOptionAccountList); +template void GncOption::set_default_value(GncMultichoiceOptionIndexVec); +template void GncOption::set_default_value(GncOptionReportPlacementVec); + +template void GncOption::get_limits(double&, double&, double&) const noexcept; +template void GncOption::get_limits(int&, int&, int&) const noexcept; +template bool GncOption::validate(bool) const; +template bool GncOption::validate(int) const; +template bool GncOption::validate(int64_t) const; +template bool GncOption::validate(double) const; +template bool GncOption::validate(const char*) const; +template bool GncOption::validate(std::string) const; +template bool GncOption::validate(const QofInstance*) const; +template bool GncOption::validate(gnc_commodity*) const; +template bool GncOption::validate(const Account*) const; +template bool GncOption::validate(const QofQuery*) const; +template bool GncOption::validate(RelativeDatePeriod) const; +template bool GncOption::validate(GncMultichoiceOptionIndexVec) const; +template bool GncOption::validate(GncOptionReportPlacementVec) const; + +template GncOption* gnc_make_option(const char*, + const char*, + const char*, + const char*, + const std::string&, + GncOptionUIType); +template GncOption* gnc_make_option(const char*, const char*, const char*, + const char*, bool, GncOptionUIType); +template GncOption* gnc_make_option(const char*, const char*, + const char*, const char*, int64_t, + GncOptionUIType); + + diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp new file mode 100644 index 0000000000..9cb97acff3 --- /dev/null +++ b/libgnucash/app-utils/gnc-option.hpp @@ -0,0 +1,261 @@ +/********************************************************************\ + * gnc-option.hpp -- Application options system * + * Copyright (C) 2020 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ +/** @addtogroup Engine + @{ */ +/** @addtogroup Options + @{ */ + +/** @file gnc-option.hpp + @brief C++ Public interface for individual options. + @author Copyright 2020-2021 John Ralls +*/ + +#ifndef GNC_OPTION_HPP_ +#define GNC_OPTION_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "gnc-option-ui.hpp" +#include "gnc-option-date.hpp" +#include + +struct OptionClassifier; +class GncOptionUIItem; +using GncOptionUIItemPtr = std::unique_ptr; +#ifndef SWIG //SWIG pulls in GncOwner from swig-engine. +struct _gncOwner; +using GncOwner = _gncOwner; +#endif +struct _QofQuery; +using QofQuery = _QofQuery; +struct QofInstance_s; +using QofInstance = QofInstance_s; +template class GncOptionValue; +class GncOptionQofInstanceValue; +class GncOptionAccountListValue; +class GncOptionAccountSelValue; +class GncOptionMultichoiceValue; +template class GncOptionRangeValue; +class GncOptionCommodityValue; +class GncOptionDateValue; +using GncOptionReportPlacement = std::tuple; +using GncOptionReportPlacementVec = std::vector; +template +struct is_OptionClassifier +{ + static constexpr bool value = + std::is_base_of_v>; +}; + +template inline constexpr bool +is_OptionClassifier_v = is_OptionClassifier::value; + +template +struct is_same_decayed +{ + static constexpr bool value = std::is_same_v, + std::decay_t>; +}; + +template inline constexpr bool +is_same_decayed_v = is_same_decayed::value; + +template +struct is_RangeValue +{ + static constexpr bool value = + (is_same_decayed_v> || + is_same_decayed_v>); +}; + +template inline constexpr bool +is_RangeValue_v = is_RangeValue::value; + + +using GncOptionVariant = std::variant, + GncOptionValue, + GncOptionValue, + GncOptionQofInstanceValue, + GncOptionValue, + GncOptionValue, + GncOptionValue, + GncOptionAccountListValue, + GncOptionAccountSelValue, + GncOptionMultichoiceValue, + GncOptionRangeValue, + GncOptionRangeValue, + GncOptionCommodityValue, + GncOptionDateValue>; + +using GncOptionVariantPtr = std::unique_ptr; + +enum class GncOptionMultichoiceKeyType +{ + SYMBOL, + STRING, + NUMBER, +}; + +/** @class GncOption + * @brief Represents the public interface for an option. + * Polymorphism is provided by a std::variant member containing GncOptionValue + * types. +*/ + +class GncOption +{ +public: + template , + int> = 0> + + GncOption(OptionType option) : + m_option{std::make_unique(option)} {} + template , + int> = 0> + GncOption(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, + GncOptionUIType ui_type = GncOptionUIType::INTERNAL); + template void set_value(ValueType value); + template void set_default_value(ValueType value); + template ValueType get_default_value() const; + template ValueType get_value() const; + void reset_default_value(); + + const std::string& get_section() const; + const std::string& get_name() const; + const std::string& get_key() const; + const std::string& get_docstring() const; + void set_ui_item(GncOptionUIItemPtr&& ui_elem); + const GncOptionUIType get_ui_type() const; + void set_ui_item_selectable(bool) const noexcept; + GncOptionUIItem* const get_ui_item() const; + void set_ui_item_from_option(); + void set_option_from_ui_item(); + void make_internal(); + bool is_changed() const noexcept; +/** @returns false unless m_option contains a GncOptionMultiselectValue or + * GncOptionAccountListValue for which multiple selections have been enabled. + */ + bool is_multiselect() const noexcept; +/** Implemented only for GncOptionNumericRange */ + template void get_limits(ValueType&, ValueType&, + ValueType&) const noexcept; +/** Not implemented for GncOptionValue. */ + template bool validate(ValueType value) const; +/** Implemented only for GncOptionMultiselectValue. */ + std::size_t num_permissible_values() const; +/** Implemented only for GncOptionMultiselectValue. */ + std::size_t permissible_value_index(const char* value) const; +/** Implemented only for GncOptionMultiselectValue. */ + const char* permissible_value(std::size_t index) const; +/** Implemented only for GncOptionMultiselectValue. */ + const char* permissible_value_name(std::size_t index) const; +/** Implemented only for GncOptionAccountListValue. */ + GList* account_type_list() const noexcept; + bool is_alternate() const noexcept; + void set_alternate(bool) noexcept; +/** Get a string suitable for storage representing the option's value. + * @return a std::string + */ + std::string serialize() const; +/** Set the option's value from a character sequence. + * @param str: The character sequence representing the value + * @return true if the value was set, false otherwise. + */ + bool deserialize(const std::string& str); +/** Set the option's value from an input stream + * @param iss: An input stream reference. + * @return the stream reference for chaining. + */ + std::istream& in_stream(std::istream& iss); + friend GncOptionVariant& swig_get_option(GncOption*); + +private: + inline static const std::string c_empty_string{""}; + GncOptionVariantPtr m_option; + GncOptionUIItemPtr m_ui_item{nullptr}; +}; + +inline bool +operator<(const GncOption& right, const GncOption& left) +{ + return right.get_key() < left.get_key(); +} + +inline std::ostream& +operator<<(std::ostream& oss, const GncOption& opt) +{ + oss << opt.serialize(); + return oss; +} + +inline std::istream& +operator>>(std::istream& iss, GncOption& opt) +{ + return opt.in_stream(iss); +} + +inline std::ostream& +output_color_value(std::ostream& oss, const std::string& value) +{ + oss << "'("; + oss << std::fixed << std::showpoint << std::setprecision(1); + auto len{value.length() > 8 ? 8 : value.length()}; + for (size_t i{}; i < len; i += 2) + { + oss << static_cast(stoi(value.substr(i, 2), nullptr, 16)); + if (i < 6) + oss << " "; + } + if (len < 8) + oss << 256.0; + oss << ")"; + return oss; +} + +/** + * Free function wrapping GncOption's constructor. The type of GncOptionValue to + * create is determined from the UI type. Some GncOptionValue types require more + * parameters for their constructors and can't be created with this function. + */ +template GncOption* +gnc_make_option(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, GncOptionUIType ui_type) +{ + return new GncOption(section, name, key, doc_string, value, ui_type); +} + +#endif //GNC_OPTION_HPP_ + +/** @} + @} */ diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp new file mode 100644 index 0000000000..5083531d95 --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -0,0 +1,191 @@ +/********************************************************************\ + * gnc-optiondb.hpp -- Collection of GncOption objects * + * Copyright (C) 2019 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ +/** @addtogroup Engine + @{ */ +/** @addtogroup Options + @{ */ +/** @file gnc-optiondb-impl.hpp + @brief Implementation details for GncOptionDB. + @author Copyright 2019-2021 John Ralls +*/ + +#ifndef GNC_OPTIONDB_P_HPP_ +#define GNC_OPTIONDB_P_HPP_ + +#include "gnc-option.hpp" +#include "gnc-option-impl.hpp" + +#include +#include +#include +#include +extern "C" +{ +#include +#include +#include +#include +#include +#include +#include +#include +} + +using GncOptionVec = std::vector; + +/** class GncOptionSection + * The upper-level classification implmentation. Contains the options for a + * section; sections are displayed as separate tabs on the option dialog. + */ +class GncOptionSection +{ + std::string m_name; + GncOptionVec m_options; +public: + GncOptionSection(const char* name) : m_name{name}, m_options{} {} + ~GncOptionSection() = default; + + void foreach_option(std::function func); + void foreach_option(std::function func) const; + const std::string& get_name() const noexcept { return m_name; } + size_t get_num_options() const noexcept { return m_options.size(); } + void add_option(GncOption&& option); + void remove_option(const char* name); + const GncOption* find_option(const char* name) const; +}; + +using GncOptionSectionPtr = std::shared_ptr; + +inline bool +operator<(const GncOptionSectionPtr& right, const GncOptionSectionPtr& left) +{ + return right->get_name() < left->get_name(); +} + +using GncOptionDBChangeCallback = void (*)(void* user_data); + +struct GncOptionDBCallback +{ + GncOptionDBCallback(size_t id, GncOptionDBChangeCallback func, + void* data) : + m_id{id}, m_func{func}, m_data{data} {} + ~GncOptionDBCallback() = default; + GncOptionDBCallback(const GncOptionDBCallback&) = delete; + GncOptionDBCallback(GncOptionDBCallback&&) = default; + GncOptionDBCallback& operator=(const GncOptionDBCallback&) = default; + GncOptionDBCallback& operator=(GncOptionDBCallback&&) = default; + + size_t m_id; + GncOptionDBChangeCallback m_func; + void* m_data; +}; + +using GncCallbackVec = std::vector; + +/** @class GncOptionDB + * Holds all of the options for a book, report, or stylesheet, organized by + * GncOptionSections. + */ +class GncOptionDB +{ +public: + GncOptionDB(); + GncOptionDB(QofBook* book); + ~GncOptionDB() = default; + +/* The non-const version can't be redirected to the const one because the + * function parameters are incompatible. + */ + void foreach_section(std::function func) + { + for (auto& section : m_sections) + func(section); + } + void foreach_section(std::function func) const + { + for (auto& section : m_sections) + func(section); + } + size_t num_sections() const noexcept { return m_sections.size(); } + bool get_changed() const noexcept { return m_dirty; } + void register_option(const char* section, GncOption&& option); + void register_option(const char* section, GncOption* option); + void unregister_option(const char* section, const char* name); + void set_default_section(const char* section); + const GncOptionSection* const get_default_section() const noexcept; + std::string lookup_string_option(const char* section, const char* name); + template + bool set_option(const char* section, const char* name, ValueType value) + { + try + { + auto option{find_option(section, name)}; + if (!option) + return false; + option->set_value(value); + return true; + } + catch(const std::invalid_argument& err) + { + printf("Set Failed: %s\n", err.what()); + return false; + } + } +// void set_selectable(const char* section, const char* name); + void make_internal(const char* section, const char* name); + void commit() {}; + GncOptionSection* find_section(const std::string& sectname) + { + return const_cast(static_cast(*this).find_section(sectname)); + } + const GncOptionSection* find_section(const std::string& sectname) const; + GncOption* find_option(const std::string& section, const char* name) + { + return const_cast(static_cast(*this).find_option(section, name)); + } + const GncOption* find_option(const std::string& section, const char* name) const; + std::ostream& save_to_key_value(std::ostream& oss) const noexcept; + std::istream& load_from_key_value(std::istream& iss); + void save_to_kvp(QofBook* book, bool clear_book) const noexcept; + void load_from_kvp(QofBook* book) noexcept; + std::ostream& save_option_key_value(std::ostream& oss, + const std::string& section, + const std::string& name) const noexcept; + std::istream& load_option_key_value(std::istream& iss); + size_t register_callback(GncOptionDBChangeCallback, void*); + void unregister_callback(size_t); + void run_callbacks(); +private: + GncOptionSection* m_default_section; + std::vector m_sections; + bool m_dirty = false; + GncCallbackVec m_callbacks{}; + + std::function m_get_ui_value; + std::function m_set_ui_value; +}; + + +#endif // GNC_OPTIONDB_P_HPP_ +/** @} + @} */ diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp new file mode 100644 index 0000000000..b36a1ba605 --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -0,0 +1,1251 @@ +/********************************************************************\ + * gnc-optiondb.cpp -- Collection of GncOption objects * + * Copyright (C) 2019 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "gnc-optiondb.h" +#include "gnc-optiondb.hpp" +#include "gnc-optiondb-impl.hpp" +#include "gnc-option-ui.hpp" + +constexpr const char* log_module{G_LOG_DOMAIN}; + +constexpr auto stream_max = std::numeric_limits::max(); +using AliasedOption = std::pair; +using OptionAlias = std::pair; +using OptionAliases = std::vector; +class Aliases +{ + static const OptionAliases c_option_aliases; +public: + static const AliasedOption* find_alias (const char* old_name) + { + if (!old_name) return nullptr; + const auto alias = + std::find_if(c_option_aliases.begin(), c_option_aliases.end(), + [old_name](auto alias){ + return std::strcmp(old_name, alias.first) == 0; + }); + if (alias == c_option_aliases.end()) + return nullptr; + + return &alias->second; + } +}; + +const OptionAliases Aliases::c_option_aliases +{ + {"Accounts to include", {nullptr, "Accounts"}}, + {"Exclude transactions between selected accounts?", + {nullptr, "Exclude transactions between selected accounts"}}, + {"Filter Accounts", {nullptr, "Filter By..."}}, + {"Flatten list to depth limit?", + {nullptr, "Flatten list to depth limit"}}, + {"From", {nullptr, "Start Date"}}, + {"Report Accounts", {nullptr, "Accounts"}}, + {"Report Currency", {nullptr, "Report's currency"}}, + {"Show Account Code?", {nullptr, "Show Account Code"}}, + {"Show Full Account Name?", {nullptr, "Show Full Account Name"}}, + {"Show Multi-currency Totals?", + {nullptr, "Show Multi-currency Totals"}}, + {"Show zero balance items?", {nullptr, "Show zero balance items"}}, + {"Sign Reverses?", {nullptr, "Sign Reverses"}}, + {"To", {nullptr, "End Date"}}, + {"Charge Type", {nullptr, "Action"}}, // easy-invoice.scm, renamed June 2018 + // the following 4 options in income-gst-statement.scm renamed Dec 2018 + {"Individual income columns", {nullptr, "Individual sales columns"}}, + {"Individual expense columns", + {nullptr, "Individual purchases columns"}}, + {"Remittance amount", {nullptr, "Gross Balance"}}, + {"Net Income", {nullptr, "Net Balance"}}, + // transaction.scm: + {"Use Full Account Name?", {nullptr, "Use Full Account Name"}}, + {"Use Full Other Account Name?", + {nullptr, "Use Full Other Account Name"}}, + {"Void Transactions?", {"Filter", "Void Transactions"}}, + {"Void Transactions", {"Filter", "Void Transactions"}}, + {"Account Substring", {"Filter", "Account Name Filter"}}, + {"Enable links", {nullptr, "Enable Links"}}, + // trep-engine: moved currency options to own tab + {"Common Currency", {"Currency", "Common Currency"}}, + {"Show original currency amount", + {"Currency", "Show original currency amount"}}, + {"Report's currency", {"Currency", "Report's currency"}}, + {"Reconcile Status", {nullptr, "Reconciled Status"}}, + // new-owner-report.scm, renamed Oct 2020 to differentiate with + // Document Links: + {"Links", {nullptr, "Transaction Links"}}, + // invoice.scm, renamed November 2018 + {"Individual Taxes", {nullptr, "Use Detailed Tax Summary"}}, + {"Show Accounts until level", {nullptr, "Levels of Subaccounts"}}, + {"Invoice number", {nullptr, "Invoice Number"}}, + {"Report title", {nullptr, "Report Title"}}, + {"Extra notes", {nullptr, "Extra Notes"}}, + // income-gst-statement.scm + {"default format", {nullptr, "Default Format"}}, + {"Report format", {nullptr, "Report Format"}}, +}; + +static bool +operator==(const std::string& str, const char* cstr) +{ + return strcmp(str.c_str(), cstr) == 0; +} + +void +GncOptionSection::foreach_option(std::function func) +{ + std::for_each(m_options.begin(), m_options.end(), func); +} + +void +GncOptionSection::foreach_option(std::function func) const +{ + std::for_each(m_options.begin(), m_options.end(), func); +} + +void +GncOptionSection::add_option(GncOption&& option) +{ + m_options.push_back(std::move(option)); + if (!std::is_sorted(m_options.begin(), m_options.end())) + std::sort(m_options.begin(), m_options.end()); +} + +void +GncOptionSection::remove_option(const char* name) +{ + m_options.erase(std::remove_if(m_options.begin(), m_options.end(), + [name](const auto& option) -> bool + { + return option.get_name() == name; + })); +} + +const GncOption* +GncOptionSection::find_option(const char* name) const +{ + auto option = std::find_if(m_options.begin(), m_options.end(), + [name](auto& option) -> bool { + return option.get_name() == name; + }); + if (option != m_options.end()) + return &*option; + + auto alias = Aliases::find_alias(name); + if (!alias || alias->first) // No alias or the alias + return nullptr; // is in a different section. + return find_option(alias->second); +} + +GncOptionDB::GncOptionDB() : m_default_section{} {} + +GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {} + +void +GncOptionDB::register_option(const char* sectname, GncOption&& option) +{ + auto section = find_section(sectname); + + if (section) + { + section->add_option(std::move(option)); + return; + } + + m_sections.push_back(std::make_shared(sectname)); + m_sections.back()->add_option(std::move(option)); + if (!std::is_sorted(m_sections.begin(), m_sections.end())) + std::sort(m_sections.begin(), m_sections.end()); +} + +void +GncOptionDB::register_option(const char* sectname, GncOption* option) +{ + register_option(sectname, std::move(*option)); + delete option; +} + +void +GncOptionDB::unregister_option(const char* sectname, const char* name) +{ + auto section = find_section(sectname); + if (section) + section->remove_option(name); +} + +void +GncOptionDB::set_default_section(const char* sectname) +{ + m_default_section = find_section(sectname); +} + +const GncOptionSection* const +GncOptionDB::get_default_section() const noexcept +{ + return m_default_section; +} + +const GncOptionSection* +GncOptionDB::find_section(const std::string& section) const +{ + auto db_section = std::find_if(m_sections.begin(), m_sections.end(), + [§ion](auto& sect) -> bool + { + return section == sect->get_name(); + }); + return db_section == m_sections.end() ? nullptr : db_section->get(); +} + +const GncOption* +GncOptionDB::find_option(const std::string& section, const char* name) const +{ + auto db_section = const_cast(this)->find_section(section); + const GncOption* option = nullptr; + if (db_section) + option = db_section->find_option(name); + if (option) + return option; + auto alias = Aliases::find_alias(name); + /* Only try again if alias.first isn't + * nullptr. GncOptionSection::find_option already checked if the alias + * should have been in the same section. + */ + if (alias && alias->first && section != alias->first) + return find_option(alias->first, alias->second); + return nullptr; +} + +std::string +GncOptionDB::lookup_string_option(const char* section, const char* name) +{ + static const std::string empty_string{}; + + auto db_opt = find_option(section, name); + if (!db_opt) + return empty_string; + return db_opt->get_value(); +} + +void +GncOptionDB::make_internal(const char* section, const char* name) +{ + + auto db_opt = find_option(section, name); + if (db_opt) + db_opt->make_internal(); +} + +static inline bool constexpr +is_eol(char c) +{ + return c == '\n'; +} + +static inline bool constexpr +is_whitespace(char c) +{ + return c == ' ' || c == '\n' || c == '\t'; +} + +static inline bool constexpr +is_begin_paren(char c) +{ + return c == '('; +} + +static inline bool constexpr +is_end_paren(char c) +{ + return c == ')'; +} + +static inline bool constexpr +is_double_quote(char c) +{ + return c == '"'; +} + +static inline bool constexpr +is_single_quote(char c) +{ + return c == '\''; +} + +static inline bool constexpr +is_semicolon(char c) +{ + return c == ';'; +} + +static inline bool constexpr +is_delim(char c) +{ + return is_begin_paren(c) || is_end_paren(c) || is_whitespace(c) || + is_single_quote(c) || is_double_quote(c) || is_semicolon(c); +} + +std::ostream& +GncOptionDB::save_option_key_value(std::ostream& oss, + const std::string& section, + const std::string& name) const noexcept +{ + + auto db_opt = find_option(section, name.c_str()); + if (!db_opt || !db_opt->is_changed()) + return oss; + oss << section.substr(0, classifier_size_max) << ":" << + name.substr(0, classifier_size_max) << "=" << *db_opt << ";"; + return oss; +} + +std::istream& +GncOptionDB::load_option_key_value(std::istream& iss) +{ + + char section[classifier_size_max], name[classifier_size_max]; + iss.getline(section, classifier_size_max, ':'); + iss.getline(name, classifier_size_max, '='); + if (!iss) + throw std::invalid_argument("Section or name delimiter not found or values too long"); + auto option = find_option(section, name); + if (!option) + iss.ignore(stream_max, ';'); + else + { + std::string value; + std::getline(iss, value, ';'); + std::istringstream item_iss{value}; + item_iss >> *option; + } + return iss; +} + +std::ostream& +GncOptionDB::save_to_key_value(std::ostream& oss) const noexcept +{ + + foreach_section( + [&oss](const GncOptionSectionPtr& section) + { + oss << "[Options]\n"; + section->foreach_option( + [&oss, §ion](auto& option) + { + if (option.is_changed()) + oss << section->get_name().substr(0, classifier_size_max) << + ':' << option.get_name().substr(0, classifier_size_max) << + '=' << option << '\n'; + }); + }); + return oss; +} + +std::istream& +GncOptionDB::load_from_key_value(std::istream& iss) +{ + if (iss.peek() == '[') + { + char buf[classifier_size_max]; + iss.getline(buf, classifier_size_max); + if (strcmp(buf, "[Options]") != 0) // safe + throw std::runtime_error("Wrong secion header for options."); + } + // Otherwise assume we were sent here correctly: + while (iss.peek() != '[') //Indicates the start of the next file section + { + load_option_key_value(iss); + } + return iss; +} + +size_t +GncOptionDB::register_callback(GncOptionDBChangeCallback cb, void* data) +{ + constexpr std::hash cb_hash; + auto id{cb_hash(cb)}; + if (std::find_if(m_callbacks.begin(), m_callbacks.end(), + [id](auto&cb)->bool{ return cb.m_id == id; }) == m_callbacks.end()) + m_callbacks.emplace_back(id, cb, data); + return id; +} + +void +GncOptionDB::unregister_callback(size_t id) +{ + std::remove_if(m_callbacks.begin(), m_callbacks.end(), + [id](auto& cb)->bool { return cb.m_id == id; }); +} + +void +GncOptionDB::run_callbacks() +{ + std::for_each(m_callbacks.begin(), m_callbacks.end(), + [](auto& cb)->void { cb.m_func(cb.m_data); }); +} + +static inline void +counter_option_path(const GncOption& option, GSList* list, std::string& name) +{ + constexpr const char* counters{"counters"}; + constexpr const char* formats{"counter_formats/"}; + auto key = option.get_key(); + name = key.substr(0, key.size() - 1); + list->next->data = (void*)name.c_str(); + if (option.get_name().rfind("format") + != std::string::npos) + list->data = (void*)formats; + else + list->data = (void*)counters; +} + +static inline void +option_path(const GncOption& option, GSList* list) +{ + list->next->data = (void*)option.get_name().c_str(); + list->data = (void*)option.get_section().c_str(); +} + +static inline KvpValue* +kvp_value_from_bool_option(const GncOption& option) +{ + auto val{option.template get_value()}; + // ~KvpValue will g_free the value. + return new KvpValue(val ? g_strdup("t") : g_strdup("f")); +} + +static bool +is_qofinstance_ui_type(GncOptionUIType type) +{ + switch (type) + { + case GncOptionUIType::ACCOUNT_SEL: + case GncOptionUIType::BUDGET: + case GncOptionUIType::OWNER: + case GncOptionUIType::CUSTOMER: + case GncOptionUIType::VENDOR: + case GncOptionUIType::EMPLOYEE: + case GncOptionUIType::INVOICE: + case GncOptionUIType::TAX_TABLE: + case GncOptionUIType::QUERY: + return true; + default: + return false; + } +} + +static inline KvpValue* +kvp_value_from_qof_instance_option(const GncOption& option) +{ + const QofInstance* inst{QOF_INSTANCE(option.template get_value())}; + auto guid = guid_copy(qof_instance_get_guid(inst)); + return new KvpValue(guid); +} + +void +GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept +{ + if (clear_options) + qof_book_options_delete(book, nullptr); + const_cast(this)->foreach_section( + [book](GncOptionSectionPtr& section) + { + section->foreach_option( + [book, §ion](auto& option) { + if (option.is_changed()) + { + /* We need the string name out here so that it stays in + * scope long enough to pass its c_str to + * gnc_book_set_option. */ + std::string name; + /* qof_book_set_option wants a GSList path. Let's avoid + * allocating and make one here. */ + GSList list_tail{}, list_head{nullptr, &list_tail}; + if (strcmp(section->get_name().c_str(), "Counters") == 0) + counter_option_path(option, &list_head, name); + else + option_path(option, &list_head); + auto type{option.get_ui_type()}; + KvpValue* kvp{}; + if (type == GncOptionUIType::BOOLEAN) + kvp = kvp_value_from_bool_option(option); + else if (is_qofinstance_ui_type(type)) + kvp = kvp_value_from_qof_instance_option(option); + else if (type == GncOptionUIType::NUMBER_RANGE) + /* The Gtk control uses a double so that's what we + * have to store. */ + kvp = new KvpValue(option.template get_value()); + else + kvp = new KvpValue{g_strdup(option.template get_value().c_str())}; + qof_book_set_option(book, kvp, &list_head); + } + }); + }); +} + +static inline void +fill_option_from_string_kvp(GncOption& option, KvpValue* kvp) +{ + auto str{kvp->get()}; + if (option.get_ui_type() == GncOptionUIType::BOOLEAN) + option.set_value(*str == 't' ? true : false); + else + option.set_value(std::string{str}); +} + +static inline void +fill_option_from_guid_kvp(GncOption& option, KvpValue* kvp) +{ + auto guid{kvp->get()}; + option.set_value( + (const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type())); +} + +void +GncOptionDB::load_from_kvp(QofBook* book) noexcept +{ + foreach_section( + [book](GncOptionSectionPtr& section) + { + section->foreach_option( + [book, §ion](GncOption& option) + { + // Make path list as above. + std::string name; + /* qof_book_set_option wants a GSList path. Let's avoid + * allocating and make one here. */ + GSList list_tail{}, list_head{nullptr, &list_tail}; + if (strcmp(section->get_name().c_str(), "Counters") == 0) + counter_option_path(option, &list_head, name); + else + option_path(option, &list_head); + auto kvp = qof_book_get_option(book, &list_head); + if (!kvp) + return; + switch (kvp->get_type()) + { + case KvpValue::Type::DOUBLE: + option.set_value(kvp->get()); + break; + case KvpValue::Type::INT64: + option.set_value(kvp->get()); + break; + case KvpValue::Type::STRING: + fill_option_from_string_kvp(option, kvp); + break; + case KvpValue::Type::GUID: + fill_option_from_guid_kvp(option, kvp); + break; + default: + return; + break; + } + }); + }); +} + +void +gnc_register_string_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::STRING}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_text_option(GncOptionDB* db, const char* section, const char* name, + const char* key, const char* doc_string, + std::string value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::TEXT}; + db->register_option(section, std::move(option)); + +} + +void +gnc_register_font_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::FONT}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_budget_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, GncBudget *value) +{ + GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string, + (const QofInstance*)value, + GncOptionUIType::BUDGET}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_color_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::COLOR}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_commodity_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, gnc_commodity *value) +{ + GncOption option{GncOptionCommodityValue{section, name, key, doc_string, + value, + GncOptionUIType::COMMODITY}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_simple_boolean_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + bool value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::BOOLEAN}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_complex_boolean_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + bool value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::BOOLEAN}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_pixmap_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::PIXMAP}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_account_list_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, + const GncOptionAccountList& value) +{ + GncOption option{GncOptionAccountListValue{section, name, key, doc_string, + GncOptionUIType::ACCOUNT_LIST, value}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_account_list_limited_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, + const char* doc_string, + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed) +{ + try + { + std::cout << "gnc_register_account_list_limited_option for accounts "; + std::for_each(value.begin(), value.end(), [](auto& guid){ + std::cout << gnc::GUID(guid).to_string() << " "; + }); + std::cout << std::endl; + GncOption option{GncOptionAccountListValue{section, name, key, doc_string, + GncOptionUIType::ACCOUNT_LIST, value, std::move(allowed)}}; + db->register_option(section, std::move(option)); + } + catch (const std::invalid_argument& err) + { + std::cerr << "Account List Limited Option, value failed validation, option not registered.\n"; + } +} + +using AccountPair = std::pair; +static void +find_children(Account* account, void* data) +{ + auto datapair = + (AccountPair*)data; + GncOptionAccountList& list = datapair->first; + const GncOptionAccountTypeList& types = datapair->second; + if (std::find(types.begin(), types.end(), + xaccAccountGetType(account)) != types.end()) + list.push_back(*qof_entity_get_guid(account)); +} + +GncOptionAccountList +gnc_account_list_from_types(QofBook *book, + const GncOptionAccountTypeList& types) +{ + GncOptionAccountList list; + AccountPair funcdata{list, types}; + Account* base_acct = gnc_book_get_root_account(book); + gnc_account_foreach_descendant(base_acct, (AccountCb)find_children, + &funcdata); + return list; +} + + +void +gnc_register_account_sel_limited_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + const Account* value, + GncOptionAccountTypeList&& allowed) +{ + try + { + GncOption option{GncOptionAccountSelValue{section, name, key, doc_string, + GncOptionUIType::ACCOUNT_SEL, value, std::move(allowed)}}; + db->register_option(section, std::move(option)); + } + catch (const std::invalid_argument& err) + { + std::cerr <<"Account Sel Limited Option, value failed validation, option not registerd.\n"; + } +} + +void +gnc_register_multichoice_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, const char* default_val, + GncMultichoiceOptionChoices&& choices) +{ + std::string defval{default_val}; + auto found{std::find_if(choices.begin(), choices.end(), + [&defval](auto& choice)->bool { + return defval == std::get<0>(choice); + })}; + if (found == choices.end()) + defval = (choices.empty() ? std::string{"None"} : + std::get<0>(choices.at(0))); + GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string, + defval.c_str(), std::move(choices)}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_list_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, const char* value, + GncMultichoiceOptionChoices&& list) +{ + GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string, + value, std::move(list), GncOptionUIType::LIST}}; + db->register_option(section, std::move(option)); +} + +/* Only balance-forecast.scm, hello-world.scm, and net-charts.scm + * use decimals and fractional steps and they can be worked around. + */ +template void +gnc_register_number_range_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, ValueType value, + ValueType min, ValueType max, ValueType step) +{ + try + { + GncOption option{GncOptionRangeValue{section, name, key, + doc_string, value, min, + max, step}}; + db->register_option(section, std::move(option)); + } + catch(const std::invalid_argument& err) + { + std::cerr <<"Number Range Option " << err.what() << ", option not registerd.\n"; + } +} + +void +gnc_register_number_plot_size_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + int value) +{ + // Pixel values don't make much sense so always use percent. + GncOption option{GncOptionRangeValue{section, name, key, doc_string, + value, 10, 100, 1}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_query_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, const QofQuery* value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::INTERNAL}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_owner_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, const GncOwner* value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::INTERNAL}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_invoice_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, GncInvoice* value) +{ + GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string, + (const QofInstance*)value, + GncOptionUIType::INVOICE}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_taxtable_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, GncTaxTable* value) +{ + GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string, + (const QofInstance*)value, + GncOptionUIType::TAX_TABLE}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_counter_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, double value) +{ + GncOption option{GncOptionRangeValue{section, name, key, doc_string, + value, 0.0, 999999999.0, 1.0}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_counter_format_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + std::string value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::STRING}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_dateformat_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::DATE_FORMAT}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_currency_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, gnc_commodity *value) +{ + GncOption option{GncOptionCommodityValue{ + section, name, key, doc_string, value,GncOptionUIType::CURRENCY + }}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_date_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, time64 time, + RelativeDateUI ui) +{ + auto ui_type = ui == RelativeDateUI::BOTH ? GncOptionUIType::DATE_BOTH : + ui == RelativeDateUI::RELATIVE ? GncOptionUIType::DATE_RELATIVE : + GncOptionUIType::DATE_ABSOLUTE; + GncOption option{GncOptionDateValue(section, name, key, doc_string, + ui_type, time)}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_date_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, RelativeDatePeriod period, + RelativeDateUI ui) +{ + auto ui_type = ui == RelativeDateUI::BOTH ? GncOptionUIType::DATE_BOTH : + ui == RelativeDateUI::RELATIVE ? GncOptionUIType::DATE_RELATIVE : + GncOptionUIType::DATE_ABSOLUTE; + GncOption option{GncOptionDateValue(section, name, key, doc_string, + ui_type, period)}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_date_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + RelativeDatePeriodVec& period_set, + bool both) +{ + auto ui_type = both ? GncOptionUIType::DATE_BOTH : + GncOptionUIType::DATE_RELATIVE; + GncOption option{GncOptionDateValue(section, name, key, doc_string, + ui_type, period_set)}; + db->register_option(section, std::move(option)); +} + + +static const RelativeDatePeriodVec begin_dates +{ + RelativeDatePeriod::TODAY, + RelativeDatePeriod::START_THIS_MONTH, + RelativeDatePeriod::START_PREV_MONTH, + RelativeDatePeriod::START_CURRENT_QUARTER, + RelativeDatePeriod::START_PREV_QUARTER, + RelativeDatePeriod::START_CAL_YEAR, + RelativeDatePeriod::START_PREV_YEAR, + RelativeDatePeriod::START_ACCOUNTING_PERIOD +}; + +void +gnc_register_start_date_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, bool both) +{ + auto ui_type = both ? GncOptionUIType::DATE_BOTH : + GncOptionUIType::DATE_RELATIVE; + GncOption option{GncOptionDateValue(section, name, key, doc_string, + ui_type, begin_dates)}; + db->register_option(section, std::move(option)); +} + +static const RelativeDatePeriodVec end_dates +{ + RelativeDatePeriod::TODAY, + RelativeDatePeriod::END_THIS_MONTH, + RelativeDatePeriod::END_PREV_MONTH, + RelativeDatePeriod::END_CURRENT_QUARTER, + RelativeDatePeriod::END_PREV_QUARTER, + RelativeDatePeriod::END_CAL_YEAR, + RelativeDatePeriod::END_PREV_YEAR, + RelativeDatePeriod::END_ACCOUNTING_PERIOD +}; + +void +gnc_register_end_date_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, bool both) +{ + auto ui_type = both ? GncOptionUIType::DATE_BOTH : + GncOptionUIType::DATE_RELATIVE; + GncOption option{GncOptionDateValue(section, name, key, doc_string, + ui_type, end_dates)}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_report_placement_option(GncOptionDBPtr& db, + const char* section, const char* name) +{ + /* This is a special option with it's own UI file so we have fake values to pass + * to the template creation function. + */ + GncOptionReportPlacementVec value; + GncOption option{GncOptionValue{section, name, + "no_key", "nodoc_string", + value,GncOptionUIType::REPORT_PLACEMENT}}; + db->register_option(section, std::move(option)); +} + +GncOptionDB* +gnc_option_db_new(void) +{ + return new GncOptionDB; +} + +void +gnc_option_db_destroy(GncOptionDB* odb) +{ + PWARN("Direct Destroy called on GncOptionDB %" G_GUINT64_FORMAT, (uint64_t)odb); +} + +GList* +gnc_option_db_commit(GncOptionDB* odb) +{ + GList* errors{}; + odb->foreach_section( + [&errors](GncOptionSectionPtr& section){ + section->foreach_option( + [&errors](GncOption& option) { + try + { + option.set_option_from_ui_item(); + } + catch (const std::invalid_argument& err) + { + PWARN("Option %s:%s failed to set its value %s", + option.get_section().c_str(), + option.get_name().c_str(), err.what()); + errors = g_list_prepend(errors, + (void*)option.get_name().c_str()); + } }); + }); + if (!errors) + odb->run_callbacks(); + return errors; +} + +void +gnc_option_db_clean(GncOptionDB* odb) +{ + odb->foreach_section( + [](GncOptionSectionPtr& section){ + section->foreach_option( + [](GncOption& option) { + option.set_ui_item_from_option(); + }); + }); +} + +void gnc_option_db_load(GncOptionDB* odb, QofBook* book) +{ + odb->load_from_kvp(book); +} + +void +gnc_option_db_save(GncOptionDB* odb, QofBook* book, + gboolean clear_options) +{ + odb->save_to_kvp(book, static_cast(clear_options)); +} + +void +gnc_option_db_book_options(GncOptionDB* odb) +{ + constexpr const char* business_section{N_("Business")}; + constexpr const char* counter_section{N_("Counters")}; + static const std::string empty_string{""}; + +//Accounts Tab + + gnc_register_number_range_option(odb, OPTION_SECTION_ACCOUNTS, + OPTION_NAME_AUTO_READONLY_DAYS, "a", + N_("Choose the number of days after which transactions will be read-only and cannot be edited anymore. This threshold is marked by a red line in the account register windows. If zero, all transactions can be edited and none are read-only."), + 0.0, 0.0, 3650.0, 1.0); + + gnc_register_simple_boolean_option(odb, OPTION_SECTION_ACCOUNTS, + OPTION_NAME_NUM_FIELD_SOURCE, "b", + N_("Check to have split action field used in registers for 'Num' field in place of transaction number; transaction number shown as 'T-Num' on second line of register. Has corresponding effect on business features, reporting and imports/exports."), + false); + gnc_register_simple_boolean_option(odb, OPTION_SECTION_ACCOUNTS, + OPTION_NAME_TRADING_ACCOUNTS, "a", + N_("Check to have trading accounts used for transactions involving more than one currency or commodity."), + false); + +//Budgeting Tab + + gnc_register_budget_option(odb, OPTION_SECTION_BUDGETING, + OPTION_NAME_DEFAULT_BUDGET, "a", + N_("Budget to be used when none has been otherwise specified."), + nullptr); + +//Counters Tab + + gnc_register_counter_option(odb, counter_section, + N_("Customer number"), "gncCustomera", + N_("The previous customer number generated. This number will be incremented to generate the next customer number."), + 0.0); + gnc_register_counter_format_option(odb, counter_section, + N_("Customer number format"), + "gncCustomerb", + N_("The format string to use for generating customer numbers. This is a printf-style format string."), + empty_string); + gnc_register_counter_option(odb, counter_section, + N_("Employee number"), "gncEmployeea", + N_("The previous employee number generated. This number will be incremented to generate the next employee number."), + 0.0); + gnc_register_counter_format_option(odb, counter_section, + N_("Employee number format"), + "gncEmployeeb", + N_("The format string to use for generating employee numbers. This is a printf-style format string."), + empty_string); + gnc_register_counter_option(odb, counter_section, + N_("Invoice number"), "gncInvoicea", + N_("The previous invoice number generated. This number will be incremented to generate the next invoice number."), + 0.0); + gnc_register_counter_format_option(odb, counter_section, + N_("Invoice number format"), + "gncInvoiceb", + N_("The format string to use for generating invoice numbers. This is a printf-style format string."), + empty_string); + gnc_register_counter_option(odb, counter_section, + N_("Bill number"), "gncBilla", + N_("The previous bill number generated. This number will be incremented to generate the next bill number."), + 0.0); + gnc_register_counter_format_option(odb, counter_section, + N_("Bill number format"), "gncBillb", + N_("The format string to use for generating bill numbers. This is a printf-style format string."), + empty_string); + gnc_register_counter_option(odb, counter_section, + N_("Expense voucher number"), "gncExpVouchera", + N_("The previous expense voucher number generated. This number will be incremented to generate the next voucher number."), + 0.0); + gnc_register_counter_format_option(odb, counter_section, + N_("Expense voucher number format"), + "gncExpVoucherb", + N_("The format string to use for generating expense voucher numbers. This is a printf-style format string."), + empty_string); + gnc_register_counter_option(odb, counter_section, + N_("Job number"), "gncJoba", + N_("The previous job number generated. This number will be incremented to generate the next job number."), + 0.0); + gnc_register_counter_format_option(odb, counter_section, + N_("Job number format"), "gncJobb", + N_("The format string to use for generating job numbers. This is a printf-style format string."), + empty_string); + gnc_register_counter_option(odb, counter_section, + N_("Order number"), "gncOrdera", + N_("The previous order number generated. This number will be incremented to generate the next order number."), + 0.0); + gnc_register_counter_format_option(odb, counter_section, + N_("Order number format"), "gncOrderb", + N_("The format string to use for generating order numbers. This is a printf-style format string."), + empty_string); + gnc_register_counter_option(odb, counter_section, + N_("Vendor number"), "gncVendora", + N_("The previous vendor number generated. This number will be incremented to generate the next vendor number."), + 0.0); + gnc_register_counter_format_option(odb, counter_section, + N_("Vendor number format"), "gncVendorb", + N_("The format string to use for generating vendor numbers. This is a printf-style format string."), + empty_string); + +//Business Tab + + gnc_register_string_option(odb, business_section, N_("Company Name"), "a", + N_("The name of your business."), + empty_string); + gnc_register_text_option(odb, business_section, N_("Company Address"), "b1", + N_("The address of your business."), + empty_string); + gnc_register_string_option(odb, business_section, + N_("Company Contact Person"), "b2", + N_("The contact person to print on invoices."), + empty_string); + gnc_register_string_option(odb, business_section, + N_("Company Phone Number"), "c1", + N_("The contact person to print on invoices."), + empty_string); + gnc_register_string_option(odb, business_section, + N_("Company Fax Number"), "c2", + N_("The fax number of your business."), + empty_string); + gnc_register_string_option(odb, business_section, + N_("Company Email Address"), "c3", + N_ ("The email address of your business."), + empty_string); + gnc_register_string_option(odb, business_section, + N_("Company Website URL"), "c4", + N_("The URL address of your website."), + empty_string); + gnc_register_string_option(odb, business_section, N_("Company ID"), "c5", + N_("The ID for your company (eg 'Tax-ID: 00-000000)."), + empty_string); + + gnc_register_taxtable_option(odb, business_section, + N_("Default Customer TaxTable"), "e", + N_("The default tax table to apply to customers."), + nullptr); + gnc_register_taxtable_option(odb, business_section, + N_("Default Vendor TaxTable"), "f", + N_("The default tax table to apply to vendors."), + nullptr); + gnc_register_dateformat_option(odb, business_section, + N_("Fancy Date Format"), "g", + N_("The default date format used for fancy printed dates."), + empty_string); + +//Tax Tab + + gnc_register_string_option(odb, N_("Tax"), N_("Tax Number"), "a", + N_("The electronic tax number of your business"), + empty_string); +} + +const char* +gnc_option_db_lookup_string_value(GncOptionDB* odb, const char* section, const char* name) +{ + auto value{odb->lookup_string_option(section, name)}; + if (value.empty()) + return nullptr; + return strdup(value.c_str()); +} + +void +gnc_option_db_set_string_value(GncOptionDB* odb, const char* section, + const char* name, const char* value) +{ + odb->set_option(section, name, value); +} + +const QofInstance* +gnc_option_db_lookup_qofinstance_value(GncOptionDB* odb, const char* section, + const char* name) +{ + auto option{odb->find_option(section, name)}; + return option->get_value(); +} + +// Force creation of templates +template void gnc_register_number_range_option(GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + int value, int min, int max, int step); +template void gnc_register_number_range_option(GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + double value, double min, + double max, double step); diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h new file mode 100644 index 0000000000..88d12300d5 --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -0,0 +1,175 @@ +/********************************************************************\ + * gnc-optiondb.h -- Collection of GncOption objects C interface * + * Copyright (C) 2019 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ +/** @addtogroup Engine + @{ */ +/** @addtogroup Options + GnuCash Options System for Book, Report, and Stylesheet Options. + + The GnuCash Options System supports two somewhat different purposes: + - File properties, such as business information and whether to use automatic + trading accounts. + + - Report options for configuring and customizing reports. Most report + options are user-configurable through a report options dialog but some are + used as a way of passing enumeration values between the scheme modules + that generate the report. + + The options system is centered on an options database or optiondb. A + separate optionsdb is created and instantiated for every use, so the book + gets one at the beginning of the session with its values loaded from KVP, + and every report gets one when the report is run, as do report stylesheets + when they are edited. Customized report and stylesheet options are saved as + Scheme code fragments in files in the user's GnuCash Config directory. + + @note + Persistence via text scheme code is a security vulnerability as it + enables an attacker to make GnuCash execute arbitrary code. The Guile + interpreter affords full system access with at least the user's privileges. + + @{ */ +/** @file gnc-optiondb.h + @brief C public interface for the Options Database. + @author Copyright 2019-2021 John Ralls +*/ +#ifndef GNC_OPTIONDB_H_ +#define GNC_OPTIONDB_H_ + +#ifdef __cplusplus +class GncOption; +class GncOptionDB; +#else +// It's a class in C++ but the C compiler can't tell. +typedef struct GncOption GncOption; +typedef struct GncOptionDB GncOptionDB; +#endif + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif +#include +#include +#include +#include +#include +#include + +/** + * Create an empty option database. + * + * @return A newly allocated GncOptionDB. Use delete to destroy it. + */ +GncOptionDB* gnc_option_db_new(void); + +/** + * Destruct and release a GncOptionDB. + * @param odb The GncOptionDB. + */ +void gnc_option_db_destroy(GncOptionDB* odb); + +/** + * Write all changed ui_item values to their options. + * @param odb The GncOptionDB. + * @return A GList* conatining the names of options that raised exceptions when + * attempting to set their values. The names are const, free only the list. + */ +GList* gnc_option_db_commit(GncOptionDB* odb); + +/** + * Reset all ui_items to the option value. + * @param odb The GncOptionDB. + */ +void gnc_option_db_clean(GncOptionDB* odb); + +/** + * Load a book's options into the GncOptionDB. + * @param odb The GncOptionDB + * @param book The book in which the options are saved. + */ +void gnc_option_db_load(GncOptionDB* odb, QofBook* book); + +/** + * Save the GncOptionDB contents into a book's options store. + * @param odb The GncOptionDB + * @param book The book in which the options are saved. + * @param clear_options TRUE if the books existing options should be removed first. + */ +void gnc_option_db_save(GncOptionDB* odb, QofBook* book, + gboolean clear_options); + +/** + * Register the standard option set for a QofBook. + * + * @param odb The GncOptionDB + */ +void gnc_option_db_book_options(GncOptionDB*); + +/** + * Retrieve the string value of an option in the GncOptionDB + * + * @param odb the GncOptionDB + * @param section the section in which the option is stored + * @param name the option name + * @return the static char* of the value or nullptr if the option isn't found + * or if its value isn't a string. + */ +const char* gnc_option_db_lookup_string_value(GncOptionDB*, const char*, + const char*); + +/** + * Set the string value of an option in the GncOptionDB. + * + * The value will not be saved if the option is not in the GncOptionDB or if the + * type of the option isn't string or text. + * + * @param odb the GncOptionDB + * @param section the section in which the option is stored + * @param name the option name + * @param value the value to be stored in the option. + */ +void gnc_option_db_set_string_value(GncOptionDB*, const char*, + const char*, const char*); + +/** + * Retrieve the string value of an option in the GncOptionDB + * + * @param odb the GncOptionDB + * @param section the section in which the option is stored + * @param name the option name + * @return the const QofInstance* of the value or nullptr if the option isn't + * found or if its value isn't a QofInstance*. + */ + +const QofInstance* gnc_option_db_lookup_qofinstance_value(GncOptionDB*, + const char*, + const char*); + +#ifdef __cplusplus +} +#endif +#endif //GNC_OPTIONDB_H_ + +/** @} + @} */ diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp new file mode 100644 index 0000000000..25920f4c2a --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -0,0 +1,893 @@ +/********************************************************************\ + * gnc-optiondb.hpp -- Collection of GncOption objects * + * Copyright (C) 2019 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ +/** @addtogroup Engine + @{ */ +/** @addtogroup Options + @{ */ +/** @file gnc-optiondb.hpp + * @brief The primary C++ interface to options for books, reports, and + * stylesheets. + * @author Copyright 2019-2021 John Ralls +*/ + +#ifndef GNC_OPTIONDB_HPP_ +#define GNC_OPTIONDB_HPP_ + +#include +#include +#include +#include +#include +#include +extern "C" +{ +#include +#include +#include +#include +#include +#include +} +#include "gnc-option.hpp" +#include + + +class GncOptionDB; +using GncOptionDBPtr = std::unique_ptr; +using GncOptionAccountList = std::vector; + +using GncOptionAccountTypeList = std::vector; +using GncMultichoiceOptionEntry = std::tuple; +using GncMultichoiceOptionChoices = std::vector; + +/** + * Extract a list of accounts in the book having one of the GNCAccountTypes in + * types. + * + * Note that in Scheme it's important to use this function and not to create a + * list of accounts using gnc-get-descendants-sorted because the latter method + * produces a SWIGTYPE for the accounts that's incompatible with the SWIGTYPE + * used in this module. + * + * @param book The book whose accounts to search + * @param types A std::vector of GNCAccountType containing the Account types to + * include in ther result + * @return A std::vector of all accounts in the book having the + * Account types in the types parameter. + */ +GncOptionAccountList +gnc_account_list_from_types(QofBook *book, + const GncOptionAccountTypeList& types); + +/** + * Create a new string option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_string_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_string_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + std::string value) +{ + gnc_register_string_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new text option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_text_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_text_option(const GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, std::string value) +{ + gnc_register_text_option(db.get(), section, name, key, doc_string, value); +} + +/** + * Create a new font option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_font_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_font_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + std::string value) +{ + gnc_register_font_option(db.get(), section, name, key, doc_string, value); +} + +/** + * Create a new budget option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_budget_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, GncBudget* value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_budget_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + GncBudget* value) +{ + gnc_register_budget_option(db.get(), section, name, key, doc_string, value); +} + +/** + * Create a new commodity option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_commodity_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity* value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_commodity_option(const GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + gnc_commodity* value) +{ + gnc_register_commodity_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new simple boolean option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_simple_boolean_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + bool value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_simple_boolean_option(const GncOptionDBPtr& db, + const char* section, + const char* name, + const char* key, + const char* doc_string, + bool value) +{ + gnc_register_simple_boolean_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new complex boolean option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_complex_boolean_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, + const char* doc_string, + bool value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_complex_boolean_option(const GncOptionDBPtr& db, + const char* section, + const char* name, + const char* key, + const char* doc_string, + bool value) +{ + gnc_register_complex_boolean_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new pixmap option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_pixmap_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_pixmap_option(GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + std::string value) +{ + gnc_register_pixmap_option(db.get(), section, name, key, doc_string, value); +} + +/** + * Create a new limited account list option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default values for the option. + * @param allowed The accounts which are available for selection. +*/ +void gnc_register_account_list_limited_option(GncOptionDB* db, + const char* section, + const char* name, const char* key, + const char* doc_string, + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_account_list_limited_option(GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed) +{ + gnc_register_account_list_limited_option(db.get(), section, name, key, + doc_string, value, + std::move(allowed)); +} + +/** + * Create a new account list option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default values for the option. + */ +void gnc_register_account_list_option(GncOptionDB* db, + const char* section, + const char* name, const char* key, + const char* doc_string, + const GncOptionAccountList& value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_account_list_option(GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + const GncOptionAccountList& value) +{ + gnc_register_account_list_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a limited account selection option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + * @param allowed The accounts which are available for selection. + */ +void gnc_register_account_sel_limited_option(GncOptionDB* db, + const char* section, + const char* name, const char* key, + const char* doc_string, + const Account* value, + GncOptionAccountTypeList&& allowed); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_account_sel_limited_option(GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + const Account* value, + GncOptionAccountTypeList&& allowed) +{ + gnc_register_account_sel_limited_option(db.get(), section, name, key, + doc_string, value, + std::move(allowed)); +} + +/** + * Create a new multichoice option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The set of possible values for the option. Only one can be selected. Note that the value will be moved from the parameter and using the parameter after this call will result in undefined behavior. + */ +void gnc_register_multichoice_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + const char* default_val, + GncMultichoiceOptionChoices&& value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_multichoice_option(GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + const char* default_val, + GncMultichoiceOptionChoices&& value) +{ + gnc_register_multichoice_option(db.get(), section, name, + key, doc_string, default_val, + std::move(value)); +} + +/** + * Create a new list option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + * @param list The values available for selection. Note that this parameter will be moved from so using it after this call will result in undefined behavior. + */ +void gnc_register_list_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, const char* value, + GncMultichoiceOptionChoices&& list); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_list_option(GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, const char* value, + GncMultichoiceOptionChoices&& list) +{ + gnc_register_list_option(db.get(), section, name, key, doc_string, + value, std::move(list)); +} + +/** + * Create a new number range option and register it in the options database. + * + * These are normally associated with spin controls and ValueType is normally + * double, but it's templated to permit other numeric types if needed. + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + * @param min The minimum value for the spin control. + * @param max The maximum value for the spin control. + * @param step The step size (increment) of the spin control. + */ +template +void gnc_register_number_range_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, ValueType min, + ValueType max, ValueType step); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +template +void gnc_register_number_range_option(GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, ValueType min, + ValueType max, ValueType step) +{ + gnc_register_number_range_option(db.get(), section, name, + key, doc_string, value, + min, max, step); +} + +/** + * Create a new plot-size option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_number_plot_size_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, + const char* doc_string, + int value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_number_plot_size_option(const GncOptionDBPtr& db, + const char* section, + const char* name, + const char* key, + const char* doc_string, + int value) +{ + gnc_register_number_plot_size_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new QofQuery option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_query_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, const QofQuery* value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_query_option(GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + const QofQuery* value) +{ + gnc_register_query_option(db.get(), section, name, key, doc_string, value); +} + +/** + * Create a new GncOwner option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_owner_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, const GncOwner* value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_owner_option(GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + const GncOwner* value) +{ + gnc_register_owner_option(db.get(), section, name, key, doc_string, value); +} + +/** + * Create a new color option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_color_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_color_option(GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + std::string value) +{ + gnc_register_color_option(db.get(), section, name, key, doc_string, value); +} + +void gnc_register_internal_option(GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, + const char* doc_string, + const std::string& value); + +void gnc_register_internal_option(GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, + const char* doc_string, + bool value); + +void gnc_register_report_placement_option(GncOptionDBPtr& db, + const char* section, const char* name); + +/** + * Create a new currency option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. It is checked with gnc_commodity_is_currency. + */ +void gnc_register_currency_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, gnc_commodity* value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_currency_option(const GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + gnc_commodity* value) +{ + gnc_register_currency_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new invoice option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_invoice_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, GncInvoice* value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_invoice_option(const GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + GncInvoice* value) +{ + gnc_register_invoice_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new taxtable option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_taxtable_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, GncTaxTable* value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_taxtable_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, + const char* doc_string, + GncTaxTable* value) +{ + gnc_register_taxtable_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new counter option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_counter_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, double value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_counter_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + double value) +{ + gnc_register_counter_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new counter format option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_counter_format_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + std::string value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_counter_format_option(GncOptionDBPtr& db, + const char* section, + const char* name, + const char* key, + const char* doc_string, + std::string value) +{ + gnc_register_counter_format_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * Create a new date format option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param value The initial and default value for the option. + */ +void gnc_register_dateformat_option(GncOptionDB* db, + const char* section, const char* name, + const char* key, const char* doc_string, + std::string value); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_dateformat_option(GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + std::string value) +{ + gnc_register_dateformat_option(db.get(), section, name, key, + doc_string, value); +} + +enum RelativeDateUI : uint8_t +{ + ABSOLUTE, + RELATIVE, + BOTH +}; + +/** + * Create a new date option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param period The default/starting relative date value for the option. + * @param ui What UI to display, relative, absolute, or both. +*/ +void gnc_register_date_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, + RelativeDatePeriod period = + RelativeDatePeriod::TODAY, + RelativeDateUI ui = RelativeDateUI::BOTH); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_date_option(GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + RelativeDatePeriod period = + RelativeDatePeriod::TODAY, + RelativeDateUI ui = RelativeDateUI::BOTH) +{ + gnc_register_date_option(db.get(), section, name, key, doc_string, + period, ui); +} + +/** + * Create a new date option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param time The initial time to set in the option. + * @param ui What UI to display, relative, absolute, or both. + */ +void gnc_register_date_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, time64 time, + RelativeDateUI ui = RelativeDateUI::BOTH); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_date_option(GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, time64 time, + RelativeDateUI ui = RelativeDateUI::BOTH) +{ + gnc_register_date_option(db.get(), section, name, key, doc_string, + time, ui); +} + +/** + * Create a new date option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param period_set A vector of relative date periods to display in the relative control. + * @param both Whether to display both a relative and absolute control or a onla a relative control. +*/ +void gnc_register_date_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, + RelativeDatePeriodVec& period_set, + bool both = true); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_date_option(GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + RelativeDatePeriodVec& period_set, + bool both = true) +{ + gnc_register_date_option(db.get(), section, name, key, doc_string, + period_set, both); +} + +/** + * Create a new start-date option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param both Whether to display both a relative and absolute control or a onla a relative control. + */ +void gnc_register_start_date_option(GncOptionDB* db, + const char* section, + const char* name, const char* key, + const char* doc_string, bool both = true); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_start_date_option(GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, bool both = true) +{ + gnc_register_start_date_option(db.get(), section, name, key, doc_string, + both); +} + +/** + * Create a new end-date option and register it in the options database. + * + * @param db A GncOptionDB* for calling from C. Caller retains ownership. + * @param section The database section for the option. + * @param name The option name. + * @param doc_string A description of the option. This will be used in tooltips and should be marked for translation. + * @param both Whether to display both a relative and absolute control or a onla a relative control. + */ +void gnc_register_end_date_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, bool both = true); + +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_end_date_option(GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, bool both = true) +{ + gnc_register_end_date_option(db.get(), section, name, key, doc_string, + both); +} + + + +#endif //GNC_OPTIONDB_HPP_ diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i new file mode 100644 index 0000000000..8f696e0f08 --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -0,0 +1,1913 @@ +/* + * gnc-optiondb.i -- Swig Guile interface for the options system. + * + * Copyright 2021 John Ralls + * + * This program 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 2 of + * the License, or (at your option) any later version. + * + * This program 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, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/* unique_ptr SWIG wrapper from + * https://stackoverflow.com/questions/27693812/how-to-handle-unique-ptrs-with-swig + */ +#if defined(SWIGGUILE) + +namespace std { + %feature("novaluewrapper") unique_ptr; + template + struct unique_ptr { + typedef Type* pointer; + + explicit unique_ptr( pointer Ptr ); + unique_ptr (unique_ptr&& Right); + template unique_ptr( unique_ptr&& Right ); + unique_ptr( const unique_ptr& Right) = delete; + + + pointer operator-> () const; + pointer release (); + void reset (pointer __p=pointer()); + void swap (unique_ptr &__u); + pointer get () const; +// operator bool () const; + + ~unique_ptr() = delete; //Otherwise swig takes the unique_ptr and calls delete on it. + }; +} + +%define wrap_unique_ptr(Name, Type) + %template(Name) std::unique_ptr; + %newobject std::unique_ptr::release; + + %typemap(out) std::unique_ptr %{ + $result = SWIG_NewPointerObj(new $1_ltype(std::move($1)), $&1_descriptor, SWIG_POINTER_OWN); + %} + +%enddef + +%typemap(in) std::size_t "$1 = scm_to_ulong($input);"; +%typemap(out) std::size_t "$result = scm_from_ulong($1);"; + +%{ +#include "gnc-optiondb.h" +#include "gnc-optiondb.hpp" +#include "gnc-optiondb-impl.hpp" +#include "gnc-option-date.hpp" +#include +#include +#include + +static const QofLogModule log_module = "gnc.optiondb"; + +SCM scm_init_sw_gnc_optiondb_module(void); +%} + +%ignore gnc_get_current_session(void); + +%include +%import +%import (module="sw_engine") +%import (module="sw_engine") +%import (module="sw_engine") +%import (module="sw_engine") +%import (module="sw_engine") +%import (module="sw_engine") +%import (module="sw_engine") +%import (module="sw_engine") + + /* Implementation Note: Plain overloads failed to compile because + * auto value{option.get_value()}; + * return scm_from_value(value); + * applied implicit conversions among bool, int, and int64_t and ranked them as + * equal to the non-converted types in overload resolution. Template type + * resolution is more strict so the overload prefers the exact decltype(value) + * to implicit conversion candidates. + */ + +%typemap (out) QofInstance_s* { + swig_type_info *type = $descriptor(QofInstance_s); + if ($1 == nullptr) + { + $result = SWIG_NewPointerObj(nullptr, type, FALSE); + } + else + { + + auto ptr{static_cast(const_cast($1))}; + if (GNC_IS_COMMODITY($1)) + type = $descriptor(gnc_commodity*); + else if (GNC_IS_ACCOUNT($1)) + type = $descriptor(Account*); + else if (GNC_IS_BUDGET($1)) + type = $descriptor(GncBudget*); + else if (GNC_IS_INVOICE($1)) + type = $descriptor(GncInvoice*); + else if (GNC_IS_TAXTABLE($1)) + type = $descriptor(GncTaxTable*); + else if (GNC_IS_CUSTOMER($1)) + type = $descriptor(_gncCustomer*); + else if (GNC_IS_EMPLOYEE($1)) + type = $descriptor(_gncEmployee*); + else if (GNC_IS_JOB($1)) + type = $descriptor(_gncJob*); + else if (GNC_IS_VENDOR($1)) + type = $descriptor(_gncVendor*); + $result = SWIG_NewPointerObj(ptr, type, FALSE); + } +} + +%typemap (in) QofInstance_s* { + if (scm_is_true($input)) + { + static const std::array types { + $descriptor(QofInstance_s*), $descriptor(gnc_commodity*), + $descriptor(GncBudget*), $descriptor(GncInvoice*), + $descriptor(GncTaxTable*), $descriptor(Account*), + $descriptor(_gncCustomer*), $descriptor(_gncEmployee*), + $descriptor(_gncJob*), $descriptor(_gncVendor*) + }; + void* ptr{}; + SCM instance{$input}; + auto pos = std::find_if(types.begin(), types.end(), + [&instance, &ptr](auto type){ + SWIG_ConvertPtr(instance, &ptr, type, 0); + return ptr != nullptr; }); + if (pos == types.end()) + $1 = nullptr; + else + $1 = static_cast(ptr); + } + else + { + $1 = nullptr; + } + } + +%inline %{ +template inline SCM + scm_from_value(ValueType value); +/*{ + return SCM_BOOL_F; + }*/ +template <> inline SCM +scm_from_value(SCM value) +{ + return value; +} + +template <> inline SCM +scm_from_value(std::string value) +{ + return scm_from_utf8_string(value.c_str()); +} + +template <> inline SCM +scm_from_value(bool value) +{ + return value ? SCM_BOOL_T : SCM_BOOL_F; +} + +template <> inline SCM +scm_from_value(int64_t value) +{ + return scm_from_int64(value); +} + +template <> inline SCM +scm_from_value(int value) +{ + return scm_from_int(value); +} + +template <> inline SCM +scm_from_value(double value) +{ + return scm_from_double(value); +} + +template <> inline SCM +scm_from_value(const QofInstance* value) +{ + swig_type_info *type = SWIGTYPE_p_QofInstance_s; + if (!value) + return SWIG_NewPointerObj(nullptr, type, FALSE); + + auto ptr{static_cast(const_cast(value))}; + if (GNC_IS_COMMODITY(value)) + type = SWIGTYPE_p_gnc_commodity; + else if (GNC_IS_ACCOUNT(value)) + type = SWIGTYPE_p_Account; + else if (GNC_IS_BUDGET(value)) + type = SWIGTYPE_p_budget_s; + else if (GNC_IS_INVOICE(value)) + type = SWIGTYPE_p__gncInvoice; + else if (GNC_IS_TAXTABLE(value)) + type = SWIGTYPE_p__gncTaxTable; + else if (GNC_IS_CUSTOMER(value)) + type = SWIGTYPE_p__gncCustomer; + else if (GNC_IS_EMPLOYEE(value)) + type = SWIGTYPE_p__gncEmployee; + else if (GNC_IS_JOB(value)) + type = SWIGTYPE_p__gncJob; + else if (GNC_IS_VENDOR(value)) + type = SWIGTYPE_p__gncVendor; + + return SWIG_NewPointerObj(ptr, type, FALSE); +} + +template <> inline SCM +scm_from_value(QofInstance* value) +{ + return scm_from_value(value); +} + +template <> inline SCM +scm_from_value(const Account* value) +{ + return scm_from_value(QOF_INSTANCE(value)); +} + +template <> inline SCM +scm_from_value(gnc_commodity* value) +{ + if (!value) + return SCM_BOOL_F; + return scm_from_value((const QofInstance*)value); +} + +template <> inline SCM +scm_from_value(QofQuery* value) +{ + return gnc_query2scm(value); +} + +template <> inline SCM +scm_from_value(const QofQuery* value) +{ + return scm_from_value(const_cast(value)); +} + +template <> inline SCM +scm_from_value(const GncOwner* value) +{ + auto ptr{static_cast(const_cast(value))}; + return SWIG_NewPointerObj(ptr, SWIGTYPE_p__gncOwner, FALSE); +} + +template <> inline SCM +scm_from_value(GncOwner* value) +{ + return scm_from_value(value); +} + +template <>inline SCM +scm_from_value(GncOptionAccountList value) +{ + SCM s_list = SCM_EOL; + auto book{gnc_get_current_book()}; + for (auto guid : value) + { + auto acct{xaccAccountLookup(&guid, book)}; + s_list = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0), + s_list); + } + return scm_reverse(s_list); +} + +template <>inline SCM +scm_from_value(GncOptionReportPlacementVec value) +{ + SCM s_list = SCM_EOL; + for (auto placement : value) + { + auto [id, wide, high] = placement; + auto scm_id{scm_from_uint32(id)}; + auto scm_wide{scm_from_uint32(wide)}; + auto scm_high{scm_from_uint32(high)}; + /* The trailing SCM_BOOL_F is a placeholder for a never-used callback function, + * present for backward compatibility so that older GnuCash versions can load a + * saved multicolumn report. + */ + s_list = scm_cons(scm_list_4(scm_id, scm_wide, scm_high, SCM_BOOL_F), s_list); + } + return scm_reverse(s_list); +} + +static std::string +scm_color_list_to_string(SCM list) +{ + std::ostringstream oss{}; + oss << std::hex << std::setfill('0'); + SCM cdr = list; + while (scm_is_pair(cdr)) + { + if (scm_is_rational(SCM_CAR(cdr))) + oss << std::setw(2) << + static_cast(scm_to_double(SCM_CAR(cdr))); + cdr = SCM_CDR(cdr); + } + if (scm_is_rational(cdr)) + oss << std::setw(2) << static_cast(scm_to_double(cdr)); + return oss.str(); +} + +template inline ValueType +scm_to_value(SCM new_value) +{ + if constexpr (is_same_decayed_v) + return new_value; + return ValueType{}; +} + +template <> inline bool +scm_to_value(SCM new_value) +{ + return scm_is_true(new_value); +} + +template <> inline std::string +scm_to_value(SCM new_value) +{ + if (scm_is_true(scm_list_p(new_value))) + return scm_color_list_to_string(new_value); + auto strval = scm_to_utf8_stringn(new_value, nullptr); + std::string retval{strval}; + free(strval); + return retval; +} + +template <> inline int +scm_to_value(SCM new_value) +{ + return scm_to_int(new_value); +} + +template <> inline int64_t +scm_to_value(SCM new_value) +{ + return scm_to_int64(new_value); +} + +template <> inline double +scm_to_value(SCM new_value) +{ + return scm_to_double(new_value); +} + +template <> inline const QofInstance* +scm_to_value(SCM new_value) +{ + if (new_value == SCM_BOOL_F) + return nullptr; + + auto info = SWIG_PointerType(new_value); + + static const std::array types{ + SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity, + SWIGTYPE_p_budget_s, SWIGTYPE_p__gncInvoice, + SWIGTYPE_p__gncTaxTable, SWIGTYPE_p_Account, + SWIGTYPE_p__gncCustomer, SWIGTYPE_p__gncEmployee, + SWIGTYPE_p__gncJob, SWIGTYPE_p__gncVendor + }; + void* ptr{}; + auto pos = std::find_if(types.begin(), types.end(), + [&new_value, &ptr](auto type){ + SWIG_ConvertPtr(new_value, &ptr, type, 0); + return ptr != nullptr; }); + if (pos == types.end()) + return nullptr; + + return static_cast(ptr); +} + +template <> inline gnc_commodity* +scm_to_value(SCM new_value) +{ + auto comm{scm_to_value(new_value)}; + if (comm) + return GNC_COMMODITY(comm); + if (scm_is_list(new_value)) + { + auto len{scm_to_uint(scm_length(new_value))}; + std::string mnemonic{scm_to_utf8_string(scm_list_ref(new_value, + scm_from_uint(0)))}; + std::string name_space{"CURRENCY"}; + if (len > 1) + name_space = scm_to_utf8_string(scm_list_ref(new_value, + scm_from_uint(1))); + auto book{gnc_get_current_book()}; + auto table = gnc_commodity_table_get_table(book); + return gnc_commodity_table_lookup(table, name_space.c_str(), + mnemonic.c_str()); + } + if (scm_is_string(new_value)) + { + auto book{gnc_get_current_book()}; + auto table = gnc_commodity_table_get_table(book); + std::string mnemonic{scm_to_utf8_string(new_value)}; + return gnc_commodity_table_lookup(table, "CURRENCY", mnemonic.c_str()); + } + return nullptr; +} + +template <> inline const Account* +scm_to_value(SCM new_value) +{ + return GNC_ACCOUNT(scm_to_value(new_value)); +} + +template <> inline const QofQuery* +scm_to_value(SCM new_value) +{ + if (new_value == SCM_BOOL_F) + return nullptr; + void* ptr{}; + SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p__QofQuery, 0); + return static_cast(ptr); +} + +template <> inline const GncOwner* +scm_to_value(SCM new_value) +{ + if (new_value == SCM_BOOL_F) + return nullptr; + void* ptr{}; + SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p__gncOwner, 0); + return static_cast(ptr); +} + +template <>inline GncOptionAccountList +scm_to_value(SCM new_value) +{ + GncOptionAccountList retval{}; + if (scm_is_false(scm_list_p(new_value)) || scm_is_null(new_value)) + return retval; + auto next{new_value}; + while (auto node{scm_car(next)}) + { + void* account{}; + SWIG_ConvertPtr(node, &account, SWIGTYPE_p_Account, 0); + if (account) + { + auto guid{qof_entity_get_guid(static_cast(account))}; + retval.push_back(*guid); + } + next = scm_cdr(next); + if (scm_is_null(next)) + break; + } + return retval; +} + +template <>inline GncOptionReportPlacementVec +scm_to_value(SCM new_value) +{ + GncOptionReportPlacementVec rp; + GncOptionAccountList retval{}; + if (scm_is_false(scm_list_p(new_value)) || scm_is_null(new_value)) + return rp; + auto next{new_value}; + while (auto node{scm_car(next)}) + { + auto id{scm_to_uint32(scm_car(node))}; + auto wide{scm_to_uint32(scm_cadr(node))}; + auto high{scm_to_uint32(scm_caddr(node))}; + rp.emplace_back(id, wide, high); + next = scm_cdr(next); + if (scm_is_null(next)) + break; + } + return rp; +} + +QofBook* gnc_option_test_book_new(); +void gnc_option_test_book_destroy(QofBook*); + +QofBook* +gnc_option_test_book_new() +{ + auto session = gnc_get_current_session(); + return gnc_get_current_book(); +} + +void +gnc_option_test_book_destroy(QofBook* book) +{ + gnc_clear_current_session(); +} + +%} + +%ignore OptionClassifier; +%ignore OptionUIItem; +%nodefaultctor GncOption; +%ignore GncOptionMultichoiceValue(GncOptionMultichoiceValue&&); +%ignore GncOptionMultichoiceValue::operator=(const GncOptionMultichoiceValue&); +%ignore GncOptionMultichoiceValue::operator=(GncOptionMultichoiceValue&&); +%ignore GncOptionQofInstanceValue(GncOptionQofInstanceValue&&); +%ignore GncOptionQofInstanceValue::operator=(const GncOptionQofInstanceValue&); +%ignore GncOptionQofInstanceValue::operator=(GncOptionQofInstanceValue&&); +%ignore GncOptionCommodityValue(GncOptionCommodityValue&&); +%ignore GncOptionCommodityValue::operator=(const GncOptionCommodityValue&); +%ignore GncOptionCommodityValue::operator=(GncOptionCommodityValue&&); +%ignore GncOptionDateValue(GncOptionDateValue&&); +%ignore GncOptionDateValue::operator=(const GncOptionDateValue&); +%ignore GncOptionDateValue::operator=(GncOptionDateValue&&); +%ignore GncOptionDateValue::set_value(size_t); // Used only by dialog-options +%ignore operator<<(std::ostream&, const GncOption&); +%ignore operator<<(std::ostream&, const RelativeDatePeriod); +%ignore operator>>(std::istream&, GncOption&); +%ignore GncOption::_get_option(); + + +%rename(gnc_register_date_option_set) + gnc_register_date_option(GncOptionDBPtr&, const char*, const char*, + const char*, const char*, RelativeDatePeriodVec&, + bool); + +%typemap(typecheck, precedence=SWIG_TYPECHECK_INT64) time64 { + $1 = scm_is_signed_integer($input, INT64_MAX, INT64_MIN); +} + +%typemap(in) RelativeDatePeriod (RelativeDatePeriod rdp) +{ + if (scm_is_integer($input)) + rdp = (RelativeDatePeriod) scm_to_int($input); + else if (scm_is_symbol($input)) + rdp = scm_relative_date_get_period($input); + else + rdp = RelativeDatePeriod::TODAY; + + $1 = rdp; +} + +%typemap(in) RelativeDatePeriodVec& (RelativeDatePeriodVec period_set) +{ + auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0; + for (std::size_t i = 0; i < len; ++i) + { + SCM s_reldateperiod = scm_list_ref($input, scm_from_size_t(i)); + period_set.push_back(scm_relative_date_get_period(s_reldateperiod)); + } + $1 = &period_set; +} + +%typemap(in) GncMultichoiceOptionIndexVec (GncMultichoiceOptionIndexVec indexes) +{ + if (scm_is_true($input)) + { + auto len{scm_to_size_t(scm_length($input))}; + for (std::size_t i = 0; i < len; ++i) + { + auto val{scm_list_ref($input, scm_from_size_t(i))}; + if (scm_is_unsigned_integer(val, 0, UINT_MAX)) + indexes.push_back(scm_to_unsigned_integer(val, 0, UINT_MAX)); + } + } + $1 = indexes; +} + +%typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices) +{ + using KeyType = GncOptionMultichoiceKeyType; + auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0; + for (std::size_t i = 0; i < len; ++i) + { + SCM vec = scm_list_ref($input, scm_from_size_t(i)); + SCM keyval, v_ref_0 = SCM_SIMPLE_VECTOR_REF(vec, 0); + GncOptionMultichoiceKeyType keytype; + if (scm_is_symbol(v_ref_0)) + { + keyval = scm_symbol_to_string(SCM_SIMPLE_VECTOR_REF(vec, 0)); + keytype = KeyType::SYMBOL; + } + else if (scm_is_string(v_ref_0)) + { + keyval = SCM_SIMPLE_VECTOR_REF(vec, 0); + keytype = KeyType::STRING; + } + else if (scm_is_integer(v_ref_0)) + { + keyval = scm_number_to_string(v_ref_0, scm_from_uint(10u)); + keytype = KeyType::NUMBER; + } + else + throw std::invalid_argument("Unsupported key type in multichoice option."); + std::string key{scm_to_utf8_string(keyval)}; + std::string name{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 1))}; + choices.push_back({std::move(key), std::move(name), keytype}); + } + $1 = &choices; + } + + +%typemap(in) GncOptionAccountList +{ + auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0; + for (std::size_t i = 0; i < len; ++i) + { + SCM s_account = scm_list_ref($input, scm_from_size_t(i)); + Account* acct = (Account*)SWIG_MustGetPtr(s_account, + SWIGTYPE_p_Account, 1, 0); + if (acct) + $1.push_back(*qof_entity_get_guid(acct)); + } +} + +%typemap(in) GncOptionAccountTypeList& (GncOptionAccountTypeList types) +{ + auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0; + for (std::size_t i = 0; i < len; ++i) + { + SCM s_type = scm_list_ref($input, scm_from_size_t(i)); + GNCAccountType type = (GNCAccountType)scm_to_int(s_type); + types.push_back(type); + } + $1 = &types; +} + +%typemap(in) GncOptionAccountTypeList&& (GncOptionAccountTypeList types) +{ + auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0; + for (std::size_t i = 0; i < len; ++i) + { + SCM s_type = scm_list_ref($input, scm_from_size_t(i)); + GNCAccountType type = (GNCAccountType)scm_to_int(s_type); + types.push_back(type); + } + $1 = &types; +} + +%typemap(in) GncOptionAccountList const & (GncOptionAccountList alist) +{ + auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0; + for (std::size_t i = 0; i < len; ++i) + { + SCM s_account = scm_list_ref($input, scm_from_size_t(i)); + Account* acct = (Account*)SWIG_MustGetPtr(s_account, + SWIGTYPE_p_Account, 1, 0); + if (acct) + alist.push_back(*qof_entity_get_guid(acct)); + } + $1 = &alist; +} + +%typemap(in) GncOptionAccountList& (GncOptionAccountList acclist) +{ + auto len = scm_is_true($input) ? scm_to_size_t(scm_length($input)) : 0; + acclist.reserve(len); + for (std::size_t i = 0; i < len; ++i) + { + SCM s_account = scm_list_ref($input, scm_from_size_t(i)); + Account* acct = (Account*)SWIG_MustGetPtr(s_account, + SWIGTYPE_p_Account, 1, 0); + acclist.push_back(*qof_entity_get_guid(acct)); + } + $1 = &acclist; +} + +%typemap (in) GncOptionReportPlacementVec& (GncOptionReportPlacementVec rp) +{ + rp = scm_to_value($input); + $1 = &rp; +} + +%typemap(out) GncOptionAccountList +{ + $result = SCM_EOL; + auto book{gnc_get_current_book()}; + for (auto guid : $1) + { + auto acct{xaccAccountLookup(&guid, book)}; + $result = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0), + $result); + } + $result = scm_reverse($result); +} + +%typemap(out) const GncOptionAccountList& +{ + $result = SCM_EOL; + auto book{gnc_get_current_book()}; + for (auto guid : *$1) + { + auto acct{xaccAccountLookup(&guid, book)}; + $result = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0), + $result); + } + $result = scm_reverse ($result) +} + +%typemap(out) const GncOptionReportPlacementVec& +{ + $result = scm_from_value($1); +} + +wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); + +%ignore swig_get_option(GncOption&); +%inline %{ +#include +#include +#include +#include +#include "gnc-option.hpp" +#include "gnc-option-impl.hpp" +#include "gnc-option-ui.hpp" + + GncOptionVariant& swig_get_option(GncOption* option) + { + assert(option); + return *option->m_option; + } +%} + +/* GncOptionDB::register_option comes in GncOption* and GncOption&& + * overloads. The latter isn't useful to SWIG, ignore it. + */ +%ignore GncOptionDB::register_option(const char*, GncOption&&); +/* The following functions are overloaded in gnc-optiondb.hpp to provide both + * GncOptionDB* and GncOptionDBPtr& versions. That confuses SWIG so ignore the + * raw-ptr version. + */ +%ignore gnc_register_string_option(GncOptionDB*, const char* section, const char* name, const char* key, const char* doc_string, std::string value); +%ignore gnc_register_text_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string); +%ignore gnc_register_font_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string); +%ignore gnc_register_budget_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncBudget*); +%ignore gnc_register_commodity_option(GncOptionDB*, const char*, const char*, const char*, const char*, gnc_commodity*); +%ignore gnc_register_simple_boolean_option(GncOptionDB*, const char* section, const char* name, const char* key, const char* doc_string, bool value); +%ignore gnc_register_complex_boolean_option(GncOptionDB*, const char* section, const char* name, const char* key, const char* doc_string, bool value); +%ignore gnc_register_pixmap_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string); +%ignore gnc_register_account_list_limited_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&, GncOptionAccountTypeList&&); +%ignore gnc_register_account_list_option(GncOptionDB*, const char*, const char*, const char*, const char*, const GncOptionAccountList&); +%ignore gnc_register_account_sel_limited_option(GncOptionDB*, const char*, const char*, const char*, const char*, const Account*, GncOptionAccountTypeList&&); +%ignore gnc_register_multichoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&); +%ignore gnc_register_list_option(GncOptionDB*, const char*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&); +%ignore gnc_register_number_Plot_size_option(GncOptionDB*, const char*, const char*, const char*, const char*, int); +%ignore gnc_register_query_option(GncOptionDB*, const char*, const char*, const char*, const char*, QofQuery*); +%ignore gnc_register_color_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string); +%ignore gnc_register_currency_option(GncOptionDB*, const char*, const char*, const char*, const char*, gnc_commodity*); +%ignore gnc_register_invoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncInvoice*); +%ignore gnc_register_taxtable_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncTaxTable*); +%ignore gnc_register_counter_option(GncOptionDB*, const char*, const char*, const char*, const char*, double); +%ignore gnc_register_counter_format_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string); +%ignore gnc_register_dateformat_option(GncOptionDB*, const char*, const char*, const char*, const char*, std::string); +%ignore gnc_register_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, RelativeDatePeriod, RelativeDateUI); +%ignore gnc_register_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, time64, RelativeDateUI); +%ignore gnc_register_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, RelativeDatePeriodVec, bool); +%ignore gnc_register_start_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool); +%ignore gnc_register_end_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, bool); +%ignore gnc_register_internal_option(GncOptionDBPtr&, const char*, const char*, const char*, const char*, const std::string&); +%ignore gnc_register_internal_option(GncOptionDBPtr&, const char*, const char*, const char*, const char*, bool); +%typemap(in) GncOption* "$1 = scm_is_true($input) ? static_cast(scm_to_pointer($input)) : nullptr;" +%typemap(out) GncOption* "$result = ($1) ? scm_from_pointer($1, nullptr) : SCM_BOOL_F;" + +%header %{ + + static std::vector reldate_values{}; + inline size_t index_of(RelativeDatePeriod per) + { + return static_cast(per) + 1; + } + + static void init_reldate_values() + { + if (!reldate_values.empty()) + return; + std::vector tmp (relative_date_periods, SCM_BOOL_F); + using rdp = RelativeDatePeriod; + tmp[index_of(rdp::ABSOLUTE)] = + scm_from_utf8_symbol("absolute"); + tmp[index_of(rdp::TODAY)] = + scm_from_utf8_symbol("today"); + tmp[index_of(rdp::ONE_WEEK_AGO)] = + scm_from_utf8_symbol("one-week-ago"); + tmp[index_of(rdp::ONE_WEEK_AHEAD)] = + scm_from_utf8_symbol("one-week-ahead"); + tmp[index_of(rdp::ONE_MONTH_AGO)] = + scm_from_utf8_symbol("one-month-ago"); + tmp[index_of(rdp::ONE_MONTH_AHEAD)] = + scm_from_utf8_symbol("one-month-ahead"); + tmp[index_of(rdp::THREE_MONTHS_AGO)] = + scm_from_utf8_symbol("three-months-ago"); + tmp[index_of(rdp::THREE_MONTHS_AHEAD)] = + scm_from_utf8_symbol("three-months-ahead"); + tmp[index_of(rdp::SIX_MONTHS_AGO)] = + scm_from_utf8_symbol("six-months-ago"); + tmp[index_of(rdp::SIX_MONTHS_AHEAD)] = + scm_from_utf8_symbol("six-months-ahead"); + tmp[index_of(rdp::ONE_YEAR_AGO)] = + scm_from_utf8_symbol("one-year-ago"); + tmp[index_of(rdp::ONE_YEAR_AHEAD)] = + scm_from_utf8_symbol("one-year-ahead"); + tmp[index_of(rdp::START_THIS_MONTH)] = + scm_from_utf8_symbol("start-this-month"); + tmp[index_of(rdp::END_THIS_MONTH)] = + scm_from_utf8_symbol("end-this-month"); + tmp[index_of(rdp::START_PREV_MONTH)] = + scm_from_utf8_symbol("start-prev-month"); + tmp[index_of(rdp::END_PREV_MONTH)] = + scm_from_utf8_symbol("end-prev-month"); + tmp[index_of(rdp::START_NEXT_MONTH)] = + scm_from_utf8_symbol("start-next-month"); + tmp[index_of(rdp::END_NEXT_MONTH)] = + scm_from_utf8_symbol("end-next-month"); + tmp[index_of(rdp::START_CURRENT_QUARTER)] = + scm_from_utf8_symbol("start-current-quarter"); + tmp[index_of(rdp::END_CURRENT_QUARTER)] = + scm_from_utf8_symbol("end-current-quarter"); + tmp[index_of(rdp::START_PREV_QUARTER)] = + scm_from_utf8_symbol("start-prev-quarter"); + tmp[index_of(rdp::END_PREV_QUARTER)] = + scm_from_utf8_symbol("end-prev-quarter"); + tmp[index_of(rdp::START_NEXT_QUARTER)] = + scm_from_utf8_symbol("start-next-quarter"); + tmp[index_of(rdp::END_NEXT_QUARTER)] = + scm_from_utf8_symbol("end-next-quarter"); + tmp[index_of(rdp::START_CAL_YEAR)] = + scm_from_utf8_symbol("start-cal-year"); + tmp[index_of(rdp::END_CAL_YEAR)] = + scm_from_utf8_symbol("end-cal-year"); + tmp[index_of(rdp::START_PREV_YEAR)] = + scm_from_utf8_symbol("start-prev-year"); + tmp[index_of(rdp::END_PREV_YEAR)] = + scm_from_utf8_symbol("end-prev-year"); + tmp[index_of(rdp::START_NEXT_YEAR)] = + scm_from_utf8_symbol("start-next-year"); + tmp[index_of(rdp::END_NEXT_YEAR)] = + scm_from_utf8_symbol("end-next-year"); + tmp[index_of(rdp::START_ACCOUNTING_PERIOD)] = + scm_from_utf8_symbol("start-accounting-period"); + tmp[index_of(rdp::END_ACCOUNTING_PERIOD)] = + scm_from_utf8_symbol("end-accounting-period"); + reldate_values = std::move(tmp); + } + + inline static RelativeDatePeriod scm_relative_date_get_period(SCM date) + { + init_reldate_values(); + auto reldate_scm{scm_is_pair(date) ? scm_cdr(date) : date}; + SCM reldate_val{SCM_BOOL_F}; + if (scm_is_procedure(reldate_scm)) + reldate_val = scm_call_0(reldate_scm); + if (scm_is_number(reldate_scm)) + reldate_val = reldate_scm; + if (scm_is_number(reldate_val)) + { + auto reldate_index = scm_to_int(reldate_val); + assert(reldate_index >= static_cast(RelativeDatePeriod::ABSOLUTE) && reldate_index < static_cast(relative_date_periods - 1)); + return static_cast(reldate_index); + } + const char* reldate_str; + if (scm_is_symbol(reldate_scm)) + reldate_str = scm_to_utf8_string(scm_symbol_to_string(reldate_scm)); + else + reldate_str = scm_to_utf8_string(reldate_scm); + + auto date_iter = + std::find_if(reldate_values.begin(), reldate_values.end(), + [&reldate_scm](auto val)->bool { + return scm_is_eq(val, reldate_scm) == 1; + }); + if (date_iter == reldate_values.end()) + return RelativeDatePeriod::ABSOLUTE; + return static_cast(date_iter - reldate_values.begin() - 1); + } + + inline static SCM scm_relative_date_from_period(RelativeDatePeriod period) + { + init_reldate_values(); + return reldate_values[static_cast(period) + 1]; + } + + inline static bool scm_date_absolute(SCM date) + { + if (scm_is_pair(date)) + { + if (scm_is_symbol(scm_car(date))) + { + auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(date)))}; + auto cdr{scm_cdr(date)}; + if (strcmp(car, "relative") == 0) + return false; + if (strcmp(car, "absolute") == 0) + return true; + + assert(false); + } + else + { + auto cdr{scm_cdr(date)}; + if (scm_is_symbol(cdr)) + return false; + if (scm_is_number(cdr)) + return true; + + assert(false); + } + } + return (!(scm_is_symbol(date) || scm_is_string(date))); + } + + inline static time64 scm_absolute_date_to_time64(SCM date) + { + if (scm_date_absolute(date)) + return scm_to_int64(scm_is_pair(date) ? scm_cdr(date) : date); + + return gnc_relative_date_to_time64(scm_relative_date_get_period(date)); + } + + %} + +%ignore GncOptionMultichoiceKeyType; + +%inline %{ + inline GncMultichoiceOptionIndexVec + scm_to_multichoices(const SCM new_value, + const GncOptionMultichoiceValue& option) + { + static const auto size_t_max = std::numeric_limits::max(); + static const char* empty{""}; + auto scm_to_str = [](auto item)->const char* { + if (scm_is_integer(item)) + scm_number_to_string(item, scm_from_uint(10u)); + if (scm_is_symbol(item)) + return scm_to_utf8_string(scm_symbol_to_string(item)); + else if (scm_is_string(item)) + return scm_to_utf8_string(item); + else return empty; + }; + GncMultichoiceOptionIndexVec vec; + auto choice_is_list{option.get_ui_type() == GncOptionUIType::LIST}; + if (scm_is_list(new_value)) + { + if (!choice_is_list) + throw std::invalid_argument{"Attempt to set multichoice with a list of values."}; + auto len{scm_to_size_t(scm_length(new_value))}; + for (std::size_t i = 0; i < len; ++i) + { + auto item{scm_list_ref(new_value, scm_from_size_t(i))}; + auto index{option.permissible_value_index(scm_to_str(item))}; + if (index < size_t_max) + vec.push_back(index); + } + } + else + { + auto index{option.permissible_value_index(scm_to_str(new_value))}; + if (index < size_t_max) + vec.push_back(index); + } + return vec; + } + + inline SCM scm_from_multichoices(const GncMultichoiceOptionIndexVec& indexes, + const GncOptionMultichoiceValue& option) + { + using KeyType = GncOptionMultichoiceKeyType; + auto scm_value = [](const char* value, KeyType keytype) -> SCM { + auto scm_str{scm_from_utf8_string(value)}; + switch (keytype) + { + case KeyType::SYMBOL: + return scm_string_to_symbol(scm_str); + case KeyType::STRING: + return scm_str; + case KeyType::NUMBER: + return scm_string_to_number(scm_str, + scm_from_int(10)); + }; + return SCM_BOOL_F; + }; + + if (option.get_ui_type() == GncOptionUIType::MULTICHOICE) + return scm_value(option.permissible_value(indexes[0]), + option.get_keytype(indexes[0])); + auto values{SCM_BOOL_F}; + for(auto index : indexes) + { + auto val{scm_list_1(scm_value(option.permissible_value(index), + option.get_keytype(index)))}; + if (scm_is_true(values)) + values = scm_append(scm_list_2(val, values)); + else + values = val; + } + return scm_reverse(values); + } + + static SCM + get_scm_value(const GncOptionMultichoiceValue& option) + { + + auto indexes = option.get_multiple(); + if (indexes.empty()) + indexes = option.get_default_multiple(); + if (indexes.empty()) + return SCM_BOOL_F; + return scm_from_multichoices(indexes, option); + } + + static SCM + get_scm_default_value(const GncOptionMultichoiceValue& option) + { + + auto indexes = option.get_default_multiple(); + if (indexes.empty()) + return SCM_BOOL_F; + return scm_from_multichoices(indexes, option); + } + + static SCM + get_scm_value(const GncOptionRangeValue& option) + { + auto val{option.get_value()}; + auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")}; + return scm_cons(desig, scm_from_int(val)); + } + + static SCM + get_scm_default_value(const GncOptionRangeValue& option) + { + auto val{option.get_default_value()}; + auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")}; + return scm_cons(desig, scm_from_int(val)); + } + + static SCM + get_scm_value(const GncOptionDateValue& option) + { + if (option.get_period() == RelativeDatePeriod::ABSOLUTE) + return scm_cons(scm_from_utf8_symbol("absolute"), + scm_from_value(option.get_value())); + else + return scm_cons(scm_from_utf8_symbol("relative"), + scm_relative_date_from_period(option.get_period())); + } + + static SCM + get_scm_default_value(const GncOptionDateValue& option) + { + if (option.get_default_period() == RelativeDatePeriod::ABSOLUTE) + return scm_cons(scm_from_utf8_symbol("absolute"), + scm_from_value(option.get_default_value())); + else + return scm_cons(scm_from_utf8_symbol("relative"), + scm_relative_date_from_period(option.get_default_period())); + } + +template +struct is_MultichoiceDateOrRange +{ + static constexpr bool value = + is_same_decayed_v || + is_same_decayed_v> || + is_same_decayed_v; +}; + +template +inline constexpr bool is_MultichoiceDateOrRange_v = is_MultichoiceDateOrRange::value; + +template +inline SCM return_scm_value(ValueType value) +{ + if constexpr (is_same_decayed_v) + return value; + return scm_from_value(static_cast(value)); +} + +%} +%ignore GncOptionDBCallback; +%ignore operator<(const GncOption&, const GncOption&); +%ignore operator<(const GncOptionSectionPtr&, const GncOptionSectionPtr&); + +%include "gnc-option-date.hpp" +%include "gnc-option.hpp" +%include "gnc-option-impl.hpp" +%include "gnc-optiondb.h" +%include "gnc-optiondb.hpp" +%include "gnc-optiondb-impl.hpp" +%include "gnc-option-uitype.hpp" + +%template(gnc_make_string_option) gnc_make_option; +%template(gnc_make_bool_option) gnc_make_option; +%template(gnc_make_int64_option) gnc_make_option; +%template(gnc_make_query_option) gnc_make_option; +%template(gnc_make_owner_option) gnc_make_option; +%extend GncOption { + bool is_budget_option() + { + auto uitype{$self->get_ui_type()}; + return uitype == GncOptionUIType::BUDGET; + } + + SCM get_scm_value() + { + if (!$self) + return SCM_BOOL_F; + return std::visit([](const auto& option)->SCM { + if constexpr (is_MultichoiceDateOrRange_v) + return get_scm_value(option); + auto value{option.get_value()}; + return return_scm_value(value); + }, swig_get_option($self)); + } + SCM get_scm_default_value() + { + if (!$self) + return SCM_BOOL_F; + return std::visit([](const auto& option)->SCM { + if constexpr (is_MultichoiceDateOrRange_v) + return get_scm_default_value(option); + auto value{option.get_default_value()}; + return return_scm_value(value); + }, swig_get_option($self)); + } + + SCM save_scm_value() + { + static const SCM plain_format_str{scm_from_utf8_string("~s")}; + static const SCM ticked_format_str{scm_from_utf8_string("'~a")}; +//scm_simple_format needs a scheme list of arguments to match the format +//placeholders. + return std::visit([$self] (auto &option) -> SCM { + static const auto no_value{scm_from_utf8_string("No Value")}; + if constexpr (is_same_decayed_v) + { + static const SCM list_format_str{scm_from_utf8_string("'~s")}; + auto guid_list{option.get_value()}; + if (guid_list.empty()) + return no_value; + SCM string_list{SCM_EOL}; + for(auto guid : guid_list) + { + auto guid_str{guid_to_string(&guid)}; + auto guid_scm{scm_from_utf8_string(guid_str)}; + string_list = scm_cons(guid_scm, string_list); + } + return scm_simple_format(SCM_BOOL_F, list_format_str, scm_list_1(string_list)); + + } + if constexpr (is_QofInstanceValue_v) + { + auto uitype{$self->get_ui_type()}; + auto serial{option.serialize()}; + if (serial.empty()) + return no_value; + auto value{scm_list_1(scm_from_utf8_string(serial.c_str()))}; + return scm_simple_format(SCM_BOOL_F, plain_format_str, value); + } + if constexpr (is_same_decayed_v) + { + auto comm{option.get_value()}; + auto mnemonic{gnc_commodity_get_mnemonic(comm)}; + if (gnc_commodity_is_currency(comm)) + { + auto value{scm_list_1(scm_from_utf8_string(mnemonic))}; + const SCM quoted_format_str{scm_from_utf8_string("~s")}; + return scm_simple_format(SCM_BOOL_F, quoted_format_str, value); + } + else + { + const SCM commodity_fmt{scm_from_utf8_string("~s ~s")}; + auto name_space{gnc_commodity_get_namespace(comm)}; + auto commodity_val{scm_list_2(scm_from_utf8_string(name_space), + scm_from_utf8_string(mnemonic))}; + return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val); + } + } + if constexpr (is_same_decayed_v) + { + auto serial{option.serialize()}; + if (serial.empty()) + return no_value; + auto value{scm_list_1(scm_from_utf8_string(serial.c_str()))}; + const SCM date_fmt{scm_from_utf8_string("'~a")}; + return scm_simple_format(SCM_BOOL_F, date_fmt, value); + } + + if constexpr (is_same_decayed_v>) + { + auto value{option.get_value()}; + auto guid{scm_from_utf8_string(qof_instance_to_string(qofOwnerGetOwner(value)).c_str())}; + auto type{scm_from_long(gncOwnerGetType(value))}; + return scm_simple_format(SCM_BOOL_F, ticked_format_str, + scm_list_1(scm_cons(type, guid))); + } + if constexpr (is_QofQueryValue_v) + { + QofQuery* value{const_cast(option.get_value())}; + return scm_simple_format(SCM_BOOL_F, ticked_format_str, + scm_list_1(gnc_query2scm(value))); + } + if constexpr (is_same_decayed_v || + is_same_decayed_v> || + is_same_decayed_v>) + { + auto serial{option.serialize()}; + if (serial.empty()) + { + return no_value; + } + else + { + auto scm_str{scm_list_1(scm_from_utf8_string(serial.c_str()))}; + return scm_simple_format(SCM_BOOL_F, ticked_format_str, scm_str); + } + } + if constexpr (is_same_decayed_v>) + { + auto scm_val{scm_list_1(return_scm_value(option.get_value()))}; + return scm_simple_format(SCM_BOOL_F, plain_format_str, + scm_val); + } + if constexpr (is_same_decayed_v>) + { + auto scm_val{scm_list_1(return_scm_value(option.get_value()))}; + return scm_simple_format(SCM_BOOL_F, ticked_format_str, + scm_val); + } + auto serial{option.serialize()}; + if (serial.empty()) + { + return no_value; + } + else if ($self->get_ui_type() == GncOptionUIType::COLOR) + { + auto red{static_cast(std::stoi(serial.substr(0, 2), + nullptr, 16))}; + auto blue{static_cast(std::stoi(serial.substr(2, 2), + nullptr, 16))}; + auto green{static_cast(std::stoi(serial.substr(4, 2), + nullptr, 16))}; + auto alpha{serial.length() > 7 ? + static_cast(std::stoi(serial.substr(6, 2), + nullptr, 16)) : + 255.0}; + std::ostringstream outs; + outs << "(" << std::fixed << std::setprecision(1) << red << + " " << blue << " " << green << " " << alpha << ")"; + auto scm_out{scm_list_1(scm_from_utf8_string(outs.str().c_str()))}; + return scm_simple_format(SCM_BOOL_F, ticked_format_str, + scm_out); + } + else + { + auto scm_str{scm_list_1(scm_from_utf8_string(serial.c_str()))}; + return scm_simple_format(SCM_BOOL_F, plain_format_str, scm_str); + } + }, swig_get_option($self)); + } + + void set_value_from_scm(SCM new_value) + { + if (!$self) + return; + try { + std::visit([new_value](auto& option) { + if constexpr (is_same_decayed_v) + { + if (scm_date_absolute(new_value)) + option.set_value(scm_absolute_date_to_time64(new_value)); + else + option.set_value(scm_relative_date_get_period(new_value)); + return; + } + + if constexpr (is_same_decayed_v) + { + option.set_multiple(scm_to_multichoices(new_value, option)); + return; + } + + if constexpr (is_same_decayed_v>) + { + if (scm_is_pair(new_value)) + option.set_value(scm_to_int(scm_cdr(new_value))); + else + option.set_value(scm_to_int(new_value)); + return; + } + if constexpr (is_same_decayed_v) + { + if (scm_list_p(new_value) == SCM_BOOL_F) + { + if (scm_is_string(new_value)) + { + auto strval{scm_to_utf8_string(new_value)}; + auto val{qof_instance_from_string(strval, option.get_ui_type())}; + option.set_value(GNC_COMMODITY(val)); + return; + } + option.set_value(scm_to_value(new_value)); + return; + } + auto len{scm_to_uint(scm_length(new_value))}; + std::string mnemonic{scm_to_utf8_string(scm_list_ref(new_value, scm_from_uint(0)))}; + if (len > 1) + { + std::string name_space{scm_to_utf8_string(scm_list_ref(new_value, scm_from_uint(1)))}; + option.deserialize(name_space + ":" + mnemonic); + } + else + { + option.deserialize(mnemonic); + } + return; + } + if constexpr (is_QofInstanceValue_v) + { + if (scm_is_string(new_value)) + { + auto strval{scm_to_utf8_string(new_value)}; + auto val{qof_instance_from_string(strval, option.get_ui_type())}; + option.set_value(val); + } + else + { + auto val{scm_to_value(new_value)}; + option.set_value(val); + } + return; + } + if constexpr (is_QofQueryValue_v) + { + if (scm_is_pair(new_value)) + { + auto val{gnc_scm2query(new_value)}; + option.set_value(val); + } + else + { + auto val{scm_to_value(new_value)}; + option.set_value(val); + } + return; + } + if constexpr (is_same_decayed_v) + { + if (scm_is_string(new_value)) + { + auto strval{scm_to_utf8_string(new_value)}; + GncGUID guid; + string_to_guid(strval, &guid); + auto book{gnc_get_current_book()}; + option.set_value(xaccAccountLookup(&guid, book)); + } + else + { + auto val{scm_to_value(new_value)}; + option.set_value(GNC_ACCOUNT(val)); + } + return; + } + auto value{scm_to_value>(new_value)}; //Can't inline, set_value takes arg by reference. + option.set_value(static_cast(value)); + }, swig_get_option($self)); + } + catch(const std::invalid_argument& err) + { + PERR("Option %s:%s failed to set value: %s", + $self->get_section().c_str(), $self->get_name().c_str(), + err.what()); + } + } + + void set_default_value_from_scm(SCM new_value) + { + if (!$self) + return; + try { + std::visit([new_value](auto& option) { + if constexpr (is_same_decayed_v) + { + if (scm_date_absolute(new_value)) + option.set_default_value(scm_absolute_date_to_time64(new_value)); + else + option.set_default_value(scm_relative_date_get_period(new_value)); + return; + } + if constexpr (is_same_decayed_v) + { + option.set_default_multiple(scm_to_multichoices(new_value, + option)); + return; + } + if constexpr (is_same_decayed_v>) + { + if (scm_is_pair(new_value)) + option.set_default_value(scm_to_int(scm_cdr(new_value))); + else + option.set_default_value(scm_to_int(new_value)); + return; + } + if constexpr (is_same_decayed_v) + { + auto comm{scm_to_value(new_value)}; + option.set_default_value(comm); + } + if constexpr (is_QofInstanceValue_v) + { + if (scm_is_string(new_value)) + { + auto strval{scm_to_utf8_string(new_value)}; + auto val{qof_instance_from_string(strval, option.get_ui_type())}; + option.set_default_value(val); + } + else + { + auto val{scm_to_value(new_value)}; + option.set_default_value(val); + } + return; + } + if constexpr (is_same_decayed_v) + { + if (scm_is_string(new_value)) + { + auto strval{scm_to_utf8_string(new_value)}; + GncGUID guid; + string_to_guid(strval, &guid); + auto book{gnc_get_current_book()}; + option.set_default_value(xaccAccountLookup(&guid, book)); + } + else + { + auto val{scm_to_value(new_value)}; + option.set_default_value(val); + } + return; + } + auto value{scm_to_value>(new_value)}; //Can't inline, set_value takes arg by reference. + option.set_default_value(value); + }, swig_get_option($self)); + } + catch(const std::invalid_argument& err) + { + PERR("Option %s:%s failed to set default value: %s", + $self->get_section().c_str(), $self->get_name().c_str(), err.what()); + } + } + + SCM get_type() + { + if (!self) + return SCM_BOOL_F; + return std::visit([](const auto& option)->SCM { + if constexpr (is_same_decayed_v) + return scm_c_eval_string("'multichoice"); + else if constexpr (std::is_same_v) + return scm_c_eval_string("'boolean"); + else + return SCM_BOOL_F; + }, swig_get_option($self)); + } +}; + +%extend GncOptionDB { + %template(set_option_string) set_option; + %template(set_option_int) set_option; + %template(set_option_time64) set_option; +}; + +%template(gnc_register_number_range_option_double) gnc_register_number_range_option; +%template(gnc_register_number_range_option_int) gnc_register_number_range_option; + +%inline %{ + /* qof_book_set_data isn't exported by sw-engine and we need it to set up a + * commodity namespace table to test currencies.*/ + static void + test_book_set_data(QofBook* book, const char* key, void* data) + { + qof_book_set_data(book, key, data); + } + + static void + test_book_clear_data(QofBook* book, const char* key) + { + qof_book_set_data(book, key, nullptr); + } + + static void + test_book_set_default_budget(QofBook* book, GncBudget* budget) + { + auto budget_guid{gnc_budget_get_guid(budget)}; + qof_book_begin_edit(book); + qof_instance_set(QOF_INSTANCE(book), "default-budget", + budget_guid, nullptr); + qof_book_commit_edit(book); + } + + static GncOption* + gnc_make_qofinstance_option(const char* section, + const char* name, const char* key, + const char* doc_string, + const QofInstance* value, + GncOptionUIType ui_type) + { + try { + return new GncOption(GncOptionQofInstanceValue{section, name, key, + doc_string, + value, ui_type}); + } + catch (const std::exception& err) + { + std::cerr << "Make QofInstance option threw unexpected exception" + << err.what() << ", option not created." << std::endl; + return nullptr; + } + } + + static GncOption* + gnc_make_account_list_option(const char* section, + const char* name, const char* key, + const char* doc_string, + const GncOptionAccountList& value) + { + try { + return new GncOption{GncOptionAccountListValue{section, name, key, + doc_string, GncOptionUIType::ACCOUNT_LIST, value}}; + } + catch (const std::exception& err) + { + std::cerr << "Make account list option threw unexpected exception " << err.what() << ", option not created." << std::endl; + return nullptr; + } + } + + static GncOption* + gnc_make_account_list_limited_option(const char* section, + const char* name, + const char* key, + const char* doc_string, + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed) + { + try + { + return new GncOption{GncOptionAccountListValue{section, name, key, + doc_string, GncOptionUIType::ACCOUNT_LIST, value, + std::move(allowed)}}; + } + catch (const std::invalid_argument& err) + { + std::cerr << "Account List Limited Option, value failed validation, option not created.\n"; + return nullptr; + } + } + + static GncOption* + gnc_make_account_sel_limited_option(const char* section, const char* name, + const char* key, const char* doc_string, + const Account* value, + GncOptionAccountTypeList&& allowed) + { + try + { + return new GncOption{GncOptionAccountSelValue{section, name, key, + doc_string, GncOptionUIType::ACCOUNT_SEL, value, + std::move(allowed)}}; + } + catch (const std::invalid_argument& err) + { + std::cerr <<"Account Sel Limited Option, value failed validation, option not creted.\n"; + return nullptr; + } + } + + static GncOption* + gnc_make_date_option(const char* section, const char* name, const char* key, + const char* doc_string, const SCM default_val, + RelativeDatePeriodVec& period_set, bool both) + { + + try { + auto absolute{scm_date_absolute(default_val)}; + auto ui_type = both ? GncOptionUIType::DATE_BOTH : absolute ? + GncOptionUIType::DATE_ABSOLUTE : GncOptionUIType::DATE_RELATIVE; + if (!period_set.empty()) + { + auto retval{new GncOption{GncOptionDateValue(section, name, key, + doc_string, ui_type, + period_set)}}; + if (absolute) + retval->set_default_value(scm_absolute_date_to_time64(default_val)); + else + retval->set_default_value(scm_relative_date_get_period(default_val)); + return retval; + } + + if (absolute) + { + auto value{scm_absolute_date_to_time64(default_val)}; + auto retval{new GncOption{GncOptionDateValue(section, name, key, + doc_string, ui_type, + value)}}; + return retval; + } + auto value{scm_relative_date_get_period(default_val)}; + auto retval{new GncOption{GncOptionDateValue(section, name, key, + doc_string, ui_type, + period_set)}}; + return retval; + } + catch (const std::invalid_argument& err) + { + std::cerr <<"Date Option, value failed validation, option not creted.\n"; + return nullptr; + } + } + + static GncOption* + gnc_make_multichoice_option(const char* section, const char* name, + const char* key, const char* doc_string, + const char* default_val, + GncMultichoiceOptionChoices&& choices) + { + try { + std::string defval{default_val}; + auto found{std::find_if(choices.begin(), choices.end(), + [&defval](auto& choice)->bool { + return defval == std::get<0>(choice); + })}; + if (found == choices.end()) + defval = (choices.empty() ? std::string{"None"} : + std::get<0>(choices.at(0))); + return new GncOption{GncOptionMultichoiceValue{section, name, key, + doc_string, defval.c_str(), std::move(choices), + GncOptionUIType::MULTICHOICE}}; + } + catch (const std::exception& err) + { + std::cerr << "Make multichoice option threw unexpected exception " << err.what() << ", option not created." << std::endl; + return nullptr; + } + } + + static GncOption* + gnc_make_list_option(const char* section, const char* name, const char* key, + const char* doc_string, + GncMultichoiceOptionIndexVec indexes, + GncMultichoiceOptionChoices&& list) + { + try { + return new GncOption{GncOptionMultichoiceValue{section, name, key, + doc_string, std::move(indexes), std::move(list), + GncOptionUIType::LIST}}; + } + catch (const std::exception& err) + { + std::cerr << "Make list option threw unexpected exception " << err.what() << ", option not created." << std::endl; + return nullptr; + } + } + + static GncOption* + gnc_make_range_value_option(const char* section, const char* name, + const char* key, const char* doc_string, + double value, double min, double max, + double step) + { + try + { + return new GncOption{GncOptionRangeValue{section, name, key, + doc_string, value, min, + max, step}}; + } + catch(const std::invalid_argument& err) + { + std::cerr <<"Number Range Option " << err.what() << ", option not created.\n"; + return nullptr; + } + } + + static GncOption* + gnc_make_plot_size_option(const char* section, const char* name, + const char* key, const char* doc_string, + int value, int min, int max, int step) + { + try + { + return new GncOption{GncOptionRangeValue{section, name, key, + doc_string, value, min, + max, step}}; + } + catch(const std::invalid_argument& err) + { + std::cerr <<"Plot Size Option " << err.what() << ", option not created.\n"; + return nullptr; + } + } + + static GncOption* + gnc_make_commodity_option(const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity *value) + { + return new GncOption{GncOptionCommodityValue{ + section, name, key, doc_string, value, + GncOptionUIType::COMMODITY}}; + } + + static GncOption* + gnc_make_commodity_option(const char* section, const char* name, + const char* key, const char* doc_string, + const char *value) + { + gnc_commodity* commodity{}; + const auto book{qof_session_get_book(gnc_get_current_session())}; + const auto commodity_table{gnc_commodity_table_get_table(book)}; + const auto namespaces{gnc_commodity_table_get_namespaces(commodity_table)}; + for (auto node = namespaces; node && commodity == nullptr; + node = g_list_next(node)) + { + commodity = gnc_commodity_table_lookup(commodity_table, + (const char*)(node->data), + value); + + if (commodity) + return gnc_make_commodity_option(section, name, key, doc_string, + commodity); + } + return nullptr; + } + + static GncOption* + gnc_make_currency_option(const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity *value) + { + try + { + return new GncOption{GncOptionCommodityValue{ + section, name, key, doc_string, value, + GncOptionUIType::CURRENCY}}; + } + catch (const std::exception& err) + { + std::cerr << "gnc_make_currency_option threw " << err.what() << + ", option not created." << std::endl; + return nullptr; + } + } + + using GncOptionDBPtr = std::unique_ptr; +/* Forward decls */ + GncOptionDBPtr new_gnc_optiondb(); + GncOption* gnc_lookup_option(const GncOptionDBPtr& optiondb, + const char* section, const char* name); + + static SCM + gnc_option_value(const GncOptionDBPtr& optiondb, const char* section, + const char* name) + { + auto db_opt = optiondb->find_option(section, name); + if (!db_opt) + return SCM_BOOL_F; + return GncOption_get_scm_value(db_opt); + } + + static SCM + gnc_option_db_lookup_value(const GncOptionDB* optiondb, const char* section, + const char* name) + { + auto db_opt = optiondb->find_option(section, name); + if (!db_opt) + return SCM_BOOL_F; + return GncOption_get_scm_value(const_cast(db_opt)); + } + + static SCM + gnc_option_default_value(const GncOptionDBPtr& optiondb, + const char* section, const char* name) + { + auto db_opt{optiondb->find_option(section, name)}; + if (!db_opt) + return SCM_BOOL_F; + return GncOption_get_scm_default_value(db_opt); + } + + static void + gnc_set_option(const GncOptionDBPtr& optiondb, const char* section, + const char* name, SCM new_value) + { + auto db_opt{optiondb->find_option(section, name)}; + if (!db_opt) + { + std::cerr <<"Attempt to write non-existent option " << section + << "/" << name; + return; + } + try + { + GncOption_set_value_from_scm(db_opt, new_value); + } + catch(const std::invalid_argument& err) + { + std::cerr << "Failed to set option " << section << "/" << name + << ": " << err.what() << "\n"; + } + } + + GncOptionDBPtr + new_gnc_optiondb() + { + auto db_ptr{std::make_unique()}; + return db_ptr; + } + + GncOption* + gnc_lookup_option(const GncOptionDBPtr& optiondb, const char* section, + const char* name) + { + return optiondb->find_option(section, name); + } + + static void + gnc_option_db_set_option_selectable_by_name(GncOptionDBPtr& odb, + const char* section, + const char* name, + bool selectable) + { + auto option{odb->find_option(section, name)}; + option->set_ui_item_selectable(selectable); + } + + static void + gnc_optiondb_foreach(GncOptionDBPtr& odb, SCM thunk) + { + odb->foreach_section( + [&thunk](const GncOptionSectionPtr& section) + { + section->foreach_option( + [&thunk](auto& option) + { + auto optvoidptr{reinterpret_cast( + const_cast(&option))}; + auto scm_opt{scm_from_pointer(optvoidptr, nullptr)}; + scm_call_1(thunk, scm_opt); + }); + }); + } + + /** Tailred for gnc:generate-restore-forms. + * @param section_op A function to be called on each section name + * @param option_op a function to be called on each option + */ + static void + gnc_optiondb_foreach2(GncOptionDBPtr& odb, SCM section_op, + SCM option_op) + { + odb->foreach_section( + [§ion_op, &option_op](const GncOptionSectionPtr& section) + { + auto scm_name{scm_from_utf8_string(section->get_name().c_str())}; + scm_call_1(section_op, scm_name); + section->foreach_option( + [&option_op](auto& option) + { + auto optvoidptr{reinterpret_cast( + const_cast(&option))}; + auto scm_opt{scm_from_pointer(optvoidptr, nullptr)}; + scm_call_1(option_op, scm_opt); + }); + }); + } +%} + +#endif //SWIGGUILE diff --git a/libgnucash/app-utils/gnc-ui-util.c b/libgnucash/app-utils/gnc-ui-util.c index cb20e044a5..7c188c4ea5 100644 --- a/libgnucash/app-utils/gnc-ui-util.c +++ b/libgnucash/app-utils/gnc-ui-util.c @@ -421,122 +421,6 @@ gnc_get_current_book_tax_type (void) } } -/** Returns TRUE if both book-currency and default gain/loss policy KVPs exist - * and are valid and trading accounts are not used. */ -gboolean -gnc_book_use_book_currency (QofBook *book) -{ - const gchar *policy; - const gchar *currency; - - if (!book) return FALSE; - - policy = qof_book_get_default_gains_policy (book); - currency = qof_book_get_book_currency_name (book); - - /* If either a default gain/loss policy or a book-currency does not exist, - book-currency accounting method not valid */ - if (!policy || !currency) - return FALSE; - - /* If both exist, both must be valid */ - if (!gnc_valid_policy_name (policy) || !gnc_commodity_table_lookup - (gnc_commodity_table_get_table - (gnc_get_current_book()), - GNC_COMMODITY_NS_CURRENCY, - currency)) - return FALSE; - - /* If both exist and are valid, there must be no trading accounts flag */ - if (qof_book_use_trading_accounts (book)) - return FALSE; - - return TRUE; -} - -/** Returns pointer to Book Currency name for book or NULL; determines - * that both book-currency and default gain/loss policy KVPs exist and that - * both are valid, a requirement for the 'book-currency' currency accounting - * method to apply. */ -const gchar * -gnc_book_get_book_currency_name (QofBook *book) -{ - if (!book) return NULL; - - if (gnc_book_use_book_currency (book)) - return qof_book_get_book_currency_name (book); - - return NULL; -} - -/** Returns pointer to Book Currency for book or NULL; determines - * that both book-currency and default gain/loss policy KVPs exist and that - * both are valid, a requirement for the 'book-currency' currency accounting - * method to apply. */ -gnc_commodity * -gnc_book_get_book_currency (QofBook *book) -{ - if (!book) return NULL; - - if (gnc_book_use_book_currency (book)) - return gnc_commodity_table_lookup - (gnc_commodity_table_get_table(book), - GNC_COMMODITY_NS_CURRENCY, - qof_book_get_book_currency_name (book)); - - return NULL; -} - -/** Returns pointer to default gain/loss policy for book or NULL; determines - * that both book-currency and default gain/loss policy KVPs exist and that - * both are valid, a requirement for the 'book-currency' currency accounting - * method to apply. */ -const gchar * -gnc_book_get_default_gains_policy (QofBook *book) -{ - if (!book) return NULL; - - if (gnc_book_use_book_currency (book)) - return qof_book_get_default_gains_policy (book); - - return NULL; -} - -/** Returns pointer to default gain/loss account for book or NULL; determines - * that both book-currency and default gain/loss policy KVPs exist and that - * both are valid, a requirement for the 'book-currency' currency accounting - * method to apply. Also, account must not be hidden or a placeholder, and - * must be of same currency as book-currency and income or expense type */ -Account * -gnc_book_get_default_gain_loss_acct (QofBook *book) -{ - Account *gains_account = NULL; - - if (!book) return NULL; - - if (gnc_book_use_book_currency (book)) - { - GncGUID *guid = qof_book_get_default_gain_loss_acct_guid (book); - gains_account = xaccAccountLookup (guid, book); - guid_free (guid); - } - - if (gains_account && - !xaccAccountGetPlaceholder(gains_account) && - !xaccAccountGetHidden(gains_account) && - (gnc_commodity_equal(xaccAccountGetCommodity(gains_account), - gnc_book_get_book_currency(book))) && - ((xaccAccountGetType(gains_account) == ACCT_TYPE_INCOME) || - (xaccAccountGetType(gains_account) == ACCT_TYPE_EXPENSE))) - { - return gains_account; - } - else - { - return NULL; - } -} - Account * gnc_get_current_root_account (void) { @@ -1151,9 +1035,6 @@ gnc_default_currency_common (gchar *requested_currency, GNC_COMMODITY_NS_CURRENCY, requested_currency); - if (gnc_book_use_book_currency (gnc_get_current_book ())) - return gnc_book_get_book_currency (gnc_get_current_book ()); - if (gnc_prefs_get_bool (section, GNC_PREF_CURRENCY_CHOICE_OTHER)) { mnemonic = gnc_prefs_get_string(section, GNC_PREF_CURRENCY_OTHER); diff --git a/libgnucash/app-utils/gnc-ui-util.h b/libgnucash/app-utils/gnc-ui-util.h index b13d44588a..6ec6ead487 100644 --- a/libgnucash/app-utils/gnc-ui-util.h +++ b/libgnucash/app-utils/gnc-ui-util.h @@ -87,40 +87,6 @@ const gchar * gnc_get_current_book_tax_type (void); * callbacks when num_field_source book option changes so that * registers/reports can update themselves; sets feature flag */ void gnc_book_option_num_field_source_change_cb (gboolean num_action); - -/** Calls gnc_book_option_book_currency_selected to initiate registered - * callbacks when currency accounting book option changes to book-currency so - * that registers/reports can update themselves; sets feature flag */ -void gnc_book_option_book_currency_selected_cb (gboolean use_book_currency); - -/** Returns TRUE if both book-currency and default gain/loss policy KVPs exist - * and are valid and trading accounts are not used */ -gboolean gnc_book_use_book_currency (QofBook *book); - -/** Returns pointer to Book Currency name for book or NULL; determines - * that both book-currency and default gain/loss policy KVPs exist and that - * both are valid, a requirement for the 'book-currency' currency accounting - * method to apply. */ -const gchar * gnc_book_get_book_currency_name (QofBook *book); - -/** Returns pointer to Book Currency for book or NULL; determines - * that both book-currency and default gain/loss policy KVPs exist and that - * both are valid, a requirement for the 'book-currency' currency accounting - * method to apply. */ -gnc_commodity * gnc_book_get_book_currency (QofBook *book); - -/** Returns pointer to default gain/loss policy for book or NULL; determines - * that both book-currency and default gain/loss policy KVPs exist and that - * both are valid, a requirement for the 'book-currency' currency accounting - * method to apply. */ -const gchar * gnc_book_get_default_gains_policy (QofBook *book); - -/** Returns pointer to default gain/loss account for book or NULL; determines - * that both book-currency and default gain/loss policy KVPs exist and that - * both are valid, a requirement for the 'book-currency' currency accounting - * method to apply. */ -Account * gnc_book_get_default_gain_loss_acct (QofBook *book); - Account * gnc_get_current_root_account (void); gnc_commodity_table * gnc_get_current_commodities (void); diff --git a/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c deleted file mode 100644 index 05c6e621ef..0000000000 --- a/libgnucash/app-utils/option-util.c +++ /dev/null @@ -1,2823 +0,0 @@ -/********************************************************************\ - * option-util.c -- GNOME<->guile option interface * - * Copyright (C) 2000 Dave Peticolas * - * Copyright (C) 2017 Aaron Laws * - * * - * This program 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 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * -\********************************************************************/ - -#include - -#include -#include -#include - -#include "Account.h" -#include "option-util.h" -#include "gnc-guile-utils.h" -#include "qof.h" -#include "swig-runtime.h" -#include "guile-mappings.h" - - -/* TODO: - - - for make-date-option, there seems to be only support for getting, - not for setting. -*/ - - -/****** Structures *************************************************/ - -struct gnc_option -{ - /* Handle to the scheme-side option */ - SCM guile_option; - - /* Flag to indicate change by the UI */ - gboolean changed; - - /* The widget which is holding this option */ - gpointer widget; - - /* The option db which holds this option */ - GNCOptionDB *odb; -}; - -struct gnc_option_section -{ - char * section_name; - - GSList * options; -}; - -struct gnc_option_db -{ - SCM guile_options; - - GSList *option_sections; - - gboolean options_dirty; - - GNCOptionDBHandle handle; - - GNCOptionGetUIValue get_ui_value; - GNCOptionSetUIValue set_ui_value; - GNCOptionSetSelectable set_selectable; -}; - -typedef struct _Getters Getters; -struct _Getters -{ - SCM section; - SCM name; - SCM type; - SCM sort_tag; - SCM documentation; - SCM getter; - SCM setter; - SCM default_getter; - SCM value_validator; - SCM option_data; - SCM index_to_name; - SCM index_to_value; - SCM value_to_index; - SCM number_of_indices; - SCM option_widget_changed_cb; - SCM date_option_subtype; - SCM date_option_show_time; - SCM date_option_value_type; - SCM date_option_value_absolute; - SCM date_option_value_relative; - SCM plot_size_option_value_type; - SCM plot_size_option_value; - SCM currency_accounting_option_currency_doc_string; - SCM currency_accounting_option_default_currency; - SCM currency_accounting_option_policy_doc_string; - SCM currency_accounting_option_default_policy; - SCM currency_accounting_option_gain_loss_account_doc_string; - SCM currency_accounting_option_method; - SCM currency_accounting_option_book_currency; - SCM currency_accounting_option_selected_default_policy; - SCM currency_accounting_option_selected_default_gain_loss_account; -}; - - -/****** Globals ****************************************************/ - -static Getters getters = {0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - -/* This static indicates the debugging module this .o belongs to. */ -static QofLogModule log_module = GNC_MOD_GUI; - -static GHashTable *option_dbs = NULL; -static int last_db_handle = 0; - - -/*******************************************************************/ -void -gnc_option_set_changed (GNCOption *option, gboolean changed) -{ - g_return_if_fail (option != NULL); - option->changed = changed; -} - -gpointer -gnc_option_get_widget (GNCOption *option) -{ - if (!option) return NULL; - return option->widget; -} - -void -gnc_option_set_widget (GNCOption *option, gpointer widget) -{ - g_return_if_fail (option != NULL); - option->widget = widget; -} - -SCM -gnc_option_get_ui_value (GNCOption *option) -{ - g_return_val_if_fail (option != NULL, SCM_UNDEFINED); - g_return_val_if_fail (option->odb != NULL, SCM_UNDEFINED); - g_return_val_if_fail (option->odb->get_ui_value != NULL, SCM_UNDEFINED); - - return option->odb->get_ui_value (option); -} - -void -gnc_option_set_ui_value (GNCOption *option, gboolean use_default) -{ - g_return_if_fail (option != NULL); - g_return_if_fail (option->odb != NULL); - - if (!option->odb->set_ui_value) - return; - - option->odb->set_ui_value (option, use_default); -} - -void -gnc_option_set_selectable (GNCOption *option, gboolean selectable) -{ - g_return_if_fail (option != NULL); - g_return_if_fail (option->odb != NULL); - g_return_if_fail (option->odb->set_selectable != NULL); - - option->odb->set_selectable (option, selectable); -} - -/********************************************************************\ - * gnc_option_db_init * - * initialize the options structures from the guile side * - * * - * Args: odb - the option database to initialize * - * Returns: nothing * -\********************************************************************/ -static void -gnc_option_db_init (GNCOptionDB *odb) -{ - SCM func = scm_c_eval_string ("gnc:send-options"); - - scm_call_2 (func, scm_from_int (odb->handle), odb->guile_options); -} - -/********************************************************************\ - * gnc_option_db_new * - * allocate a new option database and initialize its values * - * * - * Args: guile_options - SCM handle to options * - * Returns: a new option database * -\********************************************************************/ -GNCOptionDB * -gnc_option_db_new (SCM guile_options) -{ - GNCOptionDB *odb; - GNCOptionDB *lookup; - - odb = g_new0 (GNCOptionDB, 1); - - odb->guile_options = guile_options; - scm_gc_protect_object (guile_options); - - odb->option_sections = NULL; - odb->options_dirty = FALSE; - - if (option_dbs == NULL) - option_dbs = g_hash_table_new (g_int_hash, g_int_equal); - - do - { - odb->handle = last_db_handle++; - lookup = g_hash_table_lookup (option_dbs, &odb->handle); - } - while (lookup != NULL); - - g_hash_table_insert (option_dbs, &odb->handle, odb); - - gnc_option_db_init (odb); - - return odb; -} - -/* Create an option DB for a particular data type */ -/* For now, this is global, just like when it was in guile. - But, it should be make per-book. */ -static GHashTable *kvp_registry = NULL; - -static void -init_table(void) -{ - if (!kvp_registry) - kvp_registry = g_hash_table_new (g_str_hash, g_str_equal); -} - - -/* create a new options object for the requested type */ -static SCM -gnc_make_kvp_options (QofIdType id_type) -{ - GList *list, *p; - SCM gnc_new_options = SCM_UNDEFINED; - SCM options = SCM_UNDEFINED; - - init_table(); - list = g_hash_table_lookup (kvp_registry, id_type); - gnc_new_options = scm_c_eval_string ("gnc:new-options"); - options = scm_call_0 (gnc_new_options); - - for (p = list; p; p = p->next) - { - SCM generator = p->data; - scm_call_1 (generator, options); - } - return options; -} - -GNCOptionDB * -gnc_option_db_new_for_type (QofIdType id_type) -{ - SCM options; - - if (!id_type) return NULL; - options = gnc_make_kvp_options (id_type); - return gnc_option_db_new (options); -} - -void -gnc_option_db_load (GNCOptionDB* odb, QofBook *book) -{ - static SCM kvp_to_scm = SCM_UNDEFINED; - SCM scm_book; - - if (!odb || !book) return; - - if (kvp_to_scm == SCM_UNDEFINED) - { - kvp_to_scm = scm_c_eval_string ("gnc:options-kvp->scm"); - if (!scm_is_procedure (kvp_to_scm)) - { - PERR ("not a procedure\n"); - kvp_to_scm = SCM_UNDEFINED; - return; - } - } - - scm_book = SWIG_NewPointerObj (book, SWIG_TypeQuery ("_p_QofBook"), 0); - - scm_call_2 (kvp_to_scm, odb->guile_options, scm_book); -} - -void -gnc_option_db_save (GNCOptionDB* odb, QofBook *book, gboolean clear_all) -{ - static SCM scm_to_kvp = SCM_UNDEFINED; - SCM scm_book; - SCM scm_clear_all; - - if (!odb || !book) return; - - if (scm_to_kvp == SCM_UNDEFINED) - { - scm_to_kvp = scm_c_eval_string ("gnc:options-scm->kvp"); - if (!scm_is_procedure (scm_to_kvp)) - { - PERR ("not a procedure\n"); - scm_to_kvp = SCM_UNDEFINED; - return; - } - } - - scm_book = SWIG_NewPointerObj (book, SWIG_TypeQuery ("_p_QofBook"), 0); - scm_clear_all = scm_from_bool (clear_all); - - scm_call_3 (scm_to_kvp, odb->guile_options, scm_book, scm_clear_all); -} - -/********************************************************************\ - * gnc_option_db_destroy * - * unregister the scheme options and free all the memory * - * associated with an option database, including the database * - * itself * - * * - * Args: options database to destroy * - * Returns: nothing * -\********************************************************************/ -void -gnc_option_db_destroy (GNCOptionDB *odb) -{ - GSList *snode; - - if (odb == NULL) - return; - - for (snode = odb->option_sections; snode; snode = snode->next) - { - GNCOptionSection *section = snode->data; - GSList *onode; - - for (onode = section->options; onode; onode = onode->next) - { - GNCOption *option = onode->data; - - scm_gc_unprotect_object (option->guile_option); - g_free (option); - } - - /* Free the option list */ - g_slist_free (section->options); - section->options = NULL; - - if (section->section_name != NULL) - free (section->section_name); - section->section_name = NULL; - - g_free (section); - } - - g_slist_free (odb->option_sections); - - odb->option_sections = NULL; - odb->options_dirty = FALSE; - - g_hash_table_remove (option_dbs, &odb->handle); - - if (g_hash_table_size (option_dbs) == 0) - { - g_hash_table_destroy (option_dbs); - option_dbs = NULL; - } - - scm_gc_unprotect_object (odb->guile_options); - odb->guile_options = SCM_UNDEFINED; - - g_free(odb); -} - -void -gnc_option_db_set_ui_callbacks (GNCOptionDB *odb, - GNCOptionGetUIValue get_ui_value, - GNCOptionSetUIValue set_ui_value, - GNCOptionSetSelectable set_selectable) -{ - g_return_if_fail (odb != NULL); - - odb->get_ui_value = get_ui_value; - odb->set_ui_value = set_ui_value; - odb->set_selectable = set_selectable; -} - -/********************************************************************\ - * gnc_option_db_register_change_callback * - * register a callback to be called whenever an option changes * - * * - * Args: odb - the option database to register with * - * callback - the callback function to register * - * user_data - the user data for the callback * - * section - the section to get callbacks for. * - * If NULL, get callbacks for any section changes.* - * name - the option name to get callbacks for. * - * If NULL, get callbacks for any option in the * - * section. Only used if section is non-NULL. * - * Returns: SCM handle for unregistering * -\********************************************************************/ -SCM -gnc_option_db_register_change_callback (GNCOptionDB *odb, - GNCOptionChangeCallback callback, - gpointer data, - const char *section, - const char *name) -{ - SCM register_proc; - SCM arg; - SCM args; - - if (!odb || !callback) - return SCM_UNDEFINED; - - /* Get the register procedure */ - register_proc = scm_c_eval_string ("gnc:options-register-c-callback"); - if (!scm_is_procedure (register_proc)) - { - PERR("not a procedure\n"); - return SCM_UNDEFINED; - } - - /* Now build the args list for apply */ - args = SCM_EOL; - - /* first the guile options database */ - args = scm_cons (odb->guile_options, args); - - /* next the data */ - arg = SWIG_NewPointerObj (data, SWIG_TypeQuery ("_p_void"), 0); - args = scm_cons (arg, args); - - /* next the callback */ - arg = SWIG_NewPointerObj ( - callback, SWIG_TypeQuery ("GNCOptionChangeCallback"), 0); - args = scm_cons (arg, args); - - /* next the name */ - if (name == NULL) - { - arg = SCM_BOOL_F; - } - else - { - arg = scm_from_utf8_string (name); - } - args = scm_cons (arg, args); - - /* next the section */ - if (section == NULL) - { - arg = SCM_BOOL_F; - } - else - { - arg = scm_from_utf8_string (section); - } - args = scm_cons (arg, args); - - /* now apply the procedure */ - return scm_apply (register_proc, args, SCM_EOL); -} - -/********************************************************************\ - * gnc_option_db_unregister_change_callback_id * - * unregister the change callback associated with the given id * - * * - * Args: odb - the option database to register with * - * callback - the callback function to register * - * Returns: nothing * -\********************************************************************/ -void -gnc_option_db_unregister_change_callback_id (GNCOptionDB *odb, SCM callback_id) -{ - SCM proc; - - if (callback_id == SCM_UNDEFINED) - return; - - proc = scm_c_eval_string ("gnc:options-unregister-callback-id"); - if (!scm_is_procedure (proc)) - { - PERR("not a procedure\n"); - return; - } - - scm_call_2 (proc, callback_id, odb->guile_options); -} - -void -gncp_option_invoke_callback (GNCOptionChangeCallback callback, void *data) -{ - callback (data); -} - -static void -gnc_call_option_change_callbacks (GNCOptionDB *odb) -{ - SCM proc; - - proc = scm_c_eval_string ("gnc:options-run-callbacks"); - if (!scm_is_procedure (proc)) - { - PERR("not a procedure\n"); - return; - } - - scm_call_1 (proc, odb->guile_options); -} - -static void -initialize_getters(void) -{ - static gboolean getters_initialized = FALSE; - - if (getters_initialized) - return; - - getters.section = scm_c_eval_string ("gnc:option-section"); - getters.name = scm_c_eval_string ("gnc:option-name"); - getters.type = scm_c_eval_string ("gnc:option-type"); - getters.sort_tag = scm_c_eval_string ("gnc:option-sort-tag"); - getters.documentation = - scm_c_eval_string ("gnc:option-documentation"); - getters.getter = scm_c_eval_string ("gnc:option-getter"); - getters.setter = scm_c_eval_string ("gnc:option-setter"); - getters.default_getter = - scm_c_eval_string ("gnc:option-default-getter"); - getters.value_validator = - scm_c_eval_string ("gnc:option-value-validator"); - getters.option_data = scm_c_eval_string ("gnc:option-data"); - getters.index_to_name = scm_c_eval_string ("gnc:option-index-get-name"); - getters.number_of_indices = scm_c_eval_string ("gnc:option-number-of-indices"); - getters.index_to_value = scm_c_eval_string ("gnc:option-index-get-value"); - getters.value_to_index = scm_c_eval_string ("gnc:option-value-get-index"); - getters.option_widget_changed_cb = - scm_c_eval_string ("gnc:option-widget-changed-proc"); - getters.date_option_subtype = scm_c_eval_string ("gnc:date-option-get-subtype"); - getters.date_option_show_time = scm_c_eval_string ("gnc:date-option-show-time?"); - getters.date_option_value_type = scm_c_eval_string ("gnc:date-option-value-type"); - getters.date_option_value_absolute = - scm_c_eval_string ("gnc:date-option-absolute-time"); - getters.date_option_value_relative = - scm_c_eval_string ("gnc:date-option-relative-time"); - getters.plot_size_option_value_type = scm_c_eval_string ("gnc:plot-size-option-value-type"); - getters.plot_size_option_value = scm_c_eval_string ("gnc:plot-size-option-value"); - getters.currency_accounting_option_currency_doc_string = - scm_c_eval_string ("gnc:currency-accounting-option-get-curr-doc-string"); - getters.currency_accounting_option_default_currency = - scm_c_eval_string ("gnc:currency-accounting-option-get-default-curr"); - getters.currency_accounting_option_policy_doc_string = - scm_c_eval_string ("gnc:currency-accounting-option-get-policy-doc-string"); - getters.currency_accounting_option_default_policy = - scm_c_eval_string ("gnc:currency-accounting-option-get-default-policy"); - getters.currency_accounting_option_gain_loss_account_doc_string = - scm_c_eval_string ("gnc:currency-accounting-option-get-gain-loss-account-doc-string"); - getters.currency_accounting_option_method = - scm_c_eval_string ("gnc:currency-accounting-option-selected-method"); - getters.currency_accounting_option_book_currency = - scm_c_eval_string ("gnc:currency-accounting-option-selected-currency"); - getters.currency_accounting_option_selected_default_policy = - scm_c_eval_string ("gnc:currency-accounting-option-selected-policy"); - getters.currency_accounting_option_selected_default_gain_loss_account = - scm_c_eval_string ("gnc:currency-accounting-option-selected-gain-loss-account"); - - getters_initialized = TRUE; -} - -/********************************************************************\ - * gnc_option_section * - * returns the malloc'ed section name of the option, or NULL * - * if it can't be retrieved. * - * * - * Args: option - the GNCOption * - * Returns: malloc'ed char * or NULL * -\********************************************************************/ -char * -gnc_option_section (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_string (getters.section, option->guile_option); -} - -/********************************************************************\ - * gnc_option_name * - * returns the malloc'ed name of the option, or NULL * - * if it can't be retrieved. * - * * - * Args: option - the GNCOption * - * Returns: malloc'ed char * or NULL * -\********************************************************************/ -char * -gnc_option_name (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_string (getters.name, option->guile_option); -} - -/********************************************************************\ - * gnc_option_type * - * returns the malloc'ed type of the option, or NULL * - * if it can't be retrieved. * - * * - * Args: option - the GNCOption * - * Returns: malloc'ed char * or NULL * -\********************************************************************/ -char * -gnc_option_type (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_symbol_to_string (getters.type, - option->guile_option); -} - -/********************************************************************\ - * gnc_option_sort_tag * - * returns the malloc'ed sort tag of the option, or NULL * - * if it can't be retrieved. * - * * - * Args: option - the GNCOption * - * Returns: malloc'ed char * or NULL * -\********************************************************************/ -char * -gnc_option_sort_tag (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_string (getters.sort_tag, option->guile_option); -} - -/********************************************************************\ - * gnc_option_documentation * - * returns the malloc'ed documentation string of the option, or * - * NULL if it can't be retrieved. * - * * - * Args: option - the GNCOption * - * Returns: malloc'ed char * or NULL * -\********************************************************************/ -char * -gnc_option_documentation (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_string (getters.documentation, - option->guile_option); -} - -/********************************************************************\ - * gnc_option_getter * - * returns the SCM handle for the option getter function. * - * This value should be tested with scm_procedure_p before use. * - * * - * Args: option - the GNCOption * - * Returns: SCM handle to function * -\********************************************************************/ -SCM -gnc_option_getter (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_procedure (getters.getter, - option->guile_option); -} - -/********************************************************************\ - * gnc_option_setter * - * returns the SCM handle for the option setter function. * - * This value should be tested with scm_procedure_p before use. * - * * - * Args: option - the GNCOption * - * Returns: SCM handle to function * -\********************************************************************/ -SCM -gnc_option_setter (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_procedure (getters.setter, - option->guile_option); -} - -/********************************************************************\ - * gnc_option_default_getter * - * returns the SCM handle for the option default_getter function. * - * This value should be tested with scm_procedure_p before use. * - * * - * Args: option - the GNCOption * - * Returns: SCM handle to function * -\********************************************************************/ -SCM -gnc_option_default_getter (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_procedure (getters.default_getter, - option->guile_option); -} - -/********************************************************************\ - * gnc_option_value_validator * - * returns the SCM handle for the option value validator function.* - * This value should be tested with scm_procedure_p before use. * - * * - * Args: option - the GNCOption * - * Returns: SCM handle to function * -\********************************************************************/ -SCM -gnc_option_value_validator (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_procedure (getters.value_validator, - option->guile_option); -} - -/********************************************************************\ - * gnc_option_widget_changed_proc_getter * - * returns the SCM handle for the function to be called if the * - * GUI widget representing the option is changed. * - * This value should be tested with scm_procedure_p before use. * - * If no such function exists, returns SCM_UNDEFINED. * - * * - * Args: option - the GNCOption * - * Returns: SCM handle to function * - * If no such function exists, returns SCM_UNDEFINED. * -\********************************************************************/ -SCM -gnc_option_widget_changed_proc_getter (GNCOption *option) -{ - SCM cb; - - initialize_getters (); - - if (scm_is_procedure (getters.option_widget_changed_cb)) - { - /* call the callback function getter to get the actual callback function */ - cb = scm_call_1 (getters.option_widget_changed_cb, option->guile_option); - - if (scm_is_procedure (cb)) /* a callback exists */ - { - return (cb); - } - /* else no callback exists - this is a legal situation */ - } - else /* getters not set up correctly? */ - { - PERR("getters.option_widget_changed_cb is not a valid procedure\n"); - } - - return( SCM_UNDEFINED ); -} - -/**********************************************************************\ - * gnc_option_call_option_widget_changed_proc * - * If there is an option_widget_changed_cb for this option, call * - * it with the SCM value of the option that is passed in. If * - * there is no such callback function or value, do nothing. * - * * - * Args: option - the GNCOption * - * reset_changed - whether to reset the changed flag afterwards * - * Returns: void * -\**********************************************************************/ -void -gnc_option_call_option_widget_changed_proc (GNCOption *option, - gboolean reset_changed) -{ - SCM cb, value; - - cb = gnc_option_widget_changed_proc_getter (option); - - if (cb != SCM_UNDEFINED) - { - value = gnc_option_get_ui_value (option); - - if (value != SCM_UNDEFINED) - { - scm_call_1 (cb, value); - } - } - if (reset_changed) - option->changed = FALSE; -} - -/********************************************************************\ - * gnc_option_num_permissible_values * - * returns the number of permissible values in the option, or * - * -1 if there are no values available. * - * * - * Args: option - the GNCOption * - * Returns: number of permissible options or -1 * -\********************************************************************/ -int -gnc_option_num_permissible_values (GNCOption *option) -{ - SCM value; - - initialize_getters (); - - value = scm_call_1 (getters.number_of_indices, option->guile_option); - - if (scm_is_exact (value)) - { - return scm_to_int (value); - } - else - { - return -1; - } -} - -/********************************************************************\ - * gnc_option_permissible_value_index * - * returns the index of the permissible value matching the * - * provided value, or -1 if it couldn't be found * - * * - * Args: option - the GNCOption * - * value - the SCM handle of the value * - * Returns: index of permissible value, or -1 * -\********************************************************************/ -int -gnc_option_permissible_value_index (GNCOption *option, SCM search_value) -{ - SCM value; - value = scm_call_2 (getters.value_to_index, option->guile_option, search_value); - if (value == SCM_BOOL_F) - { - return -1; - } - else - { - return scm_to_int (value); - } -} - -/********************************************************************\ - * gnc_option_permissible_value * - * returns the SCM handle to the indexth permissible value in the * - * option, or SCM_UNDEFINED if the index was out of range or * - * there was some other problem. * - * * - * Args: option - the GNCOption * - * index - the index of the permissible value * - * Returns: SCM handle to option value or SCM_UNDEFINED * -\********************************************************************/ -SCM -gnc_option_permissible_value (GNCOption *option, int index) -{ - SCM value; - - if (index < 0) - return SCM_UNDEFINED; - - initialize_getters (); - - value = scm_call_2 (getters.index_to_value, option->guile_option, - scm_from_int (index)); - - return value; -} - -/********************************************************************\ - * gnc_option_permissible_value_name * - * returns the malloc'd name of the indexth permissible value in * - * the option, or NULL if the index was out of range or there are * - * no values available. * - * * - * Args: option - the GNCOption * - * index - the index of the permissible value * - * Returns: malloc'd name of permissible value or NULL * -\********************************************************************/ -char * -gnc_option_permissible_value_name (GNCOption *option, int index) -{ - SCM name; - - if (index < 0) - return NULL; - - initialize_getters (); - - name = scm_call_2 (getters.index_to_name, option->guile_option, - scm_from_int (index)); - if (name == SCM_UNDEFINED) - return NULL; - if (!scm_is_string (name)) - return NULL; - - return gnc_scm_to_utf8_string (name); -} - -/********************************************************************\ - * gnc_option_show_time * - * returns true if the gui should display the time as well as * - * the date for this option. Only use this for date options. * - * * - * Args: option - the GNCOption * - * Returns: true if time should be shown * -\********************************************************************/ -gboolean -gnc_option_show_time (GNCOption *option) -{ - SCM value; - - initialize_getters (); - - value = scm_call_1 (getters.date_option_show_time, option->guile_option); - - return scm_is_true (value); -} - -/********************************************************************\ - * gnc_option_get_option_data * - * returns the option data of this option * - * * - * Args: option - the GNCOption * - * Returns: the option data * -\********************************************************************/ -SCM -gnc_option_get_option_data (GNCOption *option) -{ - initialize_getters (); - - return scm_call_1 (getters.option_data, option->guile_option); -} - -/********************************************************************\ - * gnc_option_multiple_selection * - * returns true if the gui should allow multiple selection of * - * accounts. Only use this for account options. * - * * - * Args: option - the GNCOption * - * Returns: true if multiple selection allowed * -\********************************************************************/ -gboolean -gnc_option_multiple_selection (GNCOption *option) -{ - SCM pair; - - initialize_getters (); - - pair = scm_call_1 (getters.option_data, option->guile_option); - - return !scm_is_true (scm_not (SCM_CAR(pair))); -} - -/********************************************************************\ - * gnc_option_get_account_type_list * - * returns the list of account_types in the option (or NULL if * - * no special list is provided). Only use this for account * - * options. * - * * - * Args: option - the GNCOption * - * Returns: GList of account types (must be freed by caller) * -\********************************************************************/ -GList * -gnc_option_get_account_type_list (GNCOption *option) -{ - SCM pair; - SCM lst; - GList *type_list = NULL; - - initialize_getters (); - - pair = scm_call_1 (getters.option_data, option->guile_option); - lst = SCM_CDR(pair); - - while (!scm_is_null (lst)) - { - GNCAccountType type; - SCM item; - - /* Compute this item and the rest of the list */ - item = SCM_CAR(lst); - lst = SCM_CDR(lst); - - if (scm_is_false (scm_integer_p (item))) - { - PERR("Invalid type"); - } - else - { - type = scm_to_long (item); - type_list = g_list_prepend (type_list, GINT_TO_POINTER (type)); - } - } - - return g_list_reverse (type_list); -} - -/********************************************************************\ - * gnc_option_get_range_info * - * returns the range info for a number range option in the pointer* - * arguments. NULL arguments are ignored. Use only for number * - * range options. * - * * - * Args: option - the GNCOption * - * Returns: true if everything went ok :) * -\********************************************************************/ -gboolean gnc_option_get_range_info (GNCOption *option, - double *lower_bound, - double *upper_bound, - int *num_decimals, - double *step_size) -{ - SCM list; - SCM value; - - initialize_getters (); - - list = scm_call_1 (getters.option_data, option->guile_option); - - if (!scm_is_list (list) || scm_is_null (list)) - return FALSE; - - /* lower bound */ - value = SCM_CAR(list); - list = SCM_CDR(list); - - if (!scm_is_number (value)) - return FALSE; - - if (lower_bound != NULL) - *lower_bound = scm_to_double (value); - - if (!scm_is_list (list) || scm_is_null (list)) - return FALSE; - - /* upper bound */ - value = SCM_CAR(list); - list = SCM_CDR(list); - - if (!scm_is_number (value)) - return FALSE; - - if (upper_bound != NULL) - *upper_bound = scm_to_double (value); - - if (!scm_is_list (list) || scm_is_null (list)) - return FALSE; - - /* number of decimals */ - value = SCM_CAR(list); - list = SCM_CDR(list); - - if (!scm_is_number (value)) - return FALSE; - - /* Guile-1.6 returns this as a double, so let's use that in all cases. - * This is still safe for earlier guiles, too -- tested with 1.3.4. - */ - if (num_decimals != NULL) - { - double decimals = scm_to_double (value); - *num_decimals = (int)decimals; - } - - if (!scm_is_list (list) || scm_is_null (list)) - return FALSE; - - /* step size */ - value = SCM_CAR(list); - - if (!scm_is_number (value)) - return FALSE; - - if (step_size != NULL) - *step_size = scm_to_double (value); - - return TRUE; -} - -/********************************************************************\ - * gnc_option_color_range * - * returns the color range for rgba values. * - * Only use this for color options. * - * * - * Args: option - the GNCOption * - * Returns: color range for the option * -\********************************************************************/ -gdouble -gnc_option_color_range (GNCOption *option) -{ - SCM list; - SCM value; - - initialize_getters (); - - list = scm_call_1 (getters.option_data, option->guile_option); - if (!scm_is_list (list) || scm_is_null (list)) - return 0.0; - - value = SCM_CAR(list); - if (!scm_is_number (value)) - return 0.0; - - return scm_to_double (value); -} - -/********************************************************************\ - * gnc_option_use_alpha * - * returns true if the color option should use alpha transparency * - * Only use this for color options. * - * * - * Args: option - the GNCOption * - * Returns: true if alpha transparency should be used * -\********************************************************************/ -gdouble -gnc_option_use_alpha (GNCOption *option) -{ - SCM list; - SCM value; - - initialize_getters (); - - list = scm_call_1 (getters.option_data, option->guile_option); - if (!scm_is_list (list) || scm_is_null (list)) - return FALSE; - - list = SCM_CDR(list); - if (!scm_is_list (list) || scm_is_null (list)) - return FALSE; - - value = SCM_CAR(list); - if (!scm_is_bool (value)) - return FALSE; - - return scm_is_true (value); -} - -/********************************************************************\ - * gnc_option_get_color_argb * - * returns the argb value of a color option * - * * - * Args: option - the GNCOption * - * Returns: argb value of option * -\********************************************************************/ -guint32 -gnc_option_get_color_argb (GNCOption *option) -{ - gdouble red, green, blue, alpha; - guint32 color = 0; - - if (!gnc_option_get_color_info (option, FALSE, &red, &green, &blue, &alpha)) - return 0; - - color |= (guint32) (alpha * 255.0); - color <<= 8; - - color |= (guint32) (red * 255.0); - color <<= 8; - - color |= (guint32) (green * 255.0); - color <<= 8; - - color |= (guint32) (blue * 255.0); - - return color; -} - -/********************************************************************\ - * gnc_option_get_color_info * - * gets the color information from a color option. rgba values * - * returned are between 0.0 and 1.0. * - * * - * Args: option - option to get info from * - * use_default - use the default or current value * - * red - where to store the red value * - * blue - where to store the blue value * - * green - where to store the green value * - * alpha - where to store the alpha value * - * Return: true if everything went ok * -\********************************************************************/ -gboolean -gnc_option_get_color_info (GNCOption *option, - gboolean use_default, - gdouble *red, - gdouble *green, - gdouble *blue, - gdouble *alpha) -{ - gdouble scale; - gdouble rgba; - SCM getter; - SCM value; - - if (option == NULL) - return FALSE; - - if (use_default) - getter = gnc_option_default_getter (option); - else - getter = gnc_option_getter (option); - if (getter == SCM_UNDEFINED) - return FALSE; - - value = scm_call_0 (getter); - if (!scm_is_list (value) || scm_is_null (value) || !scm_is_number (SCM_CAR(value))) - return FALSE; - - scale = gnc_option_color_range (option); - if (scale <= 0.0) - return FALSE; - - scale = 1.0 / scale; - - rgba = scm_to_double (SCM_CAR(value)); - if (red != NULL) - *red = MIN(1.0, rgba * scale); - - value = SCM_CDR(value); - if (!scm_is_list (value) || scm_is_null (value) || !scm_is_number (SCM_CAR(value))) - return FALSE; - - rgba = scm_to_double (SCM_CAR(value)); - if (green != NULL) - *green = MIN(1.0, rgba * scale); - - value = SCM_CDR(value); - if (!scm_is_list (value) || scm_is_null (value) || !scm_is_number (SCM_CAR(value))) - return FALSE; - - rgba = scm_to_double (SCM_CAR(value)); - if (blue != NULL) - *blue = MIN(1.0, rgba * scale); - - value = SCM_CDR(value); - if (!scm_is_list (value) || scm_is_null (value) || !scm_is_number (SCM_CAR(value))) - return FALSE; - - rgba = scm_to_double (SCM_CAR(value)); - if (alpha != NULL) - *alpha = MIN(1.0, rgba * scale); - - return TRUE; -} - -/********************************************************************\ - * gnc_option_set_default * - * set the option to its default value * - * * - * Args: option - the GNCOption * - * Returns: nothing * -\********************************************************************/ -void -gnc_option_set_default (GNCOption *option) -{ - SCM default_getter; - SCM setter; - SCM value; - - if (option == NULL) - return; - - default_getter = gnc_option_default_getter (option); - if (default_getter == SCM_UNDEFINED) - return; - - value = scm_call_0 (default_getter); - - setter = gnc_option_setter (option); - if (setter == SCM_UNDEFINED) - return; - - scm_call_1 (setter, value); -} - -static gint -compare_sections (gconstpointer a, gconstpointer b) -{ - const GNCOptionSection *sa = a; - const GNCOptionSection *sb = b; - - return g_strcmp0 (sa->section_name, sb->section_name); -} - -static gint -compare_option_tags (gconstpointer a, gconstpointer b) -{ - GNCOption *oa = (GNCOption *) a; - GNCOption *ob = (GNCOption *) b; - char *tag_a = gnc_option_sort_tag (oa); - char *tag_b = gnc_option_sort_tag (ob); - gint result; - - result = g_strcmp0 (tag_a, tag_b); - - if (tag_a != NULL) - free (tag_a); - - if (tag_b != NULL) - free (tag_b); - - return result; -} - -/********************************************************************\ - * gnc_option_db_dirty * - * returns true if guile has registered more options into the * - * database since the last time the database was cleaned. * - * * - * Returns: dirty flag * -\********************************************************************/ -gboolean -gnc_option_db_dirty (GNCOptionDB *odb) -{ - g_return_val_if_fail (odb, FALSE); - - return odb->options_dirty; -} - -/********************************************************************\ - * gnc_option_db_clean * - * resets the dirty flag of the option database * - * * -\********************************************************************/ -void -gnc_option_db_clean (GNCOptionDB *odb) -{ - g_return_if_fail (odb); - - odb->options_dirty = FALSE; -} - -/********************************************************************\ - * _gnc_option_db_register_option * - * registers an option with an option database. Intended to be * - * called from guile. * - * * - * Args: odb - the option database * - * option - the guile option * - * Returns: nothing * -\********************************************************************/ -void -gnc_option_db_register_option (GNCOptionDBHandle handle, SCM guile_option) -{ - GNCOptionDB *odb; - GNCOption *option; - GNCOptionSection *section; - - odb = g_hash_table_lookup (option_dbs, &handle); - - g_return_if_fail (odb != NULL); - - odb->options_dirty = TRUE; - - /* Make the option structure */ - option = g_new0 (GNCOption, 1); - option->guile_option = guile_option; - option->changed = FALSE; - option->widget = NULL; - option->odb = odb; - - /* Prevent guile from garbage collecting the option */ - scm_gc_protect_object (guile_option); - - /* Make the section structure */ - section = g_new0 (GNCOptionSection, 1); - section->section_name = gnc_option_section (option); - section->options = NULL; - - /* See if the section is already there */ - { - GSList *old; - - old = g_slist_find_custom (odb->option_sections, section, compare_sections); - - if (old != NULL) - { - if (section->section_name != NULL) - free (section->section_name); - g_free (section); - section = old->data; - } - else - odb->option_sections = g_slist_insert_sorted (odb->option_sections, - section, - compare_sections); - } - - section->options = g_slist_insert_sorted (section->options, option, - compare_option_tags); -} - -/********************************************************************\ - * gnc_option_db_num_sections * - * returns the number of option sections registered so far in the * - * database * - * * - * Args: odb - the database to count sections for * - * Returns: number of option sections * -\********************************************************************/ -guint -gnc_option_db_num_sections (GNCOptionDB *odb) -{ - return g_slist_length (odb->option_sections); -} - -/********************************************************************\ - * gnc_option_db_get_section * - * returns the ith option section in the database, or NULL * - * * - * Args: odb - the option database * - * i - index of section * - * Returns: ith option sectioin * -\********************************************************************/ -GNCOptionSection * -gnc_option_db_get_section (GNCOptionDB *odb, gint i) -{ - return g_slist_nth_data (odb->option_sections, i); -} - -/********************************************************************\ - * gnc_option_section_name * - * returns the name of the options section * - * * - * Args: section - section to get name of * - * Returns: name of option section * -\********************************************************************/ -const char * -gnc_option_section_name (GNCOptionSection *section) -{ - return section->section_name; -} - -/********************************************************************\ - * gnc_option_section_num_options * - * returns the number of options in a given section * - * * - * Args: section - section to count options for * - * Returns: number of options in section * -\********************************************************************/ -guint -gnc_option_section_num_options (GNCOptionSection *section) -{ - return g_slist_length (section->options); -} - -/********************************************************************\ - * gnc_get_option_section_option * - * returns the ith option in a given section * - * * - * Args: section - section to retrieve option for * - * i - index of option * - * Returns: ith option in section * -\********************************************************************/ -GNCOption * -gnc_get_option_section_option (GNCOptionSection *section, int i) -{ - return g_slist_nth_data (section->options, i); -} - -/********************************************************************\ - * gnc_option_db_get_option_by_name * - * returns an option given section name and name * - * * - * Args: odb - option database to search in * - * section_name - name of section to search for * - * name - name to search for * - * Returns: given option, or NULL if none * -\********************************************************************/ -GNCOption * -gnc_option_db_get_option_by_name (GNCOptionDB *odb, - const char *section_name, - const char *name) -{ - GSList *section_node; - GSList *option_node; - GNCOptionSection section_key; - GNCOptionSection *section; - GNCOption *option; - gint result; - char *node_name; - - if (odb == NULL) - return NULL; - - section_key.section_name = (char *) section_name; - - section_node = g_slist_find_custom (odb->option_sections, §ion_key, - compare_sections); - - if (section_node == NULL) - return NULL; - - section = section_node->data; - option_node = section->options; - - while (option_node != NULL) - { - option = option_node->data; - - node_name = gnc_option_name (option); - result = g_strcmp0 (name, node_name); - free (node_name); - - if (result == 0) - return option; - - option_node = option_node->next; - } - return NULL; -} - -/********************************************************************\ - * gnc_option_db_get_option_by_SCM * - * returns an option given SCM handle. Uses section and name. * - * * - * Args: odb - option database to search in * - * guile_option - SCM handle of option * - * Returns: given option, or NULL if none * -\********************************************************************/ -GNCOption * -gnc_option_db_get_option_by_SCM (GNCOptionDB *odb, SCM guile_option) -{ - GNCOption option_key; - GNCOption *option; - char *section_name; - char *name; - - option_key.guile_option = guile_option; - - section_name = gnc_option_section (&option_key); - name = gnc_option_name (&option_key); - - option = gnc_option_db_get_option_by_name (odb, section_name, name); - - if (section_name != NULL) - free (section_name); - - if (name != NULL) - free (name); - - return option; -} - -static SCM -gnc_option_valid_value (GNCOption *option, SCM value) -{ - SCM validator; - SCM result, ok; - - validator = gnc_option_value_validator (option); - - result = scm_call_1 (validator, value); - if (!scm_is_list (result) || scm_is_null (result)) - return SCM_UNDEFINED; - - ok = SCM_CAR(result); - if (!scm_is_bool (ok)) - return SCM_UNDEFINED; - - if (!scm_is_true (ok)) - return SCM_UNDEFINED; - - result = SCM_CDR(result); - if (!scm_is_list (result) || scm_is_null (result)) - return SCM_UNDEFINED; - - return SCM_CAR(result); -} - -static char* -gnc_commit_option (GNCOption *option) -{ - SCM validator, setter, value; - SCM result, ok; - char* retval = NULL; - - /* Validate the ui's value */ - value = gnc_option_get_ui_value (option); - if (value == SCM_UNDEFINED) - return NULL; - - validator = gnc_option_value_validator (option); - - result = scm_call_1(validator, value); - if (!scm_is_list (result) || scm_is_null (result)) - { - PERR("bad validation result\n"); - return NULL; - } - - /* First element determines validity */ - ok = SCM_CAR(result); - if (!scm_is_bool (ok)) - { - PERR("bad validation result\n"); - return NULL; - } - - if (scm_is_true (ok)) - { - /* Second element is value to use */ - value = SCM_CADR(result); - setter = gnc_option_setter (option); - - scm_call_1 (setter, value); - - gnc_option_set_ui_value (option, FALSE); - } - else - { - SCM oops; - char *section, *name; - const char *message = NULL; - const char *format = _("There is a problem with option %s:%s.\n%s"); - const char *bad_value = _("Invalid option value"); - - name = gnc_option_name (option); - section = gnc_option_section (option); - - /* Second element is error message */ - oops = SCM_CADR(result); - if (!scm_is_string (oops)) - { - PERR("bad validation result\n"); - retval = g_strdup_printf (format, - section ? section : "(null)", - name ? name : "(null)", - bad_value); - } - else - { - message = gnc_scm_to_utf8_string (oops); - retval = g_strdup_printf (format, - section ? section : "(null)", - name ? name : "(null)", - message ? message : "(null)"); - } - if (name != NULL) - free (name); - if (section != NULL) - free (section); - g_free ((gpointer *) message); - } - return retval; -} - -/********************************************************************\ - * gnc_option_db_get_changed * - * returns a boolean value, TRUE if any option has changed, * - * FALSE is none of the options have changed * - * * - * Args: odb - option database to check * - * Return: boolean * -\********************************************************************/ -gboolean -gnc_option_db_get_changed (GNCOptionDB *odb) -{ - GSList *section_node; - GSList *option_node; - GNCOptionSection *section; - GNCOption *option; - - g_return_val_if_fail (odb, FALSE); - - for (section_node = odb->option_sections; section_node; - section_node = section_node->next) - { - section = section_node->data; - - for (option_node = section->options; option_node; - option_node = option_node->next) - { - option = option_node->data; - - if (option->changed) - return TRUE; - } - } - return FALSE; -} - -/********************************************************************\ - * gnc_option_db_commit * - * commits the options which have changed, and which are valid * - * for those which are not valid, error dialogs are shown. * - * * - * Args: odb - option database to commit * - * Return: nothing * -\********************************************************************/ -GList* -gnc_option_db_commit (GNCOptionDB *odb) -{ - GSList *section_node; - GSList *option_node; - GNCOptionSection *section; - GNCOption *option; - gboolean changed_something = FALSE; - GList *commit_errors = NULL; - - g_return_val_if_fail (odb, NULL); - - section_node = odb->option_sections; - while (section_node != NULL) - { - section = section_node->data; - - option_node = section->options; - while (option_node != NULL) - { - option = option_node->data; - - if (option->changed) - { - char *result = NULL; - result = gnc_commit_option (option_node->data); - if (result) - commit_errors = g_list_append (commit_errors, result); - changed_something = TRUE; - option->changed = FALSE; - } - option_node = option_node->next; - } - section_node = section_node->next; - } - if (changed_something) - gnc_call_option_change_callbacks (odb); - - return commit_errors; -} - -/********************************************************************\ - * gnc_option_db_section_reset_widgets * - * reset all option widgets in one section to their default. * - * values * - * * - * Args: odb - option database to reset * - * Return: nothing * -\********************************************************************/ -void -gnc_option_db_section_reset_widgets (GNCOptionSection *section) -{ - GSList *option_node; - GNCOption *option; - - g_return_if_fail (section); - - /* Don't reset "invisible" options. - * If the section name begins "__" we should not reset - */ - if (section->section_name == NULL || - strncmp (section->section_name, "__", 2) == 0) - return; - - for (option_node = section->options; - option_node != NULL; - option_node = option_node->next) - { - option = option_node->data; - gnc_option_set_ui_value (option, TRUE); - } -} - -/********************************************************************\ - * gnc_option_db_reset_widgets * - * reset all option widgets to their default values. * - * * - * Args: odb - option database to reset * - * Return: nothing * -\********************************************************************/ -void -gnc_option_db_reset_widgets (GNCOptionDB *odb) -{ - GSList *section_node; - GNCOptionSection *section; - - g_return_if_fail (odb); - - for (section_node = odb->option_sections; - section_node != NULL; - section_node = section_node->next) - { - section = section_node->data; - gnc_option_db_section_reset_widgets (section); - } -} - -/********************************************************************\ - * gnc_option_db_get_default_section * - * returns the malloc'd section name of the default section, * - * or NULL if there is none. * - * * - * Args: odb - option database to get default page for * - * Return: g_malloc'd default section name * -\********************************************************************/ -char * -gnc_option_db_get_default_section (GNCOptionDB *odb) -{ - SCM getter; - SCM value; - - if (odb == NULL) - return NULL; - - getter = scm_c_eval_string ("gnc:options-get-default-section"); - if (!scm_is_procedure (getter)) - return NULL; - - value = scm_call_1 (getter, odb->guile_options); - if (!scm_is_string (value)) - return NULL; - - return gnc_scm_to_utf8_string (value); -} - -/********************************************************************\ - * gnc_option_db_lookup_option * - * looks up an option. If present, returns its SCM value, * - * otherwise returns the default. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * default - default value if not found * - * Return: option value * -\********************************************************************/ -SCM -gnc_option_db_lookup_option (GNCOptionDB *odb, - const char *section, - const char *name, - SCM default_value) -{ - GNCOption *option; - SCM getter; - - option = gnc_option_db_get_option_by_name (odb, section, name); - - if (option == NULL) - return default_value; - - getter = gnc_option_getter (option); - if (getter == SCM_UNDEFINED) - return default_value; - - return scm_call_0 (getter); -} - -/********************************************************************\ - * gnc_option_db_lookup_boolean_option * - * looks up a boolean option. If present, returns its value, * - * otherwise returns the default. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * default - default value if not found * - * Return: gboolean option value * -\********************************************************************/ -gboolean -gnc_option_db_lookup_boolean_option (GNCOptionDB *odb, - const char *section, - const char *name, - gboolean default_value) -{ - GNCOption *option; - SCM getter; - SCM value; - - option = gnc_option_db_get_option_by_name (odb, section, name); - - if (option == NULL) - return default_value; - - getter = gnc_option_getter (option); - if (getter == SCM_UNDEFINED) - return default_value; - - value = scm_call_0 (getter); - - if (scm_is_bool (value)) - return scm_is_true (value); - else - return default_value; -} - -/********************************************************************\ - * gnc_option_db_lookup_string_option * - * looks up a string option. If present, returns its malloc'ed * - * value, otherwise returns the strdup'ed default, or NULL if * - * default was NULL. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * default - default value if not found * - * Return: char * option value * -\********************************************************************/ -char * -gnc_option_db_lookup_string_option (GNCOptionDB *odb, - const char *section, - const char *name, - const char *default_value) -{ - GNCOption *option; - SCM getter; - SCM value; - - option = gnc_option_db_get_option_by_name (odb, section, name); - - if (option != NULL) - { - getter = gnc_option_getter (option); - if (getter != SCM_UNDEFINED) - { - value = scm_call_0 (getter); - if (scm_is_string (value)) - return gnc_scm_to_utf8_string (value); - } - } - - if (default_value == NULL) - return NULL; - - return strdup (default_value); -} - -/********************************************************************\ - * gnc_option_db_lookup_font_option * - * looks up a font option. If present, returns its malloc'ed * - * string value, otherwise returns the strdup'ed default, or NULL * - * if default was NULL. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * default - default value if not found * - * Return: char * option value * -\********************************************************************/ -char * -gnc_option_db_lookup_font_option (GNCOptionDB *odb, - const char *section, - const char *name, - const char *default_value) -{ - return gnc_option_db_lookup_string_option (odb, section, name, default_value); -} - -/********************************************************************\ - * gnc_option_db_lookup_multichoice_option * - * looks up a multichoice option. If present, returns its * - * name as a malloc'ed string * - * value, otherwise returns the strdup'ed default, or NULL if * - * default was NULL. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * default - default value if not found * - * Return: char * option value * -\********************************************************************/ -char * -gnc_option_db_lookup_multichoice_option (GNCOptionDB *odb, - const char *section, - const char *name, - const char *default_value) -{ - GNCOption *option; - SCM getter; - SCM value; - - option = gnc_option_db_get_option_by_name (odb, section, name); - - if (option != NULL) - { - getter = gnc_option_getter (option); - if (getter != SCM_UNDEFINED) - { - value = scm_call_0 (getter); - if (scm_is_symbol (value)) - return gnc_scm_symbol_to_locale_string (value); - } - } - - if (default_value == NULL) - return NULL; - - return strdup (default_value); -} - -/********************************************************************\ - * gnc_option_db_lookup_number_option * - * looks up a number option. If present, returns its value * - * as a gdouble, otherwise returns the default_value. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * default - default value if not found * - * Return: gdouble representation of value * -\********************************************************************/ -gdouble -gnc_option_db_lookup_number_option (GNCOptionDB *odb, - const char *section, - const char *name, - gdouble default_value) -{ - GNCOption *option; - SCM getter; - SCM value; - - option = gnc_option_db_get_option_by_name (odb, section, name); - - if (option != NULL) - { - getter = gnc_option_getter (option); - if (getter != SCM_UNDEFINED) - { - value = scm_call_0 (getter); - if (scm_is_number (value)) - return scm_to_double (value); - } - } - return default_value; -} - -/********************************************************************\ - * gnc_option_db_lookup_color_option * - * looks up a color option. If present, returns its value in the * - * color variable, otherwise leaves the color variable alone. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * red - where to store the red value * - * blue - where to store the blue value * - * green - where to store the green value * - * alpha - where to store the alpha value * - * Return: true if option was found * -\********************************************************************/ -gboolean gnc_option_db_lookup_color_option (GNCOptionDB *odb, - const char *section, - const char *name, - gdouble *red, - gdouble *green, - gdouble *blue, - gdouble *alpha) -{ - GNCOption *option; - - option = gnc_option_db_get_option_by_name (odb, section, name); - - return gnc_option_get_color_info (option, FALSE, red, green, blue, alpha); -} - -/********************************************************************\ - * gnc_option_db_lookup_color_option_argb * - * looks up a color option. If present, returns its argb value, * - * otherwise returns the given default value. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * default_value - default value to return if problem * - * Return: argb value * -\********************************************************************/ -guint32 gnc_option_db_lookup_color_option_argb (GNCOptionDB *odb, - const char *section, - const char *name, - guint32 default_value) -{ - GNCOption *option; - - option = gnc_option_db_get_option_by_name (odb, section, name); - if (option == NULL) - return default_value; - - return gnc_option_get_color_argb (option); -} - -/********************************************************************\ - * gnc_option_db_lookup_list_option * - * looks up a list option. If present, returns its value as a * - * list of strings representing the symbols. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * default_value - default value to return if problem * - * Return: list of values * -\********************************************************************/ -GSList * -gnc_option_db_lookup_list_option (GNCOptionDB *odb, - const char *section, - const char *name, - GSList *default_value) -{ - GNCOption *option; - GSList *list = NULL; - SCM getter; - SCM value; - SCM item; - - option = gnc_option_db_get_option_by_name (odb, section, name); - if (option == NULL) - return default_value; - - getter = gnc_option_getter (option); - if (getter == SCM_UNDEFINED) - return default_value; - - value = scm_call_0 (getter); - while (scm_is_list (value) && !scm_is_null (value)) - { - item = SCM_CAR(value); - value = SCM_CDR(value); - - if (!scm_is_symbol (item)) - { - gnc_free_list_option_value (list); - - return default_value; - } - - list = g_slist_prepend (list, gnc_scm_symbol_to_locale_string (item)); - } - - if (!scm_is_list (value) || !scm_is_null (value)) - { - gnc_free_list_option_value (list); - - return default_value; - } - return list; -} - -/********************************************************************\ - * gnc_option_db_lookup_currency_option * - * looks up a currency option. If present, returns its value as a * - * gnc_commodity object. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * default_value - default value to return if problem * - * Return: commodity or NULL if no commodity found * -\********************************************************************/ -gnc_commodity * -gnc_option_db_lookup_currency_option (GNCOptionDB *odb, - const char *section, - const char *name, - gnc_commodity *default_value) -{ - GNCOption *option; - SCM getter; - SCM value; - - option = gnc_option_db_get_option_by_name (odb, section, name); - if (option == NULL) - return default_value; - - getter = gnc_option_getter (option); - if (getter == SCM_UNDEFINED) - return default_value; - - value = scm_call_0 (getter); - - return gnc_scm_to_commodity (value); -} - -static void -free_helper (gpointer string, gpointer not_used) -{ - if (string) - free (string); -} - -void -gnc_free_list_option_value (GSList *list) -{ - g_slist_foreach (list, free_helper, NULL); - g_slist_free (list); -} - -/********************************************************************\ - * gnc_option_db_set_option_default * - * set the option to its default value * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * Returns: nothing * -\********************************************************************/ -void -gnc_option_db_set_option_default (GNCOptionDB *odb, - const char *section, - const char *name) -{ - GNCOption *option; - - option = gnc_option_db_get_option_by_name (odb, section, name); - - gnc_option_set_default (option); -} - -/********************************************************************\ - * gnc_option_db_set_option * - * sets the option to the given value. If successful * - * returns TRUE, otherwise FALSE. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * value - value to set to * - * Return: success indicator * -\********************************************************************/ -gboolean -gnc_option_db_set_option (GNCOptionDB *odb, - const char *section, - const char *name, - SCM value) -{ - GNCOption *option; - SCM setter; - - option = gnc_option_db_get_option_by_name (odb, section, name); - if (option == NULL) - return FALSE; - - value = gnc_option_valid_value (option, value); - if (value == SCM_UNDEFINED) - return FALSE; - - setter = gnc_option_setter (option); - if (setter == SCM_UNDEFINED) - return FALSE; - - scm_call_1 (setter, value); - - return TRUE; -} - -/********************************************************************\ - * gnc_option_db_set_number_option * - * sets the number option to the given value. If successful * - * returns TRUE, otherwise FALSE. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * value - value to set to * - * Return: success indicator * -\********************************************************************/ -gboolean -gnc_option_db_set_number_option (GNCOptionDB *odb, - const char *section, - const char *name, - gdouble value) -{ - GNCOption *option; - SCM scm_value; - SCM setter; - - option = gnc_option_db_get_option_by_name (odb, section, name); - if (option == NULL) - return FALSE; - - scm_value = scm_from_double (value); - - scm_value = gnc_option_valid_value (option, scm_value); - if (scm_value == SCM_UNDEFINED) - return FALSE; - - setter = gnc_option_setter (option); - if (setter == SCM_UNDEFINED) - return FALSE; - - scm_call_1 (setter, scm_value); - - return TRUE; -} - -/********************************************************************\ - * gnc_option_db_set_boolean_option * - * sets the boolean option to the given value. If successful * - * returns TRUE, otherwise FALSE. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * value - value to set to * - * Return: success indicator * -\********************************************************************/ -gboolean -gnc_option_db_set_boolean_option (GNCOptionDB *odb, - const char *section, - const char *name, - gboolean value) -{ - GNCOption *option; - SCM scm_value; - SCM setter; - - option = gnc_option_db_get_option_by_name (odb, section, name); - if (option == NULL) - return FALSE; - - scm_value = SCM_BOOL(value); - - scm_value = gnc_option_valid_value (option, scm_value); - if (scm_value == SCM_UNDEFINED) - return FALSE; - - setter = gnc_option_setter (option); - if (setter == SCM_UNDEFINED) - return FALSE; - - scm_call_1 (setter, scm_value); - - return TRUE; -} - -/********************************************************************\ - * gnc_option_db_set_string_option * - * sets the string option to the given value. If successful * - * returns TRUE, otherwise FALSE. * - * * - * Args: odb - option database to search in * - * section - section name of option * - * name - name of option * - * value - value to set to * - * Return: success indicator * -\********************************************************************/ -gboolean -gnc_option_db_set_string_option (GNCOptionDB *odb, - const char *section, - const char *name, - const char *value) -{ - GNCOption *option; - SCM scm_value; - SCM setter; - - option = gnc_option_db_get_option_by_name (odb, section, name); - if (option == NULL) - return FALSE; - - if (value) - scm_value = scm_from_utf8_string (value); - else - scm_value = SCM_BOOL_F; - - scm_value = gnc_option_valid_value (option, scm_value); - if (scm_value == SCM_UNDEFINED) - return FALSE; - - setter = gnc_option_setter (option); - if (setter == SCM_UNDEFINED) - return FALSE; - - scm_call_1 (setter, scm_value); - - return TRUE; -} - -/*******************************************************************\ - * gnc_option_date_option_get_subtype * - * find out whether a date option is a relative or absolute date * - * * - * Args: option - option to get date subtype for * - * Return: newly allocated subtype string or NULL * -\*******************************************************************/ -char * -gnc_option_date_option_get_subtype (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_symbol_to_string (getters.date_option_subtype, option->guile_option); -} - -/*******************************************************************\ - * gnc_date_option_value_get_type * - * get the type of a date option value * - * * - * Args: option_value - option value to get type of * - * Return: newly allocated type string or NULL * -\*******************************************************************/ -char * -gnc_date_option_value_get_type (SCM option_value) -{ - initialize_getters (); - - return gnc_scm_call_1_symbol_to_string (getters.date_option_value_type, option_value); -} - -/*******************************************************************\ - * gnc_date_option_value_get_absolute * - * get the absolute time of a date option value * - * * - * Args: option_value - option value to get absolute time of * - * Return: time64 value * -\*******************************************************************/ -time64 -gnc_date_option_value_get_absolute (SCM option_value) -{ - SCM value; - initialize_getters (); - value = scm_call_1 (getters.date_option_value_absolute, option_value); - return scm_to_int64 (value); -} - -/*******************************************************************\ - * gnc_date_option_value_get_relative * - * get the relative time of a date option value * - * * - * Args: option_value - option value to get relative time of * - * Return: SCM value * -\*******************************************************************/ -SCM -gnc_date_option_value_get_relative (SCM option_value) -{ - initialize_getters (); - - return scm_call_1 (getters.date_option_value_relative, option_value); -} - -/*******************************************************************\ - * gnc_plot_size_option_value_get_type * - * get the type of a plot size option value * - * * - * Args: option_value - option value to get type of * - * Return: newly allocated type string or NULL * -\*******************************************************************/ -char * -gnc_plot_size_option_value_get_type (SCM option_value) -{ - initialize_getters (); - - return gnc_scm_call_1_symbol_to_string (getters.plot_size_option_value_type, option_value); -} - -/*******************************************************************\ - * gnc_plot_size_option_value_get_value * - * get the plot size option value * - * * - * Args: option_value - option value to get the plot size of * - * Return: double value * -\*******************************************************************/ -gdouble -gnc_plot_size_option_value_get_value (SCM option_value) -{ - SCM value; - - initialize_getters (); - - value = scm_call_1 (getters.plot_size_option_value, option_value); - - if (scm_is_number (value)) - return scm_to_double (value); - else - return 1.0; -} - -/********************************************************************\ - * gnc_currency_accounting_option_currency_documentation * - * returns the malloc'ed documentation string for currency * - * selector of the currency-accounting option, or NULL if it * - * can't be retrieved. * - * * - * Args: option - the GNCOption * - * Returns: malloc'ed char * or NULL * -\********************************************************************/ -char * -gnc_currency_accounting_option_currency_documentation (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_string - (getters.currency_accounting_option_currency_doc_string, - option->guile_option); -} - -/********************************************************************\ - * gnc_currency_accounting_option_get_default_currency * - * returns the SCM value for the currency-accounting option * - * default currency. * - * * - * Args: option - the GNCOption * - * Returns: SCM value * -\********************************************************************/ -SCM -gnc_currency_accounting_option_get_default_currency (GNCOption *option) -{ - initialize_getters (); - - return scm_call_1 - (getters.currency_accounting_option_default_currency, - option->guile_option); -} - -/********************************************************************\ - * gnc_currency_accounting_option_policy_documentation * - * returns the malloc'ed documentation string for policy * - * selector of the currency-accounting option, or NULL if it * - * can't be retrieved. * - * * - * Args: option - the GNCOption * - * Returns: malloc'ed char * or NULL * -\********************************************************************/ -char * -gnc_currency_accounting_option_policy_documentation (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_string - (getters.currency_accounting_option_policy_doc_string, - option->guile_option); -} - -/********************************************************************\ - * gnc_currency_accounting_option_get_default_policy * - * returns the SCM value for the currency-accounting option * - * default policy. * - * * - * Args: option - the GNCOption * - * Returns: SCM value * -\********************************************************************/ -SCM -gnc_currency_accounting_option_get_default_policy (GNCOption *option) -{ - initialize_getters (); - - return scm_call_1 - (getters.currency_accounting_option_default_policy, - option->guile_option); -} - -/********************************************************************\ - * gnc_currency_accounting_option_gain_loss_account_documentation * - * returns the malloc'ed documentation string for account * - * selector of the currency-accounting option, or NULL if it * - * can't be retrieved. * - * * - * Args: option - the GNCOption * - * Returns: malloc'ed char * or NULL * -\********************************************************************/ -char * -gnc_currency_accounting_option_gain_loss_account_documentation (GNCOption *option) -{ - initialize_getters (); - - return gnc_scm_call_1_to_string - (getters.currency_accounting_option_gain_loss_account_doc_string, - option->guile_option); -} - -/*******************************************************************\ - * gnc_currency_accounting_option_value_get_method * - * get the currency accounting method of the option as a symbol * - * * - * Args: option_value - option value to get method of * - * Return: SCM value * -\*******************************************************************/ -SCM -gnc_currency_accounting_option_value_get_method (SCM option_value) -{ - initialize_getters (); - - return scm_call_1 (getters.currency_accounting_option_method, - option_value); -} - -/*******************************************************************\ - * gnc_currency_accounting_option_value_get_book_currency * - * get the book-currency if that is the currency accounting * - * method of the option as a symbol * - * * - * Args: option_value - option value to get method of * - * Return: SCM value * -\*******************************************************************/ -SCM -gnc_currency_accounting_option_value_get_book_currency (SCM option_value) -{ - initialize_getters (); - - return scm_call_1 (getters.currency_accounting_option_book_currency, - option_value); -} - -/*******************************************************************\ - * gnc_currency_accounting_option_value_get_default_policy * - * get the default policy if book-currency is the currency * - * accounting method of the option as a symbol * - * * - * Args: option_value - option value to get method of * - * Return: SCM value * -\*******************************************************************/ -SCM -gnc_currency_accounting_option_value_get_default_policy (SCM option_value) -{ - initialize_getters (); - - return scm_call_1 - (getters.currency_accounting_option_selected_default_policy, - option_value); -} - -/*******************************************************************\ - * gnc_currency_accounting_option_value_get_default_account * - * get the default gain/loss account if book-currency is the * - * currency accounting method, if one is specified, of the * - * option as a symbol * - * * - * Args: option_value - option value to get method of * - * Return: SCM value * -\*******************************************************************/ -SCM -gnc_currency_accounting_option_value_get_default_account (SCM option_value) -{ - initialize_getters (); - - return scm_call_1 - (getters.currency_accounting_option_selected_default_gain_loss_account, - option_value); -} - -static int -find_option_db_with_selectable_pred (gpointer key, gpointer value, gpointer data) -{ - SCM guile_options = data; - GNCOptionDB *odb = value; - - if (odb && (odb->guile_options == guile_options) && odb->set_selectable) - return TRUE; - - return FALSE; -} - -static GNCOptionDB * -find_option_db_with_selectable (SCM guile_options) -{ - return g_hash_table_find (option_dbs, find_option_db_with_selectable_pred, - guile_options); -} - -/*******************************************************************\ - * gnc_option_db_set_option_selectable_by_name * - * set the sensitivity of the option widget * - * * - * Args: guile_options - guile side option db * - * section - section of option * - * name - name of option * - * selectable - selectable status * - * Return: SCM value * -\*******************************************************************/ -void -gnc_option_db_set_option_selectable_by_name (SCM guile_options, - const char *section, - const char *name, - gboolean selectable) -{ - GNCOptionDB *odb; - GNCOption *option; - - odb = find_option_db_with_selectable (guile_options); - if (!odb) - return; - - option = gnc_option_db_get_option_by_name (odb, section, name); - if (!option) - return; - - gnc_option_set_selectable (option, selectable); -} - -/* the value is a list of: - * format(symbol), month(symbol), include-years(bool), custom-string(string) - */ - -gboolean gnc_dateformat_option_value_parse (SCM value, - QofDateFormat *format, - GNCDateMonthFormat *months, - gboolean *years, - char **custom) -{ - SCM val; - gchar *str; - - if (!scm_is_list (value) || scm_is_null (value)) - return TRUE; - - do - { - /* Parse the format */ - val = SCM_CAR(value); - value = SCM_CDR(value); - if (!scm_is_symbol (val)) - break; - str = gnc_scm_symbol_to_locale_string (val); - if (!str) - break; - - if (format) - { - if (gnc_date_string_to_dateformat (str, format)) - { - g_free (str); - break; - } - } - g_free (str); - - /* parse the months */ - val = SCM_CAR(value); - value = SCM_CDR(value); - if (!scm_is_symbol (val)) - break; - str = gnc_scm_symbol_to_locale_string (val); - if (!str) - break; - - if (months) - { - if (gnc_date_string_to_monthformat (str, months)) - { - g_free (str); - break; - } - } - g_free (str); - - /* parse the years */ - val = SCM_CAR(value); - value = SCM_CDR(value); - if (!scm_is_bool (val)) - break; - - if (years) - *years = scm_is_true (val); - - /* parse the custom */ - val = SCM_CAR(value); - value = SCM_CDR(value); - if (!scm_is_string (val)) - break; - if (!scm_is_null (value)) - break; - - if (custom) - *custom = gnc_scm_to_utf8_string (val); - - return FALSE; - } - while (FALSE); - - return TRUE; -} - -SCM gnc_dateformat_option_set_value (QofDateFormat format, - GNCDateMonthFormat months, - gboolean years, - const char *custom) -{ - SCM value = SCM_EOL; - SCM val; - const char *str; - - /* build the list in reverse order */ - if (custom) - val = scm_from_utf8_string (custom); - else - val = SCM_BOOL_F; - value = scm_cons (val, value); - - val = SCM_BOOL(years); - value = scm_cons (val, value); - - str = gnc_date_monthformat_to_string (months); - if (str) - val = scm_from_locale_symbol (str); - else - val = SCM_BOOL_F; - value = scm_cons (val, value); - - str = gnc_date_dateformat_to_string (format); - if (str) - val = scm_from_locale_symbol (str); - else - val = SCM_BOOL_F; - value = scm_cons (val, value); - - return value; -} - -/* - * the generator should be a procedure that takes one argument, - * an options object. The procedure should fill in the options with - * its defined kvp options. - */ -void -gnc_register_kvp_option_generator (QofIdType id_type, SCM generator) -{ - GList *list; - init_table(); - list = g_hash_table_lookup (kvp_registry, id_type); - list = g_list_prepend (list, generator); - g_hash_table_insert (kvp_registry, (gpointer) id_type, list); - scm_gc_protect_object (generator); -} diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h deleted file mode 100644 index d555495a74..0000000000 --- a/libgnucash/app-utils/option-util.h +++ /dev/null @@ -1,287 +0,0 @@ -/********************************************************************\ - * option-util.h -- GNOME<->guile option interface * - * Copyright (C) 1998,1999 Linas Vepstas * - * Copyright (C) 2000 Dave Peticolas * - * Copyright (C) 2017 Aaron Laws * - * * - * This program 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 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * -\********************************************************************/ - -#ifndef OPTION_UTIL_H -#define OPTION_UTIL_H - -#include -#include -#include "guile-mappings.h" - -#include "gnc-commodity.h" -#include "gnc-engine-guile.h" -#include "qof.h" - -typedef struct gnc_option GNCOption; -typedef struct gnc_option_section GNCOptionSection; -/* typedef struct gnc_option_db GNCOptionDB is in qof-book.h */ - -typedef int GNCOptionDBHandle; - -typedef SCM (*GNCOptionGetUIValue) (GNCOption *option); -typedef void (*GNCOptionSetUIValue) (GNCOption *option, - gboolean use_default); -typedef void (*GNCOptionSetSelectable) (GNCOption *option, - gboolean selectable); -typedef void (*GNCOptionChangeCallback) (gpointer user_data); - -/***** Prototypes ********************************************************/ - -void gnc_option_set_changed (GNCOption *option, gboolean changed); - -/** Returns an opaque pointer to the widget of this option. The actual - * GUI implementation in dialog-options.c will store a GtkWidget* in - * here. */ -gpointer gnc_option_get_widget (GNCOption *option); - -/** Store an opaque pointer to the widget of this option. The actual - * GUI implementation in dialog-options.c will store a GtkWidget* in - * here. */ -void gnc_option_set_widget (GNCOption *option, gpointer widget); - -SCM gnc_option_get_ui_value (GNCOption *option); -void gnc_option_set_ui_value (GNCOption *option, gboolean use_default); -void gnc_option_set_selectable (GNCOption *option, gboolean selectable); - -GNCOptionDB * gnc_option_db_new (SCM guile_options); -void gnc_option_db_destroy (GNCOptionDB *odb); - -/* Create an option DB for a particular type, and save/load from a kvp. - * This assumes the gnc:*kvp-option-path* location for the options - * in the kvp. - */ -GNCOptionDB * gnc_option_db_new_for_type (QofIdType id_type); -void gnc_option_db_load (GNCOptionDB* odb, QofBook *book); -void gnc_option_db_save (GNCOptionDB* odb, QofBook *book, gboolean clear_all); - -void gnc_register_kvp_option_generator (QofIdType id_type, SCM generator); - -void gnc_option_db_set_ui_callbacks (GNCOptionDB *odb, - GNCOptionGetUIValue get_ui_value, - GNCOptionSetUIValue set_ui_value, - GNCOptionSetSelectable set_selectable); - -SCM gnc_option_db_register_change_callback (GNCOptionDB *odb, - GNCOptionChangeCallback callback, - gpointer data, - const char *section, - const char *name); - -void gnc_option_db_unregister_change_callback_id (GNCOptionDB *odb, - SCM callback_id); - -char * gnc_option_section (GNCOption *option); -char * gnc_option_name (GNCOption *option); -char * gnc_option_type (GNCOption *option); -char * gnc_option_sort_tag (GNCOption *option); -char * gnc_option_documentation (GNCOption *option); -SCM gnc_option_getter (GNCOption *option); -SCM gnc_option_setter (GNCOption *option); -SCM gnc_option_default_getter (GNCOption *option); -SCM gnc_option_value_validator (GNCOption *option); -SCM gnc_option_widget_changed_proc_getter (GNCOption *option); -SCM gnc_option_get_option_data (GNCOption *option); - -int gnc_option_num_permissible_values (GNCOption *option); -int gnc_option_permissible_value_index (GNCOption *option, SCM value); -SCM gnc_option_permissible_value (GNCOption *option, int index); -char * gnc_option_permissible_value_name (GNCOption *option, int index); - -gboolean gnc_option_show_time (GNCOption *option); - -gboolean gnc_option_multiple_selection (GNCOption *option); -GList * gnc_option_get_account_type_list (GNCOption *option); - -gboolean gnc_option_get_range_info (GNCOption *option, - double *lower_bound, - double *upper_bound, - int *num_decimals, - double *step_size); - -gdouble gnc_option_color_range (GNCOption *option); -gdouble gnc_option_use_alpha (GNCOption *option); -guint32 gnc_option_get_color_argb (GNCOption *option); -gboolean gnc_option_get_color_info (GNCOption *option, - gboolean use_default, - gdouble *red, - gdouble *green, - gdouble *blue, - gdouble *alpha); - -void gnc_option_call_option_widget_changed_proc (GNCOption *option, - gboolean reset_changed); - -void gnc_option_set_default (GNCOption *option); - -guint gnc_option_db_num_sections (GNCOptionDB *odb); - -const char * gnc_option_section_name (GNCOptionSection *section); -guint gnc_option_section_num_options (GNCOptionSection *section); - -GNCOptionSection * gnc_option_db_get_section (GNCOptionDB *odb, gint i); - -GNCOption * gnc_get_option_section_option (GNCOptionSection *section, int i); - -GNCOption * gnc_option_db_get_option_by_name (GNCOptionDB *odb, - const char *section_name, - const char *name); - -GNCOption * gnc_option_db_get_option_by_SCM (GNCOptionDB *odb, - SCM guile_option); - -gboolean gnc_option_db_dirty (GNCOptionDB *odb); -void gnc_option_db_clean (GNCOptionDB *odb); - -gboolean gnc_option_db_get_changed (GNCOptionDB *odb); -GList* gnc_option_db_commit (GNCOptionDB *odb); - -char * gnc_option_db_get_default_section (GNCOptionDB *odb); - -SCM gnc_option_db_lookup_option (GNCOptionDB *odb, - const char *section, - const char *name, - SCM default_value); - -gboolean gnc_option_db_lookup_boolean_option (GNCOptionDB *odb, - const char *section, - const char *name, - gboolean default_value); - -char * gnc_option_db_lookup_string_option (GNCOptionDB *odb, - const char *section, - const char *name, - const char *default_value); - -char * gnc_option_db_lookup_font_option (GNCOptionDB *odb, - const char *section, - const char *name, - const char *default_value); - -char * gnc_option_db_lookup_multichoice_option (GNCOptionDB *odb, - const char *section, - const char *name, - const char *default_value); - -gdouble gnc_option_db_lookup_number_option (GNCOptionDB *odb, - const char *section, - const char *name, - gdouble default_value); - -gboolean gnc_option_db_lookup_color_option (GNCOptionDB *odb, - const char *section, - const char *name, - gdouble *red, - gdouble *green, - gdouble *blue, - gdouble *alpha); - -guint32 gnc_option_db_lookup_color_option_argb (GNCOptionDB *odb, - const char *section, - const char *name, - guint32 default_value); - -GSList * gnc_option_db_lookup_list_option (GNCOptionDB *odb, - const char *section, - const char *name, - GSList *default_value); - -void gnc_free_list_option_value (GSList *list); - -gnc_commodity * gnc_option_db_lookup_currency_option (GNCOptionDB *odb, - const char *section, - const char *name, - gnc_commodity *default_value); - -void gnc_option_db_set_option_default (GNCOptionDB *odb, - const char *section, - const char *name); - -gboolean gnc_option_db_set_option (GNCOptionDB *odb, - const char *section, - const char *name, - SCM value); - -gboolean gnc_option_db_set_number_option (GNCOptionDB *odb, - const char *section, - const char *name, - gdouble value); - -gboolean gnc_option_db_set_boolean_option (GNCOptionDB *odb, - const char *section, - const char *name, - gboolean value); - -gboolean gnc_option_db_set_string_option (GNCOptionDB *odb, - const char *section, - const char *name, - const char *value); - -char * gnc_option_date_option_get_subtype (GNCOption *option); - -char * gnc_date_option_value_get_type (SCM option_value); -time64 gnc_date_option_value_get_absolute (SCM option_value); -SCM gnc_date_option_value_get_relative (SCM option_value); - -char * gnc_plot_size_option_value_get_type (SCM option_value); -gdouble gnc_plot_size_option_value_get_value (SCM option_value); - -char * gnc_currency_accounting_option_currency_documentation (GNCOption *option); -SCM gnc_currency_accounting_option_get_default_currency (GNCOption *option); -char * gnc_currency_accounting_option_policy_documentation (GNCOption *option); -SCM gnc_currency_accounting_option_get_default_policy (GNCOption *option); -char * gnc_currency_accounting_option_gain_loss_account_documentation (GNCOption *option); -SCM gnc_currency_accounting_option_value_get_method (SCM option_value); -SCM gnc_currency_accounting_option_value_get_book_currency (SCM option_value); -SCM gnc_currency_accounting_option_value_get_default_policy (SCM option_value); -SCM gnc_currency_accounting_option_value_get_default_account (SCM option_value); - -void gnc_option_db_set_option_selectable_by_name (SCM guile_options, - const char *section, - const char *name, - gboolean selectable); - -gboolean gnc_dateformat_option_value_parse (SCM value, - QofDateFormat *format, - GNCDateMonthFormat *months, - gboolean *years, char **custom); - -SCM gnc_dateformat_option_set_value (QofDateFormat format, - GNCDateMonthFormat months, - gboolean years, - const char *custom); - -void gnc_option_db_register_option (GNCOptionDBHandle handle, - SCM guile_option); - -/* private */ -void gncp_option_invoke_callback (GNCOptionChangeCallback callback, - gpointer data); - -/* Reset all the widgets in one section to their default values */ -void gnc_option_db_section_reset_widgets (GNCOptionSection *section); - -/* Reset all the widgets to their default values */ -void gnc_option_db_reset_widgets (GNCOptionDB *odb); - -#endif /* OPTION_UTIL_H */ diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 9e950dcfaa..51086d6b27 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -25,2110 +25,87 @@ (use-modules (gnucash core-utils)) (use-modules (gnucash engine)) (use-modules (sw_app_utils)) -(use-modules (gnucash app-utils date-utilities)) (use-modules (gnucash utilities)) (use-modules (srfi srfi-1)) (use-modules (ice-9 regex)) +(use-modules (ice-9 format)) +(use-modules (ice-9 pretty-print)) -(export gnc:color->html) -(export gnc:color-option->hex-string) -(export gnc:color-option->html) -(export gnc:currency-accounting-option-get-curr-doc-string) -(export gnc:currency-accounting-option-get-default-curr) -(export gnc:currency-accounting-option-get-default-policy) -(export gnc:currency-accounting-option-get-gain-loss-account-doc-string) -(export gnc:currency-accounting-option-get-policy-doc-string) -(export gnc:currency-accounting-option-selected-currency) -(export gnc:currency-accounting-option-selected-gain-loss-account) -(export gnc:currency-accounting-option-selected-method) -(export gnc:currency-accounting-option-selected-policy) -(export gnc:date-option-absolute-time) -(export gnc:date-option-get-subtype) -(export gnc:date-option-relative-time) -(export gnc:date-option-show-time?) -(export gnc:date-option-value-type) -(export gnc:dateformat-get-format) -(export gnc:generate-restore-forms) -(export gnc:get-rd-option-data-rd-list) -(export gnc:get-rd-option-data-show-time) -(export gnc:get-rd-option-data-subtype) -(export gnc:lookup-option) -(export gnc:make-account-list-limited-option) -(export gnc:make-account-list-option) -(export gnc:make-account-sel-limited-option) -(export gnc:make-account-sel-option) -(export gnc:make-budget-option) -(export gnc:make-color-option) -(export gnc:make-commodity-option) -(export gnc:make-complex-boolean-option) -(export gnc:make-currency-option) -(export gnc:make-date-option) -(export gnc:make-dateformat-option) -(export gnc:make-font-option) -(export gnc:make-internal-option) -(export gnc:make-list-option) -(export gnc:make-multichoice-callback-option) -(export gnc:make-multichoice-option) -(export gnc:make-number-plot-size-option) -(export gnc:make-number-range-option) -(export gnc:make-option) -(export gnc:make-pixmap-option) -(export gnc:make-query-option) -(export gnc:make-radiobutton-callback-option) -(export gnc:make-radiobutton-option) -(export gnc:make-simple-boolean-option) -(export gnc:make-string-option) -(export gnc:make-text-option) -(export gnc:multichoice-list-lookup) -(export gnc:new-options) -(export gnc:option-data) -(export gnc:option-data-fns) -(export gnc:option-default-getter) -(export gnc:option-default-value) -(export gnc:option-documentation) -(export gnc:option-generate-restore-form) -(export gnc:option-get-value) -(export gnc:option-getter) -(export gnc:option-index-get-name) -(export gnc:option-index-get-value) -(export gnc:option-kvp->scm) -(export gnc:option-make-internal!) -(export gnc:option-name) -(export gnc:option-number-of-indices) -(export gnc:option-scm->kvp) -(export gnc:option-section) -(export gnc:option-set-changed-callback) -(export gnc:option-set-default-value) -(export gnc:option-set-value) -(export gnc:option-setter) -(export gnc:option-sort-tag) -(export gnc:option-strings-getter) -(export gnc:option-type) -(export gnc:option-value) -(export gnc:option-value-get-index) -(export gnc:option-value-validator) -(export gnc:option-widget-changed-proc) -(export gnc:options-clear-changes) -(export gnc:options-copy-values) -(export gnc:options-for-each) -(export gnc:options-for-each-general) -(export gnc:options-get-default-section) -(export gnc:options-kvp->scm) -(export gnc:options-make-date-interval!) -(export gnc:options-make-end-date!) -(export gnc:options-register-c-callback) -(export gnc:options-register-callback) -(export gnc:options-run-callbacks) -(export gnc:options-scm->kvp) -(export gnc:options-set-default-section) -(export gnc:options-touch) -(export gnc:options-unregister-callback-id) -(export gnc:plot-size-option-value) -(export gnc:plot-size-option-value-type) -(export gnc:register-option) -(export gnc:restore-form-generator) -(export gnc:send-options) -(export gnc:set-option-kvp->scm) -(export gnc:set-option-scm->kvp) -(export gnc:unregister-option) -(export gnc:value->string) +(define-public (gnc:lookup-option options section name) + (if options + (gnc-lookup-option (options 'lookup) section name) + #f)) -(export gnc:*option-name-trading-accounts*) -(export gnc:*option-name-book-currency*) -(export gnc:*option-section-accounts*) -(export gnc:*option-name-default-gains-policy*) -(export gnc:*option-name-default-gain-loss-account*) +(define-public (gnc:option-setter option) + (issue-deprecation-warning "gnc:option-setter is deprecated. Option values are set and retrieved by gnc-set-option and gnc-option-db-lookup.") + (lambda (value) + (GncOption-set-value-from-scm option value) + )) -(define gnc:*option-section-accounts* OPTION-SECTION-ACCOUNTS) -(define gnc:*option-name-trading-accounts* OPTION-NAME-TRADING-ACCOUNTS) -(define gnc:*option-name-currency-accounting* OPTION-NAME-CURRENCY-ACCOUNTING) -(define gnc:*option-name-book-currency* OPTION-NAME-BOOK-CURRENCY) -(define gnc:*option-name-default-gains-policy* OPTION-NAME-DEFAULT-GAINS-POLICY) -(define gnc:*option-name-default-gain-loss-account* - OPTION-NAME-DEFAULT-GAINS-LOSS-ACCT-GUID) +(define-public (gnc:option-set-value option value) + (issue-deprecation-warning "gnc:option-set-value and indeed all direct option access is deprecated. Use gnc-set-option instead.") + (GncOption-set-value-from-scm option value)) -(define (gnc:option-get-value book category key) +(define-public (gnc:option-set-default-value option value) + (issue-deprecation-warning "gnc:option-set-default-value and indeed all direct option access is deprecated. Use gnc-set-option instead.") + (GncOption-set-default-value-from-scm option value)) + +(define-public (gnc:option-section option) + (GncOption-get-section option)) + +(define-public (gnc:option-name option) + (GncOption-get-name option)) + +(define-public (gnc:option-default-value option) + (GncOption-get-scm-default-value option)) + +(define-public (gnc:option-value option) + (issue-deprecation-warning "gnc:option-value and indeed all direct option access is deprecated. Use gnc-option-db-lookup-option instead.") + (GncOption-get-scm-value option)) + +(define-public (gnc:color-option->html opt) + ;; HTML doesn't like alpha values. + (let* ((color (GncOption-get-scm-value opt)) + (html-color (if (> (string-length color) 6) + (substring color 0 6) + color))) + (format #f "#~a" html-color))) + +(define-public (gnc:color-option->hex-string opt) + (format #f "~a" (GncOption-get-scm-value opt))) + +(define-public (gnc:option-get-value book category key) (define acc (if (pair? key) cons list)) (qof-book-get-option book (acc category key))) -(define (rpterror-earlier type newoption fallback) - ;; Translators: the 3 ~a below refer to (1) option type (2) unknown - ;; new option name, (3) fallback option name. The order is - ;; important, and must not be changed. - (let* ((template (N_ "This report was saved using a later version of \ -GnuCash. One of the newer ~a options '~a' is not available, fallback to \ -the option '~a'.")) - (console-msg (format #f template type newoption fallback)) - (ui-msg (format #f (G_ template) type newoption fallback))) - (gnc:gui-warn console-msg ui-msg))) +(define-public (gnc:option-make-internal! options section name) + (let ((option (gnc-lookup-option (options 'lookup) section name))) + (and option (GncOption-make-internal option)))) -(define (gnc:make-option - ;; The category of this option - section - name - ;; The sort-tag determines the relative ordering of options in - ;; this category. It is used by the gui for display. - sort-tag - type - documentation-string - getter - ;; The setter is responsible for ensuring that the value is valid. - setter - default-getter - ;; Restore form generator should generate an ascii representation - ;; of a function taking one argument. The argument will be an - ;; option. The function should restore the option to the original - ;; value. - generate-restore-form - ;; the scm->kvp and kvp->scm functions should save and load - ;; the option to the book. The arguments to these function will be - ;; a book and a base key-path list for this option. - scm->kvp - kvp->scm - ;; Validation func should accept a value and return (#t value) - ;; on success, and (#f "failure-message") on failure. If #t, - ;; the supplied value will be used by the gui to set the option. - value-validator - ;;; free-form storage depending on type. - option-data - ;; If this is a "multiple choice" type of option, - ;; this should be a vector of the following five functions: - ;; - ;; Function 1: taking no arguments, giving the number of choices - ;; - ;; Function 2: taking one argument, a non-negative integer, that - ;; returns the scheme value (usually a symbol) matching the - ;; nth choice - ;; - ;; Function 3: taking one argument, a non-negative integer, - ;; that returns the string matching the nth choice - ;; - ;; Function 4: #f, this was the individual tool tip and not used now - ;; - ;; Function 5: giving a possible value and returning the index - ;; if an option doesn't use these, this should just be a #f - option-data-fns - ;; This function should return a list of all the strings - ;; in the option other than the section, name, (define - ;; (list-lookup list item) and documentation-string that - ;; might be displayed to the user (and thus should be - ;; translated). - strings-getter - ;; This function will be called when the GUI representation - ;; of the option is changed. This will normally occur before - ;; the setter is called, because setters are only called when - ;; the user selects "OK" or "Apply". Therefore, this - ;; callback shouldn't be used to make changes to the actual - ;; options database. - option-widget-changed-proc) - (let ((changed-callback #f)) - (vector section - name - sort-tag - type - documentation-string - getter - (lambda args - (apply setter args) - (if changed-callback (changed-callback))) - default-getter - generate-restore-form - scm->kvp - kvp->scm - value-validator - option-data - option-data-fns - (lambda (callback) (set! changed-callback callback)) - strings-getter - option-widget-changed-proc))) +(define-public (gnc:option-type option) + (GncOption-get-type option)) -(define (gnc:option-section option) - (vector-ref option 0)) -(define (gnc:option-name option) - (vector-ref option 1)) -(define (gnc:option-sort-tag option) - (vector-ref option 2)) -(define (gnc:option-type option) - (vector-ref option 3)) -(define (gnc:option-documentation option) - (vector-ref option 4)) -(define (gnc:option-getter option) - (vector-ref option 5)) -(define (gnc:option-setter option) - (vector-ref option 6)) -(define (gnc:option-default-getter option) - (vector-ref option 7)) -(define (gnc:option-generate-restore-form option) - (vector-ref option 8)) -(define (gnc:option-scm->kvp option) - (vector-ref option 9)) -(define (gnc:set-option-scm->kvp option v) - (vector-set! option 9 v)) -(define (gnc:option-kvp->scm option) - (vector-ref option 10)) -(define (gnc:set-option-kvp->scm option v) - (vector-set! option 10 v)) -(define (gnc:option-value-validator option) - (vector-ref option 11)) -(define (gnc:option-data option) - (vector-ref option 12)) -(define (gnc:option-data-fns option) - (vector-ref option 13)) +;; Create the database and return a dispatch function. +(define-public (gnc:new-options) + (let ((optiondb (new-gnc-optiondb))) + (define (dispatch key) + optiondb) + dispatch)) -(define (gnc:option-set-changed-callback option callback) - (let ((cb-setter (vector-ref option 14))) - (cb-setter callback))) -(define (gnc:option-strings-getter option) - (vector-ref option 15)) -(define (gnc:option-widget-changed-proc option) - (vector-ref option 16)) +;; Use the dispatch function to get the optiondb +(define-public (gnc:options-get dispatch) + (dispatch 'get)) -(define (gnc:option-value option) - (let ((getter (gnc:option-getter option))) - (getter))) +(define-public (gnc:options-set-default-section optiondb section) + (GncOptionDB-set-default-section (GncOptionDBPtr-get (optiondb 'set-default-section)) section)) -(define (gnc:option-set-value option value) - (let ((setter (gnc:option-setter option))) - (setter value))) - -(define (gnc:option-index-get-name option index) - (let* ((option-data-fns (gnc:option-data-fns option)) - (name-fn (vector-ref option-data-fns 2))) - (name-fn index))) - -(define (gnc:option-index-get-value option index) - (let* ((option-data-fns (gnc:option-data-fns option)) - (name-fn (vector-ref option-data-fns 1))) - (name-fn index))) - -(define (gnc:option-value-get-index option value) - (let* ((option-data-fns (gnc:option-data-fns option)) - (name-fn (vector-ref option-data-fns 4))) - (name-fn value))) - -(define (gnc:option-number-of-indices option) - (let* ((option-data-fns (gnc:option-data-fns option)) - (name-fn (vector-ref option-data-fns 0))) - (name-fn))) - -(define (gnc:option-default-value option) - (let ((getter (gnc:option-default-getter option))) - (getter))) - -;; Attention: this function can only be used with restrictions -;; - only during option generation, not in arbitrary code -;; - only for option types for which no conversion is required -;; between default-value and value. In the various gnc:make-option -;; functions below this is ok when -;; 1. there's (value default-value) in the let* call -;; 2. default-getter is set to (lambda() default-value) -(define (gnc:option-set-default-value option default-value) - (vector-set! option 7 (lambda() default-value)) - (gnc:option-set-value option default-value)) - - -(define (gnc:restore-form-generator value->string) - (lambda () - (string-append "(lambda (o) (if o (gnc:option-set-value o " - (value->string) ")))"))) - -(define (gnc:value->string value) - (format #f "~s" value)) - -(define (gnc:make-string-option - section - name - sort-tag - documentation-string - default-value) - (let* ((value default-value) - (value->string (lambda () (gnc:value->string value)))) - (gnc:make-option - section name sort-tag 'string documentation-string - (lambda () value) - (lambda (x) (set! value x)) - (lambda () default-value) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b value p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (set! value v)))) - (lambda (x) - (cond ((string? x)(list #t x)) - (else (list #f "string-option: not a string")))) - #f #f #f #f))) - -(define (gnc:make-text-option - section - name - sort-tag - documentation-string - default-value) - (let* ((value default-value) - (value->string (lambda () (gnc:value->string value)))) - (gnc:make-option - section name sort-tag 'text documentation-string - (lambda () value) - (lambda (x) (set! value x)) - (lambda () default-value) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b value p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (set! value v)))) - (lambda (x) - (cond ((string? x)(list #t x)) - (else (list #f "text-option: not a string")))) - #f #f #f #f))) - -;;; font options store fonts as strings a la the X Logical -;;; Font Description. You should always provide a default -;;; value, as currently there seems to be no way to go from -;;; an actual font to a logical font description, and thus -;;; there is no way for the gui to pick a default value. - -(define (gnc:make-font-option - section - name - sort-tag - documentation-string - default-value) - (let* ((value default-value) - (value->string (lambda () (gnc:value->string value)))) - (gnc:make-option - section - name - sort-tag - 'font - documentation-string - (lambda () value) - (lambda (x) (set! value x)) - (lambda () default-value) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b value p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (set! value v)))) - (lambda (x) - (cond ((string? x)(list #t x)) - (else (list #f "font-option: not a string")))) - #f #f #f #f))) - -;; currency options use a specialized widget for entering currencies -;; in the GUI implementation. -(define (gnc:make-currency-option - section - name - sort-tag - documentation-string - default-value) - - (define (currency->scm currency) - (if (string? currency) - currency - (gnc-commodity-get-mnemonic currency))) - - (define (scm->currency currency) - (if (string? currency) - (gnc-commodity-table-lookup - (gnc-commodity-table-get-table (gnc-get-current-book)) - (GNC-COMMODITY-NS-CURRENCY) currency) - currency)) - - (let* ((value (currency->scm default-value)) - (value->string (lambda () (gnc:value->string value)))) - (gnc:make-option - section name sort-tag 'currency documentation-string - (lambda () (scm->currency value)) - (lambda (x) (set! value (currency->scm x))) - (lambda () (scm->currency default-value)) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b value p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (set! value v)))) - (lambda (x) (list #t x)) - #f #f #f #f))) - -;; budget option -;; TODO: need to double-check this proc (dates back to r11545 or eariler) -;; -;; Always takes/returns a budget -;; Stores the GUID in the KVP -;; -(define (gnc:make-budget-option - section - name - sort-tag - documentation-string) - - (define (convert-to-guid item) - (if (string? item) item (gncBudgetGetGUID item))) - - (define (convert-to-budget item) - (if (string? item) (gnc-budget-lookup item (gnc-get-current-book)) item)) - - (let* ((initial-budget (gnc-budget-get-default (gnc-get-current-book))) - (selection-budget (convert-to-guid initial-budget))) - - (gnc:make-option - section - name - sort-tag - 'budget - documentation-string - - ;; getter -- Return a budget pointer - (lambda () - (convert-to-budget selection-budget)) - - ;; setter -- takes a budget - (lambda (x) - (set! selection-budget (convert-to-guid x))) - - ;; default-getter - ;; Default now is #f so saving is independent of book-level default - (lambda () - #f) - - ;; generate-restore-form - ;; "return 'ascii represention of a function' - ;; that will set the option passed as its lone parameter - ;; to the value it was when the picker was first displayed" - ;; - ;; *This* is used to restore reports, not the KVP -- and is stored as text - ;; This does not run in closure with direct access to the option's - ;; internal variables, so the setter generally gets used - (lambda () - (string-append - "(lambda (option) " - "(if option ((gnc:option-setter option) " - "(gnc-budget-lookup " - (gnc:value->string selection-budget) - " (gnc-get-current-book)))))")) - - ;; scm->kvp -- commit the change - ;; b -- book; p -- key-path - (lambda (b p) - (qof-book-set-option b selection-budget p)) - - ;; kvp->scm -- get the stored value - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (set! selection-budget (convert-to-guid - (gnc-budget-lookup v (gnc-get-current-book))))))) - - ;; value-validator -- returns (#t value) or (#f "failure message") - ;; As no user-generated input, this legacy hard-wire is probably ok - (lambda (x) - (list #t x)) - - ;; option-data - #f - - ;; option-data-fns -- used for multi-pick (this isn't one), or #f - ;; Vector of five functions - ;; 1) () => number of choices - ;; 2) (n) => key for the nth choice - ;; 3) (n) => string for the nth choice - ;; 4) (n) => description for the nth choice - ;; 5) (key) => n (assuming this is the reverse key lookup) - #f - - ;; strings-getter -- list of all translatable strings in the option - #f - - ;; options-widget-changed-proc -- callback for what it sounds like - #f - - ))) ;; completes gnc:make-budget-option - - -;; commodity options use a specialized widget for entering commodities -;; in the GUI implementation. -(define (gnc:make-commodity-option - section - name - sort-tag - documentation-string - default-value) - - (define (commodity->scm commodity) - (if (string? commodity) - (list 'commodity-scm - (GNC-COMMODITY-NS-CURRENCY) - commodity) - (list 'commodity-scm - (gnc-commodity-get-namespace commodity) - (gnc-commodity-get-mnemonic commodity)))) - - (define (scm->commodity scm) - (gnc-commodity-table-lookup - (gnc-commodity-table-get-table (gnc-get-current-book)) - (cadr scm) (caddr scm))) - - (let* ((value (commodity->scm default-value)) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name sort-tag 'commodity documentation-string - (lambda () (scm->commodity value)) - (lambda (x) (if (and (pair? x) (eqv? (car x) 'commodity-scm)) - (set! value x) - (set! value (commodity->scm x)))) - (lambda () default-value) - (gnc:restore-form-generator value->string) - (lambda (b p) - (qof-book-set-option b (cadr value) (append p '("ns"))) - (qof-book-set-option b (caddr value) (append p '("monic")))) - (lambda (b p) - (let ((ns (qof-book-get-option b (append p '("ns")))) - (monic (qof-book-get-option b (append p '("monic"))))) - (if (and ns monic (string? ns) (string? monic)) - (set! value (list 'commodity-scm ns monic))))) - (lambda (x) (list #t x)) - #f #f #f #f))) - - -(define (gnc:make-simple-boolean-option - section - name - sort-tag - documentation-string - default-value) - (gnc:make-complex-boolean-option section - name - sort-tag - documentation-string - default-value - #f - #f)) - -;; Complex boolean options are the same as simple boolean options (see -;; above), with the addition of two function arguments. (If both of -;; them are #f, you have exactly a simple-boolean-option.) Both -;; functions should expect one boolean argument. When the option's -;; value is changed, the function option-widget-changed-cb will be -;; called with the new option value at the time that the GUI widget -;; representing the option is changed, and the function -;; setter-function-called-cb will be called when the option's setter -;; is called (that is, when the user selects "OK" or "Apply"). - -;; The option-widget-changed-cb is tested for procedurehood before -;; it is called, so it is not validated to be a procedure here. -;; However, since there could be an option-widget-changed-cb but not -;; a setter-function-called-cb, the procedurehood of the -;; setter-function-called-cb is checked here. -(define (gnc:make-complex-boolean-option - section - name - sort-tag - documentation-string - default-value - setter-function-called-cb - option-widget-changed-cb) - (let* ((value default-value) - (value->string (lambda () (gnc:value->string value)))) - (gnc:make-option - section name sort-tag 'boolean documentation-string - (lambda () value) - (lambda (x) (set! value x) - (if (procedure? setter-function-called-cb) - (setter-function-called-cb x))) - (lambda () default-value) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b - ;; As no boolean KvpValue exists, as a workaround - ;; we store the string "t" for TRUE and "f" for - ;; FALSE in a string KvpValue. - (if value "t" "f") - p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - ;; As no boolean KvpValue exists, as a workaround we store - ;; the string "t" for TRUE and "f" for FALSE. - (cond ((equal? v "t") (set! v #t)) - ((equal? v "f") (set! v #f))) - (if (and v (boolean? v) (not (equal? v default-value))) - (set! value v)))) - (lambda (x) - (if (boolean? x) - (list #t x) - (list #f "boolean-option: not a boolean"))) - #f #f #f (and option-widget-changed-cb - (lambda (x) (option-widget-changed-cb x)))))) - - -(define (gnc:make-pixmap-option - section name sort-tag doc-string - default-value) - (let* ((value default-value)) - (gnc:make-option - section name sort-tag 'pixmap doc-string - (lambda () value) - (lambda (x) (set! value x)) - (lambda () default-value) - (gnc:restore-form-generator (lambda () (gnc:value->string value))) - #f - #f - (lambda (x) - (if (string? x) - (begin - (list #t x)) - (begin - (list #f "pixmap-option: not a string")))) - #f #f #f #f))) - -;; show-time is boolean -;; subtype should be one of 'relative 'absolute or 'both -;; if subtype is 'absolute then relative-date-list should be #f -;; relative-date-list should be the list of relative dates permitted -;; gnc:all-relative-dates contains a list of all relative dates. - -(define (gnc:make-date-option - section - name - sort-tag - documentation-string - default-getter - show-time - subtype - relative-date-list) - (define (date-legal date) - (and (pair? date) - (or - (and (eq? 'relative (car date)) (symbol? (cdr date))) - (and (eq? 'absolute (car date)) - (or (and (pair? (cdr date)) ; we can still accept - (exact? (cadr date)) ; old-style timepairs - (exact? (cddr date))) - (and (number? (cdr date)) - (exact? (cdr date)))))))) - (define (maybe-convert-to-time64 date) - ;; compatibility shim. this is triggered only when date is type - ;; (cons 'absolute (cons sec nsec)) - we'll convert to - ;; (cons 'absolute sec). this shim must always be kept for gnucash - ;; to reload saved reports, or reload reports launched at startup, - ;; which had been saved as timepairs. - (if (pair? (cdr date)) - (cons (car date) (cadr date)) - date)) - (define (list-lookup full-list item) - (or (list-index (lambda (i) (eq? i item)) full-list) - (begin - (rpterror-earlier "date" item (car full-list)) - 0))) - (if show-time - (issue-deprecation-warning - (format #f "Date options with time of day values are deprecated and will be removed in GnuCash 5."))) - - (let* ((value (default-getter)) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name sort-tag 'date documentation-string - (lambda () value) - (lambda (date) - (if (date-legal date) - (set! value (maybe-convert-to-time64 date)) - (gnc:error "Illegal date value set:" date))) - default-getter - (gnc:restore-form-generator value->string) - (lambda (b p) - (qof-book-set-option b (symbol->string (car value)) - (append p '("type"))) - (qof-book-set-option b - (if (symbol? (cdr value)) - (symbol->string (cdr value)) - (cdr value)) - (append p '("value")))) - (lambda (b p) - (let ((t (qof-book-get-option b (append p '("type")))) - (v (qof-book-get-option b (append p '("value"))))) - (if (and t v (string? t)) - (set! value (cons (string->symbol t) - (if (string? v) (string->symbol v) v)))))) - (lambda (date) - (if (date-legal date) - (list #t date) - (list #f "date-option: illegal date"))) - (vector subtype show-time relative-date-list) - (vector (lambda () (length relative-date-list)) - (lambda (x) (list-ref relative-date-list x)) - (lambda (x) (gnc:get-relative-date-string - (list-ref relative-date-list x))) - (lambda (x) (gnc:get-relative-date-desc - (list-ref relative-date-list x))) - (lambda (x) (list-lookup relative-date-list x))) - #f - #f))) - -(define (gnc:get-rd-option-data-subtype option-data) - (vector-ref option-data 0)) - -(define (gnc:get-rd-option-data-show-time option-data) - (vector-ref option-data 1)) - -(define (gnc:get-rd-option-data-rd-list option-data) - (vector-ref option-data 2)) - -(define (gnc:date-option-get-subtype option) - (if (eq? (gnc:option-type option) 'date) - (gnc:get-rd-option-data-subtype (gnc:option-data option)) - (gnc:error "Not a date option"))) - -(define (gnc:date-option-show-time? option) - (if (eq? (gnc:option-type option) 'date) - (gnc:get-rd-option-data-show-time (gnc:option-data option)) - (gnc:error "Not a date option"))) - -(define (gnc:date-option-value-type option-value) - (car option-value)) - -(define (gnc:date-option-absolute-time option-value) - (if (eq? (car option-value) 'absolute) - (cdr option-value) - (gnc:get-absolute-from-relative-date (cdr option-value)))) - -(define (gnc:date-option-relative-time option-value) - (if (eq? (car option-value) 'absolute) - #f - (cdr option-value))) - -;; Just like gnc:make-account-list-limited-option except it -;; does not limit the types of accounts that are available -;; to the user. -(define (gnc:make-account-list-option - section - name - sort-tag - documentation-string - default-getter - value-validator - multiple-selection) - - (gnc:make-account-list-limited-option - section name sort-tag documentation-string - default-getter value-validator multiple-selection '())) - -;; account-list options use the option-data as a pair; the car is -;; a boolean value, the cdr is a list of account-types. If the boolean is -;; true, the gui should allow the user to select multiple accounts. -;; If the cdr is an empty list, then all account types are shown. -;; Internally, values are always a list of guids. Externally, both -;; guids and account pointers may be used to set the value of the -;; option. The option always returns a list of account pointers. -(define (gnc:make-account-list-limited-option - section - name - sort-tag - documentation-string - default-getter - value-validator - multiple-selection - acct-type-list) - - (define (convert-to-guid item) - (if (string? item) - item - (gncAccountGetGUID item))) - - (define (convert-to-account item) - (if (string? item) - (xaccAccountLookup item (gnc-get-current-book)) - item)) - - (let* ((option (map convert-to-guid (default-getter))) - (option-set #f) - (getter (lambda () (map convert-to-account - (if option-set - option - (default-getter))))) - (value->string (lambda () - (string-append - "'" (gnc:value->string (if option-set option #f))))) - (validator - (if (not value-validator) - (lambda (account-list) (list #t account-list)) - (lambda (account-list) - (value-validator (map convert-to-account account-list)))))) - (gnc:make-option - section name sort-tag 'account-list documentation-string getter - (lambda (account-list) - (if (or (not account-list) (null? account-list)) - (set! account-list (default-getter))) - (set! account-list - (filter (lambda (x) (if (string? x) - (xaccAccountLookup - x (gnc-get-current-book)) - x)) account-list)) - (let* ((result (validator account-list)) - (valid (car result)) - (value (cadr result))) - (if valid - (begin - (set! option (map convert-to-guid value)) - (set! option-set #t)) - (gnc:error "Illegal account list value set")))) - (lambda () (map convert-to-account (default-getter))) - (gnc:restore-form-generator value->string) - (lambda (b p) - (when option-set - (qof-book-set-option b (length option) (append p '("len"))) - (let loop ((option option) (idx 0)) - (unless (null? option) - (qof-book-set-option - b (car option) (append p (list (format #f "acc~a" idx)))) - (loop (cdr option) (1+ idx)))))) - (lambda (b p) - (let ((len (qof-book-get-option b (append p '("len"))))) - (when (and len (integer? len)) - (set! option - (map - (lambda (idx) - (qof-book-get-option b (append p (list (format #f "acc~a" idx))))) - (iota len))) - (set! option-set #t)))) - validator - (cons multiple-selection acct-type-list) #f #f #f))) - -;; Just like gnc:make-account-sel-limited-option except it -;; does not limit the types of accounts that are available -;; to the user. -(define (gnc:make-account-sel-option - section - name - sort-tag - documentation-string - default-getter - value-validator) - - (gnc:make-account-sel-limited-option - section name sort-tag documentation-string - default-getter value-validator '())) - -;; account-sel options use the option-data as a pair; the car is -;; ignored, the cdr is a list of account-types. If the cdr is an empty -;; list, then all account types are shown. Internally, the value is -;; always a guid. Externally, both guids and account pointers may be -;; used to set the value of the option. The option always returns the -;; "current" account pointer. -(define (gnc:make-account-sel-limited-option - section - name - sort-tag - documentation-string - default-getter - value-validator - acct-type-list) - - (define (convert-to-guid item) - (if (string? item) - item - (gncAccountGetGUID item))) - - (define (convert-to-account item) - (if (string? item) - (xaccAccountLookup item (gnc-get-current-book)) - item)) - - (define (find-first-account) - (define (find-first account-list) - (if (null? account-list) - '() - (let* ((this-account (car account-list)) - (account-type (xaccAccountGetType this-account))) - (if (if (null? acct-type-list) - #t - (member account-type acct-type-list)) - this-account - (find-first (cdr account-list)))))) - - (let* ((current-root (gnc-get-current-root-account)) - (account-list (gnc-account-get-descendants-sorted current-root))) - (find-first account-list))) - - (define (get-default) - (if default-getter - (default-getter) - (find-first-account))) - - (let* ((option (convert-to-guid (get-default))) - (option-set #f) - (getter (lambda () (convert-to-account - (if option-set - option - (get-default))))) - (value->string (lambda () - (string-append - (gnc:value->string (if option-set option #f))))) - (validator - (if (not value-validator) - (lambda (account) (list #t account)) - (lambda (account) - (value-validator (convert-to-account account)))))) - (gnc:make-option - section name sort-tag 'account-sel documentation-string getter - (lambda (account) - (if (or (not account) (null? account)) (set! account (get-default))) - (set! account (convert-to-account account)) - (let* ((result (validator account)) - (valid (car result)) - (value (cadr result))) - (if valid - (begin - (set! option (convert-to-guid value)) - (set! option-set #t)) - (gnc:error "Illegal account value set")))) - (lambda () (convert-to-account (get-default))) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b option p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (set! option v)))) - validator - (cons #f acct-type-list) #f #f #f))) - -(define (gnc:multichoice-list-lookup full-lst item) - (or (list-index (lambda (i) (eq? (vector-ref i 0) item)) full-lst) - (begin - (rpterror-earlier "multichoice" item (car full-lst)) - 0))) - -(define (check-ok-values ok-values fn) - (for-each - (lambda (ok-value) - (when (> (vector-length ok-value) 2) - (issue-deprecation-warning - (format #f "~a: the tooltip in ~a is not supported anymore. Please remove." fn ok-value)))) - ok-values)) - -;; multichoice options use the option-data as a list of vectors. -;; Each vector contains a permissible value (scheme symbol), a -;; name, and a description string. -(define (gnc:make-multichoice-option - section - name - sort-tag - documentation-string - default-value - ok-values) - (gnc:make-multichoice-callback-option section - name - sort-tag - documentation-string - default-value - ok-values - #f - #f)) - -;; The multichoice-option with callback function is the same as the -;; usual multichoice options (see above), with the addition of two -;; function arguments. (If both of them are #f, you have exactly a -;; multichoice-option.) Both functions should expect one argument. -;; When the option's value is changed, the function -;; option-widget-changed-cb will be called with the new option value -;; at the time that the GUI widget representing the option is changed, -;; and the function setter-function-called-cb will be called when the -;; option's setter is called (that is, when the user selects "OK" or -;; "Apply"). -(define (gnc:make-multichoice-callback-option - section - name - sort-tag - documentation-string - default-value - ok-values - setter-function-called-cb - option-widget-changed-cb) - (define (multichoice-legal val p-vals) - (cond ((null? p-vals) #f) - ((eq? val (vector-ref (car p-vals) 0)) #t) - (else (multichoice-legal val (cdr p-vals))))) - - (define (multichoice-strings p-vals) - (if (null? p-vals) - '() - (cons (vector-ref (car p-vals) 1) - (multichoice-strings (cdr p-vals))))) - - (check-ok-values ok-values "gnc:make-multichoice-[callback-]option") - - (let* ((value default-value) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name sort-tag 'multichoice documentation-string - (lambda () value) - (lambda (x) - (cond - ((and (equal? section "Display") - (equal? name "Parent account subtotals") - (equal? x 'canonically-tabbed)) - (gnc:warn "canonically-tabbed obsolete. switching to 't") - (set! value 't)) - ((not (multichoice-legal x ok-values)) - (rpterror-earlier "multichoice" x default-value)) - (else - (set! value x) - (if (procedure? setter-function-called-cb) - (setter-function-called-cb x))))) - (lambda () default-value) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b (symbol->string value) p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (set! value (string->symbol v))))) - (lambda (x) - (if (multichoice-legal x ok-values) - (list #t x) - (list #f "multichoice-option: illegal choice"))) - ok-values - (vector (lambda () (length ok-values)) - (lambda (x) (vector-ref (list-ref ok-values x) 0)) - (lambda (x) (vector-ref (list-ref ok-values x) 1)) - #f ;old tooltip - (lambda (x) - (gnc:multichoice-list-lookup ok-values x))) - (lambda () (multichoice-strings ok-values)) - (and option-widget-changed-cb - (lambda (x) (option-widget-changed-cb x)))))) - - -;; radiobutton options use the option-data as a list of vectors. -;; Each vector contains a permissible value (scheme symbol), a -;; name, and a description string. -(define (gnc:make-radiobutton-option - section - name - sort-tag - documentation-string - default-value - ok-values) - (gnc:make-radiobutton-callback-option section - name - sort-tag - documentation-string - default-value - ok-values - #f - #f)) - -;; The radiobutton-option with callback function is the same as the -;; usual radiobutton options (see above), with the addition of two -;; function arguments. (If both of them are #f, you have exactly a -;; radiobutton-option.) Both functions should expect one argument. -;; When the option's value is changed, the function -;; option-widget-changed-cb will be called with the new option value -;; at the time that the GUI widget representing the option is changed, -;; and the function setter-function-called-cb will be called when the -;; option's setter is called (that is, when the user selects "OK" or -;; "Apply"). -(define (gnc:make-radiobutton-callback-option - section - name - sort-tag - documentation-string - default-value - ok-values - setter-function-called-cb - option-widget-changed-cb) - (define (radiobutton-legal val p-vals) - (cond ((null? p-vals) #f) - ((eq? val (vector-ref (car p-vals) 0)) #t) - (else (radiobutton-legal val (cdr p-vals))))) - - (define (radiobutton-strings p-vals) - (if (null? p-vals) - '() - (cons (vector-ref (car p-vals) 1) - (radiobutton-strings (cdr p-vals))))) - - (let* ((value default-value) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name sort-tag 'radiobutton documentation-string - (lambda () value) - (lambda (x) - (if (radiobutton-legal x ok-values) - (begin - (set! value x) - (if (procedure? setter-function-called-cb) - (setter-function-called-cb x))) - (rpterror-earlier "radiobutton" x default-value))) - (lambda () default-value) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b (symbol->string value) p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (string? v)) - (set! value (string->symbol v))))) - (lambda (x) - (if (radiobutton-legal x ok-values) - (list #t x) - (list #f "radiobutton-option: illegal choice"))) - ok-values - (vector (lambda () (length ok-values)) - (lambda (x) (vector-ref (list-ref ok-values x) 0)) - (lambda (x) (vector-ref (list-ref ok-values x) 1)) - #f ;old tooltip - (lambda (x) - (gnc:multichoice-list-lookup ok-values x))) - (lambda () (radiobutton-strings ok-values)) - (and option-widget-changed-cb - (lambda (x) (option-widget-changed-cb x)))))) - - -;; list options use the option-data in the same way as multichoice -;; options. List options allow the user to select more than one option. -(define (gnc:make-list-option - section - name - sort-tag - documentation-string - default-value - ok-values) - - (define (legal-value? value legal-values) - (cond ((null? legal-values) #f) - ((eq? value (vector-ref (car legal-values) 0)) #t) - (else (legal-value? value (cdr legal-values))))) - - (define (list-legal values) - (cond ((null? values) #t) - (else - (and - (legal-value? (car values) ok-values) - (list-legal (cdr values)))))) - - (define (list-strings p-vals) - (if (null? p-vals) - '() - (cons (vector-ref (car p-vals) 1) - (list-strings (cdr p-vals))))) - - (check-ok-values ok-values "gnc:make-list-option") - - (let* ((value default-value) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name sort-tag 'list documentation-string - (lambda () value) - (lambda (x) - (if (list-legal x) - (set! value x) - (rpterror-earlier "list" x default-value))) - (lambda () default-value) - (gnc:restore-form-generator value->string) - (lambda (b p) - (qof-book-set-option b (length value) (append p '("len"))) - (let loop ((value value) (idx 0)) - (unless (null? value) - (qof-book-set-option - b (caar value) (append p (list (format #f "item~a" idx)))) - (loop (cdr value) (1+ idx))))) - (lambda (b p) - (let ((len (qof-book-get-option b (append p '("len"))))) - (if (and len (integer? len)) - (set! value - (map - (lambda (idx) - (qof-book-get-option b (append p (list (format #f "item~a" idx))))) - (iota len)))))) - (lambda (x) - (if (list-legal x) - (list #t x) - (list #f "list-option: illegal value"))) - ok-values - (vector (lambda () (length ok-values)) - (lambda (x) (vector-ref (list-ref ok-values x) 0)) - (lambda (x) (vector-ref (list-ref ok-values x) 1)) - #f ;old tooltip - (lambda (x) (gnc:multichoice-list-lookup ok-values x))) - (lambda () (list-strings ok-values)) #f))) - -;; number range options use the option-data as a list whose -;; elements are: (lower-bound upper-bound num-decimals step-size) -(define (gnc:make-number-range-option - section - name - sort-tag - documentation-string - default-value - lower-bound - upper-bound - num-decimals - step-size) - (let* ((value default-value) - (value->string (lambda () (number->string value)))) - (gnc:make-option - section name sort-tag 'number-range documentation-string - (lambda () value) - (lambda (x) (set! value x)) - (lambda () default-value) - (gnc:restore-form-generator value->string) - (lambda (b p) (qof-book-set-option b value p)) - (lambda (b p) - (let ((v (qof-book-get-option b p))) - (if (and v (number? v)) - (set! value v)))) - (lambda (x) - (cond ((not (number? x)) (list #f "number-range-option: not a number")) - ((and (>= value lower-bound) - (<= value upper-bound)) - (list #t x)) - (else (list #f "number-range-option: out of range")))) - (list lower-bound upper-bound num-decimals step-size) - #f #f #f))) - -;; number plot size options use the option-data as a list whose -;; elements are: (lower-bound upper-bound num-decimals step-size) -;; which is used for the valid pixel range -(define (gnc:make-number-plot-size-option - section - name - sort-tag - documentation-string - default-value - lower-bound - upper-bound - num-decimals - step-size) - (let* ((value default-value) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name sort-tag 'plot-size documentation-string - (lambda () value) ;;getter - (lambda (x) - (if (number? x) ;; this is for old style plot size - (set! value (cons 'pixels x)) - (set! value x))) ;;setter - - (lambda () default-value) ;;default-getter - (gnc:restore-form-generator value->string) ;;restore-form - (lambda (b p) - (qof-book-set-option b (symbol->string (car value)) - (append p '("type"))) - (qof-book-set-option b (if (symbol? (cdr value)) - (symbol->string (cdr value)) - (cdr value)) - (append p '("value")))) ;;scm->kvp - (lambda (b p) - (let ((t (qof-book-get-option b (append p '("type")))) - (v (qof-book-get-option b (append p '("value"))))) - (if (and t v (string? t)) - (set! value (cons (string->symbol t) - (if (string? v) (string->number v) v)))))) ;;kvp->scm - (lambda (x) - (if (eq? 'pixels (car x)) - (cond ((not (number? (cdr x))) (list #f "number-plot-size-option-pixels: not a number")) - ((and (>= (cdr x) lower-bound) - (<= (cdr x) upper-bound)) - (list #t x)) - (else (list #f "number-plot-size-option-pixels: out of range"))) - (cond ((not (number? (cdr x))) (list #f "number-plot-size-option-percentage: not a number")) - ((and (>= (cdr x) 10) - (<= (cdr x) 100)) - (list #t x)) - (else (list #f "number-plot-size-option-percentage: out of range"))) - ) - ) ;;value-validator - (list lower-bound upper-bound num-decimals step-size) ;;option-data - #f #f #f))) ;;option-data-fns, strings-getter, option-widget-changed-proc - -(define (gnc:plot-size-option-value-type option-value) - (car option-value)) - -(define (gnc:plot-size-option-value option-value) - (cdr option-value)) - -(define (gnc:make-internal-option - section - name - default-value) - (let* ((value default-value) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name "" 'internal #f - (lambda () value) - (lambda (x) (set! value x)) - (lambda () default-value) - (gnc:restore-form-generator value->string) - #f - #f - (lambda (x) (list #t x)) - #f #f #f #f))) - - -(define (gnc:make-query-option - section - name - default-value) - (let* ((value (if (list? default-value) - default-value - (gnc-query2scm default-value))) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name "" 'query #f - (lambda () value) - (lambda (x) (set! value (if (list? x) x (gnc-query2scm x)))) - (lambda () (if (list? default-value) - default-value - (gnc-query2scm default-value))) - (gnc:restore-form-generator value->string) - #f - #f - (lambda (x) (list #t x)) - #f #f #f #f))) - - -;; Color options store rgba values in a list. -;; The option-data is a list, whose first element -;; is the range of possible rgba values and whose -;; second element is a boolean indicating whether -;; to use alpha transparency. -(define (gnc:make-color-option - section - name - sort-tag - documentation-string - default-value - range - use-alpha) - - (define (canonicalize values) - (map exact->inexact values)) - - (define (values-in-range values) - (if (null? values) - #t - (let ((value (car values))) - (and (number? value) - (>= value 0) - (<= value range) - (values-in-range (cdr values)))))) - - (define (validate-color color) - (cond ((not (list? color)) (list #f "color-option: not a list")) - ((not (= 4 (length color))) (list #f "color-option: wrong length")) - ((not (values-in-range color)) - (list #f "color-option: bad color values")) - (else (list #t color)))) - - (let* ((value (canonicalize default-value)) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name sort-tag 'color documentation-string - (lambda () value) - (lambda (x) (set! value (canonicalize x))) - (lambda () (canonicalize default-value)) - (gnc:restore-form-generator value->string) - #f - #f - validate-color - (list range use-alpha) - #f #f #f))) - -(define (gnc:color->hex-string color range) - (define (html-value value) - (inexact->exact - (min 255.0 - (truncate (* (/ 255.0 range) value))))) - (define (number->hex-string number) - (let ((ret (number->string number 16))) - (cond ((< (string-length ret) 2) (string-append "0" ret)) - (else ret)))) - (let ((red (car color)) - (green (cadr color)) - (blue (caddr color))) - (string-append - (number->hex-string (html-value red)) - (number->hex-string (html-value green)) - (number->hex-string (html-value blue))))) - -(define (gnc:color->html color range) - (string-append "#" - (gnc:color->hex-string color range))) - -(define (gnc:color-option->html color-option) - (let ((color (gnc:option-value color-option)) - (range (car (gnc:option-data color-option)))) - (gnc:color->html color range))) - -(define (gnc:color-option->hex-string color-option) - (let ((color (gnc:option-value color-option)) - (range (car (gnc:option-data color-option)))) - (gnc:color->hex-string color range))) - -;; -;; dateformat option -;; -(define (gnc:make-dateformat-option - section - name - sort-tag - documentation-string - default-value) - - (define (def-value) - (if (list? default-value) - default-value - (list 'unset 'number #t ""))) - - (let* ((value (def-value)) - (value->string (lambda () - (string-append "'" (gnc:value->string value))))) - (gnc:make-option - section name sort-tag 'dateformat documentation-string - (lambda () value) - (lambda (x) (set! value x)) - (lambda () (def-value)) - (gnc:restore-form-generator value->string) - (lambda (b p) - (if (eq? (car value) 'unset) - (qof-book-options-delete b p );; delete the kvp when unset - (begin - (qof-book-set-option - b (symbol->string (car value)) (append p '("fmt"))) - (qof-book-set-option - b (symbol->string (cadr value)) (append p '("month"))) - (qof-book-set-option - b (if (caddr value) 1 0) (append p '("years"))) - (qof-book-set-option - b (cadddr value) (append p '("custom")))))) - (lambda (f p) - (let ((fmt (qof-book-get-option f (append p '("fmt")))) - (month (qof-book-get-option f (append p '("month")))) - (years (qof-book-get-option f (append p '("years")))) - (custom (qof-book-get-option f (append p '("custom"))))) - (if (and - fmt (string? fmt) - month (string? month) - years (number? years) - custom (string? custom)) - (set! value (list (string->symbol fmt) (string->symbol month) - (if (= years 0) #f #t) custom))))) - (lambda (x) - (cond ((not (list? x)) (list #f "dateformat-option: not a list")) - ((not (= (length x) 4)) - (list #f "dateformat-option: wrong list length" (length x))) - ((not (symbol? (car x))) - (list #f "dateformat-option: no format symbol")) - ((not (symbol? (cadr x))) - (list #f "dateformat-option: no months symbol")) - ((not (string? (cadddr x))) - (list #f "dateformat-option: no custom string")) - (else (list #t x)))) - #f #f #f #f))) - -(define (gnc:dateformat-get-format v) - (cadddr v)) - -(define (gnc:make-currency-accounting-option - section - name - sort-tag - radiobutton-documentation-string - default-radiobutton-value - ok-radiobutton-values - book-currency-documentation-string - default-book-currency-value - default-cap-gains-policy-documentation-string - default-cap-gains-policy-value - default-gains-loss-account-documentation-string - ) - (define (legal-val val p-vals) - (cond ((null? p-vals) #f) - ((not (symbol? val)) #f) - ((eq? val (vector-ref (car p-vals) 0)) #t) - (else (legal-val val (cdr p-vals))))) - - (define (currency-lookup currency-string) - (if (string? currency-string) - (gnc-commodity-table-lookup - (gnc-commodity-table-get-table (gnc-get-current-book)) - (GNC-COMMODITY-NS-CURRENCY) currency-string) - #f)) - - (define (currency? val) - (gnc-commodity-is-currency (currency-lookup val))) - - (define (vector-strings p-vals) - (if (null? p-vals) - '() - (cons (vector-ref (car p-vals) 1) - (cons (vector-ref (car p-vals) 2) - (vector-strings (cdr p-vals)))))) - - (define (currency->scm currency) - (if (string? currency) - currency - (gnc-commodity-get-mnemonic currency))) - - (define (scm->currency currency) - (currency-lookup currency)) - - (define (valid-gains-loss-account? book-currency gains-loss-account-guid) - ;; xaccAccountLookup returns Account if guid valid otherwise NULL; also must - ;; be in book-currency, income or expense, and not placeholder nor hidden - (let* ((account (xaccAccountLookup gains-loss-account-guid - (gnc-get-current-book)))) - (and account - (not (null? account)) - (not (xaccAccountIsHidden account)) - (not (xaccAccountGetPlaceholder account)) - (memv (xaccAccountGetType account) - (list ACCT-TYPE-INCOME ACCT-TYPE-EXPENSE)) - (gnc-commodity-equal - (currency-lookup book-currency) - (xaccAccountGetCommodity account))))) - - (let* ((value (if (eq? 'book-currency default-radiobutton-value) - (list default-radiobutton-value - default-book-currency-value - default-cap-gains-policy-value) - (list default-radiobutton-value))) - (value->string (lambda () - (string-append "'" (gnc:value->string - (car value))))) - (trading-accounts-path (list gnc:*option-section-accounts* - gnc:*option-name-trading-accounts*)) - (book-currency-path (list gnc:*option-section-accounts* - gnc:*option-name-book-currency*)) - (gains-policy-path (list gnc:*option-section-accounts* - gnc:*option-name-default-gains-policy*)) - (gains-loss-account-path (list gnc:*option-section-accounts* - gnc:*option-name-default-gain-loss-account*))) - (gnc:make-option - section name sort-tag 'currency-accounting - radiobutton-documentation-string - (lambda () value) ;; getter - (lambda (x) - (if (legal-val (car x) ok-radiobutton-values) - (set! value x) - (gnc:error "Illegal Radiobutton option set"))) ;;setter - (lambda () (if (eq? 'book-currency default-radiobutton-value) - (list default-radiobutton-value - default-book-currency-value - default-cap-gains-policy-value) - (list default-radiobutton-value))) ;; default-getter - (gnc:restore-form-generator value->string) - (lambda (b p) ;; scm->kvp - (case (car value) - ((book-currency) - ;; Currency = selected currency - (qof-book-set-option b (currency->scm (cadr value)) - book-currency-path) - ;; Default Gains Policy = selected policy - (qof-book-set-option b (symbol->string (caddr value)) - gains-policy-path) - ;; Default Gains Account = if selected, selected account - (if (car (cdddr value)) - (qof-book-set-option b (car (cdddr value)) - gains-loss-account-path))) - ((trading) - ;; Use Trading Accounts = "t" - (qof-book-set-option b "t" trading-accounts-path)))) - (lambda (b p) ;; kvp->scm - (let* ((trading-option-path-kvp? - (qof-book-get-option b trading-accounts-path)) - (trading? (and trading-option-path-kvp? - (string=? "t" trading-option-path-kvp?))) - (book-currency #f) - (cap-gains-policy #f) - (gains-loss-account-guid #f) - (v (if trading? - 'trading - (let* ((book-currency-option-path-kvp? - (qof-book-get-option - b book-currency-path)) - (gains-policy-option-path-kvp? - (qof-book-get-option - b gains-policy-path)) - (gains-loss-account-option-path-kvp? - (qof-book-get-option - b gains-loss-account-path)) - (book-currency? - (if (and book-currency-option-path-kvp? - gains-policy-option-path-kvp? - (string? - book-currency-option-path-kvp?) - (string? - gains-policy-option-path-kvp?) - (if book-currency-option-path-kvp? - (currency? - book-currency-option-path-kvp?)) - (if gains-policy-option-path-kvp? - (gnc-valid-policy-name - gains-policy-option-path-kvp?))) - (begin - (set! book-currency - book-currency-option-path-kvp?) - (set! cap-gains-policy - gains-policy-option-path-kvp?) - (if gains-loss-account-option-path-kvp? - (if (valid-gains-loss-account? - book-currency - gains-loss-account-option-path-kvp?) - (set! gains-loss-account-guid - gains-loss-account-option-path-kvp?))) - #t) - #f))) - (if book-currency? - 'book-currency - 'neither))))) - (if (and v (symbol? v) (legal-val v ok-radiobutton-values)) - (set! value (cons v (if (eq? 'book-currency v) - (list (scm->currency book-currency) - (string->symbol cap-gains-policy) - gains-loss-account-guid) - '()))) - (set! value (list 'neither))))) - (lambda (x) ;; value validator - (cond - ((not (list? x)) - (list #f "value not a list")) - ((not (legal-val (car x) ok-radiobutton-values)) - (list #f "radiobutton-option: illegal choice")) - ((not (eq? 'book-currency (car x))) - (list #t x)) - ((not (currency? (currency->scm (cadr x)))) - (list #f "currency-option: illegal value")) - ((not (gnc-valid-policy-name (symbol->string (caddr x)))) - (list #f "cap-gains-policy-option: illegal value")) - ((not (car (cdddr x))) - (list #t x)) - ((not (valid-gains-loss-account? (currency->scm (cadr x)) - (car (cdddr x)))) - (list #f "gains-loss-account-option: illegal value")) - (else - (list #t x)))) - (vector book-currency-documentation-string - default-book-currency-value - default-cap-gains-policy-documentation-string - default-cap-gains-policy-value - default-gains-loss-account-documentation-string) - (vector (lambda () (length ok-radiobutton-values)) - (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 0)) - (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 1)) - (lambda (x) (vector-ref (list-ref ok-radiobutton-values x) 2)) - (lambda (x) - (gnc:multichoice-list-lookup ok-radiobutton-values x))) - (lambda () (vector-strings ok-radiobutton-values)) - #f))) - -(define (gnc:get-currency-accounting-option-data-curr-doc-string option-data) - (vector-ref option-data 0)) - -(define (gnc:get-currency-accounting-option-data-default-curr option-data) - (vector-ref option-data 1)) - -(define (gnc:get-currency-accounting-option-data-policy-doc-string option-data) - (vector-ref option-data 2)) - -(define (gnc:get-currency-accounting-option-data-policy-default option-data) - (vector-ref option-data 3)) - -(define (gnc:get-currency-accounting-option-data-gain-loss-account-doc-string option-data) - (vector-ref option-data 4)) - -(define (gnc:currency-accounting-option-get-curr-doc-string option) - (if (eq? (gnc:option-type option) 'currency-accounting) - (gnc:get-currency-accounting-option-data-curr-doc-string - (gnc:option-data option)) - (gnc:error "Not a currency accounting option"))) - -(define (gnc:currency-accounting-option-get-default-curr option) - (if (eq? (gnc:option-type option) 'currency-accounting) - (gnc:get-currency-accounting-option-data-default-curr - (gnc:option-data option)) - (gnc:error "Not a currency accounting option"))) - -(define (gnc:currency-accounting-option-get-policy-doc-string option) - (if (eq? (gnc:option-type option) 'currency-accounting) - (gnc:get-currency-accounting-option-data-policy-doc-string - (gnc:option-data option)) - (gnc:error "Not a currency accounting option"))) - -(define (gnc:currency-accounting-option-get-default-policy option) - (if (eq? (gnc:option-type option) 'currency-accounting) - (gnc:get-currency-accounting-option-data-policy-default - (gnc:option-data option)) - (gnc:error "Not a currency accounting option"))) - -(define (gnc:currency-accounting-option-get-gain-loss-account-doc-string option) - (if (eq? (gnc:option-type option) 'currency-accounting) - (gnc:get-currency-accounting-option-data-gain-loss-account-doc-string - (gnc:option-data option)) - (gnc:error "Not a currency accounting option"))) - -(define (gnc:currency-accounting-option-selected-method option-value) - (car option-value)) - -(define (gnc:currency-accounting-option-selected-currency option-value) - (if (eq? (car option-value) 'book-currency) - (cadr option-value) - #f)) - -(define (gnc:currency-accounting-option-selected-policy option-value) - (if (eq? (car option-value) 'book-currency) - (caddr option-value) - #f)) - -(define (gnc:currency-accounting-option-selected-gain-loss-account option-value) - (if (eq? (car option-value) 'book-currency) - (car (cdddr option-value)) - #f)) - -;; Create a new options database -(define (gnc:new-options) - (define option-hash (make-hash-table 23)) - - (define options-changed #f) - (define changed-hash (make-hash-table 23)) - - (define callback-hash (make-hash-table 23)) - (define last-callback-id 0) - (define new-names-alist - '(("Accounts to include" #f "Accounts") - ("Exclude transactions between selected accounts?" #f - "Exclude transactions between selected accounts") - ("Filter Accounts" #f "Filter By...") - ("Flatten list to depth limit?" #f "Flatten list to depth limit") - ("From" #f "Start Date") - ("Report Accounts" #f "Accounts") - ("Report Currency" #f "Report's currency") - ("Show Account Code?" #f "Show Account Code") - ("Show Full Account Name?" #f "Show Full Account Name") - ("Show Multi-currency Totals?" #f "Show Multi-currency Totals") - ("Show zero balance items?" #f "Show zero balance items") - ("Sign Reverses?" #f "Sign Reverses") - ("To" #f "End Date") - ("Charge Type" #f "Action") ;easy-invoice.scm, renamed June 2018 - ;; the following 4 options in income-gst-statement.scm renamed Dec 2018 - ("Individual income columns" #f "Individual sales columns") - ("Individual expense columns" #f "Individual purchases columns") - ("Remittance amount" #f "Gross Balance") - ("Net Income" #f "Net Balance") - ;; transaction.scm: - ("Use Full Account Name?" #f "Use Full Account Name") - ("Use Full Other Account Name?" #f "Use Full Other Account Name") - ("Void Transactions?" "Filter" "Void Transactions") - ("Void Transactions" "Filter" "Void Transactions") - ("Account Substring" "Filter" "Account Name Filter") - ("Enable links" #f "Enable Links") - ;; trep-engine: moved currency options to own tab - ("Common Currency" "Currency" "Common Currency") - ("Show original currency amount" "Currency" "Show original currency amount") - ("Report's currency" "Currency" "Report's currency") - ("Reconcile Status" #f "Reconciled Status") - ;; new-owner-report.scm, renamed Oct 2020 to differentiate with - ;; Document Links: - ("Links" #f "Transaction Links") - ;; invoice.scm, renamed November 2018 - ("Individual Taxes" #f "Use Detailed Tax Summary") - ;; renamed in several reports, April 2021 - ("Show Accounts until level" #f "Levels of Subaccounts") - ;; receipt.scm, renamed July 2021 - ("Invoice number" #f "Invoice Number") - ;; receipt.scm and taxinvoice.scm, renamed July 2021 - ("Report title" #f "Report Title") - ("Extra notes" #f "Extra Notes") - ;; income-gst-statement.scm - ("default format" #f "Default Format") - ("Report format" #f "Report Format") - )) - - (define (lookup-option section name) - (let ((section-hash (hash-ref option-hash section))) - (and section-hash - (or (hash-ref section-hash name) - ;; Option name was not found. Perhaps it was renamed? - ;; Let's try to map to a known new name. The alist - ;; new-names-alist will try match names - car is the old - ;; name, cdr is the 2-element list describing - ;; newsection newname. If newsection is #f then reuse - ;; previous section name. Please note the rename list - ;; currently supports renaming individual option names, - ;; or individual option names moved to another - ;; section. It does not currently support renaming - ;; whole sections. - (let ((name-match (assoc-ref new-names-alist name))) - (and name-match - (let ((new-section (car name-match)) - (new-name (cadr name-match))) - (gnc:warn - (format #f "option ~a/~a has been renamed to ~a/~a\n" - section name new-section new-name)) - (cond - ;; new-name only - ((not new-section) - (lookup-option section new-name)) - ;; new-section different to current section - ;; name, and possibly new-name - ((not (string=? new-section section)) - (lookup-option new-section new-name)) - ;; no match, return #f - (else #f))))))))) - - (define (option-changed section name) - (set! options-changed #t) - (let ((section-changed-hash (hash-ref changed-hash section))) - (if (not section-changed-hash) - (begin - (set! section-changed-hash (make-hash-table 23)) - (hash-set! changed-hash section section-changed-hash))) - (hash-set! section-changed-hash name #t))) - - (define (clear-changes) - (set! options-changed #f) - (set! changed-hash (make-hash-table 23))) - - (define (register-option new-option) - (let* ((name (gnc:option-name new-option)) - (section (gnc:option-section new-option)) - (section-hash (hash-ref option-hash section))) - (if (not section-hash) - (begin - (set! section-hash (make-hash-table 23)) - (hash-set! option-hash section section-hash))) - (hash-set! section-hash name new-option) - (gnc:option-set-changed-callback - new-option - (lambda () (option-changed section name))))) - - (define (unregister-option section name) - (let* ((section-hash (hash-ref option-hash section))) - (if (and section-hash - (hash-ref section-hash name)) - (begin - (hash-remove! section-hash name) - (if (zero? (hash-count (const #t) section-hash)) - (hash-remove! option-hash section))) - (gnc:error "options:unregister-option: no such option\n")))) - -; Call (thunk option) for each option in the database - (define (options-for-each thunk) - (define (section-for-each section-hash thunk) - (hash-for-each - (lambda (name option) - (thunk option)) - section-hash)) - (hash-for-each - (lambda (section hash) - (section-for-each hash thunk)) - option-hash)) - - (define (options-for-each-general section-thunk option-thunk) - (define (section-for-each section-hash thunk) - (hash-for-each - (lambda (name option) - (thunk option)) - section-hash)) - (hash-for-each - (lambda (section hash) - (if section-thunk - (section-thunk section hash)) - (if option-thunk - (section-for-each hash option-thunk))) - option-hash)) - - (define (generate-restore-forms options-string) - - (define (generate-option-restore-form option restore-code) - (let* ((section (gnc:option-section option)) - (name (gnc:option-name option))) - (string-append - "(let ((option (gnc:lookup-option " options-string "\n" - " " (gnc:value->string section) "\n" - " " (gnc:value->string name) ")))\n" - " (" restore-code " option))\n\n"))) - - (define (generate-forms port) - (options-for-each-general - (lambda (section hash) - (display - (string-append "\n; Section: " section "\n\n") - port)) - (lambda (option) - (let ((value (gnc:option-value option)) - (default-value (gnc:option-default-value option))) - (if (not (equal? value default-value)) - (let* ((generator (gnc:option-generate-restore-form option)) - (restore-code (false-if-exception (generator)))) - (if restore-code - (display - (generate-option-restore-form option restore-code) - port)))))))) - - (call-with-output-string generate-forms)) - - (define (scm->kvp book) - (options-for-each - (lambda (option) - (let ((value (gnc:option-value option)) - (default-value (gnc:option-default-value option)) - (section (gnc:option-section option)) - (name (gnc:option-name option))) -;; (gnc:debug "value: " value "; default: " default-value -;; "; section: " section "; name: " name) - (if (not (equal? value default-value)) - (let ((save-fcn (gnc:option-scm->kvp option))) -;; (gnc:debug "save-fcn: " save-fcn) - (if save-fcn - (save-fcn book (list section name))))))))) - - (define (kvp->scm book) - (options-for-each - (lambda (option) - (let ((section (gnc:option-section option)) - (name (gnc:option-name option)) - (load-fcn (gnc:option-kvp->scm option))) - (if load-fcn - (load-fcn book (list section name))))))) - - (define (register-callback section name callback) - (let ((id last-callback-id) - (data (list section name callback))) - (set! last-callback-id (+ last-callback-id 1)) - (hashv-set! callback-hash id data) - id)) - - (define (unregister-callback-id id) - (if (hashv-ref callback-hash id) - (hashv-remove! callback-hash id) - (gnc:error "options:unregister-callback-id: no such id\n"))) - - (define (run-callbacks) - (define (run-callback id cbdata) - (let ((section (car cbdata)) - (name (cadr cbdata)) - (callback (caddr cbdata))) - (if (not section) - (callback) - (let ((section-changed-hash (hash-ref changed-hash section))) - (if section-changed-hash - (if (not name) - (callback) - (if (hash-ref section-changed-hash name) - (callback)))))))) - - (if options-changed - (let ((cblist '())) - (hash-for-each - (lambda (k v) (set! cblist (cons (cons k v) cblist))) - callback-hash) - (set! cblist (sort cblist - (lambda (a b) - (< (car a) (car b))))) - (for-each - (lambda (elt) (run-callback (car elt) (cdr elt))) - cblist))) - (clear-changes)) - - (define default-section #f) - - (define (touch) - (set! options-changed #t) - (run-callbacks)) - - (define (set-default-section section-name) - (set! default-section section-name)) - - (define (get-default-section) - default-section) - - (define (dispatch key) - (case key - ((lookup) lookup-option) - ((register-option) register-option) - ((unregister-option) unregister-option) - ((register-callback) register-callback) - ((unregister-callback-id) unregister-callback-id) - ((for-each) options-for-each) - ((for-each-general) options-for-each-general) - ((generate-restore-forms) generate-restore-forms) - ((scm->kvp) scm->kvp) - ((kvp->scm) kvp->scm) - ((touch) touch) - ((clear-changes) clear-changes) - ((run-callbacks) run-callbacks) - ((set-default-section) set-default-section) - ((get-default-section) get-default-section) - (else (gnc:warn "options: bad key: " key "\n")))) - - dispatch) - -(define (gnc:register-option options new-option) - ((options 'register-option) new-option)) - -(define (gnc:options-register-callback section name callback options) - ((options 'register-callback) section name callback)) - -(define (gnc:options-register-c-callback section name c-callback data options) - (let ((callback (lambda () (gncp-option-invoke-callback c-callback data)))) - ((options 'register-callback) section name callback))) - -(define (gnc:options-unregister-callback-id id options) - ((options 'unregister-callback-id) id)) - -(define (gnc:options-for-each thunk options) - ((options 'for-each) thunk)) - -(define (gnc:options-for-each-general section-thunk option-thunk options) - ((options 'for-each-general) section-thunk option-thunk)) - -(define (gnc:lookup-option options section name) - (if options - ((options 'lookup) section name) - #f)) - -(define (gnc:unregister-option options section name) - ((options 'unregister-option) section name)) - -(define (gnc:generate-restore-forms options options-string) - ((options 'generate-restore-forms) options-string)) - -(define (gnc:options-scm->kvp options book clear-option?) - (if clear-option? - (qof-book-options-delete book '())) - ((options 'scm->kvp) book)) - -(define (gnc:options-kvp->scm options book) - ((options 'kvp->scm) book)) - -(define (gnc:options-clear-changes options) - ((options 'clear-changes))) - -(define (gnc:options-touch options) - ((options 'touch))) - -(define (gnc:options-run-callbacks options) - ((options 'run-callbacks))) - -(define (gnc:options-set-default-section options section-name) - ((options 'set-default-section) section-name)) - -(define (gnc:options-get-default-section options) - ((options 'get-default-section))) +(define-public (gnc:options-for-each func optdb) + (gnc-optiondb-foreach (optdb 'foreach) func)) ;; Copies all values from src-options to dest-options, that is, it ;; copies the values of all options from src which exist in dest to ;; there. -(define (gnc:options-copy-values src-options dest-options) +(define-public (gnc:options-copy-values src-options dest-options) (if dest-options (gnc:options-for-each @@ -2141,58 +118,312 @@ the option '~a'.")) (gnc:option-value src-option))))) src-options))) -(define (gnc:send-options db_handle options) - (gnc:options-for-each - (lambda (option) - (gnc-option-db-register-option db_handle option)) - options)) +;; Get scheme commands to set changed options, used to write a file that will +;; restore a customized report or stylesheet. +(define-public (gnc:value->string value) + (format #f "~s" value)) -(define (gnc:options-make-end-date! options pagename optname sort-tag info) - (gnc:register-option - options - (gnc:make-date-option - pagename optname - sort-tag info - (lambda () - (cons 'relative 'end-accounting-period)) - #f 'both - '( - today - end-this-month - end-prev-month - end-current-quarter - end-prev-quarter - end-cal-year - end-prev-year - end-accounting-period - )))) +(define-public (gnc:generate-restore-forms options toplevel-name) + (define (section-op section-name) + (display + (string-append "\n; Section: " section-name "\n\n"))) -(define (gnc:options-make-date-interval! options pagename name-from info-from - name-to info-to sort-tag) - (gnc:register-option - options - (gnc:make-date-option - pagename name-from - (string-append sort-tag "a") info-from - (lambda () (cons 'relative 'start-accounting-period)) - #f 'both - '( - today - start-this-month - start-prev-month - start-current-quarter - start-prev-quarter - start-cal-year - start-prev-year - start-accounting-period - ))) - (gnc:options-make-end-date! options pagename name-to - (string-append sort-tag "b") info-to)) + (define (gnc:option-is-budget? option) + (GncOption-is-budget-option option)) -(define (gnc:option-make-internal! options section name) - ;; this function will hide the option specified - ;; the option functionality is unchanged - (let ((opt (gnc:lookup-option options section name))) - (if opt - (vector-set! opt 3 'internal) - (gnc:error "gnc:option-make-internal! cannot find " section " / " name)))) + (define (option-op option) + (let ((value (gnc:option-value option)) + (default-value (gnc:option-default-value option))) + (if (not (equal? value default-value)) + (display (string-append + "(let ((option (gnc:lookup-option " toplevel-name "\n" + " " + (gnc:value->string (gnc:option-section option)) "\n" + " " + (gnc:value->string (gnc:option-name option)) ")))\n" + " (" + (cond + ((gnc:option-is-budget? option) + (let* ((budget (gnc:option-value option)) + (guid (gncBudgetGetGUID budget)) + (guid-string (gnc:value->string guid))) + (if (string? guid-string) + (string-append + "(lambda (option) " + "(if option ((gnc:option-setter option) " + "(gnc-budget-lookup " guid-string + " (gnc-get-current-book)))))" + ) + ("Failed to get GUID for budget option.")))) + (else + (string-append + "(lambda (o) (if o (gnc:option-set-value o " + (GncOption-save-scm-value option) ")))" + ))) + " option))\n\n"))))) + + (define (generate-forms) + (let ((odb (options 'generate-restore-forms))) + (gnc-optiondb-foreach2 odb section-op option-op))) + + (with-output-to-string generate-forms)) + + +;; The following implement the old API that separated creation from registration. +(define-public (gnc:register-option optdb opt) + (issue-deprecation-warning "gnc:register-option is deprecated. Use gnc-register-foo-option instead.") + (GncOptionDB-register-option (GncOptionDBPtr-get (optdb 'register-option)) + (GncOption-get-section opt) opt)) + +(define-public (gnc:unregister-option optdb section name) + (GncOptionDB-unregister-option (GncOptionDBPtr-get (optdb 'unregister-option)) section name)) + +(define-public (gnc:make-string-option section name key docstring default) + (issue-deprecation-warning "gnc:make-string-option is deprecated. Make and register the option in one command with gnc-register-string-option.") + (gnc-make-string-option section name key docstring default (GncOptionUIType-STRING))) +(define-public (gnc:make-text-option section name key docstring default) + (issue-deprecation-warning "gnc:make-text-option is deprecated. Make and register the option in one command with gnc-register-text-option.") + (gnc-make-string-option section name key docstring default (GncOptionUIType-TEXT))) +(define-public (gnc:make-font-option section name key docstring default) + (issue-deprecation-warning "gnc:make-font-option is deprecated. Make and register the option in one command with gnc-register-font-option.") + (gnc-make-string-option section name key docstring default (GncOptionUIType-FONT))) +(define-public (gnc:make-color-option section name key docstring colors range use-alpha) + (issue-deprecation-warning "gnc:make-color-option is deprecated. Make and register the option in one command with gnc-register-color-option.") + (let ((color-str (if use-alpha ;; It's always false... + (format #f "~x~x~x~x" (car colors) (cadr colors) (caddr colors) (cadddr colors)) + (format #f "~x~x~x" (car colors) (cadr colors) (caddr colors))))) + (gnc-make-string-option section name key docstring color-str (GncOptionUIType-COLOR)))) +(define-public (gnc:make-budget-option section name key docstring) + (issue-deprecation-warning "gnc:make-budget-option is deprecated. Make and register the option in one command with gnc-register-color-option.") + (let ((option (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-BUDGET)))) + (gnc:option-set-default-value option + (gnc-budget-get-default (gnc-get-current-book))) + option)) +(define-public (gnc:make-commodity-option section name key docstring default) + (issue-deprecation-warning "gnc:make-commodity-option is deprecated. Make and register the option in one command with gnc-register-commodity-option.") + (gnc-make-commodity-option section name key docstring default)) +(define-public (gnc:make-simple-boolean-option section name key docstring default) + (issue-deprecation-warning "gnc:make-simple-boolean-option is deprecated. Make and register the option in one command with gnc-register-simple-boolean-option.") + (gnc-make-bool-option section name key docstring default (GncOptionUIType-BOOLEAN))) +(define-public (gnc:make-complex-boolean-option section name key docstring default setter-cb widget-changed-cb) + (issue-deprecation-warning "gnc:make-complex-boolean-option is deprecated and its functionality removed. Make and register a simple-boolean in one command with gnc-register-simple-boolean-option and figure out some other way to change widget sensitivity.") + (gnc-make-bool-option section name key docstring default (GncOptionUIType-BOOLEAN))) +(define-public (gnc:make-pixmap-option section name key docstring default) + (issue-deprecation-warning "gnc:make-pixmap-option is deprecated. Make and register the option in one command with gnc-register-pixmap-option.") + (gnc-make-string-option section name key docstring default (GncOptionUIType-PIXMAP))) +;; gnc:make-account-list-option's getter and validator parameters are functions. +(define-public (gnc:make-account-list-option section name key docstring default validator multi) + (issue-deprecation-warning "gnc:make-account-list-option is deprecated. Make and register the option in one command with gnc-register-account-list-option.") + (gnc-make-account-list-option section name key docstring (default))) +(define-public (gnc:make-account-list-limited-option section name key docstring default validator multi permitted) + (issue-deprecation-warning "gnc:make-account-list-limited-option is deprecated. Make and register the option in one command with gnc-register-account-list-limited-option.") + (gnc-make-account-list-limited-option section name key docstring (default) permitted)) +(define-public (gnc:make-account-sel-limited-option section name key docstring default validator permitted) + (issue-deprecation-warning "gnc:make-account-sel-limited-option is deprecated. Make and register the option in one command with gnc-register-account-sel-limited-option.") + (let ((defval (if default (default) '()))) + (gnc-make-account-sel-limited-option section name key docstring defval permitted))) +(define-public (gnc:make-account-sel-option section name key docstring default validator) + (let ((defval (if default (default) '()))) + (gnc-make-account-sel-limited-option section name key docstring defval '()))) +(define-public (gnc:make-multichoice-option section name key docstring default multichoice) + (issue-deprecation-warning "gnc:make-multichoice-option is deprecated. Make and register the option in one command with gnc-register-multichoice-option.") + (let ((defval (cond ((symbol? default) + (symbol->string default)) + ((number? default) + (number->string default)) + (else default)))) + (gnc-make-multichoice-option section name key docstring defval multichoice))) +(define-public (gnc:make-multichoice-callback-option section name key docstring default multichoice setter-cb widget-changed-cb) + (issue-deprecation-warning "gnc:make-multichoice-callback-option is deprecated in favor of a not-yet-written but more sensible way to conditionally enable and disable option widgets.") + (gnc:make-multichoice-option section name key docstring default multichoice)) + +(define-public (gnc:make-list-option section name key docstring default multichoice) + (issue-deprecation-warning "gnc:make-list-option is deprecated. Make and register the option in one command with gnc-register-list-option.") + (let ((indexes (if default (map (lambda (def-item) + (list-index (lambda (choice) + (eq? def-item + (vector-ref choice 0))) + multichoice)) + default) 0))) + (gnc-make-list-option section name key docstring indexes multichoice))) +(define-public (gnc:make-radiobutton-option section name key docstring default multichoice) + (gnc:warn "gnc:make-radiobutton-option is no longer available. Using gnc:make-multichoice-option instead.") + (gnc:make-multichoice-option section name key docstring default multichoice)) +(define-public (gnc:make-number-range-option section name key docstring default min max dec-places step) + (issue-deprecation-warning "gnc:make-number-range-option is deprecated. Make and register the option in one command with gnc-register-number-range-option.") + (gnc-make-range-value-option section name key docstring default min max step)) +(define-public (gnc:make-number-plot-size-option section name key docstring default min max dec-places step) + (issue-deprecation-warning "gnc:make-number-plot-size-option is deprecated. Make and register the option in one command with gnc-register-plot-size-range-option.") + ;; Ignore what the call asks for, only 10-100% makes sense. + (gnc-make-plot-size-option section name key docstring 100 10 100 1)) +(define-public (gnc:make-query-option section name default) + (issue-deprecation-warning "gnc:make-query-option is deprecated. Make and register the option in one command with gnc-register-query-option.") + (let ((defv (if (list? default) default (gnc-query2scm default)))) + (gnc-make-query-option section name "" "query" defv (GncOptionUIType-INTERNAL)))) +(define-public (gnc:make-internal-option section name default) + (issue-deprecation-warning "gnc:make-internal-option is deprecated. Make and register the option in one command with gnc-register-internal-option.") + (let ((type (GncOptionUIType-INTERNAL)) + (key "_") + (desc "internal")) + (cond + ((boolean? default) (gnc-make-bool-option section name key desc default + (GncOptionUIType-INTERNAL))) + ((string? default) (gnc-make-string-option section name key desc default + (GncOptionUIType-INTERNAL))) + ((procedure? default) + (format #t "gnc:make-internal-option passed procedure resolving to ~a~%" (default)) + (gnc-make-bool-option section name key desc #f (GncOptionUIType-INTERNAL))) + (else + (format #t "gnc:make-internal-option passed something unknown that looks like ~a~%" default) + (gnc-make-bool-option section name key desc #f (GncOptionUIType-INTERNAL)))))) + +(define-public (gnc:make-owner-option section name key docstring getter validator owner-type) + (issue-deprecation-warning "gnc:make-owner-option is deprecated. Make and register the option in one command with gnc-register-owner-option.") + (let* ((ui-type (cond + ((eqv? owner-type GNC-OWNER-CUSTOMER) (GncOptionUIType-CUSTOMER)) + ((eqv? owner-type GNC-OWNER-VENDOR) (GncOptionUIType-VENDOR)) + ((eqv? owner-type GNC-OWNER-EMPLOYEE) (GncOptionUIType-EMPLOYEE)) + ((eqv? owner-type GNC-OWNER-JOB) (GncOptionUIType-JOB)) + (else (GncOptionUIType-INTERNAL)))) + + (guid (gncOwnerReturnGUID (getter))) + (book (gnc-get-current-book)) + (defval (cond + ((eqv? owner-type GNC-OWNER-CUSTOMER) (gncCustomerLookupFlip guid book)) + ((eqv? owner-type GNC-OWNER-VENDOR) (gncVendorLookupFlip guid book)) + ((eqv? owner-type GNC-OWNER-EMPLOYEE) (gncEmployeeLookupFlip guid book)) + ((eqv? owner-type GNC-OWNER-JOB) (gncJobLookupFlip guid book))))) + + (gnc-make-owner-option section name key docstring defval ui-type))) +(define-public (gnc:make-invoice-option section name key docstring getter validator) + (issue-deprecation-warning "gnc:make-invoice-option is deprecated. Make and register the option in one command with gnc-register-ionvoice-option.") + (let ((defval (if getter (getter) #f))) + (gnc-make-qofinstance-option section name key docstring defval (GncOptionUIType-INVOICE)))) +(define-public (gnc:make-taxtable-option section name key docstring default) + (issue-deprecation-warning "gnc:make-taxtable-option is deprecated. Make and register the option in one command with gnc-register-taxtable-option.") + (gnc-make-qofinstance-option section name key docstring default (GncOptionUIType-TAX_TABLE))) +(define-public (gnc:make-counter-option section name key docstring default) + (issue-deprecation-warning "gnc:make-number-range-option is deprecated. Make and register the option in one command with gnc-register-number-range-option.") + (gnc-make-range-value-option section name key docstring default 0.0 999999999.0, 1.0)) + +(define-public (gnc:make-counter-format-option section name key docstring default) + (issue-deprecation-warning "gnc:make-counter-format-option is deprecated. Make and register the option in one command with gnc-register-counter-format-option.") + (gnc-make-string-option section name key docstring default (GncOptionUIType-STRING))) +(define-public (gnc:make-date-format-option section name key docstring default) + (issue-deprecation-warning "gnc:make-date-format-option is deprecated. Make and register the option in one command with gnc-register-date-format-option.") + (gnc-make-string-option section name key docstring default (GncOptionUIType-DATE_FORMAT))) +(define-public (gnc:make-currency-option section name key docstring default) + (issue-deprecation-warning "gnc:make-currency-option is deprecated. Make and register the option in one command with gnc-register-currency-option.") + (gnc-make-currency-option section name key docstring default)) +(define-public (gnc:make-date-option section name key docstring getter showtime + subtype relative-date-list) + (let ((default (getter)) + (both (if (eq? subtype 'both) #t #f))) + (gnc-make-date-option section name key docstring default relative-date-list both))) + +(define-public (gnc:options-make-end-date! optiondb pagename optname sort-tag docstring) + (gnc-register-end-date-option (optiondb 'make-option) pagename optname sort-tag docstring)) + +(define-public (gnc:options-make-date-interval! optiondb pagename name-from info-from name-to info-to sort-tag) + (gnc-register-start-date-option (optiondb 'make-option) pagename name-from + (string-append sort-tag "a") info-from) + (gnc-register-end-date-option (optiondb 'make-option) pagename name-to + (string-append sort-tag "b") info-to)) +(define-public (gnc:date-option-absolute-time option-value) + (if (pair? option-value) + (if (eq? (car option-value) 'absolute) + (cdr option-value) + (gnc-relative-date-to-time64 (cdr option-value))) + option-value)) +;; This is a special case where we can't use the exported registration function +;; because we need to transform the default argument first depending on its +;; Scheme type. +(define-public (gnc:register-multichoice-option options section name key docstring default multichoice) + (issue-deprecation-warning "gnc:make-multichoice-option is deprecated. Make and register the option in one command with gnc-register-multichoice-option.") + (let ((defval (cond ((symbol? default) + (symbol->string default)) + ((number? default) + (number->string default)) + (else default)))) + (gnc-register-multichoice-option options section name key docstring defval multichoice))) + +;; Scheme code for supporting options for the business modules +;; +;; Created by: Derek Atkins +;; + +;; Internally, values are always a guid. Externally, both guids and +;; invoice pointers may be used to set the value of the option. The +;; option always returns a single invoice pointer. + +(use-modules (gnucash core-utils)) +(use-modules (gnucash engine)) +(use-modules (gnucash utilities)) +(use-modules (gnucash app-utils options)) +(use-modules (sw_app_utils)) + +(export gnc:*business-label*) +(export gnc:*company-name*) +(export gnc:*company-addy*) +(export gnc:*company-id*) +(export gnc:*company-phone*) +(export gnc:*company-fax*) +(export gnc:*company-url*) +(export gnc:*company-email*) +(export gnc:*company-contact*) +(export gnc:*fancy-date-label*) +(export gnc:*fancy-date-format*) +(export gnc:*tax-label*) +(export gnc:*tax-nr-label*) +(export gnc:company-info) +(export gnc:fancy-date-info) +(export gnc:*option-section-budgeting*) +(export gnc:*option-name-auto-readonly-days*) +(export gnc:*option-name-num-field-source*) +(export gnc:*kvp-option-path*) +(export gnc:options-fancy-date) +(export gnc:*option-name-default-budget*) + +(define gnc:*kvp-option-path* (list KVP-OPTION-PATH)) +(define gnc:*option-name-auto-readonly-days* OPTION-NAME-AUTO-READONLY-DAYS) +(define gnc:*option-name-num-field-source* OPTION-NAME-NUM-FIELD-SOURCE) + +(define gnc:*option-section-budgeting* OPTION-SECTION-BUDGETING) +(define gnc:*option-name-default-budget* OPTION-NAME-DEFAULT-BUDGET) + +(define gnc:*business-label* (N_ "Business")) +(define gnc:*company-name* (N_ "Company Name")) +(define gnc:*company-addy* (N_ "Company Address")) +(define gnc:*company-id* (N_ "Company ID")) +(define gnc:*company-phone* (N_ "Company Phone Number")) +(define gnc:*company-fax* (N_ "Company Fax Number")) +(define gnc:*company-url* (N_ "Company Website URL")) +(define gnc:*company-email* (N_ "Company Email Address")) +(define gnc:*company-contact* (N_ "Company Contact Person")) +(define gnc:*fancy-date-label* (N_ "Fancy Date Format")) +(define gnc:*fancy-date-format* (N_ "custom")) +(define gnc:*tax-label* (N_ "Tax")) +(define gnc:*tax-nr-label* (N_ "Tax Number")) + + +(define (gnc:options-fancy-date book) + (let ((date-format (gnc:fancy-date-info book gnc:*fancy-date-format*))) + (if (boolean? date-format) ;; date-format does not exist + (qof-date-format-get-string (qof-date-format-get)) + date-format))) + +(define (gnc:company-info book key) + ;; Access company info from key-value pairs for current book + (gnc:option-get-value book gnc:*business-label* key)) + +(define (gnc:fancy-date-info book key) + ;; Access fancy date info from key-value pairs for current book + (gnc:option-get-value book gnc:*business-label* (list gnc:*fancy-date-label* key))) + + + +(define (gnc:options-fancy-date book) + (let ((date-format (gnc:fancy-date-info book gnc:*fancy-date-format*))) + (if (boolean? date-format) ;; date-format does not exist + (qof-date-format-get-string (qof-date-format-get)) + date-format))) diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index 77ba839765..0ffa2b6e5d 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -1,3 +1,4 @@ +set(MODULEPATH ${CMAKE_SOURCE_DIR}/libgnucash/app-utils) set(APP_UTILS_TEST_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/common # for config.h @@ -11,8 +12,6 @@ set(APP_UTILS_TEST_INCLUDE_DIRS set(APP_UTILS_TEST_LIBS gnc-app-utils gnc-test-engine test-core ${GIO_LDFLAGS} ${GUILE_LDFLAGS}) -set(test_app_utils_SOURCES test-app-utils.c test-option-util.cpp test-gnc-ui-util.c) - macro(add_app_utils_test _TARGET _SOURCE_FILES) gnc_add_test(${_TARGET} "${_SOURCE_FILES}" APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS) endmacro() @@ -28,6 +27,26 @@ gnc_add_test_with_guile(test-scm-query-string test-scm-query-string.cpp ) add_app_utils_test(test-sx test-sx.cpp) +set(gtest_gnc_option_SOURCES + gtest-gnc-option.cpp + gtest-gnc-optiondb.cpp) + +set(gtest_gnc_option_INCLUDES + ${MODULEPATH} + ${CMAKE_SOURCE_DIR}/libgnucash/engine + ${CMAKE_BINARY_DIR}/common # for config.h + ${GLIB2_INCLUDE_DIRS} + ${GUILE_INCLUDE_DIRS}) + +set(gtest_gnc_option_LIBS + gnc-app-utils + gnc-engine + ${GLIB2_LDFLAGS} + ${GUILE_LDFLAGS} + gtest) + +gnc_add_test(test-gnc-option "${gtest_gnc_option_SOURCES}" gtest_gnc_option_INCLUDES gtest_gnc_option_LIBS) + set(GUILE_DEPENDS scm-test-engine scm-app-utils @@ -60,17 +79,20 @@ gnc_add_scheme_test_targets(scm-test-c-interface gnc_add_scheme_tests("${test_app_utils_scheme_SOURCES}") if (HAVE_SRFI64) - gnc_add_scheme_test_targets(scm-test-app-utils-srfi64 - SOURCES "${test_app_utils_scheme_SRFI64_SOURCES}" - OUTPUT_DIR "tests" - DEPENDS "${GUILE_DEPENDS};scm-srfi64-extras") + gnc_add_scheme_test_targets(scm-test-app-utils-srfi64 + SOURCES "${test_app_utils_scheme_SRFI64_SOURCES}" + OUTPUT_DIR "tests" + DEPENDS "${GUILE_DEPENDS};scm-srfi64-extras") - gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}") + gnc_add_scheme_test_targets(scm-test-gnc-optiondb + SOURCES "test-gnc-optiondb.scm" "test-gnc-option-scheme-output.scm" + OUTPUT_DIR "tests" + DEPENDS "swig-apputils-guile-cpp;scm-srfi64-extras") + gnc_add_scheme_tests("test-gnc-optiondb.scm") + gnc_add_scheme_tests("test-gnc-option-scheme-output.scm") + gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}") endif() -# Doesn't work yet: -gnc_add_test_with_guile(test-app-utils "${test_app_utils_SOURCES}" APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS) - set_dist_list(test_app_utils_DIST CMakeLists.txt test-exp-parser.c diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp new file mode 100644 index 0000000000..e235b2c46d --- /dev/null +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -0,0 +1,1329 @@ +/******************************************************************** + * gtest-gnc-option.cpp -- unit tests for GncOption class. * + * Copyright (C) 2019 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * + *******************************************************************/ + +#include +#include +#include +#include +#include +extern "C" +{ +#include +#include +#include +#include +#include +#include +#include +#include +#include +} + +TEST(GncOption, test_string_ctor) +{ + EXPECT_NO_THROW({ + GncOption option("foo", "bar", "baz", "Phony Option", + std::string{"waldo"}); + }); +} + +TEST(GncOption, test_string_classifier_getters) +{ + GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); + EXPECT_STREQ("foo", option.get_section().c_str()); + EXPECT_STREQ("bar", option.get_name().c_str()); + EXPECT_STREQ("baz", option.get_key().c_str()); + EXPECT_STREQ("Phony Option", option.get_docstring().c_str()); +} + +TEST(GncOption, test_string_default_value) +{ + GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); + EXPECT_STREQ("waldo", option.get_default_value().c_str()); + EXPECT_STREQ("waldo", option.get_value().c_str()); + EXPECT_EQ(0, option.get_value()); +} + +TEST(GncOption, test_string_value) +{ + GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); + option.set_value(std::string{"pepper"}); + EXPECT_STREQ("waldo", option.get_default_value().c_str()); + EXPECT_NO_THROW({ + EXPECT_STREQ("pepper", option.get_value().c_str()); + }); +} + +TEST(GncOption, test_string_stream_out) +{ + GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); + std::ostringstream oss; + oss << option; + EXPECT_EQ(oss.str(), option.get_value()); +} + +TEST(GncOption, test_string_stream_in) +{ + GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); + std::string pepper{"pepper"}; + std::istringstream iss{pepper}; + iss >> option; + EXPECT_EQ(pepper, option.get_value()); +} + +TEST(GncOption, test_int64_t_value) +{ + GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); + option.set_value(INT64_C(987654321)); + EXPECT_TRUE(option.get_default_value().empty()); + EXPECT_EQ(INT64_C(987654321), option.get_value()); +} + +TEST(GncOption, test_int64_stream_out) +{ + GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); + std::ostringstream oss; + oss << option; + EXPECT_STREQ(oss.str().c_str(), "123456789"); +} + +TEST(GncOption, test_int64_stream_in) +{ + GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); + std::string number{"987654321"}; + std::istringstream iss{number}; + iss >> option; + EXPECT_EQ(INT64_C(987654321), option.get_value()); +} + +TEST(GncOption, test_bool_stream_out) +{ + GncOption option("foo", "bar", "baz", "Phony Option", false); + std::ostringstream oss; + oss << option; + EXPECT_STREQ(oss.str().c_str(), "False"); + oss.str(""); + option.set_value(true); + oss << option; + EXPECT_STREQ(oss.str().c_str(), "True"); +} + +TEST(GncOption, test_bool_stream_in) +{ + GncOption option("foo", "bar", "baz", "Phony Option", false); + std::istringstream iss("#t"); + iss >> option; + EXPECT_TRUE(option.get_value()); + iss.str("#f"); + iss >> option; + EXPECT_FALSE(option.get_value()); +} + + +class GncOptionTest : public ::testing::Test +{ +protected: + GncOptionTest() : m_session{gnc_get_current_session()}, m_book{gnc_get_current_book()} {} + ~GncOptionTest() { gnc_clear_current_session(); } + + QofSession* m_session; + QofBook* m_book; +}; + +TEST_F(GncOptionTest, test_budget_ctor) +{ + auto budget = gnc_budget_new(m_book); + EXPECT_NO_THROW({ + GncOption option(GncOptionQofInstanceValue{"foo", "bar", "baz", + "Phony Option", + (const QofInstance*)budget}); + }); + gnc_budget_destroy(budget); +} + +TEST_F(GncOptionTest, test_budget_out) +{ + auto budget = gnc_budget_new(m_book); + GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", (const QofInstance*)budget}}; + + auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()}; + std::ostringstream oss; + oss << option; + EXPECT_EQ(budget_guid, oss.str()); + gnc_budget_destroy(budget); +} + +TEST_F(GncOptionTest, test_budget_in) +{ + auto budget = gnc_budget_new(m_book); + auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()}; + std::istringstream iss{budget_guid}; + GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}}; + iss >> option; + EXPECT_EQ(QOF_INSTANCE(budget), option.get_value()); + gnc_budget_destroy(budget); +} + +TEST_F(GncOptionTest, test_commodity_ctor) +{ + auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.", + "NYSE", "HPE", NULL, 1); + EXPECT_NO_THROW({ + GncOption option(GncOptionQofInstanceValue{"foo", "bar", "baz", + "Phony Option", + (const QofInstance*)hpe}); + }); + gnc_commodity_destroy(hpe); +} + +class GncOptionCommodityTest : public ::testing::Test +{ +protected: + GncOptionCommodityTest() : m_session{gnc_get_current_session()}, + m_book{gnc_get_current_book()}, + m_table{gnc_commodity_table_new()} + { +/* We can't initialize the commodities with their values because we first must + * set the book's commodity table. + */ + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, m_table); + m_eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100); + gnc_commodity_table_insert(m_table, m_eur); + m_usd = gnc_commodity_new(m_book, "United States Dollar", "CURRENCY", + "USD", NULL, 100); + gnc_commodity_table_insert(m_table, m_usd); + m_aapl = gnc_commodity_new(m_book, "Apple", "NASDAQ", "AAPL", NULL, 1); + gnc_commodity_table_insert(m_table, m_aapl); + m_hpe = gnc_commodity_new(m_book, "Hewlett Packard", "NYSE", "HPE", + NULL, 1); + gnc_commodity_table_insert(m_table, m_hpe); + } + ~GncOptionCommodityTest() + { + gnc_commodity_destroy(m_hpe); + gnc_commodity_destroy(m_aapl); + gnc_commodity_destroy(m_usd); + gnc_commodity_destroy(m_eur); + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr); + gnc_commodity_table_destroy(m_table); + gnc_clear_current_session(); + } + + QofSession* m_session; + QofBook* m_book; + gnc_commodity_table * m_table; + gnc_commodity *m_eur; + gnc_commodity *m_usd; + gnc_commodity *m_aapl; + gnc_commodity *m_hpe; +}; + +static GncOption +make_currency_option (const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity *value, bool is_currency=false) +{ + GncOption option{GncOptionCommodityValue{ + section, name, key, doc_string, value, + is_currency ? GncOptionUIType::CURRENCY : GncOptionUIType::COMMODITY} + }; + return option; +} + +TEST_F(GncOptionCommodityTest, test_currency_ctor) +{ + EXPECT_THROW({ + auto option = make_currency_option("foo", "bar", "baz", + "Phony Option", m_hpe, true); + }, std::invalid_argument); + EXPECT_NO_THROW({ + auto option = make_currency_option("foo", "bar", "baz", + "Phony Option", m_eur, true); + }); + EXPECT_NO_THROW({ + auto option = make_currency_option("foo", "bar", "baz", + "Phony Option", m_usd, true); + }); +} + +TEST_F(GncOptionCommodityTest, test_currency_setter) +{ + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", + m_eur, true); + EXPECT_NO_THROW({ + option.set_value(m_usd); + }); + EXPECT_PRED2(gnc_commodity_equal, m_usd, + GNC_COMMODITY(option.get_value())); + EXPECT_THROW({ + option.set_value(m_hpe); + }, std::invalid_argument); + EXPECT_PRED2(gnc_commodity_equal, m_usd, + GNC_COMMODITY(option.get_value())); +} + +TEST_F(GncOptionCommodityTest, test_currency_validator) +{ + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", + m_eur, true); + EXPECT_TRUE(option.validate(m_usd)); + EXPECT_FALSE(option.validate(m_aapl)); +} + +static inline std::string make_currency_str(gnc_commodity* cur) +{ + std::string cur_str{gnc_commodity_get_mnemonic(cur)}; + return cur_str; +} + +static inline std::string make_commodity_str(gnc_commodity* com) +{ + std::string com_str{gnc_commodity_get_namespace(com)}; + com_str += ":"; + com_str += gnc_commodity_get_mnemonic(com); + return com_str; +} + +TEST_F(GncOptionCommodityTest, test_currency_out) +{ + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", + m_eur, true); + + std::string eur_str{make_currency_str(m_eur)}; + std::ostringstream oss; + oss << option; + EXPECT_EQ(eur_str, oss.str()); +} + +TEST_F(GncOptionCommodityTest, test_commodity_out) +{ + GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe, + GncOptionUIType::COMMODITY}}; + std::string hpe_str{make_commodity_str(m_hpe)}; + std::ostringstream oss; + oss << option; + EXPECT_EQ(hpe_str, oss.str()); +} + +TEST_F(GncOptionCommodityTest, test_currency_in) +{ + + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", + m_eur, true); + + EXPECT_THROW({ + std::string hpe_str{make_commodity_str(m_hpe)}; + std::istringstream iss{hpe_str}; + iss >> option; + }, std::invalid_argument); + EXPECT_NO_THROW({ + std::string usd_str{make_currency_str(m_usd)}; + std::istringstream iss{usd_str}; + iss >> option; + EXPECT_EQ(m_usd, option.get_value()); + }); +} + +TEST_F(GncOptionCommodityTest, test_commodity_in) +{ + GncOption option{GncOptionCommodityValue{"foo", "bar", "baz", "Phony Option", m_aapl, + GncOptionUIType::COMMODITY}}; + + std::string hpe_str{make_commodity_str(m_hpe)}; + std::istringstream iss{hpe_str}; + iss >> option; + EXPECT_EQ(m_hpe, option.get_value()); +} + +class GncUIType +{ +public: + void set_value(const std::string& value) const noexcept { m_value = value; } + const std::string& get_value() const noexcept { return m_value; } + void clear() noexcept { m_value.clear(); } +private: + mutable std::string m_value; +}; + + +class OptionUIItem : public GncOptionUIItem +{ + GncUIType m_widget; + bool m_dirty = false; +public: + OptionUIItem() : GncOptionUIItem{GncOptionUIType::STRING} {} + ~OptionUIItem() = default; + void set_dirty(bool status) noexcept override { m_dirty = status; } + bool get_dirty() const noexcept override { return m_dirty; } + void set_selectable(bool selectable) const noexcept override {} + void clear_ui_item() override { m_widget.clear(); } + void set_ui_item_from_option(GncOption& option) noexcept override + { + m_widget.set_value(option.get_value()); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + option.set_value(m_widget.get_value()); + } + void set_widget_value(const std::string& value) const noexcept + { + m_widget.set_value(value); + } + const std::string& get_widget_value() const noexcept + { + return m_widget.get_value(); + } +}; + +class GncOptionUITest : public ::testing::Test +{ +protected: + GncOptionUITest() : + m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}, + GncOptionUIType::STRING} + { + auto ui_item{std::make_unique()}; + m_option.set_ui_item(std::move(ui_item)); + } + GncOption m_option; +}; + +using GncOptionUI = GncOptionUITest; + +TEST_F(GncOptionUI, test_option_ui_type) +{ + EXPECT_EQ(GncOptionUIType::STRING, m_option.get_ui_type()); +} + +TEST_F(GncOptionUI, test_ui_value_from_option) +{ + const char* value{"waldo"}; + + m_option.set_value(value); + m_option.set_ui_item_from_option(); + auto ui_item{dynamic_cast(m_option.get_ui_item())}; + ASSERT_TRUE(ui_item != nullptr); + EXPECT_STREQ(value, ui_item->get_widget_value().c_str()); +} + +TEST_F(GncOptionUI, test_option_value_from_ui) +{ + const char* value{"pepper"}; + auto ui_item{dynamic_cast(m_option.get_ui_item())}; + ASSERT_TRUE(ui_item != nullptr); + ui_item->set_widget_value(value); + m_option.set_option_from_ui_item(); + EXPECT_STREQ(value, m_option.get_value().c_str()); +} + +class GncOptionRangeTest : public ::testing::Test +{ +protected: + GncOptionRangeTest() : + m_intoption{GncOptionRangeValue{"foo", "bar", "baz", + "Phony Option", 15, 1, 30, 1}}, + m_doubleoption{GncOptionRangeValue{"waldo", "pepper", "salt", + "Phonier Option", 1.5, 1.0, + 3.0, 0.1}} {} + + GncOption m_intoption; + GncOption m_doubleoption; +}; + +using GncRangeOption = GncOptionRangeTest; + +TEST_F(GncRangeOption, test_initialization) +{ + EXPECT_EQ(15, m_intoption.get_value()); + EXPECT_EQ(1.5, m_doubleoption.get_value()); + EXPECT_EQ(15, m_intoption.get_default_value()); + EXPECT_EQ(1.5, m_doubleoption.get_default_value()); +} + +TEST_F(GncRangeOption, test_setter) +{ + EXPECT_THROW({ m_intoption.set_value(45); }, std::invalid_argument); + EXPECT_NO_THROW({ m_intoption.set_value(20); }); + EXPECT_EQ(20, m_intoption.get_value()); + EXPECT_EQ(15, m_intoption.get_default_value()); + EXPECT_THROW({ m_doubleoption.set_value(4.5); }, std::invalid_argument); + EXPECT_NO_THROW({ m_doubleoption.set_value(2.0); }); + EXPECT_EQ(2.0, m_doubleoption.get_value()); + EXPECT_EQ(1.5, m_doubleoption.get_default_value()); +} + +TEST_F(GncRangeOption, test_get_info) +{ + int imax{}, imin{}, istep{}; + double dmax{}, dmin{}, dstep{}; + m_intoption.get_limits(imax, imin, istep); + m_doubleoption.get_limits(dmax, dmin, dstep); + EXPECT_EQ(30, imax); + EXPECT_EQ(1, imin); + EXPECT_EQ(1, istep); + EXPECT_EQ(1.0, dmin); + EXPECT_EQ(3.0, dmax); + EXPECT_EQ(0.1, dstep); +} + +TEST_F(GncRangeOption, test_range_out) +{ + std::ostringstream oss; + oss << "Integer " << m_intoption << " Double " << m_doubleoption << "."; + EXPECT_STREQ("Integer 15 Double 1.500000.", oss.str().c_str()); +} + +TEST_F(GncRangeOption, test_range_in) +{ + std::istringstream iss{std::string{"45 4.5 20 2.0"}}; + EXPECT_THROW({ iss >> m_intoption; }, std::invalid_argument); + EXPECT_THROW({ iss >> m_doubleoption; }, std::invalid_argument); + EXPECT_NO_THROW({ iss >> m_intoption; }); + EXPECT_NO_THROW({ iss >> m_doubleoption; }); + EXPECT_EQ(20, m_intoption.get_value()); + EXPECT_EQ(2.0, m_doubleoption.get_value()); +} + +using AccountPair = std::pair; +static void +find_children(Account* account, void* data) +{ + auto datapair = + (AccountPair*)data; + GncOptionAccountList& list = datapair->first; + const GncOptionAccountTypeList& types = datapair->second; + if (std::find(types.begin(), types.end(), + xaccAccountGetType(account)) != types.end()) + list.push_back(*qof_entity_get_guid(account)); +} + +class GncOptionAccountTest : public ::testing::Test +{ +protected: + GncOptionAccountTest() : + m_session{gnc_get_current_session()}, m_book{gnc_get_current_book()}, + m_root{gnc_account_create_root(m_book)} + { + auto create_account = [this](Account* parent, GNCAccountType type, + const char* name)->Account* { + auto account = xaccMallocAccount(this->m_book); + xaccAccountBeginEdit(account); + xaccAccountSetType(account, type); + xaccAccountSetName(account, name); + xaccAccountBeginEdit(parent); + gnc_account_append_child(parent, account); + xaccAccountCommitEdit(parent); + xaccAccountCommitEdit(account); + return account; + }; + auto assets = create_account(m_root, ACCT_TYPE_ASSET, "Assets"); + auto liabilities = create_account(m_root, ACCT_TYPE_LIABILITY, "Liabilities"); + auto expenses = create_account(m_root, ACCT_TYPE_EXPENSE, "Expenses"); + create_account(assets, ACCT_TYPE_BANK, "Bank"); + auto broker = create_account(assets, ACCT_TYPE_ASSET, "Broker"); + auto stocks = create_account(broker, ACCT_TYPE_STOCK, "Stocks"); + create_account(stocks, ACCT_TYPE_STOCK, "AAPL"); + create_account(stocks, ACCT_TYPE_STOCK, "MSFT"); + create_account(stocks, ACCT_TYPE_STOCK, "HPE"); + create_account(broker, ACCT_TYPE_BANK, "Cash Management"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Food"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Gas"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Rent"); + } + ~GncOptionAccountTest() + { + xaccAccountBeginEdit(m_root); + xaccAccountDestroy(m_root); //It does the commit + gnc_clear_current_session(); + } + GncOptionAccountList list_of_types(const GncOptionAccountTypeList& types) + { + GncOptionAccountList list; + AccountPair funcdata{list, types}; + gnc_account_foreach_descendant(m_root, (AccountCb)find_children, + &funcdata); + return list; + } + + QofSession* m_session; + QofBook* m_book; + Account* m_root; +}; + +static bool +operator==(const GncGUID& l, const GncGUID& r) +{ + return guid_equal(&l, &r); +} + +TEST_F(GncOptionAccountTest, test_test_constructor_and_destructor) +{ + EXPECT_TRUE(m_book != NULL); + EXPECT_TRUE(QOF_IS_BOOK(m_book)); + EXPECT_TRUE(m_root != NULL); + EXPECT_TRUE(GNC_IS_ACCOUNT(m_root)); + GncOptionAccountList list{list_of_types({ACCT_TYPE_BANK})}; + EXPECT_EQ(2U, list.size()); + list = list_of_types({ACCT_TYPE_ASSET, ACCT_TYPE_STOCK}); + EXPECT_EQ(6U, list.size()); +} + +TEST_F(GncOptionAccountTest, test_option_no_value_constructor) +{ + GncOptionAccountListValue option{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST}; + EXPECT_TRUE(option.get_value().empty()); + EXPECT_TRUE(option.get_default_value().empty()); +} + +TEST_F(GncOptionAccountTest, test_option_value_constructor) +{ + GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; + GncOptionAccountListValue option{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, acclist}; + EXPECT_EQ(2U, option.get_value().size()); + EXPECT_EQ(2U, option.get_default_value().size()); + EXPECT_EQ(acclist[0], option.get_value()[0]); +} + +TEST_F(GncOptionAccountTest, test_option_no_value_limited_constructor) +{ + GncOptionAccountList acclistgood{list_of_types({ACCT_TYPE_BANK})}; + GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; + GncOptionAccountListValue option{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + GncOptionAccountTypeList{ACCT_TYPE_BANK}}; + EXPECT_EQ(1U, option.get_value().size()); + EXPECT_EQ(1U, option.get_default_value().size()); + EXPECT_EQ(true, option.validate(acclistgood)); + EXPECT_EQ(false, option.validate(acclistbad)); +} + +TEST_F(GncOptionAccountTest, test_option_value_limited_constructor) +{ + GncOptionAccountList acclistgood{list_of_types({ACCT_TYPE_BANK})}; + GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; + EXPECT_THROW({ + GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclistbad, + GncOptionAccountTypeList{ACCT_TYPE_BANK}); + }, std::invalid_argument); + + EXPECT_THROW({ + GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_SEL, + acclistgood, + GncOptionAccountTypeList{ACCT_TYPE_BANK}); + }, std::invalid_argument); + + EXPECT_NO_THROW({ + GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclistgood, + GncOptionAccountTypeList{ACCT_TYPE_BANK}); + }); + + EXPECT_NO_THROW({ + GncOptionAccountList accsel{acclistgood[0]}; + GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + accsel, + GncOptionAccountTypeList{ACCT_TYPE_BANK}); + }); + GncOptionAccountListValue option {"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, acclistgood, + GncOptionAccountTypeList{ACCT_TYPE_BANK}}; + EXPECT_FALSE(option.get_value().empty()); + EXPECT_FALSE(option.get_default_value().empty()); + EXPECT_EQ(true, option.validate(acclistgood)); + EXPECT_EQ(false, option.validate(acclistbad)); +} + +TEST_F(GncOptionAccountTest, test_account_list_out) +{ + GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; + GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclist}}; + std::ostringstream oss; + std::string acc_guids{gnc::GUID{acclist[0]}.to_string()}; + acc_guids += " "; + acc_guids += gnc::GUID{acclist[1]}.to_string(); + + oss << option; + EXPECT_EQ(acc_guids, oss.str()); + + GncOptionAccountList accsel{acclist[0]}; + GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz", + "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + accsel, + GncOptionAccountTypeList{ACCT_TYPE_BANK}}}; + acc_guids = gnc::GUID{accsel[0]}.to_string(); + + oss.str(""); + oss << sel_option; + EXPECT_EQ(acc_guids, oss.str()); +} + +TEST_F(GncOptionAccountTest, test_account_list_in) +{ + GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; + GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclist}}; + std::string acc_guids{gnc::GUID{acclist[0]}.to_string()}; + acc_guids += " "; + acc_guids += gnc::GUID{acclist[1]}.to_string(); + + std::istringstream iss{acc_guids}; + iss >> option; + EXPECT_EQ(acclist, option.get_value()); + + GncOptionAccountList accsel{acclist[0]}; + GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz", + "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + accsel, + GncOptionAccountTypeList{ACCT_TYPE_BANK}}}; + GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; + acc_guids = gnc::GUID{acclistbad[1]}.to_string(); + acc_guids += " "; + + iss.str(acc_guids); + iss >> sel_option; + EXPECT_EQ(accsel, sel_option.get_value()); + + iss.clear(); //Reset the failedbit from the invalid selection type. + acc_guids = gnc::GUID{acclist[1]}.to_string(); + EXPECT_NO_THROW({ + iss.str(acc_guids); + iss >> sel_option; + }); + EXPECT_EQ(acclist[1], sel_option.get_value()[0]); +} + +using KT = GncOptionMultichoiceKeyType; +class GncOptionMultichoiceTest : public ::testing::Test +{ +protected: + GncOptionMultichoiceTest() : + m_option{GncOptionMultichoiceValue + {"foo", "bar", "baz", + "Phony Option", "plugh", + { + {"plugh", "xyzzy", KT::STRING}, + {"waldo", "pepper", KT::STRING}, + {"pork", "sausage", KT::STRING}, + {"corge", "grault", KT::STRING} + }}} {} + GncOption m_option; +}; + +using GncMultichoiceOption = GncOptionMultichoiceTest; + +TEST_F(GncMultichoiceOption, test_option_ui_type) +{ + EXPECT_EQ(GncOptionUIType::MULTICHOICE, m_option.get_ui_type()); +} + +TEST_F(GncMultichoiceOption, test_validate) +{ + EXPECT_TRUE( + m_option.validate(std::string{"waldo"}) + ); + EXPECT_FALSE(m_option.validate(std::string{"grault"})); +} + +TEST_F(GncMultichoiceOption, test_set_value) +{ + EXPECT_NO_THROW({ + m_option.set_value(std::string{"pork"}); + EXPECT_STREQ("pork", m_option.get_value().c_str()); + }); + EXPECT_THROW({ m_option.set_value(std::string{"salt"}); }, std::invalid_argument); + EXPECT_STREQ("pork", m_option.get_value().c_str()); +} + +TEST_F(GncMultichoiceOption, test_num_permissible) +{ + EXPECT_EQ(4U, m_option.num_permissible_values()); +} + +TEST_F(GncMultichoiceOption, test_permissible_value_stuff) +{ + EXPECT_NO_THROW({ + EXPECT_EQ(3U, m_option.permissible_value_index("corge")); + EXPECT_STREQ("waldo", m_option.permissible_value(1)); + EXPECT_STREQ("sausage", m_option.permissible_value_name(2)); + }); + EXPECT_THROW({ auto result = m_option.permissible_value(7); }, + std::out_of_range); + EXPECT_THROW({ auto result = m_option.permissible_value_name(9); }, + std::out_of_range); + EXPECT_EQ(std::numeric_limits::max(), + m_option.permissible_value_index("xyzzy")); +} + +TEST_F(GncMultichoiceOption, test_multichoice_out) +{ + std::ostringstream oss; + oss << m_option; + EXPECT_EQ(oss.str(), m_option.get_value()); +} + +TEST_F(GncMultichoiceOption, test_multichoice_in) +{ + std::istringstream iss{"grault"}; + EXPECT_THROW({ + iss >> m_option; + }, std::invalid_argument); + iss.clear(); //reset failedbit + iss.str("pork"); + iss >> m_option; + EXPECT_EQ(iss.str(), m_option.get_value()); +} + +class GncOptionListTest : public ::testing::Test +{ +protected: + GncOptionListTest() : + m_option{GncOptionMultichoiceValue{ + "foo", "bar", "baz", "Phony Option", + GncMultichoiceOptionIndexVec{0, 2}, + { + {"plugh", "xyzzy", KT::STRING}, + {"waldo", "pepper", KT::STRING}, + {"pork", "sausage", KT::STRING}, + {"corge", "grault", KT::STRING} + }}} {} + GncOption m_option; +}; + +using GncListOption = GncOptionListTest; + +TEST_F(GncListOption, test_option_ui_type) +{ + EXPECT_EQ(GncOptionUIType::LIST, m_option.get_ui_type()); +} + +TEST_F(GncListOption, test_validate) +{ + EXPECT_TRUE(m_option.validate(std::string{"pork"})); + EXPECT_TRUE(m_option.validate(GncMultichoiceOptionIndexVec{1, 3})); + EXPECT_FALSE(m_option.validate(GncMultichoiceOptionIndexVec{2, 6})); +} + +TEST_F(GncListOption, test_set_value) +{ + EXPECT_NO_THROW({ + m_option.set_value(GncMultichoiceOptionIndexVec{1, 3}); + EXPECT_STREQ("multiple values", + m_option.get_value().c_str()); + EXPECT_EQ(1U, m_option.get_value()); + auto vec{m_option.get_value()}; + ASSERT_EQ(2U, vec.size()); + EXPECT_EQ(1U, vec[0]); + EXPECT_EQ(3U, vec[1]); + }); + EXPECT_THROW({ m_option.set_value(GncMultichoiceOptionIndexVec{2, 5}); }, std::invalid_argument); + EXPECT_EQ(1U, m_option.get_value()); +} + +TEST_F(GncListOption, test_list_out) +{ + auto vec{m_option.get_value()}; + std::string value{m_option.permissible_value(vec[0])}; + value += " "; + value += m_option.permissible_value(vec[1]); + std::ostringstream oss; + oss << m_option; + EXPECT_EQ(oss.str(), value); +} + +TEST_F(GncListOption, test_list_in) +{ + std::istringstream iss{"grault"}; + EXPECT_THROW({ + iss >> m_option; + }, std::invalid_argument); + iss.clear(); //reset failedbit + iss.str("pork"); + iss >> m_option; + EXPECT_EQ(iss.str(), m_option.get_value()); +} + +static time64 +time64_from_gdate(const GDate* g_date, DayPart when) +{ + GncDate date{g_date_get_year(g_date), g_date_get_month(g_date), + g_date_get_day(g_date)}; + GncDateTime time{date, when}; + return static_cast(time); +} + +TEST(GncOptionDate, test_gnc_relative_date_is_single) +{ + EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::ABSOLUTE)); + EXPECT_TRUE(gnc_relative_date_is_single(RelativeDatePeriod::TODAY)); + EXPECT_TRUE(gnc_relative_date_is_single(RelativeDatePeriod::ONE_YEAR_AHEAD)); + EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::START_THIS_MONTH)); + EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::END_CURRENT_QUARTER)); + EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::START_ACCOUNTING_PERIOD)); + EXPECT_FALSE(gnc_relative_date_is_single(RelativeDatePeriod::END_ACCOUNTING_PERIOD)); +} + +TEST(GncOptionDate, test_gnc_relative_date_is_starting) +{ + EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::ABSOLUTE)); + EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::TODAY)); + EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::ONE_YEAR_AHEAD)); + EXPECT_TRUE(gnc_relative_date_is_starting(RelativeDatePeriod::START_THIS_MONTH)); + EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::END_CURRENT_QUARTER)); + EXPECT_TRUE(gnc_relative_date_is_starting(RelativeDatePeriod::START_ACCOUNTING_PERIOD)); + EXPECT_FALSE(gnc_relative_date_is_starting(RelativeDatePeriod::END_ACCOUNTING_PERIOD)); +} + +TEST(GncOptionDate, test_gnc_relative_date_is_ending) +{ + EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::ABSOLUTE)); + EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::TODAY)); + EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::ONE_YEAR_AHEAD)); + EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::START_CURRENT_QUARTER)); + EXPECT_TRUE(gnc_relative_date_is_ending(RelativeDatePeriod::END_CURRENT_QUARTER)); + EXPECT_FALSE(gnc_relative_date_is_ending(RelativeDatePeriod::START_ACCOUNTING_PERIOD)); + EXPECT_TRUE(gnc_relative_date_is_ending(RelativeDatePeriod::END_ACCOUNTING_PERIOD)); +} + +TEST(GncOptionDate, test_gnc_relative_date_storage_string) +{ + EXPECT_EQ(nullptr, gnc_relative_date_storage_string(RelativeDatePeriod::ABSOLUTE)); + EXPECT_STREQ("one-month-ago", gnc_relative_date_storage_string(RelativeDatePeriod::ONE_MONTH_AGO)); +} + +TEST(GncOptionDate, test_gnc_relative_date_display_string) +{ + EXPECT_EQ(nullptr, gnc_relative_date_display_string(RelativeDatePeriod::ABSOLUTE)); + EXPECT_STREQ("Start of next month", gnc_relative_date_display_string(RelativeDatePeriod::START_NEXT_MONTH)); +} + +TEST(GncOptionDate, test_gnc_relative_date_description) +{ + EXPECT_EQ(nullptr, gnc_relative_date_description(RelativeDatePeriod::ABSOLUTE)); + EXPECT_STREQ("First day of the next month.", gnc_relative_date_description(RelativeDatePeriod::START_NEXT_MONTH)); +} + +TEST(GncOptionDate, test_gnc_relative_date_from_storage_string) +{ + // EXPECT_EQ(RelativeDatePeriod::ABSOLUTE, gnc_relative_date_from_storage_string("foo")); + EXPECT_EQ(RelativeDatePeriod::ONE_MONTH_AHEAD, gnc_relative_date_from_storage_string("one-month-ahead")); + EXPECT_EQ(RelativeDatePeriod::START_CURRENT_QUARTER, gnc_relative_date_from_storage_string("start-current-quarter")); + EXPECT_EQ(RelativeDatePeriod::END_ACCOUNTING_PERIOD, gnc_relative_date_from_storage_string("end-prev-fin-year")); +} + +TEST(GncOptionDate, test_gnc_relative_date_to_time64) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_month_start(&date); + time64 time1{time64_from_gdate(&date, DayPart::start)}; + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::START_THIS_MONTH)); + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_month_end(&date); + time1 = time64_from_gdate(&date, DayPart::end); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::END_THIS_MONTH)); + + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_month_start(&date); + time1 = time64_from_gdate(&date, DayPart::start); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::START_PREV_MONTH)); + + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_month_end(&date); + time1 = time64_from_gdate(&date, DayPart::end); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::END_PREV_MONTH)); + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_quarter_start(&date); + time1 = time64_from_gdate(&date, DayPart::start); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::START_CURRENT_QUARTER)); + + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_quarter_end(&date); + time1 = time64_from_gdate(&date, DayPart::end); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::END_CURRENT_QUARTER)); + + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_quarter_start(&date); + time1 = time64_from_gdate(&date, DayPart::start); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::START_PREV_QUARTER)); + + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_quarter_end(&date); + time1 = time64_from_gdate(&date, DayPart::end); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::END_PREV_QUARTER)); + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_year_start(&date); + time1 = time64_from_gdate(&date, DayPart::start); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::START_CAL_YEAR)); + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_year_end(&date); + time1 = time64_from_gdate(&date, DayPart::end); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::END_CAL_YEAR)); + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_year_start(&date); + time1 = time64_from_gdate(&date, DayPart::start); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::START_PREV_YEAR)); + + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_year_end(&date); + time1 = time64_from_gdate(&date, DayPart::end); + EXPECT_EQ(time1, + gnc_relative_date_to_time64(RelativeDatePeriod::END_PREV_YEAR)); +} + +class GncOptionDateOptionTest : public ::testing::Test +{ +protected: + GncOptionDateOptionTest() : + m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option", + GncOptionUIType::DATE_BOTH}} {} + GncOption m_option; +}; + +class GncOptionDateOptionListTest : public ::testing::Test +{ +protected: + GncOptionDateOptionListTest() : + m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option", + GncOptionUIType::DATE_BOTH, c_begin_dates}} {} + GncOption m_option; + + static const RelativeDatePeriodVec c_begin_dates; +}; + +const RelativeDatePeriodVec GncOptionDateOptionListTest::c_begin_dates +{ + RelativeDatePeriod::TODAY, + RelativeDatePeriod::START_THIS_MONTH, + RelativeDatePeriod::START_PREV_MONTH, + RelativeDatePeriod::START_CURRENT_QUARTER, + RelativeDatePeriod::START_PREV_QUARTER, + RelativeDatePeriod::START_CAL_YEAR, + RelativeDatePeriod::START_PREV_YEAR, + RelativeDatePeriod::START_ACCOUNTING_PERIOD +}; + +using GncDateOption = GncOptionDateOptionTest; +using GncDateOptionList = GncOptionDateOptionListTest; + +TEST_F(GncDateOption, test_set_and_get_absolute) +{ + time64 time1{static_cast(GncDateTime("2019-07-19 15:32:26 +05:00"))}; + m_option.set_value(time1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOptionList, test_set_and_get_relative) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_month_start(&date); + time64 time1{time64_from_gdate(&date, DayPart::start)}; + EXPECT_EQ(RelativeDatePeriod::START_ACCOUNTING_PERIOD, m_option.get_value()); + m_option.set_value(RelativeDatePeriod::START_THIS_MONTH); + EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, m_option.get_value()); + size_t index(std::find(c_begin_dates.begin(), c_begin_dates.end(), + RelativeDatePeriod::START_THIS_MONTH) - c_begin_dates.begin()); + EXPECT_EQ(index, m_option.get_value()); + // And check that nothing happens when we try to set m_option to an end date + m_option.set_value(RelativeDatePeriod::END_THIS_MONTH); + EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, m_option.get_value()); + m_option.set_value(static_cast(5)); + EXPECT_EQ(RelativeDatePeriod::START_CAL_YEAR, m_option.get_value()); + EXPECT_EQ(5u, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_out) +{ + time64 time1{static_cast(GncDateTime("2019-07-19 15:32:26 +05:00"))}; + m_option.set_value(time1); + std::ostringstream oss; + oss << time1; + std::string timestr{"(absolute . "}; + timestr += oss.str() + ")"; + oss.str(""); + oss << m_option; + EXPECT_EQ(oss.str(), timestr); + + m_option.set_value(RelativeDatePeriod::TODAY); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . today)"); + + m_option.set_value(RelativeDatePeriod::START_THIS_MONTH); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . start-this-month)"); + + m_option.set_value(RelativeDatePeriod::END_THIS_MONTH); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . end-this-month)"); + + m_option.set_value(RelativeDatePeriod::START_PREV_MONTH); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . start-prev-month)"); + + m_option.set_value(RelativeDatePeriod::END_PREV_MONTH); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . end-prev-month)"); + + m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . start-current-quarter)"); + + m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . end-current-quarter)"); + + m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . start-prev-quarter)"); + + m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . end-prev-quarter)"); + + m_option.set_value(RelativeDatePeriod::START_CAL_YEAR); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . start-cal-year)"); + + m_option.set_value(RelativeDatePeriod::END_CAL_YEAR); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . end-cal-year)"); + + m_option.set_value(RelativeDatePeriod::START_PREV_YEAR); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . start-prev-year)"); + + m_option.set_value(RelativeDatePeriod::END_PREV_YEAR); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . end-prev-year)"); + + m_option.set_value(RelativeDatePeriod::START_ACCOUNTING_PERIOD); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . start-prev-fin-year)"); + + m_option.set_value(RelativeDatePeriod::END_ACCOUNTING_PERIOD); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "(relative . end-prev-fin-year)"); +} + +TEST_F(GncDateOption, test_stream_in_absolute) +{ + time64 time1{static_cast(GncDateTime("2019-07-19 15:32:26 +05:00"))}; + std::ostringstream oss; + oss << time1; + std::string timestr{"absolute . "}; + timestr += oss.str(); + + std::istringstream iss{timestr}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_month_start) +{ + GDate month_start; + g_date_set_time_t(&month_start, time(nullptr)); + gnc_gdate_set_month_start(&month_start); + time64 time1{time64_from_gdate(&month_start, DayPart::start)}; + std::istringstream iss{"relative . start-this-month"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + + +TEST_F(GncDateOption, test_stream_in_month_end) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_month_end(&date); + time64 time1{time64_from_gdate(&date, DayPart::end)}; + std::istringstream iss{"relative . end-this-month"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_prev_month_start) +{ + GDate prev_month_start; + g_date_set_time_t(&prev_month_start, time(nullptr)); + gnc_gdate_set_prev_month_start(&prev_month_start); + time64 time1{time64_from_gdate(&prev_month_start, DayPart::start)}; + std::istringstream iss{"relative . start-prev-month"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_prev_month_end) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_month_end(&date); + time64 time1{time64_from_gdate(&date, DayPart::end)}; + std::istringstream iss{"relative . end-prev-month"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_quarter_start) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_quarter_start(&date); + time64 time1{time64_from_gdate(&date, DayPart::start)}; + std::istringstream iss{"relative . start-current-quarter"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_quarter_end) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_quarter_end(&date); + time64 time1{time64_from_gdate(&date, DayPart::end)}; + std::istringstream iss{"relative . end-current-quarter"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_prev_quarter_start) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_quarter_start(&date); + time64 time1{time64_from_gdate(&date, DayPart::start)}; + std::istringstream iss{"relative . start-prev-quarter"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_prev_quarter_end) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_quarter_end(&date); + time64 time1{time64_from_gdate(&date, DayPart::end)}; + std::istringstream iss{"relative . end-prev-quarter"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_year_start) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_year_start(&date); + time64 time1{time64_from_gdate(&date, DayPart::start)}; + std::istringstream iss{"relative . start-cal-year"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_year_end) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_year_end(&date); + time64 time1{time64_from_gdate(&date, DayPart::end)}; + std::istringstream iss{"relative . end-cal-year"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_prev_year_start) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_year_start(&date); + time64 time1{time64_from_gdate(&date, DayPart::start)}; + std::istringstream iss{"relative . start-prev-year"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_stream_in_prev_year_end) +{ + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_prev_year_end(&date); + time64 time1{time64_from_gdate(&date, DayPart::end)}; + std::istringstream iss{"relative . end-prev-year"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST(GncOption, test_create) +{ + uint32_t report_id = 123; + uint32_t wide = 2, high = 2; + GncOptionReportPlacementVec rp{{report_id, wide, high}}; + + GncOptionValue rpv("foo", "bar", "baz", "Phony Option", rp); + GncOption option{rpv}; + auto value{option.get_value()}; + EXPECT_EQ(value.size(), 1); + auto [sid, swide, shigh] = value.at(0); + EXPECT_EQ(report_id, sid); + EXPECT_EQ(wide, swide); + EXPECT_EQ(high, shigh); + +} \ No newline at end of file diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp new file mode 100644 index 0000000000..9804aa77ff --- /dev/null +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -0,0 +1,408 @@ +/******************************************************************** + * gtest-gnc-optiondb.cpp -- unit tests for GncOption class. * + * Copyright (C) 2019 John Ralls * + * * + * This program 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 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * + *******************************************************************/ + +#include +#include +#include +#include +#include +#include + +extern "C" +{ +#include +#include +} + +using GncOptionDBPtr = std::unique_ptr; + +class GncOptionDBTest : public ::testing::Test +{ +protected: + GncOptionDBTest() : m_db{std::make_unique()} {} + + GncOptionDBPtr m_db; +}; + +TEST_F(GncOptionDBTest, test_ctor) +{ + EXPECT_NO_THROW ({ GncOptionDB optiondb; }); +} + +TEST_F(GncOptionDBTest, test_register_option) +{ + GncOption option1{"foo", "bar", "baz", "Phony Option", + std::string{"waldo"}}; + m_db->register_option("foo", std::move(option1)); + EXPECT_EQ(1u, m_db->num_sections()); +} + +TEST_F(GncOptionDBTest, test_lookup_string_option) +{ + GncOption option1{"foo", "bar", "baz", "Phony Option", + std::string{"waldo"}}; + m_db->register_option("foo", std::move(option1)); + EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str()); +} + +TEST_F(GncOptionDBTest, test_unregister_option) +{ + GncOption option1{"foo", "bar", "baz", "Phony Option", + std::string{"waldo"}}; + m_db->register_option("foo", std::move(option1)); + m_db->unregister_option("foo", "bar"); + EXPECT_TRUE(m_db->lookup_string_option("foo", "bar").empty()); +} + +TEST_F(GncOptionDBTest, test_register_string_option) +{ + gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option", + std::string{"waldo"}); + EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str()); +} + +TEST_F(GncOptionDBTest, test_register_report_placement_option) +{ + uint32_t report_id = 456; + uint32_t wide = 2, high = 2; + GncOptionReportPlacementVec rp{{report_id, wide, high}}; + + gnc_register_report_placement_option(m_db, "foo", "bar"); + auto option{m_db->find_option("foo", "bar")}; + option->set_value(rp); + auto value{option->get_value()}; + EXPECT_EQ(value.size(), 1); + auto [v_id, v_wide, v_height] = value.at(0); + EXPECT_EQ(report_id, v_id); +} + +/* Note: The following test-fixture code is also present in slightly different + * form in gtest-gnc-option.cpp. + */ + + +struct GncOptionDBAccountTest : public ::testing::Test +{ + GncOptionDBAccountTest() : + m_sess{gnc_get_current_session()}, m_book{gnc_get_current_book()}, + m_root{gnc_account_create_root(m_book)}, + m_db{std::make_unique()} + { + auto create_account = [this](Account* parent, GNCAccountType type, + const char* name)->Account* { + auto account = xaccMallocAccount(this->m_book); + xaccAccountBeginEdit(account); + xaccAccountSetType(account, type); + xaccAccountSetName(account, name); + xaccAccountBeginEdit(parent); + gnc_account_append_child(parent, account); + xaccAccountCommitEdit(parent); + xaccAccountCommitEdit(account); + return account; + }; + auto assets = create_account(m_root, ACCT_TYPE_ASSET, "Assets"); + auto liabilities = create_account(m_root, ACCT_TYPE_LIABILITY, "Liabilities"); + auto expenses = create_account(m_root, ACCT_TYPE_EXPENSE, "Expenses"); + create_account(assets, ACCT_TYPE_BANK, "Bank"); + auto broker = create_account(assets, ACCT_TYPE_ASSET, "Broker"); + auto stocks = create_account(broker, ACCT_TYPE_STOCK, "Stocks"); + create_account(stocks, ACCT_TYPE_STOCK, "AAPL"); + create_account(stocks, ACCT_TYPE_STOCK, "MSFT"); + create_account(stocks, ACCT_TYPE_STOCK, "HPE"); + create_account(broker, ACCT_TYPE_BANK, "Cash Management"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Food"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Gas"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Rent"); + } + ~GncOptionDBAccountTest() + { + xaccAccountBeginEdit(m_root); + xaccAccountDestroy(m_root); //It does the commit + gnc_clear_current_session(); + } + + QofSession* m_sess; + QofBook* m_book; + Account* m_root; + GncOptionDBPtr m_db; +}; + +TEST_F(GncOptionDBAccountTest, test_register_account_list_option) +{ + auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})}; + gnc_register_account_list_option(m_db, "foo", "bar", "baz", + "Phony Option", acclist); + EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get_value().size()); + EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value().at(3)); +} + +TEST_F(GncOptionDBAccountTest, test_register_account_list_limited_option) +{ + auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})}; + gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", + "Phony Option", acclist, + {ACCT_TYPE_STOCK}); + EXPECT_EQ(4u, m_db->find_option("foo", "bar")->get_value().size()); + EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value().at(3)); +} + +TEST_F(GncOptionDBAccountTest, test_register_account_sel_limited_option) +{ + auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})}; + GncOptionAccountList accsel{acclist[2]}; + gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", + "Phony Option", accsel, + {ACCT_TYPE_STOCK}); + EXPECT_EQ(1u, m_db->find_option("foo", "bar")->get_value().size()); + EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get_value().at(0)); +} + +TEST_F(GncOptionDBAccountTest, test_register_account_sel_limited_option_fail_construct) +{ + auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})}; + GncOptionAccountList accsel{acclist[2]}; + gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", + accsel, {ACCT_TYPE_BANK}); + EXPECT_FALSE(m_db->find_option("foo", "bar")); + gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", + "Phony Option", acclist, + {ACCT_TYPE_BANK}); + EXPECT_FALSE(m_db->find_option("foo", "bar")); +} + +using KT = GncOptionMultichoiceKeyType; +TEST_F(GncOptionDBTest, test_register_multichoice_option) +{ + GncMultichoiceOptionChoices choices{ + { "plugh", "xyzzy", KT::STRING}, + { "waldo", "pepper", KT::STRING}, + { "pork", "sausage", KT::STRING}, + { "corge", "grault", KT::STRING}}; + gnc_register_multichoice_option(m_db, "foo", "bar", "baz", + "Phony Option", "waldo", + std::move(choices)); + EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str()); + ASSERT_TRUE(m_db->set_option("foo", "bar", std::string{"corge"})); + EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str()); +} + +static time64 +time64_from_gdate(const GDate* g_date, DayPart when) +{ + GncDate date{g_date_get_year(g_date), g_date_get_month(g_date), + g_date_get_day(g_date)}; + GncDateTime time1{date, when}; + return static_cast(time1); +} + + +TEST_F(GncOptionDBTest, test_register_relative_date_option) +{ + gnc_register_date_option(m_db, "foo", "bar", "baz", "Phony Option", + RelativeDatePeriod::START_ACCOUNTING_PERIOD); + GDate prev_year_start; + g_date_set_time_t(&prev_year_start, time(nullptr)); + gnc_gdate_set_prev_year_start(&prev_year_start); + time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)}; + ASSERT_TRUE(m_db->set_option("foo", "bar", time1)); + EXPECT_EQ(time1, m_db->find_option("foo", "bar")->get_value()); +} + +TEST_F(GncOptionDBTest, test_register_absolute_date_option) +{ + time64 time1{static_cast(GncDateTime("2019-07-19 15:32:26 +05:00"))}; + + gnc_register_date_option(m_db, "foo", "bar", "baz", "Phony Option", + time1); + GDate prev_year_start; + g_date_set_time_t(&prev_year_start, time(nullptr)); + gnc_gdate_set_prev_year_start(&prev_year_start); + ASSERT_TRUE(m_db->set_option("foo", "bar", time1)); + EXPECT_EQ(time1, + m_db->find_option("foo", "bar")->get_value()); +} + +/* Copied from gnc-optiondb.cpp for the purpose of finding the index of the + * option in the following test. + */ +static const RelativeDatePeriodVec begin_dates +{ + RelativeDatePeriod::TODAY, + RelativeDatePeriod::START_THIS_MONTH, + RelativeDatePeriod::START_PREV_MONTH, + RelativeDatePeriod::START_CURRENT_QUARTER, + RelativeDatePeriod::START_PREV_QUARTER, + RelativeDatePeriod::START_CAL_YEAR, + RelativeDatePeriod::START_PREV_YEAR, + RelativeDatePeriod::START_ACCOUNTING_PERIOD +}; + +TEST_F(GncOptionDBTest, test_register_start_date_option) +{ + gnc_register_start_date_option(m_db, "foo", "bar", "baz", + "Phony Option"); + GDate prev_year_start; + g_date_set_time_t(&prev_year_start, time(nullptr)); + gnc_gdate_set_prev_year_start(&prev_year_start); + time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)}; + EXPECT_EQ(RelativeDatePeriod::START_ACCOUNTING_PERIOD, + m_db->find_option("foo", "bar")->get_value()); + ASSERT_TRUE(m_db->set_option("foo", "bar", time1)); + EXPECT_EQ(time1, + m_db->find_option("foo", "bar")->get_value()); + EXPECT_EQ(RelativeDatePeriod::ABSOLUTE, + m_db->find_option("foo", "bar")->get_value()); + m_db->set_option("foo", "bar", RelativeDatePeriod::START_THIS_MONTH); + EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, + m_db->find_option("foo", "bar")->get_value()); + const RelativeDatePeriod start_this_month {RelativeDatePeriod::START_THIS_MONTH}; + auto index = + std::find(begin_dates.begin(), begin_dates.end(), start_this_month); + /* If this fails check that the begin_dates vector above matches the one in + * gnc-optiondb.cpp. + */ + EXPECT_EQ(static_cast(std::distance(begin_dates.begin(), index)), + m_db->find_option("foo", "bar")->get_value()); + m_db->set_option("foo", "bar", RelativeDatePeriod::END_THIS_MONTH); + EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, + m_db->find_option("foo", "bar")->get_value()); + m_db->set_option("foo", "bar", static_cast(5)); + EXPECT_EQ(5u, m_db->find_option("foo", "bar")->get_value()); + +} + +static bool +operator==(const GncGUID& l, const GncGUID& r) +{ + return guid_equal(&l, &r); +} + +class GncOptionDBIOTest : public ::testing::Test +{ +protected: + GncOptionDBIOTest() : + m_book{gnc_get_current_book()}, m_root{gnc_account_create_root(m_book)}, + m_db{std::make_unique()} + { + auto create_account = [this](Account* parent, GNCAccountType type, + const char* name)->Account* { + auto account = xaccMallocAccount(this->m_book); + xaccAccountBeginEdit(account); + xaccAccountSetType(account, type); + xaccAccountSetName(account, name); + xaccAccountBeginEdit(parent); + gnc_account_append_child(parent, account); + xaccAccountCommitEdit(parent); + xaccAccountCommitEdit(account); + return account; + }; + auto assets = create_account(m_root, ACCT_TYPE_ASSET, "Assets"); + auto liabilities = create_account(m_root, ACCT_TYPE_LIABILITY, "Liabilities"); + auto expenses = create_account(m_root, ACCT_TYPE_EXPENSE, "Expenses"); + create_account(assets, ACCT_TYPE_BANK, "Bank"); + auto broker = create_account(assets, ACCT_TYPE_ASSET, "Broker"); + auto stocks = create_account(broker, ACCT_TYPE_STOCK, "Stocks"); + auto aapl = create_account(stocks, ACCT_TYPE_STOCK, "AAPL"); + create_account(stocks, ACCT_TYPE_STOCK, "MSFT"); + auto hpe = create_account(stocks, ACCT_TYPE_STOCK, "HPE"); + create_account(broker, ACCT_TYPE_BANK, "Cash Management"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Food"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Gas"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Rent"); + + gnc_register_string_option(m_db, "foo", "bar", "baz", + "Phony Option", std::string{"waldo"}); + gnc_register_text_option(m_db, "foo", "sausage", "links", + "Phony Option", std::string{"waldo"}); + gnc_register_string_option(m_db, "qux", "grault", "baz", + "Phony Option", std::string{""}); + gnc_register_text_option(m_db, "qux", "garply", "fred", + "Phony Option", std::string{"waldo"}); + gnc_register_date_option(m_db, "pork", "garply", "first", + "Phony Date Option", + RelativeDatePeriod::START_CURRENT_QUARTER); + gnc_register_account_list_option(m_db, "quux", "xyzzy", "second", + "Phony AccountList Option", + {*qof_entity_get_guid(aapl), + *qof_entity_get_guid(hpe)}); + } + + ~GncOptionDBIOTest() + { + xaccAccountBeginEdit(m_root); + xaccAccountDestroy(m_root); //It does the commit + gnc_clear_current_session(); + } + + QofBook* m_book; + Account* m_root; + GncOptionDBPtr m_db; +}; + +TEST_F(GncOptionDBIOTest, test_option_key_value_output) +{ + std::ostringstream oss; + m_db->save_option_key_value(oss, "foo", "sausage"); + EXPECT_STREQ("", oss.str().c_str()); + m_db->set_option("foo", "sausage", std::string{"pepper"}); +// EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); +// EXPECT_TRUE(m_db->find_option("foo", "sausage")->get().is_changed()); + m_db->save_option_key_value(oss, "foo", "sausage"); + EXPECT_STREQ("foo:sausage=pepper;", oss.str().c_str()); +} + +TEST_F(GncOptionDBIOTest, test_option_key_value_input) +{ + std::istringstream iss{"foo:sausage=pepper;"}; + EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "sausage").c_str()); + m_db->load_option_key_value(iss); + EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); +} + +TEST_F(GncOptionDBIOTest, test_option_kvp_save) +{ + m_db->save_to_kvp(m_book, false); + auto foo = "foo"; + auto bar = "bar"; + auto sausage = "sausage"; + auto grault = "grault"; + auto garply = "garply"; + GSList foo_bar_tail{(void*)bar, nullptr}; + GSList foo_bar_head{(void*)foo, &foo_bar_tail}; + GSList foo_sausage_tail{(void*)sausage, nullptr}; + GSList foo_sausage_head{(void*)foo, &foo_sausage_tail}; + GSList qux_grault_tail{(void*)grault, nullptr}; + GSList qux_grault_head{(void*)foo, &qux_grault_tail}; + GSList qux_garply_tail{(void*)garply, nullptr}; + GSList qux_garply_head{(void*)foo, &qux_grault_tail}; + m_db->set_option("foo", "sausage", std::string{"pepper"}); + m_db->save_to_kvp(m_book, true); + auto foo_bar = qof_book_get_option(m_book, &foo_bar_head); + auto foo_sausage = qof_book_get_option(m_book, &foo_sausage_head); + auto qux_garply = qof_book_get_option(m_book, &qux_garply_head); + auto qux_grault = qof_book_get_option(m_book, &qux_grault_head); + EXPECT_EQ(nullptr, foo_bar); + EXPECT_EQ(nullptr, qux_garply); + EXPECT_EQ(nullptr, qux_grault); + EXPECT_STREQ("pepper", foo_sausage->get()); +} diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm new file mode 100644 index 0000000000..8b070433fb --- /dev/null +++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm @@ -0,0 +1,591 @@ + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; test-gnc-option-scheme-output.scm -- Test Scheme option i/o. ; + ; Copyright (C) 2021 John Ralls ; + ; ; + ; This program 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 2 of ; + ; the License, or (at your option) any later version. ; + ; ; + ; This program 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, contact: ; + ; ; + ; Free Software Foundation Voice: +1-617-542-5942 ; + ; 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 ; + ; Boston, MA 02110-1301, USA gnu@gnu.org ; + ; ; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(use-modules (srfi srfi-64)) +(use-modules (tests srfi64-extras)) +(use-modules (gnucash app-utils options)) +(use-modules (sw_app_utils)) +(use-modules (sw_engine)) + +(define (run-test) + (test-runner-factory gnc:test-runner) + (test-begin "test-gnc-option-scheme-io") + (test-gnc-string-option-to-scheme) + (test-gnc-text-option-to-scheme) + (test-gnc-font-option-to-scheme) + (test-gnc-currency-option-to-scheme) + (test-gnc-budget-option-to-scheme) + (test-gnc-commodity-option-to-scheme) + (test-gnc-bool-option-to-scheme) + (test-gnc-pixmap-option-to-scheme) + (test-gnc-date-option-to-scheme) + (test-gnc-account-options-to-scheme) + (test-gnc-multichoice-option-to-scheme) + (test-gnc-list-option-to-scheme) + (test-gnc-number-range-option-to-scheme) + (test-gnc-number-plot-size-option-to-scheme) + (test-gnc-query-option-to-scheme) + (test-gnc-color-option-to-scheme) + (test-gnc-invoice-option-to-scheme) + (test-gnc-owner-option-to-scheme) + (test-gnc-internal-option-to-scheme) + (test-end "test-gnc-option-scheme-io")) + +(define test-unchanged-section-output-template + " +; Section: foo + +" + ) + +(define (test-string-output-template value) + (format #f " +; Section: foo + +(let ((option (gnc:lookup-option options + \"foo\" + \"bar\"))) + ((lambda (o) (if o (gnc:option-set-value o ~s))) option)) + +" value)) + +(define (test-literal-output-template value) + (format #f " +; Section: foo + +(let ((option (gnc:lookup-option options + \"foo\" + \"bar\"))) + ((lambda (o) (if o (gnc:option-set-value o '~a))) option)) + +" value)) + +(define (test-currency-output-template value) + (format #f " +; Section: foo + +(let ((option (gnc:lookup-option options + \"foo\" + \"bar\"))) + ((lambda (o) (if o (gnc:option-set-value o ~s))) option)) + +" value)) + +(define (test-commodity-output-template value) + (let ((value-parts (string-split value #\:))) + (format #f " +; Section: foo + +(let ((option (gnc:lookup-option options + \"foo\" + \"bar\"))) + ((lambda (o) (if o (gnc:option-set-value o ~s ~s))) option)) + +" (car value-parts) (cadr value-parts)))) + +(define (test-budget-output-template value) + (format #f " +; Section: foo + +(let ((option (gnc:lookup-option options + \"foo\" + \"bar\"))) + ((lambda (option) (if option ((gnc:option-setter option) (gnc-budget-lookup ~s (gnc-get-current-book))))) option)) + +" + (gncBudgetGetGUID value))) + + +(define (test-option-scheme-output name make-option-func get-value-func test-template default value) + (let ((odb (gnc:new-options)) + (option (make-option-func "foo" "bar" "baz" "Test Option" default))) + (gnc:register-option odb option) + (test-equal (string-append name " unchanged") + test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") value) + (test-equal (string-append name " value") + (test-template (get-value-func (gnc:lookup-option odb "foo" "bar"))) + (gnc:generate-restore-forms odb "options")))) + +(define (test-gnc-string-option-to-scheme) + (test-begin "test-gnc-string-option-to-scheme") + (test-option-scheme-output "string" + gnc:make-string-option GncOption-get-scm-value + test-string-output-template + "waldo" "pepper") + (test-end "test-gnc-string-option-to-scheme")) + +(define (test-gnc-text-option-to-scheme) + (test-begin "test-gnc-text-option-to-scheme") + (test-option-scheme-output "text" + gnc:make-string-option GncOption-get-scm-value + test-string-output-template + "" +"Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium +doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore +veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") + (test-end "test-gnc-text-option-to-scheme")) + +(define (test-gnc-font-option-to-scheme) + (test-begin "test-gnc-font-option-to-scheme") + (test-option-scheme-output "font" + gnc:make-font-option GncOption-get-scm-value + test-string-output-template + "URW Bookman L Bold Italic 12" + "Helvetica 12") + (test-end "test-gnc-font-option-to-scheme")) + +(define (test-gnc-currency-option-to-scheme) + (test-begin "test-gnc-currency-option-to-scheme") + (let ((session (gnc-get-current-session)) + (book (gnc-get-current-book)) + (table (gnc-commodity-table-new))) + (test-book-set-data book "gnc-commodity-table" table) + (let ((USD (gnc-commodity-new book "United States Dollar" "CURRENCY" "USD" "" 100)) + (EUR (gnc-commodity-new book "European Union Euro" "CURRENCY" "EUR" "" 100))) + (gnc-commodity-table-insert table USD) + (gnc-commodity-table-insert table EUR) + (test-option-scheme-output "currency" + gnc:make-currency-option GncOption-serialize + test-currency-output-template + USD EUR) +;; Garbage collection has already eaten USD and EUR. + (test-book-clear-data book "gnc-commodity-table") + (gnc-commodity-table-destroy table) + (gnc-clear-current-session))) + (test-end "test-gnc-currency-option-to-scheme")) + +(define (test-gnc-budget-option-to-scheme) + (test-begin "test-gnc-budget-option-to-scheme") + (let* ((book (gnc-get-current-book)) + (budget2 (gnc-budget-new book)) + (budget1 (gnc-budget-new book)) + (guid1 (gncBudgetGetGUID budget1)) + (guid2 (gncBudgetGetGUID budget2))) + + (test-book-set-default-budget book budget1) + (gnc-budget-set-name budget1 "First Budget") + (gnc-budget-set-name budget2 "Second Budget") + + (let ((odb (gnc:new-options)) + (option (gnc:make-budget-option "foo" "bar" "baz" "Test Option"))) + (gnc:register-option odb option) + (test-equal "budget unchanged" + test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") budget2) + (test-equal "default budget value" (gnc-budget-get-default book) budget1) + (test-equal "budget restore form" (test-budget-output-template budget2) + (gnc:generate-restore-forms odb "options"))) + (gnc-clear-current-session)) + (test-end "test-gnc-budget-option-to-scheme")) + +(define (test-gnc-commodity-option-to-scheme) + (test-begin "test-gnc-commodity-option-to-scheme") + (let* ((session (gnc-get-current-session)) + (book (gnc-get-current-book)) + (comm-tbl (gnc-commodity-table-get-table book)) + (AAPL (gnc-commodity-new book "Apple" "NASDAQ" "AAPL" "" 1)) + (FMAGX (gnc-commodity-new book "Fidelity Magellan Fund" "FUND" "FMAGX" "" 1000))) + (gnc-commodity-table-insert comm-tbl AAPL) + (gnc-commodity-table-insert comm-tbl FMAGX) + (test-option-scheme-output "commodity" + gnc:make-commodity-option GncOption-serialize + test-commodity-output-template + AAPL FMAGX)) + (test-end "test-gnc-commodity-option-to-scheme")) + +(define (test-gnc-bool-option-to-scheme) + (test-begin "test-gnc-bool-option-to-scheme") + (test-option-scheme-output "bool" + gnc:make-simple-boolean-option + GncOption-get-scm-value + test-string-output-template #f #t) + (test-end "test-gnc-bool-option-to-scheme")) + +(define (test-gnc-pixmap-option-to-scheme) + (test-begin "test-gnc-pixmap-option-to-scheme") + (test-option-scheme-output "pixmap" + gnc:make-pixmap-option GncOption-get-scm-value + test-string-output-template + "" "~/mybusiness/mylogo.png") + (test-end "test-gnc-pixmap-option-to-scheme")) + +(define (test-gnc-date-option-to-scheme) + (test-begin "test-gnc-date-option-to-scheme") + (let ((odb (gnc:new-options))) + (gnc:options-make-end-date! odb "foo" "bar" "baz" "Phoney Option") + (test-equal "Date Unchanged" test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let* ((option (gnc:lookup-option odb "foo" "bar")) + (test-template test-literal-output-template) + (time (gnc-dmy2time64 25 12 2020)) + (value `(absolute . ,time))) + (gnc:option-set-value option value) + (test-equal "Absolute Date" (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")) + (set! value '(relative . end-prev-year)) + (gnc:option-set-value option value) + (test-equal "Relative Date Value" value (GncOption-get-scm-value option)) + (test-equal "Relative Date" (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")))) + (test-end "test-gnc-date-option-to-scheme")) + +(define (test-gnc-account-options-to-scheme) + (define (create-account book parent type name) + (let ((account (xaccMallocAccount book))) + (xaccAccountBeginEdit account) + (xaccAccountSetType account type) + (xaccAccountSetName account name) + (xaccAccountBeginEdit parent) + (gnc-account-append-child parent account) + (xaccAccountCommitEdit parent) + (xaccAccountCommitEdit account) + account)) + + (define (make-account-tree book root) + (let* ((assets (create-account book root ACCT-TYPE-ASSET "Assets")) + (liabilities (create-account book root ACCT-TYPE-LIABILITY "Liabilities")) + (equity (create-account book root ACCT-TYPE-EQUITY "Equity")) + (expenses (create-account book root ACCT-TYPE-EXPENSE "Expenses")) + (equity (create-account book root ACCT-TYPE-INCOME "Income")) + (broker (create-account book assets ACCT-TYPE-EQUITY "broker")) + (stocks (create-account book broker ACCT-TYPE-STOCK "Stocks"))) + (create-account book assets ACCT-TYPE-BANK "Bank") + (create-account book stocks ACCT-TYPE-STOCK "AAPL") + (create-account book stocks ACCT-TYPE-STOCK "MSFT") + (create-account book stocks ACCT-TYPE-STOCK "HPE") + (create-account book broker ACCT-TYPE-BANK "Cash Management") + (create-account book expenses ACCT-TYPE-EXPENSE "Food") + (create-account book expenses ACCT-TYPE-EXPENSE "Gas") + (create-account book expenses ACCT-TYPE-EXPENSE "Rent"))) + + (define (cleanup book root) +;; Destroying the book destroys the account tree too + (gnc-option-test-book-destroy book)) + + (define (test-gnc-account-list-option-to-scheme book) + (define (test-account-list-output-template value) + (format #f " +; Section: foo + +(let ((option (gnc:lookup-option options + \"foo\" + \"bar\"))) + ((lambda (o) (if o (gnc:option-set-value o '~s))) option)) + +" (reverse (string-split value #\ )))) + + (test-begin "test-gnc-account-list-option-to-scheme") + (let ((odb (gnc:new-options)) + (acctlist (gnc-account-list-from-types book + (list ACCT-TYPE-STOCK)))) + (gnc:register-option odb + (gnc:make-account-list-option + "foo" "bar" "a" "baz" (lambda () acctlist) + (lambda (ac) + (let ((type (xaccAccountGetAccountType ac))) + (or (eq type ACCT-TYPE-STOCK) + (eq type ACCT-TYPE-BANK)))) #t)) + (test-equal "account list unchanged" + test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let ((option (gnc:lookup-option odb "foo" "bar")) + (test-template test-account-list-output-template) + (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK)))) + (gnc:option-set-value option new-acclist) + (test-equal "account list form" ;;fails + (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-account-list-option-to-scheme")) + + (define (test-gnc-account-sel-option-to-scheme book) + (test-begin "test-gnc-account-sel-option-to-scheme") + (let ((odb (gnc:new-options)) + (bank (gnc-account-lookup-by-name(gnc-book-get-root-account book) + "Bank"))) + (gnc:register-option odb + (gnc:make-account-sel-option + "foo" "bar" "a" "baz" (lambda () '()) + (lambda (ac) + (let ((type (xaccAccountGetAccountType ac))) + (or (eq type ACCT-TYPE-STOCK) + (eq type ACCT-TYPE-BANK)))))) + (test-equal "account sel unchanged" test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let ((option (gnc:lookup-option odb "foo" "bar")) + (test-template test-string-output-template)) + (gnc:option-set-value option bank) + (test-equal "account sel form" ;; fails + (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-account-sel-option-to-scheme")) + + (let* ((book (gnc-option-test-book-new)) + (root-account (gnc-account-create-root book))) + (test-group-with-cleanup "test-gnc-account-options-to-schemes" + (make-account-tree book root-account) + (test-gnc-account-list-option-to-scheme book) + (test-gnc-account-sel-option-to-scheme book) + (cleanup book root-account)))) + + +(define (test-gnc-multichoice-option-to-scheme) + (test-begin "test-gnc-multichoice-option-to-scheme") + (let ((odb (gnc:new-options)) + (test-template test-literal-output-template) + (value "5")) + (gnc:register-option + odb + (gnc:make-multichoice-option + "foo" "bar" "baz" "Phoney Option" 3 + (list (vector 'all "All") + (vector 1 "1") (vector 2 "2") (vector 3 "3") + (vector 4 "4") (vector 5 "5") (vector 6 "6")))) + (test-equal "multichoice unchanged" test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let ((option (gnc:lookup-option odb "foo" "bar"))) + (gnc:option-set-value option value) + (test-equal "multichoice form" (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")))) + (test-end "test-gnc-multichoice-option-to-scheme")) + +(define (test-gnc-list-option-to-scheme) + (test-begin "test-gnc-list-option-to-scheme") + (let ((odb (gnc:new-options)) + (choices (list (vector 'good "The Good") + (vector 'bad "The Bad") + (vector 'ugly "The Ugly")))) + (gnc:register-option odb + (gnc:make-list-option + "foo" "bar" "a" "baz" '(bad) choices)) + (test-equal "list unchanged" test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let ((option (gnc:lookup-option odb "foo" "bar")) + (test-template test-literal-output-template)) + (gnc:option-set-value option '(ugly)) + (test-equal "list form" (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-list-option-to-scheme")) + +(define (test-gnc-number-range-option-to-scheme) + (test-begin "test-gnc-number-range-option-to-scheme") + (let ((odb (gnc:new-options)) + (min-value 0.0) + (max-value 100.0) + (dec-places 2.0) + (step 0.10)) + (gnc:register-option odb + (gnc:make-number-range-option + "foo" "bar" "a" "baz" 49.0 min-value + max-value dec-places step)) + (test-equal "number-range unchanged" test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let ((option (gnc:lookup-option odb "foo" "bar")) + (test-template test-literal-output-template)) + (gnc:option-set-value option 42.0) + (test-equal "number-range form" + (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-number-range-option-to-scheme")) + +(define (test-gnc-number-plot-size-option-to-scheme) + (test-begin "test-gnc-number-plot-size-option-to-scheme") + (let ((odb (gnc:new-options)) + (min-value 10) + (max-value 100) + (dec-places 0) + (step 5)) + (gnc:register-option odb + (gnc:make-number-plot-size-option + "foo" "bar" "a" "baz" 49 min-value + max-value dec-places step)) + (test-equal "number-plot unchanged" test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let ((option (gnc:lookup-option odb "foo" "bar")) + (test-template test-literal-output-template)) + (gnc:option-set-value option 42) + (test-equal "number-plot form" + (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-number-plot-size-option-to-scheme")) + +(define (test-gnc-query-option-to-scheme) + (define query-unchanged-section-output-template + " +; Section: __reg + +" + ) + + (define (query-literal-output-template value) + (format #f " +; Section: __reg + +(let ((option (gnc:lookup-option options + \"__reg\" + \"query\"))) + ((lambda (o) (if o (gnc:option-set-value o '~a))) option)) + +" value)) + + (test-begin "test-gnc-query-option-to-scheme") + (let ((odb (gnc:new-options)) + (query-scm '(query-v2 + (terms (((("book" "guid") #f guid 3 1 ("3a5a4bc736d84b879b776ea8caadd3b2")) + (("account" "guid") #f guid 3 1 ("b7e4ca23652049fca62a0e4f95296a15"))))) + (search-for Split) + (primary-sort (("QofQueryDefaultSort") 0 #t)) + (secondary-sort #f) + (tertiary-sort #f) + (max-results -1)))) + (gnc:register-option odb + (gnc:make-query-option "__reg" "query" '())) + (test-equal "query unchanged" query-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let ((option (gnc:lookup-option odb "__reg" "query")) + (test-template query-literal-output-template)) + (gnc:option-set-value option (gnc-scm2query query-scm)) + (test-equal "query form" (test-template (GncOption-get-scm-value option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-query-option-to-scheme")) + +(define (test-gnc-color-option-to-scheme) + (define (test-color-output-template value) + (let* ((len (string-length value)) + (red (string->number (substring/shared value 0 2) 16)) + (blue (string->number (substring/shared value 2 4) 16)) + (green (string->number (substring/shared value 4 6) 16)) + (alpha (if (> len 7) + (string->number (substring/shared value 6 8) 16) + #xff))) + (format #f " +; Section: foo + +(let ((option (gnc:lookup-option options + \"foo\" + \"bar\"))) + ((lambda (o) (if o (gnc:option-set-value o '(~f ~f ~f ~f)))) option)) + +" red blue green alpha))) + (test-begin "test-gnc-coloroption-to-scheme") + (let ((odb (gnc:new-options)) + (default-color (list #xb2 #x22 #x22 #xff)) + (new-color (list #x00 #xca #x3b #xff))) + (gnc:register-option odb + (gnc:make-color-option + "foo" "bar" "a" "baz" default-color #f #t)) + (test-equal "color unchanged" test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let ((option (gnc:lookup-option odb "foo" "bar")) + (test-template test-color-output-template)) + (gnc:option-set-value option new-color) + (test-equal "color form" + (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-color-option-to-scheme")) + +(define (test-gnc-invoice-option-to-scheme) + (test-begin "test-gnc-invoice-option-to-scheme") + (let ((odb (gnc:new-options))) + (gnc:register-option odb + (gnc:make-invoice-option "foo" "bar" "a" "baz" + (lambda () '()) (lambda () #t))) + (test-equal "invoice unchanged" test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let* ((book (gnc-get-current-book)) + (inv (gncInvoiceCreate book)) + (option (gnc:lookup-option odb "foo" "bar")) + (test-template test-string-output-template)) + (gnc:option-set-value option inv) + (test-equal "invoice form" (test-template (GncOption-serialize option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-invoice-option-to-scheme")) + +(define (test-gnc-owner-option-to-scheme) + (test-begin "test-owner-option-to-scheme") + (let ((odb (gnc:new-options))) + (gnc:register-option odb + (gnc:make-owner-option "foo" "bar" "a" "baz" + (lambda () '()) (lambda () #t) + GNC-OWNER-CUSTOMER)) + (test-equal "owner unchanged" test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let* ((option (gnc:lookup-option odb "foo" "bar")) + (test-template test-literal-output-template) + (book (gnc-get-current-book)) + (owner (gncOwnerNew))) + (gncOwnerInitCustomer owner (gncCustomerCreate book)) + (gnc:option-set-value option owner) + (test-equal "owner form" + (test-template (cons (gncOwnerGetType owner) + (gncOwnerReturnGUID owner))) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-owner-option-to-scheme")) + +(define (test-gnc-internal-option-to-scheme) + (define (test-output-template name value) + (format #f " +(let ((option (gnc:lookup-option options + \"__reg\" + ~s))) + ((lambda (o) (if o (gnc:option-set-value o ~s))) option)) +" name value)) + (test-begin "test-gnc-internal-option-to-scheme") + (let ((odb (gnc:new-options)) + (option-b (gnc:make-internal-option "__reg" "bar" #f)) + (option-s (gnc:make-internal-option "__reg" "baz" "waldo"))) + (gnc:register-option odb option-b) + (gnc:register-option odb option-s) + (test-equal "Internal unchanged" " +; Section: __reg + +" + (gnc:generate-restore-forms odb "options")) + (gnc:option-set-value (gnc:lookup-option odb "__reg" "bar") #t) + (gnc:option-set-value (gnc:lookup-option odb "__reg" "baz") "pepper") + (test-equal "internal form" (format #f " +; Section: __reg +~a~a +" + (test-output-template "bar" #t) + (test-output-template "baz" "pepper")) + (gnc:generate-restore-forms odb "options")) + ) + (test-end "test-gnc-internal-option-to-scheme")) + +;; The following are saved only to KVP, no Scheme generator needed: +;;(define (test-gnc-dateformat-option-to-scheme) +;;(define (test-gnc-taxtable-option-to-scheme) +;;(define (test-gnc-counter-option-to-scheme) +;;(define (test-gnc-counter-format-option-to-scheme) diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm new file mode 100644 index 0000000000..7174a4f537 --- /dev/null +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -0,0 +1,249 @@ + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; test-gnc-option.scm -- unit tests for GncOption class. ; + ; Copyright (C) 2019 John Ralls ; + ; ; + ; This program 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 2 of ; + ; the License, or (at your option) any later version. ; + ; ; + ; This program 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, contact: ; + ; ; + ; Free Software Foundation Voice: +1-617-542-5942 ; + ; 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 ; + ; Boston, MA 02110-1301, USA gnu@gnu.org ; + ; ; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(use-modules (srfi srfi-64)) +(use-modules (tests srfi64-extras)) + +;; This is a special case where we can't use the exported registration function +;; because we need to transform the default argument first depending on its +;; Scheme type. +(define (gnc:register-multichoice-option options section name key docstring default multichoice) + (issue-deprecation-warning "gnc:make-multichoice-option is deprecated. Make and register the option in one command with gnc-register-multichoice-option.") + (let ((defval (cond ((symbol? default) + (symbol->string default)) + ((number? default) + (number->string default)) + (else default)))) + (gnc-register-multichoice-option options section name key docstring defval multichoice))) + +;; Load the C++ option implementation, avoiding the options.scm ones. +(eval-when + (compile load eval expand) + (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module")) + +(use-modules (gnucash engine)) +(use-modules (sw_app_utils)) + +(define (run-test) + (test-runner-factory gnc:test-runner) + (test-begin "test-gnc-optiondb-scheme") + (test-gnc-make-text-option) + (test-gnc-make-account-list-options) + (test-gnc-make-multichoice-option) + (test-gnc-make-list-option) + (test-gnc-make-date-option) + (test-gnc-make-date-set-option) + (test-gnc-make-number-range-option) + (test-gnc-make-report-placement-option) + (test-end "test-gnc-optiondb-scheme")) + +(define (test-gnc-make-text-option) + (test-begin "test-gnc-test-string-option") + (let* ((option-db (new-gnc-optiondb)) + (string-opt (gnc-register-string-option option-db "foo" "bar" "baz" + "Phony Option" "waldo"))) + (test-equal "waldo" (gnc-option-value option-db "foo" "bar")) + + (gnc-set-option option-db "foo" "bar" "pepper") + (test-equal "pepper" (gnc-option-value option-db "foo" "bar"))) + (test-end "test-gnc-make-string-option")) + +(define (test-gnc-make-account-list-options) + (define (create-account book parent type name) + (let ((account (xaccMallocAccount book))) + (xaccAccountBeginEdit account) + (xaccAccountSetType account type) + (xaccAccountSetName account name) + (xaccAccountBeginEdit parent) + (gnc-account-append-child parent account) + (xaccAccountCommitEdit parent) + (xaccAccountCommitEdit account) + account)) + + (define (make-account-tree book root) + (let* ((assets (create-account book root ACCT-TYPE-ASSET "Assets")) + (liabilities (create-account book root ACCT-TYPE-LIABILITY "Liabilities")) + (equity (create-account book root ACCT-TYPE-EQUITY "Equity")) + (expenses (create-account book root ACCT-TYPE-EXPENSE "Expenses")) + (equity (create-account book root ACCT-TYPE-INCOME "Income")) + (broker (create-account book assets ACCT-TYPE-EQUITY "broker")) + (stocks (create-account book broker ACCT-TYPE-STOCK "Stocks"))) + (create-account book assets ACCT-TYPE-BANK "Bank") + (create-account book stocks ACCT-TYPE-STOCK "AAPL") + (create-account book stocks ACCT-TYPE-STOCK "MSFT") + (create-account book stocks ACCT-TYPE-STOCK "HPE") + (create-account book broker ACCT-TYPE-BANK "Cash Management") + (create-account book expenses ACCT-TYPE-EXPENSE "Food") + (create-account book expenses ACCT-TYPE-EXPENSE "Gas") + (create-account book expenses ACCT-TYPE-EXPENSE "Rent"))) + + (define (cleanup book root) +;; Destroying the book destroys the account tree too + (gnc-option-test-book-destroy book)) + + (define (test-make-account-list-option book) + (test-group "test-make-account-list-option" + (let ((option-db (new-gnc-optiondb)) + (acctlist (gnc-account-list-from-types book + (list ACCT-TYPE-STOCK)))) + (gnc-register-account-list-option option-db "foo" "bar" "baz" + "Phony Option" acctlist) + (let ((acct-list (gnc-option-value option-db "foo" "bar"))) + (test-equal (length acctlist) (length acct-list)) + (test-equal (car acctlist) (car acct-list))) ))) + + (define (test-make-account-list-limited-option book) + (test-group "test-make-account-list-limited-option" + (let ((option-db (new-gnc-optiondb)) + (acctlist (gnc-account-list-from-types book + (list ACCT-TYPE-STOCK)))) + (gnc-register-account-list-limited-option ;; Error not account type twice + option-db "foo" "bar" "baz" + "Phony Option" acctlist (list ACCT-TYPE-STOCK)) + (let ((acct-list (gnc-option-value option-db "foo" "bar"))) + (test-equal (length acctlist) (length acct-list)) ;; fails acct-list 4 vs #f + (test-equal (cadr acctlist) (cadr acct-list))) ;; fails () vs. #f both wrong + (gnc-register-account-list-limited-option + option-db "waldo" "pepper" "baz" + "Phony Option" acctlist (list ACCT-TYPE-BANK)) + (let ((acct-list (gnc-option-value option-db "waldo" "pepper"))) + (test-equal #f (length acct-list)))))) + + (define (test-make-account-sel-limited-option book) + (test-group "test-make-account-list-option" + (let ((option-db (new-gnc-optiondb)) + (acctlist (gnc-account-list-from-types book + (list ACCT-TYPE-STOCK)))) + (gnc-register-account-sel-limited-option + option-db "salt" "pork" "baz" + "Phony Option" (cadr acctlist) (list ACCT-TYPE-STOCK)) + (let ((acct (gnc-option-value option-db "salt" "pork"))) + (test-equal (cadr acctlist) acct))))) + + (let* ((book (gnc-option-test-book-new)) + (root-account (gnc-account-create-root book))) + (test-group-with-cleanup "test-gnc-make-account-list-options" + (make-account-tree book root-account) + (test-make-account-list-option book) + (test-make-account-list-limited-option book) + (test-make-account-sel-limited-option book) + (cleanup book root-account)))) + + +(define (test-gnc-make-multichoice-option) + + (define (keylist->vectorlist keylist) + (map + (lambda (item) + (vector + (car item) + (keylist-get-info keylist (car item) 'text) + (keylist-get-info keylist (car item) 'tip))) + keylist)) + + (define (keylist-get-info keylist key info) + (assq-ref (assq-ref keylist key) info)) + + (test-begin "test-gnc-test-multichoice-option") + (let* ((option-db (new-gnc-optiondb)) + (multilist (list + (list "plugh" (cons 'text "xyzzy") (cons 'tip "thud")) + (list 'waldo (cons 'text "pepper") (cons 'tip "salt")) + (list "pork" (cons 'text "sausage") (cons 'tip "links")) + (list "corge" (cons 'text "grault") (cons 'tip "garply")))) + (multichoice (keylist->vectorlist multilist)) + (multi-opt (gnc:register-multichoice-option option-db "foo" "bar" "baz" + "Phony Option" 'waldo multichoice))) + + (test-equal 'waldo (gnc-option-value option-db "foo" "bar")) + (gnc-set-option option-db "foo" "bar" "corge") + (test-equal "corge" (gnc-option-value option-db "foo" "bar")) + (test-equal 'waldo (gnc-option-default-value option-db "foo" "bar"))) + (test-end "test-gnc-test-multichoice-option")) + +(define (test-gnc-make-list-option) + (test-begin "test-gnc-test-list-option") + (let* ((option-db (new-gnc-optiondb)) + (value-list (list (vector "AvgBalPlot" "Average" "Average Balance") + (vector "GainPlot" "Profit" "Profit (Gain minus Loss)") + (vector "GLPlot" "Gain/Loss" "Gain and Loss"))) + (list-op (gnc-register-list-option option-db "foo" "bar" "baz" + "Phony Option" "AvgBalPlot" + value-list))) + (test-equal '("AvgBalPlot") (gnc-option-value option-db "foo" "bar")) + (gnc-set-option option-db "foo" "bar" '("GainPlot" "GLPlot")) + (test-equal '("GainPlot" "GLPlot") (gnc-option-value option-db "foo" "bar")) + (test-equal '("AvgBalPlot") (gnc-option-default-value option-db "foo" "bar"))) + (test-end "test-gnc-test-list-option")) + +(define (test-gnc-make-date-option) + (test-begin "test-gnc-test-date-option") + (let* ((option-db (new-gnc-optiondb)) + (date-opt (gnc-register-date-option option-db "foo" "bar" + "baz" "Phony Option" + (RelativeDatePeriod-TODAY))) + (a-time (gnc-dmy2time64 11 07 2019))) + (test-equal '(relative . today) (gnc-option-value option-db "foo" "bar")) + (gnc-set-option option-db "foo" "bar" a-time) + (test-equal `(absolute . ,a-time) (gnc-option-value option-db "foo" "bar"))) + (test-end "test-gnc-test-date-option")) + +(define (test-gnc-make-date-set-option) + (test-begin "test-gnc-test-date-set-option") + (let* ((option-db (new-gnc-optiondb)) + (date-opt (gnc-register-date-option-set + option-db "foo" "bar" "baz" "Phony Option" + '(today + start-this-month + start-prev-month + start-current-quarter + start-prev-quarter + start-cal-year + start-cal-year + start-prev-year + start-accounting-period) #t))) + (test-equal '(relative . start-accounting-period) + (gnc-option-value option-db "foo" "bar"))) + (test-end "test-gnc-test-date-set-option")) + +(define (test-gnc-make-number-range-option) + (test-begin "test-gnc-number-range-option") + (let* ((option-db (new-gnc-optiondb)) + (number-opt (gnc-register-number-range-option-double option-db "foo" "bar" + "baz" "Phony Option" + 15 5 30 1))) + (test-equal 15.0 (gnc-option-value option-db "foo" "bar")) + (gnc-set-option option-db "foo" "bar" 20) + (test-equal 20.0 (gnc-option-value option-db "foo" "bar"))) + (test-end "test-gnc-number-range-option")) + +(define (test-gnc-make-report-placement-option) + (test-begin "test-gnc-report-placement-option") + (let* ((report1 123) + (report2 456) + (rp (list (list report1 2 3) (list report2 3 2))) + (option-db (new-gnc-optiondb))) + (gnc-register-report-placement-option option-db "foo" "bar") + (gnc-set-option option-db "foo" "bar" rp) + (test-equal report2 (car (cadr (gnc-option-value option-db "foo" "bar"))))) + (test-end "test-gnc-report-placement-option")) \ No newline at end of file diff --git a/libgnucash/app-utils/test/test-gnc-ui-util.c b/libgnucash/app-utils/test/test-gnc-ui-util.c deleted file mode 100644 index 2be3250a59..0000000000 --- a/libgnucash/app-utils/test/test-gnc-ui-util.c +++ /dev/null @@ -1,262 +0,0 @@ -/******************************************************************** - * test-gnc-ui-util.c: GLib g_test test suite for gnc-ui-util.c. * - * Copyright 2015 Alex Aycinena * - * * - * This program 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 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, you can retrieve it from * - * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html * - * or contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * - ********************************************************************/ - -#include -#include -#include -#include -#include "test-engine-stuff.h" - -#include "../gnc-ui-util.h" - -static const gchar *suitename = "/app-utils/gnc-ui-util"; -void test_suite_gnc_ui_util (void); - -typedef struct -{ - QofBook *book; - GSList *hdlrs; -} Fixture; - -/* Expose a mostly-private QofInstance function to load options into - * the Book. - */ -/*extern KvpFrame *qof_instance_get_slots (const QofInstance*); */ - -static void -setup (Fixture *fixture, gconstpointer pData) -{ - fixture->book = qof_book_new (); - fixture->hdlrs = NULL; -} - -static void -teardown (Fixture *fixture, gconstpointer pData) -{ - qof_book_destroy (fixture->book); - g_slist_free_full (fixture->hdlrs, test_free_log_handler); - test_clear_error_list(); -} - -static void -test_book_use_book_currency( Fixture *fixture, gconstpointer pData ) -{ - const gchar *cur; - const gchar *pol; - Account *acct, *acc; - - g_test_message( "Testing with no currency accounting method selected" ); - cur = gnc_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = gnc_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct = gnc_book_get_default_gain_loss_acct ( fixture-> book ); - g_assert (acct == NULL ); - g_assert( !gnc_book_use_book_currency ( fixture-> book )); - - g_test_message( "Testing with trading accounts set to true - t" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "trading-accts", "t", - NULL); - cur = gnc_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = gnc_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct = gnc_book_get_default_gain_loss_acct ( fixture-> book ); - g_assert (acct == NULL ); - g_assert( !gnc_book_use_book_currency ( fixture-> book )); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with valid book-currency but default-gains-policy set to nonsense and default-gain-loss-account-guid set to random valid acct" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "USD", - NULL); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gains-policy", "random", - NULL); - acc = get_random_account( fixture-> book ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)), - NULL); - cur = gnc_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = gnc_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct = gnc_book_get_default_gain_loss_acct ( fixture-> book ); - g_assert (acct == NULL ); - g_assert( !gnc_book_use_book_currency ( fixture-> book )); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - -/* g_test_message( "Testing with valid default-gains-policy but book-currency set to nonsense and default-gain-loss-account-guid set to random valid acct" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "myMoney", - NULL); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gains-policy", "fifo", - NULL); - acc = get_random_account( fixture-> book ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)), - NULL); - cur = gnc_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = gnc_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct = gnc_book_get_default_gain_loss_acct ( fixture-> book ); - g_assert (acct == NULL ); - g_assert( !gnc_book_use_book_currency ( fixture-> book )); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with book-currency and default-gains-policy set to nonsense and default-gain-loss-account-guid set to random valid acct" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "myMoney", - NULL); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gains-policy", "random", - NULL); - acc = get_random_account( fixture-> book ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)), - NULL); - cur = gnc_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = gnc_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct = gnc_book_get_default_gain_loss_acct ( fixture-> book ); - g_assert (acct == NULL ); - g_assert( !gnc_book_use_book_currency ( fixture-> book )); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with book-currency set and no default-gains-policy and default-gain-loss-account-guid set to random valid acct" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "USD", - NULL); - acc = get_random_account( fixture-> book ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)), - NULL); - cur = gnc_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = gnc_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct = gnc_book_get_default_gain_loss_acct ( fixture-> book ); - g_assert (acct == NULL ); - g_assert( !gnc_book_use_book_currency ( fixture-> book )); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with default-gains-policy set and no book-currency and default-gain-loss-account-guid set to random valid acct" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gains-policy", "fifo", - NULL); - acc = get_random_account( fixture-> book ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)), - NULL); - cur = gnc_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = gnc_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct = gnc_book_get_default_gain_loss_acct ( fixture-> book ); - g_assert (acct == NULL ); - g_assert( !gnc_book_use_book_currency ( fixture-> book )); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with book-currency, default-gains-policy and default-gain-loss-account-guid set to valid values and with trading accounts set to true - t" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "trading-accts", "t", - NULL); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "USD", - NULL); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gains-policy", "fifo", - NULL); - acc = get_random_account( fixture-> book ); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)), - NULL); - cur = gnc_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = gnc_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct = gnc_book_get_default_gain_loss_acct ( fixture-> book ); - g_assert (acct == NULL ); - g_assert( !gnc_book_use_book_currency ( fixture-> book )); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with book-currency, default-gains-policy and default-gain-loss-account-guid set to valid values and no trading accounts flag" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "USD", - NULL); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gains-policy", "fifo", - NULL); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gain-loss-account-guid", qof_entity_get_guid(QOF_INSTANCE(acc)), - NULL); - cur = gnc_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , "USD" ); - pol = gnc_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , "fifo" ); - acct = gnc_book_get_default_gain_loss_acct ( fixture-> book ); - g_assert ( xaccAccountEqual(acct, acc, TRUE) ); - g_assert( gnc_book_use_book_currency ( fixture-> book )); - qof_book_commit_edit (fixture->book); */ -} - -void -test_suite_gnc_ui_util (void) -{ - GNC_TEST_ADD( suitename, "use book-currency", Fixture, NULL, setup, test_book_use_book_currency, teardown ); - -} diff --git a/libgnucash/app-utils/test/test-option-util.cpp b/libgnucash/app-utils/test/test-option-util.cpp deleted file mode 100644 index c4b1c0c40b..0000000000 --- a/libgnucash/app-utils/test/test-option-util.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************************** - * test-option-util.cpp: GLib test suite for option-util.c. * - * Copyright 2013 John Ralls * - * * - * This program 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 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, you can retrieve it from * - * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html * - * or contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * - ********************************************************************/ -#include -#include -#include -#include - -extern "C" -{ -#include -#include -#include "test-engine-stuff.h" -#include "../option-util.h" -} - -static const gchar *suitename = "/app-utils/option-util"; -extern "C" void test_suite_option_util (void); - -typedef struct -{ - QofBook *book; - GSList *hdlrs; -} Fixture; - -/* Expose a mostly-private QofInstance function to load options into - * the Book. - */ -extern "C" KvpFrame *qof_instance_get_slots (const QofInstance*); - -static void -setup (Fixture *fixture, gconstpointer pData) -{ - fixture->book = qof_book_new (); - fixture->hdlrs = NULL; -} - -static void -setup_kvp (Fixture *fixture, gconstpointer pData) -{ - QofBook *book; - KvpFrame *slots; - setup (fixture, pData); - book = fixture->book; - slots = qof_instance_get_slots (QOF_INSTANCE (book)); - qof_begin_edit (QOF_INSTANCE (book)); - qof_instance_set (QOF_INSTANCE (book), - "trading-accts", "t", - "split-action-num-field", "t", - "autoreadonly-days", (double)21, - NULL); - - slots->set_path({"options", "Business", "Company Name"}, - new KvpValue(g_strdup ("Bogus Company"))); - qof_commit_edit (QOF_INSTANCE (book)); -} - -static void -teardown (Fixture *fixture, gconstpointer pData) -{ - qof_book_destroy (fixture->book); - g_slist_free_full (fixture->hdlrs, test_free_log_handler); - test_clear_error_list(); -} - -static void -test_option_load (Fixture *fixture, gconstpointer pData) -{ - gchar *str = NULL; - SCM symbol_value; - QofBook *book = fixture->book; - GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK); - - qof_book_load_options (book, gnc_option_db_load, odb); - g_assert (gnc_option_db_lookup_boolean_option (odb, OPTION_SECTION_ACCOUNTS, OPTION_NAME_TRADING_ACCOUNTS, FALSE)); - g_assert (gnc_option_db_lookup_boolean_option (odb, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_NUM_FIELD_SOURCE, FALSE)); - g_assert_cmpstr (gnc_option_db_lookup_string_option (odb, "Business", "Company Name", FALSE), ==, "Bogus Company"); - g_assert_cmpfloat (gnc_option_db_lookup_number_option (odb, OPTION_SECTION_ACCOUNTS, OPTION_NAME_AUTO_READONLY_DAYS, FALSE), ==, 21); - - gnc_option_db_destroy (odb); -} - -static void -test_option_save (Fixture *fixture, gconstpointer pData) -{ - QofBook *book = fixture->book; - GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK); - KvpFrame *slots = qof_instance_get_slots (QOF_INSTANCE (book)); - - g_assert (gnc_option_db_set_boolean_option (odb, OPTION_SECTION_ACCOUNTS, - OPTION_NAME_TRADING_ACCOUNTS, - TRUE)); - g_assert (gnc_option_db_set_boolean_option (odb, OPTION_SECTION_ACCOUNTS, - OPTION_NAME_NUM_FIELD_SOURCE, - TRUE)); - g_assert (gnc_option_db_set_string_option (odb, "Business", "Company Name", - "Bogus Company")); - g_assert (gnc_option_db_set_number_option (odb, OPTION_SECTION_ACCOUNTS, - OPTION_NAME_AUTO_READONLY_DAYS, - 17)); - qof_book_save_options (book, gnc_option_db_save, odb, TRUE); - g_assert_cmpstr (slots->get_slot({"options", "Accounts", "Use Trading Accounts"})->get(), == , "t"); - g_assert_cmpstr (slots->get_slot({"options", "Accounts", "Use Split Action Field for Number"})->get(), == , "t"); - g_assert_cmpstr (slots->get_slot({"options", "Business", "Company Name"})->get(), ==, "Bogus Company"); - g_assert_cmpfloat (slots->get_slot({"options", "Accounts", "Day Threshold for Read-Only Transactions (red line)"})->get(), ==, 17); - - gnc_option_db_destroy (odb); -} - -extern "C" void -test_suite_option_util (void) -{ - GNC_TEST_ADD (suitename, "Option DB Load", Fixture, NULL, setup_kvp, test_option_load, teardown); - GNC_TEST_ADD (suitename, "Option DB Save", Fixture, NULL, setup, test_option_save, teardown); -} diff --git a/libgnucash/app-utils/test/test-options.scm b/libgnucash/app-utils/test/test-options.scm index 27b970692d..8e28da9199 100644 --- a/libgnucash/app-utils/test/test-options.scm +++ b/libgnucash/app-utils/test/test-options.scm @@ -1,4 +1,9 @@ -(use-modules (gnucash app-utils)) +;;(use-modules (gnucash app-utils)) +;; Load the C++ option implementation, avoiding the options.scm ones. +(eval-when + (compile load eval expand) + (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module")) +(use-modules (sw_app_utils)) (use-modules (srfi srfi-64)) (use-modules (tests srfi64-extras)) @@ -9,19 +14,16 @@ (test-end "test-options")) (define (test-lookup-option) - (let ((options (gnc:new-options))) - (gnc:register-option + (let* ((options (new-gnc-optiondb)) + (string-opt (gnc-register-string-option options "Section" "Start Date" + "sort-tag" "docstring" "waldo") + )) + + (gnc-register-simple-boolean-option options - (gnc:make-simple-boolean-option - "Section" "Start Date" "sort-tag" "docstring" 'default-val)) - - (gnc:register-option - options - (gnc:make-simple-boolean-option - "Filter" "Void Transactions" "sort-tag" "docstring" 'default-val)) - - (test-assert "lookup-option changed name" - (gnc:lookup-option options "Section" "From")) - - (test-assert "lookup-option changed section and name" - (gnc:lookup-option options "Section" "Void Transactions?")))) + "Filter" "Void Transactions" "sort-tag" "docstring" 'default-val) + ;; Testing that the old option name aliases work. + (let ((option (gnc-lookup-option options "Section" "From"))) + (test-assert "lookup-option changed name" option)) + (let ((option (gnc-lookup-option options "Section" "Void Transactions?"))) + (test-assert "lookup-option changed section and name" option)))) diff --git a/libgnucash/engine/engine-helpers.c b/libgnucash/engine/engine-helpers.c index f6ac9aeb76..8a1395e032 100644 --- a/libgnucash/engine/engine-helpers.c +++ b/libgnucash/engine/engine-helpers.c @@ -188,22 +188,6 @@ gnc_book_option_num_field_source_change (gboolean num_action) g_hook_list_invoke(bo_final_hook_list, TRUE); } -/** Calls registered callbacks when book_currency book option changes so that - * registers/reports can update themselves */ -void -gnc_book_option_book_currency_selected (gboolean use_book_currency) -{ - GHookList *hook_list; - const gchar *key = OPTION_NAME_BOOK_CURRENCY; - - g_once(&bo_init_once, bo_init, NULL); - - hook_list = g_hash_table_lookup(bo_callback_hash, key); - if (hook_list != NULL) - g_hook_list_marshal(hook_list, TRUE, bo_call_hook, &use_book_currency); - g_hook_list_invoke(bo_final_hook_list, TRUE); -} - void gnc_book_option_register_cb (gchar *key, GncBOCb func, gpointer user_data) { diff --git a/libgnucash/engine/engine-helpers.h b/libgnucash/engine/engine-helpers.h index 517d4e1489..29a504ce94 100644 --- a/libgnucash/engine/engine-helpers.h +++ b/libgnucash/engine/engine-helpers.h @@ -74,11 +74,6 @@ void gnc_set_num_action (Transaction *trans, Split *split, void gnc_book_option_num_field_source_change (gboolean num_action); -/** Calls registered callbacks when book_currency book option changes so that - * registers/reports can update themselves */ -void -gnc_book_option_book_currency_selected (gboolean use_book_currency); - /** Registers callbacks to be called when the book option changes for the * specified book option key */ void diff --git a/libgnucash/engine/gnc-date.cpp b/libgnucash/engine/gnc-date.cpp index 3a969a46dc..969ef7a2c4 100644 --- a/libgnucash/engine/gnc-date.cpp +++ b/libgnucash/engine/gnc-date.cpp @@ -1548,15 +1548,15 @@ gnc_gdate_set_quarter_end (GDate *date) void gnc_gdate_set_prev_quarter_start (GDate *date) { - gnc_gdate_set_quarter_start(date); g_date_subtract_months(date, 3); + gnc_gdate_set_quarter_start(date); } void gnc_gdate_set_prev_quarter_end (GDate *date) { - gnc_gdate_set_quarter_end(date); g_date_subtract_months(date, 3); + gnc_gdate_set_quarter_end(date); } /* ================================================= */ diff --git a/libgnucash/engine/gnc-features.c b/libgnucash/engine/gnc-features.c index e6622712c4..580db719f7 100644 --- a/libgnucash/engine/gnc-features.c +++ b/libgnucash/engine/gnc-features.c @@ -45,7 +45,6 @@ static gncFeature known_features[] = { GNC_FEATURE_CREDIT_NOTES, "Customer and vendor credit notes (requires at least GnuCash 2.5.0)" }, { GNC_FEATURE_NUM_FIELD_SOURCE, "User specifies source of 'num' field'; either transaction number or split action (requires at least GnuCash 2.5.0)" }, { GNC_FEATURE_KVP_EXTRA_DATA, "Extra data for addresses, jobs or invoice entries (requires at least GnuCash 2.6.4)" }, - { GNC_FEATURE_BOOK_CURRENCY, "User specifies a 'book-currency'; costs of other currencies/commodities tracked in terms of book-currency (requires at least GnuCash 2.7.0)" }, { GNC_FEATURE_GUID_BAYESIAN, "Use account GUID as key for Bayesian data (requires at least GnuCash 2.6.12)" }, { GNC_FEATURE_GUID_FLAT_BAYESIAN, "Use account GUID as key for bayesian data and store KVP flat (requires at least Gnucash 2.6.19)" }, { GNC_FEATURE_SQLITE3_ISO_DATES, "Use ISO formatted date-time strings in SQLite3 databases (requires at least GnuCash 2.6.20)"}, diff --git a/libgnucash/engine/gnc-features.h b/libgnucash/engine/gnc-features.h index b5960ac7ad..9702177b1a 100644 --- a/libgnucash/engine/gnc-features.h +++ b/libgnucash/engine/gnc-features.h @@ -48,7 +48,6 @@ extern "C" { #define GNC_FEATURE_CREDIT_NOTES "Credit Notes" #define GNC_FEATURE_NUM_FIELD_SOURCE "Number Field Source" #define GNC_FEATURE_KVP_EXTRA_DATA "Extra data in addresses, jobs or invoice entries" -#define GNC_FEATURE_BOOK_CURRENCY "Use a Book-Currency" #define GNC_FEATURE_GUID_BAYESIAN "Account GUID based Bayesian data" #define GNC_FEATURE_GUID_FLAT_BAYESIAN "Account GUID based bayesian with flat KVP" #define GNC_FEATURE_SQLITE3_ISO_DATES "ISO-8601 formatted date strings in SQLite3 databases." diff --git a/libgnucash/engine/gnc-numeric.cpp b/libgnucash/engine/gnc-numeric.cpp index 277e01e0f2..0915f4156e 100644 --- a/libgnucash/engine/gnc-numeric.cpp +++ b/libgnucash/engine/gnc-numeric.cpp @@ -1090,7 +1090,7 @@ gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places) } catch (const std::exception& err) { - PWARN("%s", err.what()); + DEBUG("%s", err.what()); return FALSE; } } diff --git a/libgnucash/engine/kvp-value.cpp b/libgnucash/engine/kvp-value.cpp index bbfedf6aab..456138a39a 100644 --- a/libgnucash/engine/kvp-value.cpp +++ b/libgnucash/engine/kvp-value.cpp @@ -29,6 +29,8 @@ #include #include +using boost::typeindex::type_id; + KvpValueImpl::KvpValueImpl(KvpValueImpl const & other) noexcept { duplicate(other); @@ -58,7 +60,7 @@ KvpValueImpl * KvpValueImpl::add(KvpValueImpl * val) noexcept { /* If already a glist here, just append */ - if (this->datastore.type() == typeid(GList*)) + if (this->datastore.type() == type_id()) { GList * list = boost::get(datastore); datastore = g_list_append (list, val); @@ -75,23 +77,23 @@ KvpValueImpl::add(KvpValueImpl * val) noexcept KvpValue::Type KvpValueImpl::get_type() const noexcept { - if (datastore.type() == typeid(int64_t)) + if (datastore.type() == type_id()) return KvpValue::Type::INT64; - else if (datastore.type() == typeid(double)) + else if (datastore.type() == type_id()) return KvpValue::Type::DOUBLE; - else if (datastore.type() == typeid(gnc_numeric)) + else if (datastore.type() == type_id()) return KvpValue::Type::NUMERIC; - else if (datastore.type() == typeid(const gchar *)) + else if (datastore.type() == type_id()) return KvpValue::Type::STRING; - else if (datastore.type() == typeid(GncGUID *)) + else if (datastore.type() == type_id()) return KvpValue::Type::GUID; - else if (datastore.type() == typeid(Time64)) + else if (datastore.type() == type_id()) return KvpValue::Type::TIME64; - else if (datastore.type() == typeid(GList *)) + else if (datastore.type() == type_id()) return KvpValue::Type::GLIST; - else if (datastore.type() == typeid(KvpFrameImpl *)) + else if (datastore.type() == type_id()) return KvpValue::Type::FRAME; - else if (datastore.type() == typeid(GDate)) + else if (datastore.type() == type_id()) return KvpValue::Type::GDATE; return KvpValue::Type::INVALID; @@ -100,7 +102,7 @@ KvpValueImpl::get_type() const noexcept KvpFrame * KvpValueImpl::replace_frame_nc (KvpFrame * new_value) noexcept { - if (datastore.type() != typeid(KvpFrame *)) + if (datastore.type() != type_id()) return {}; auto ret = boost::get(datastore); datastore = new_value; @@ -110,7 +112,7 @@ KvpValueImpl::replace_frame_nc (KvpFrame * new_value) noexcept GList * KvpValueImpl::replace_glist_nc (GList * new_value) noexcept { - if (datastore.type() != typeid(GList *)) + if (datastore.type() != type_id()) return {}; auto ret = boost::get(datastore); datastore = new_value; @@ -206,7 +208,7 @@ struct to_string_visitor : boost::static_visitor std::string KvpValueImpl::to_string(std::string const & prefix) const noexcept { - if (this->datastore.type() == typeid(KvpFrame*)) + if (this->datastore.type() == type_id()) return this->get()->to_string(prefix); std::ostringstream ret; to_string_visitor visitor {ret}; @@ -390,13 +392,13 @@ KvpValueImpl::~KvpValueImpl() noexcept void KvpValueImpl::duplicate(const KvpValueImpl& other) noexcept { - if (other.datastore.type() == typeid(const gchar *)) + if (other.datastore.type() == type_id()) this->datastore = const_cast(g_strdup(other.get())); - else if (other.datastore.type() == typeid(GncGUID*)) + else if (other.datastore.type() == type_id()) this->datastore = guid_copy(other.get()); - else if (other.datastore.type() == typeid(GList*)) + else if (other.datastore.type() == type_id()) this->datastore = kvp_glist_copy(other.get()); - else if (other.datastore.type() == typeid(KvpFrame*)) + else if (other.datastore.type() == type_id()) this->datastore = new KvpFrame(*other.get()); else this->datastore = other.datastore; diff --git a/libgnucash/engine/kvp-value.hpp b/libgnucash/engine/kvp-value.hpp index 61b7dfd99f..f5f1768709 100644 --- a/libgnucash/engine/kvp-value.hpp +++ b/libgnucash/engine/kvp-value.hpp @@ -174,7 +174,7 @@ KvpValueImpl::KvpValueImpl(T newvalue) noexcept: template T KvpValueImpl::get() const noexcept { - if (this->datastore.type() != typeid(T)) return {}; + if (this->datastore.type() != boost::typeindex::type_id()) return {}; return boost::get(datastore); } diff --git a/libgnucash/engine/policy-p.h b/libgnucash/engine/policy-p.h index 0ca7b04bc6..76cd97e0ac 100644 --- a/libgnucash/engine/policy-p.h +++ b/libgnucash/engine/policy-p.h @@ -63,9 +63,6 @@ struct gncpolicy_s { - char *name; - char *description; - char *hint; GNCLot * (*PolicyGetLot) (GNCPolicy *, Split *split); Split * (*PolicyGetSplit) (GNCPolicy *, GNCLot *lot); void (*PolicyGetLotOpening) (GNCPolicy *, GNCLot *lot, diff --git a/libgnucash/engine/policy.c b/libgnucash/engine/policy.c index 506da094e8..fddd7ff576 100644 --- a/libgnucash/engine/policy.c +++ b/libgnucash/engine/policy.c @@ -63,47 +63,6 @@ //static QofLogModule log_module = GNC_MOD_LOT; -GList * -gnc_get_valid_policy_list (void) -{ - GList *return_list = NULL; - -/* return_list = g_list_prepend (return_list, xaccGetManualPolicy()); - return_list = g_list_prepend (return_list, xaccGetAveragePolicy()); */ - return_list = g_list_prepend (return_list, xaccGetLIFOPolicy()); - return_list = g_list_prepend (return_list, xaccGetFIFOPolicy()); - - return return_list; -} - -gboolean -gnc_valid_policy_name (const gchar *policy_name) -{ - GList *list_of_policies = NULL; - gboolean ret_val = FALSE; - - if (!policy_name) - return ret_val; - - list_of_policies = gnc_get_valid_policy_list(); - if (!list_of_policies) - { - return ret_val; - } - else - { - GList *l = NULL; - for (l = list_of_policies; l != NULL; l = l->next) - { - GNCPolicy *list_pcy = l->data; - if (g_strcmp0(PolicyGetName (list_pcy), policy_name) == 0) - ret_val = TRUE; - } - g_list_free(list_of_policies); - return ret_val; - } -} - static Split * DirectionPolicyGetSplit (GNCPolicy *pcy, GNCLot *lot, short reverse) { @@ -189,26 +148,6 @@ donext: return NULL; } -const char * -PolicyGetName (const GNCPolicy *pcy) -{ - if(!pcy) return NULL; - return pcy->name; -} - -const char * -PolicyGetDescription (const GNCPolicy *pcy) -{ - if(!pcy) return NULL; - return pcy->description; -} -const char * -PolicyGetHint (const GNCPolicy *pcy) -{ - if(!pcy) return NULL; - return pcy->hint; -} - /* ============================================================== */ static GNCLot * @@ -255,9 +194,6 @@ xaccGetFIFOPolicy (void) if (!pcy) { pcy = g_new (GNCPolicy, 1); - pcy->name = FIFO_POLICY; - pcy->description = FIFO_POLICY_DESC; - pcy->hint = FIFO_POLICY_HINT; pcy->PolicyGetLot = FIFOPolicyGetLot; pcy->PolicyGetSplit = FIFOPolicyGetSplit; pcy->PolicyGetLotOpening = FIFOPolicyGetLotOpening; @@ -304,23 +240,4 @@ LIFOPolicyIsOpeningSplit (GNCPolicy *pcy, GNCLot *lot, Split *split) return (split == opening_split); } -GNCPolicy * -xaccGetLIFOPolicy (void) -{ - static GNCPolicy *pcy = NULL; - - if (!pcy) - { - pcy = g_new (GNCPolicy, 1); - pcy->name = LIFO_POLICY; - pcy->description = LIFO_POLICY_DESC; - pcy->hint = LIFO_POLICY_HINT; - pcy->PolicyGetLot = LIFOPolicyGetLot; - pcy->PolicyGetSplit = LIFOPolicyGetSplit; - pcy->PolicyGetLotOpening = LIFOPolicyGetLotOpening; - pcy->PolicyIsOpeningSplit = LIFOPolicyIsOpeningSplit; - } - return pcy; -} - /* =========================== END OF FILE ======================= */ diff --git a/libgnucash/engine/policy.h b/libgnucash/engine/policy.h index 71e4c0ed8e..012172d06b 100644 --- a/libgnucash/engine/policy.h +++ b/libgnucash/engine/policy.h @@ -43,24 +43,6 @@ extern "C" { typedef struct gncpolicy_s GNCPolicy; -/** Valid Policy List - * Provides a glist of implemented policies. - * - * List must be freed with g_list_free(). - */ -GList * gnc_get_valid_policy_list (void); - -/** Valid Policy Name - * Uses the Valid Policy List to determine if a policy name is valid. - */ -gboolean gnc_valid_policy_name (const gchar *policy_name); - -const char *PolicyGetName (const GNCPolicy *pcy); - -const char *PolicyGetDescription (const GNCPolicy *pcy); - -const char *PolicyGetHint (const GNCPolicy *pcy); - /** First-in, First-out Policy * This policy will create FIFO Lots. FIFO Lots have the following * properties: @@ -69,24 +51,10 @@ const char *PolicyGetHint (const GNCPolicy *pcy); * -- Splits are added to the lot in date order, with earliest splits * added first. * -- All splits in the lot share the same transaction currency as - * the split that opened the lot (if book-currency book option - * selected, this will always be book currency). + * the split that opened the lot */ GNCPolicy *xaccGetFIFOPolicy (void); -/** Last-in, Last-out Policy - * This policy will create LIFO Lots. LIFO Lots have the following - * properties: - * -- The lot is started with the latest posted split that isn't - * a part of another lot already. - * -- Splits are added to the lot in date order, with latest splits - * added first. - * -- All splits in the lot share the same transaction currency as - * the split that opened the lot (if book-currency book option - * selected, this will always be book currency). - */ -GNCPolicy *xaccGetLIFOPolicy (void); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp index 1cbc673eb8..e03fda12ed 100644 --- a/libgnucash/engine/qofbook.cpp +++ b/libgnucash/engine/qofbook.cpp @@ -70,22 +70,7 @@ enum PROP_0, // PROP_ROOT_ACCOUNT, /* Table */ // PROP_ROOT_TEMPLATE, /* Table */ -/* keep trading accounts property, while adding book-currency, default gains - policy and default gains account properties, so that files prior to 2.7 can - be read/processed; GUI changed to use all four properties as of 2.7. - Trading accounts, on the one hand, and book-currency plus default-gains- - policy, and optionally, default gains account, on the other, are mutually - exclusive */ PROP_OPT_TRADING_ACCOUNTS, /* KVP */ -/* Book currency and default gains policy properties only apply if currency - accounting method selected in GUI is 'book-currency'; both required and - both are exclusive with trading accounts */ - PROP_OPT_BOOK_CURRENCY, /* KVP */ - PROP_OPT_DEFAULT_GAINS_POLICY, /* KVP */ -/* Default gains account property only applies if currency accounting method - selected in GUI is 'book-currency'; its use is optional but exclusive with - trading accounts */ - PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID, /* KVP */ PROP_OPT_AUTO_READONLY_DAYS, /* KVP */ PROP_OPT_NUM_FIELD_SOURCE, /* KVP */ PROP_OPT_DEFAULT_BUDGET, /* KVP */ @@ -186,18 +171,6 @@ qof_book_get_property (GObject* object, qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS}); break; - case PROP_OPT_BOOK_CURRENCY: - qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, - str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY}); - break; - case PROP_OPT_DEFAULT_GAINS_POLICY: - qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, - str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY}); - break; - case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID: - qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, - str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID}); - break; case PROP_OPT_AUTO_READONLY_DAYS: qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS}); @@ -241,18 +214,6 @@ qof_book_set_property (GObject *object, qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_TRADING_ACCOUNTS}); break; - case PROP_OPT_BOOK_CURRENCY: - qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, - str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_BOOK_CURRENCY}); - break; - case PROP_OPT_DEFAULT_GAINS_POLICY: - qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, - str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_POLICY}); - break; - case PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID: - qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, - str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID}); - break; case PROP_OPT_AUTO_READONLY_DAYS: qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH, str_OPTION_SECTION_ACCOUNTS, str_OPTION_NAME_AUTO_READONLY_DAYS}); @@ -297,41 +258,6 @@ qof_book_class_init (QofBookClass *klass) NULL, G_PARAM_READWRITE)); - g_object_class_install_property - (gobject_class, - PROP_OPT_BOOK_CURRENCY, - g_param_spec_string("book-currency", - "Select Book Currency", - "The reference currency used to manage multiple-currency " - "transactions when 'book-currency' currency accounting method " - "selected; requires valid default gains/loss policy.", - NULL, - G_PARAM_READWRITE)); - - g_object_class_install_property - (gobject_class, - PROP_OPT_DEFAULT_GAINS_POLICY, - g_param_spec_string("default-gains-policy", - "Select Default Gains Policy", - "The default policy to be used to calculate gains/losses on " - "dispositions of currencies/commodities other than " - "'book-currency' when 'book-currency' currency accounting " - "method selected; requires valid book-currency.", - NULL, - G_PARAM_READWRITE)); - - g_object_class_install_property - (gobject_class, - PROP_OPT_DEFAULT_GAINS_ACCOUNT_GUID, - g_param_spec_boxed("default-gain-loss-account-guid", - "Select Default Gain/Loss Account", - "The default account to be used for calculated gains/losses on " - "dispositions of currencies/commodities other than " - "'book-currency' when 'book-currency' currency accounting " - "method selected; requires valid book-currency.", - GNC_TYPE_GUID, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, PROP_OPT_NUM_FIELD_SOURCE, @@ -982,52 +908,6 @@ qof_book_normalize_counter_format_internal(const gchar *p, return normalized_str; } -/** Returns pointer to book-currency name for book, if one exists in the - * KVP, or NULL; does not validate contents nor determine if there is a valid - * default gain/loss policy, both of which are required, for the - * 'book-currency' currency accounting method to apply. Use instead - * 'gnc_book_get_book_currency_name' which does these validations. */ -const gchar * -qof_book_get_book_currency_name (QofBook *book) -{ - const gchar *opt = NULL; - qof_instance_get (QOF_INSTANCE (book), - "book-currency", &opt, - NULL); - return opt; -} - -/** Returns pointer to default gain/loss policy for book, if one exists in the - * KVP, or NULL; does not validate contents nor determine if there is a valid - * book-currency, both of which are required, for the 'book-currency' - * currency accounting method to apply. Use instead - * 'gnc_book_get_default_gains_policy' which does these validations. */ -const gchar * -qof_book_get_default_gains_policy (QofBook *book) -{ - const gchar *opt = NULL; - qof_instance_get (QOF_INSTANCE (book), - "default-gains-policy", &opt, - NULL); - return opt; -} - -/** Returns pointer to default gain/loss account GUID for book, if one exists in - * the KVP, or NULL; does not validate contents nor determine if there is a - * valid book-currency, both of which are required, for the 'book-currency' - * currency accounting method to apply. Use instead - * 'gnc_book_get_default_gain_loss_acct' which does these validations. */ -GncGUID * -qof_book_get_default_gain_loss_acct_guid (QofBook *book) -{ - GncGUID *guid = NULL; - qof_instance_get (QOF_INSTANCE (book), - "default-gain-loss-account-guid", &guid, - NULL); - return guid; - -} - /* Determine whether this book uses trading accounts */ gboolean qof_book_use_trading_accounts (const QofBook *book) @@ -1277,14 +1157,14 @@ qof_book_unset_feature (QofBook *book, const gchar *key, const gchar *descr) } void -qof_book_load_options (QofBook *book, GNCOptionLoad load_cb, GNCOptionDB *odb) +qof_book_load_options (QofBook *book, GncOptionLoad load_cb, GncOptionDB *odb) { load_cb (odb, book); } void -qof_book_save_options (QofBook *book, GNCOptionSave save_cb, - GNCOptionDB* odb, gboolean clear) +qof_book_save_options (QofBook *book, GncOptionSave save_cb, + GncOptionDB* odb, gboolean clear) { /* Wrap this in begin/commit so that it commits only once instead of doing * so for every option. Qof_book_set_option will take care of dirtying the diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h index a262299380..01d9b2b657 100644 --- a/libgnucash/engine/qofbook.h +++ b/libgnucash/engine/qofbook.h @@ -42,6 +42,11 @@ #ifdef __cplusplus #include //To preempt it being included extern "C" in a later header. +class GncOptionDB; +#else +typedef struct GncOptionDB GncOptionDB; +#endif +#ifdef __cplusplus extern "C" { #endif @@ -74,10 +79,8 @@ typedef struct KvpValueImpl KvpValue; typedef void (*QofBookDirtyCB) (QofBook *, gboolean dirty, gpointer user_data); -typedef struct gnc_option_db GNCOptionDB; - -typedef void (*GNCOptionSave) (GNCOptionDB*, QofBook*, gboolean); -typedef void (*GNCOptionLoad) (GNCOptionDB*, QofBook*); +typedef void (*GncOptionSave) (GncOptionDB*, QofBook*, gboolean); +typedef void (*GncOptionLoad) (GncOptionDB*, QofBook*); /* Book structure */ struct _QofBook @@ -267,27 +270,6 @@ gboolean qof_book_empty(const QofBook *book); /** Returns flag indicating whether this book uses trading accounts */ gboolean qof_book_use_trading_accounts (const QofBook *book); -/** Returns pointer to book-currency name for book, if one exists in the - * KVP, or NULL; does not validate contents nor determine if there is a valid - * default gain/loss policy, both of which are required, for the - * 'book-currency' currency accounting method to apply. Use instead - * 'gnc_book_get_book_currency_name' which does these validations. */ -const gchar * qof_book_get_book_currency_name (QofBook *book); - -/** Returns pointer to default gain/loss policy for book, if one exists in the - * KVP, or NULL; does not validate contents nor determine if there is a valid - * book-currency, both of which are required, for the 'book-currency' - * currency accounting method to apply. Use instead - * 'gnc_book_get_default_gains_policy' which does these validations. */ -const gchar * qof_book_get_default_gains_policy (QofBook *book); - -/** Returns pointer to default gain/loss account GUID for book, if one exists in - * the KVP, or NULL; does not validate contents nor determine if there is a - * valid book-currency, both of which are required, for the 'book-currency' - * currency accounting method to apply. Use instead - * 'gnc_book_get_default_gain_loss_acct' which does these validations. */ -GncGUID * qof_book_get_default_gain_loss_acct_guid (QofBook *book); - /** Returns TRUE if the auto-read-only feature should be used, otherwise * FALSE. This is just a wrapper on qof_book_get_num_days_autoreadonly() == 0. */ gboolean qof_book_uses_autoreadonly (const QofBook *book); @@ -392,21 +374,21 @@ void qof_book_commit_edit(QofBook *book); /** @ingroup KVP @{ */ -/** Load a GNCOptionsDB from KVP data. +/** Load a GncOptionsDB from KVP data. * @param book: The book. * @param load_cb: A callback function that does the loading. - * @param odb: The GNCOptionDB to load. + * @param odb: The GncOptionDB to load. */ -void qof_book_load_options (QofBook *book, GNCOptionLoad load_cb, - GNCOptionDB *odb); -/** Save a GNCOptionsDB back to the book's KVP. +void qof_book_load_options (QofBook *book, GncOptionLoad load_cb, + GncOptionDB *odb); +/** Save a GncOptionsDB back to the book's KVP. * @param book: The book. * @param save_cb: A callback function that does the saving. - * @param odb: The GNCOptionsDB to save from. - * @param clear: Should the GNCOptionsDB be emptied after the save? + * @param odb: The GncOptionsDB to save from. + * @param clear: Should the GncOptionsDB be emptied after the save? */ -void qof_book_save_options (QofBook *book, GNCOptionSave save_cb, - GNCOptionDB* odb, gboolean clear); +void qof_book_save_options (QofBook *book, GncOptionSave save_cb, + GncOptionDB* odb, gboolean clear); /** Save a single option value. * Used from Scheme, the KvpValue<-->SCM translation is handled by the functions * in kvp-scm.c and automated by SWIG. The starting element is set as diff --git a/libgnucash/engine/qofbookslots.h b/libgnucash/engine/qofbookslots.h index 10e1d6450b..4ed5fd8c25 100644 --- a/libgnucash/engine/qofbookslots.h +++ b/libgnucash/engine/qofbookslots.h @@ -48,7 +48,6 @@ * It is tied from this C #define in * src/app-utils/app-utils.scm * and is extensively used in - * src/app-utils/option-util.c * src/gnome-utils/gnome-utils.scm * various reports */ @@ -64,10 +63,6 @@ #define OPTION_SECTION_ACCOUNTS N_("Accounts") #define OPTION_NAME_TRADING_ACCOUNTS N_("Use Trading Accounts") -#define OPTION_NAME_CURRENCY_ACCOUNTING N_("Currency Accounting") -#define OPTION_NAME_BOOK_CURRENCY N_("Book Currency") -#define OPTION_NAME_DEFAULT_GAINS_POLICY N_("Default Gains Policy") -#define OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID N_("Default Gain or Loss Account") #define OPTION_NAME_AUTO_READONLY_DAYS N_("Day Threshold for Read-Only Transactions (red line)") #define OPTION_NAME_NUM_FIELD_SOURCE N_("Use Split Action Field for Number") @@ -80,11 +75,6 @@ * KVP-OPTION-PATH * OPTION-SECTION-ACCOUNTS * OPTION-NAME-TRADING-ACCOUNTS - * OPTION-NAME-CURRENCY-ACCOUNTING - * OPTION-NAME-BOOK-CURRENCY - * OPTION_NAME_DEFAULT_GAINS_POLICY - * OPTION_NAME_DEFAULT_GAINS_LOSS_ACCT_GUID - * OPTION-NAME-AUTO-READONLY-DAYS * OPTION-NAME_NUM-FIELD-SOURCE * OPTION-SECTION-BUDGETING * OPTION-NAME-DEFAULT-BUDGET diff --git a/libgnucash/engine/test/test-qofbook.c b/libgnucash/engine/test/test-qofbook.c index 794dc52749..67f4a4e3e6 100644 --- a/libgnucash/engine/test/test-qofbook.c +++ b/libgnucash/engine/test/test-qofbook.c @@ -453,111 +453,6 @@ test_book_use_trading_accounts( Fixture *fixture, gconstpointer pData ) } -static void -test_book_use_book_currency( Fixture *fixture, gconstpointer pData ) -{ - const gchar *cur; - const gchar *pol; - GncGUID *acct; - const GncGUID *acct2; - - cur = qof_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = qof_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book ); - g_assert (acct2 == NULL ); - - g_test_message( "Testing with existing trading accounts set to true - t" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "trading-accts", "t", - NULL); - cur = qof_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = qof_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book ); - g_assert (acct2 == NULL ); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with book-currency set and no default-gains-policy or account" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "USD", - NULL); - cur = qof_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , "USD" ); - pol = qof_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , NULL ); - acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book ); - g_assert (acct2 == NULL ); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with default-gains-policy set and no book-currency" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gains-policy", "fifo", - NULL); - cur = qof_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , NULL ); - pol = qof_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , "fifo" ); - acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book ); - g_assert (acct2 == NULL ); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with book-currency and default-gains-policy set to nonsense" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "myMoney", - NULL); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gains-policy", "random", - NULL); - cur = qof_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , "myMoney" ); - pol = qof_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , "random" ); - acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book ); - g_assert (acct2 == NULL ); - qof_book_commit_edit (fixture->book); - - qof_book_destroy( fixture->book ); - fixture->book = qof_book_new(); - - g_test_message( "Testing with book-currency, default-gains-policy and default-gains-account set to valid values" ); - qof_book_begin_edit (fixture->book); - qof_instance_set (QOF_INSTANCE (fixture->book), - "book-currency", "USD", - NULL); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gains-policy", "fifo", - NULL); - acct = guid_new(); - qof_instance_set (QOF_INSTANCE (fixture->book), - "default-gain-loss-account-guid", acct, - NULL); - cur = qof_book_get_book_currency_name( fixture-> book ); - g_assert_cmpstr( cur, == , "USD" ); - pol = qof_book_get_default_gains_policy( fixture-> book ); - g_assert_cmpstr( pol, == , "fifo" ); - acct2 = qof_book_get_default_gain_loss_acct_guid( fixture-> book ); - g_assert_cmpstr( guid_to_string (acct), == , guid_to_string (acct2) ); - g_assert (guid_equal(acct, acct2)); - guid_free (acct); - qof_book_commit_edit (fixture->book); -} - static void test_book_get_num_days_autofreeze( Fixture *fixture, gconstpointer pData ) { @@ -931,7 +826,6 @@ test_suite_qofbook ( void ) GNC_TEST_ADD( suitename, "get counter format", Fixture, NULL, setup, test_book_get_counter_format, teardown ); GNC_TEST_ADD( suitename, "increment and format counter", Fixture, NULL, setup, test_book_increment_and_format_counter, teardown ); GNC_TEST_ADD( suitename, "use trading accounts", Fixture, NULL, setup, test_book_use_trading_accounts, teardown ); - GNC_TEST_ADD( suitename, "use book-currency", Fixture, NULL, setup, test_book_use_book_currency, teardown ); GNC_TEST_ADD( suitename, "get autofreeze days", Fixture, NULL, setup, test_book_get_num_days_autofreeze, teardown ); GNC_TEST_ADD( suitename, "use split action for num field", Fixture, NULL, setup, test_book_use_split_action_for_num_field, teardown ); GNC_TEST_ADD( suitename, "mark session dirty", Fixture, NULL, setup, test_book_mark_session_dirty, teardown ); diff --git a/po/POTFILES.in b/po/POTFILES.in index 8cd8b24507..70e024f22b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -48,12 +48,12 @@ borrowed/libc/setenv.c borrowed/libc/strptime.c doc/tip_of_the_day.list.c gnucash/gnome/assistant-acct-period.c -gnucash/gnome/assistant-hierarchy.c +gnucash/gnome/assistant-hierarchy.cpp gnucash/gnome/assistant-loan.cpp gnucash/gnome/assistant-stock-split.c gnucash/gnome/assistant-stock-transaction.cpp gnucash/gnome/business-gnome-utils.c -gnucash/gnome/business-options-gnome.c +gnucash/gnome/business-options-gnome.cpp gnucash/gnome/business-urls.c gnucash/gnome/dialog-billterms.c gnucash/gnome/dialog-choose-owner.c @@ -77,8 +77,8 @@ gnucash/gnome/dialog-price-edit-db.c gnucash/gnome/dialog-price-editor.c gnucash/gnome/dialog-print-check.c gnucash/gnome/dialog-progress.c -gnucash/gnome/dialog-report-column-view.c -gnucash/gnome/dialog-report-style-sheet.c +gnucash/gnome/dialog-report-column-view.cpp +gnucash/gnome/dialog-report-style-sheet.cpp gnucash/gnome/dialog-sx-editor.c gnucash/gnome/dialog-sx-from-trans.c gnucash/gnome/dialog-sx-since-last-run.c @@ -94,7 +94,7 @@ gnucash/gnome/gnc-plugin-page-budget.c gnucash/gnome/gnc-plugin-page-invoice.c gnucash/gnome/gnc-plugin-page-owner-tree.c gnucash/gnome/gnc-plugin-page-register.c -gnucash/gnome/gnc-plugin-page-report.c +gnucash/gnome/gnc-plugin-page-report.cpp gnucash/gnome/gnc-plugin-page-sx-list.c gnucash/gnome/gnc-plugin-register.c gnucash/gnome/gnc-plugin-report-system.c @@ -107,7 +107,7 @@ gnucash/gnome/search-owner.c gnucash/gnome/top-level.c gnucash/gnome/window-autoclear.c gnucash/gnome/window-reconcile.c -gnucash/gnome/window-report.c +gnucash/gnome/window-report.cpp gnucash/gnome-search/dialog-search.c gnucash/gnome-search/gnc-general-search.c gnucash/gnome-search/search-account.c @@ -130,7 +130,7 @@ gnucash/gnome-utils/dialog-doclink-utils.c gnucash/gnome-utils/dialog-dup-trans.c gnucash/gnome-utils/dialog-file-access.c gnucash/gnome-utils/dialog-object-references.c -gnucash/gnome-utils/dialog-options.c +gnucash/gnome-utils/dialog-options.cpp gnucash/gnome-utils/dialog-preferences.c gnucash/gnome-utils/dialog-query-view.c gnucash/gnome-utils/dialog-reset-warnings.c @@ -168,7 +168,7 @@ gnucash/gnome-utils/gnc-gtk-utils.c gnucash/gnome-utils/gnc-gui-query.c gnucash/gnome-utils/gnc-icons.c gnucash/gnome-utils/gnc-keyring.c -gnucash/gnome-utils/gnc-main-window.c +gnucash/gnome-utils/gnc-main-window.cpp gnucash/gnome-utils/gnc-menu-extensions.c gnucash/gnome-utils/gnc-menu-extensions.scm gnucash/gnome-utils/gnc-period-select.c @@ -429,7 +429,7 @@ gnucash/report/commodity-utilities.scm gnucash/report/eguile-html-utilities.scm gnucash/report/eguile.scm gnucash/report/eguile-utilities.scm -gnucash/report/gnc-report.c +gnucash/report/gnc-report.cpp gnucash/report/html-acct-table.scm gnucash/report/html-anytag.scm gnucash/report/html-chart.scm @@ -500,9 +500,6 @@ gnucash/report/stylesheets/head-or-tail.scm gnucash/report/stylesheets/plain.scm gnucash/report/trep-engine.scm libgnucash/app-utils/app-utils.scm -libgnucash/app-utils/business-options.c -libgnucash/app-utils/business-options.scm -libgnucash/app-utils/business-prefs.scm libgnucash/app-utils/calculation/expression_parser.c libgnucash/app-utils/calculation/fin.c libgnucash/app-utils/c-interface.scm @@ -519,13 +516,16 @@ libgnucash/app-utils/gnc-exp-parser.c libgnucash/app-utils/gnc-gsettings.cpp libgnucash/app-utils/gnc-helpers.c libgnucash/app-utils/gnc-help-utils.c +libgnucash/app-utils/gnc-option.cpp +libgnucash/app-utils/gnc-option-date.cpp +libgnucash/app-utils/gnc-optiondb.cpp +libgnucash/app-utils/gnc-option-impl.cpp libgnucash/app-utils/gnc-prefs-utils.c libgnucash/app-utils/gnc-state.c libgnucash/app-utils/gnc-sx-instance-model.c libgnucash/app-utils/gnc-ui-balances.c libgnucash/app-utils/gnc-ui-util.c libgnucash/app-utils/options.scm -libgnucash/app-utils/option-util.c libgnucash/app-utils/QuickFill.c libgnucash/backend/dbi/gnc-backend-dbi.cpp libgnucash/backend/dbi/gnc-dbisqlconnection.cpp