From 0b77641e9f0506be331c69afbb98a9f103e9d880 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 5 Jul 2019 14:29:33 -0700 Subject: [PATCH 001/298] Remove gnc_options_dialog bindings from gnome-utils.i. They're unused. --- gnucash/gnome-utils/gnome-utils.i | 8 -------- 1 file changed, 8 deletions(-) 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, ...); From b495da4e2952fa45c8a98c70e914ec5d12049e2e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 11 Jul 2019 14:35:55 -0700 Subject: [PATCH 002/298] [C++ Options] Remove some unused C API from options-utils. --- libgnucash/app-utils/option-util.c | 356 +---------------------------- libgnucash/app-utils/option-util.h | 102 +++++++-- 2 files changed, 90 insertions(+), 368 deletions(-) diff --git a/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c index 05c6e621ef..01c475204e 100644 --- a/libgnucash/app-utils/option-util.c +++ b/libgnucash/app-utils/option-util.c @@ -720,7 +720,7 @@ gnc_option_default_getter (GNCOption *option) 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. * @@ -728,8 +728,8 @@ gnc_option_default_getter (GNCOption *option) * Args: option - the GNCOption * * Returns: SCM handle to function * \********************************************************************/ -SCM -gnc_option_value_validator (GNCOption *option) +static SCM +gnc_option_value_validator(GNCOption *option) { initialize_getters (); @@ -737,7 +737,8 @@ gnc_option_value_validator (GNCOption *option) 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. * @@ -748,8 +749,8 @@ gnc_option_value_validator (GNCOption *option) * Returns: SCM handle to function * * If no such function exists, returns SCM_UNDEFINED. * \********************************************************************/ -SCM -gnc_option_widget_changed_proc_getter (GNCOption *option) +static SCM +gnc_option_widget_changed_proc_getter(GNCOption *option) { SCM cb; @@ -1151,36 +1152,6 @@ gnc_option_use_alpha (GNCOption *option) 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 * @@ -1317,21 +1288,6 @@ compare_option_tags (gconstpointer a, gconstpointer 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 * @@ -1525,38 +1481,6 @@ gnc_option_db_get_option_by_name (GNCOptionDB *odb, 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) { @@ -1776,30 +1700,6 @@ gnc_option_db_section_reset_widgets (GNCOptionSection *section) } } -/********************************************************************\ - * 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, * @@ -1939,69 +1839,6 @@ gnc_option_db_lookup_string_option (GNCOptionDB *odb, 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 * @@ -2038,185 +1875,6 @@ gnc_option_db_lookup_number_option (GNCOptionDB *odb, 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 * diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h index d555495a74..c059e18962 100644 --- a/libgnucash/app-utils/option-util.h +++ b/libgnucash/app-utils/option-util.h @@ -201,26 +201,93 @@ guint32 gnc_option_db_lookup_color_option_argb (GNCOptionDB *odb, 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_option_db_unregister_change_callback_id(GNCOptionDB *odb, + SCM callback_id); -void gnc_free_list_option_value (GSList *list); +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_get_option_data(GNCOption *option); -gnc_commodity * gnc_option_db_lookup_currency_option (GNCOptionDB *odb, - const char *section, - const char *name, - gnc_commodity *default_value); +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); +char * gnc_option_permissible_value_description(GNCOption *option, int index); -void gnc_option_db_set_option_default (GNCOptionDB *odb, - const char *section, - const char *name); +gboolean gnc_option_show_time(GNCOption *option); -gboolean gnc_option_db_set_option (GNCOptionDB *odb, - const char *section, - const char *name, - SCM value); +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); +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); + +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); + +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); + +gdouble gnc_option_db_lookup_number_option(GNCOptionDB *odb, + const char *section, + const char *name, + gdouble default_value); + +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, @@ -281,7 +348,4 @@ void gncp_option_invoke_callback (GNCOptionChangeCallback callback, /* 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 */ From c0ba3e2706985d15ac53c95a85a81aeec1587ad0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 9 Jul 2019 09:40:29 -0700 Subject: [PATCH 003/298] [C++ Options] Begin Implementation for basic and validated options. --- libgnucash/app-utils/gnc-option.cpp | 103 +++++++++ libgnucash/app-utils/gnc-option.hpp | 215 ++++++++++++++++++ libgnucash/app-utils/test/CMakeLists.txt | 20 ++ .../app-utils/test/gtest-gnc-option.cpp | 174 ++++++++++++++ po/POTFILES.in | 2 + 5 files changed, 514 insertions(+) create mode 100644 libgnucash/app-utils/gnc-option.cpp create mode 100644 libgnucash/app-utils/gnc-option.hpp create mode 100644 libgnucash/app-utils/test/gtest-gnc-option.cpp diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp new file mode 100644 index 0000000000..447328cee9 --- /dev/null +++ b/libgnucash/app-utils/gnc-option.cpp @@ -0,0 +1,103 @@ +/********************************************************************\ + * gnc-option.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.hpp" + +template<> SCM +scm_from_value(std::string value) +{ + return scm_from_utf8_string(value.c_str()); +} + +template<> SCM +scm_from_value(bool value) +{ + return value ? SCM_BOOL_T : SCM_BOOL_F; +} + +template<> SCM +scm_from_value(int64_t value) +{ + return scm_from_int64(value); +} + +template<> SCM +scm_from_value(QofInstance* value) +{ + auto guid = guid_to_string(qof_instance_get_guid(value)); + auto scm_guid = scm_from_utf8_string(guid); + g_free(guid); + return scm_guid; +} + +std::shared_ptr> +gnc_make_string_option(const char* section, const char* name, + const char* key, const char* doc_string, + std::string value) +{ + return std::make_shared>( + section, name, key, doc_string, value); +} + +std::shared_ptr> +gnc_make_text_option(const char* section, const char* name, + const char* key, const char* doc_string, + std::string value) +{ + return gnc_make_string_option(section, name, key, doc_string, value); +} + +std::shared_ptr> +gnc_make_budget_option(const char* section, const char* name, + const char* key, const char* doc_string, + GncBudget *value) +{ + return std::make_shared>( + section, name, key, doc_string, QOF_INSTANCE(value)); +} + +std::shared_ptr> +gnc_make_commodity_option(const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity *value) +{ + return std::make_shared>( + section, name, key, doc_string, QOF_INSTANCE(value)); +} + + +std::shared_ptr> +gnc_make_currency_option(const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity *value) +{ + return std::make_shared>( + section, name, key, doc_string, QOF_INSTANCE(value), + [](QofInstance* new_value) -> bool + { + return GNC_IS_COMMODITY (new_value) && + gnc_commodity_is_currency(GNC_COMMODITY(new_value)); + } + ); +} diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp new file mode 100644 index 0000000000..f8bcb84aba --- /dev/null +++ b/libgnucash/app-utils/gnc-option.hpp @@ -0,0 +1,215 @@ +/********************************************************************\ + * gnc-option.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 * + * * +\********************************************************************/ + +#ifndef GNC_OPTION_HPP_ +#define GNC_OPTION_HPP_ + +extern "C" +{ +#include +#include +#include +#include +} +#include +#include + +/* + * Unused base class to document the structure of the current Scheme option + * vector, re-expressed in C++. The comment-numbers on the right indicate which + * item in the Scheme vector each item implements. + * + * Not everything here needs to be implemented, nor will it necessarily be + * implemented the same way. For example, part of the purpose of this redesign + * is to convert from saving options as strings of Scheme code to some form of + * key-value pair in the book options, so generate_restore_form() will likely be + * supplanted with save_to_book(). + +template +class GncOptionBase +{ +public: + virtual ~GncOption = default; + virtual ValueType get_value() const = 0; //5 + virtual ValueType get_default_value() = 0; + virtual SCM get_SCM_value() = 0; + virtual SCM get_SCM_default_value() const = 0; //7 + virtual void set_value(ValueType) = 0; //6 +// generate_restore_form outputs a Scheme expression (a "form") that finds an +// option and sets it to the current value. e.g.: +//(let ((option (gnc:lookup-option options +// "Display" +// "Amount"))) +// ((lambda (option) (if option ((gnc:option-setter option) 'none))) option)) +// it uses gnc:value->string to generate the "'none" (or whatever the option's +// value would be as input to the scheme interpreter). + + virtual std::string generate_restore_form(); //8 + virtual void save_to_book(QofBook*) const noexcept; //9 + virtual void read_from_book(QofBook*); //10 + virtual std::vector get_option_strings(); //15 + virtual set_changed_callback(std::function); //14 +protected: + const std::string m_section; //0 + const std::string m_name; //1 + const std::string m_sort_tag; //2 + const std::type_info m_kvp_type; //3 + const std::string m_doc_string; //4 + std::function m_changed_callback; //Part of the make-option closure + std::functionm_option_widget_changed_callback; //16 +}; +*/ + +struct OptionClassifier +{ + const std::string m_section; + const std::string m_name; + const std::string m_sort_tag; +// const std::type_info m_kvp_type; + const std::string m_doc_string; +}; + +template +SCM scm_from_value(ValueType); + +/* This design pattern is called the Curiously Recursive Template Pattern, or + * CRTP. See https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern + * for a detailed explanation. + */ +template +class GncOption +{ +public: + ValueType get_value() const + { + return static_cast(*this).get_value(); + } + void set_value(ValueType value) + { + static_cast(*this).set_value(value); + } + ValueType get_default_value() const + { + return static_cast(*this).get_default_value(); + } + SCM get_scm_value() + { + ValueType value{static_cast(*this).get_value()}; + return scm_from_value(value); + } + SCM get_scm_default_value() + { + ValueType value{static_cast(*this).get_default_value()}; + return scm_from_value(value); + } +}; + +template +class GncOptionValue : + public OptionClassifier, + public GncOption> +{ +public: + GncOptionValue(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value) : + OptionClassifier{section, name, key, doc_string}, + m_value{value}, m_default_value{value} {} + ValueType get_value() const { return m_value; } + ValueType get_default_value() const { return m_default_value; } + void set_value(ValueType new_value) { m_value = new_value; } +protected: + ValueType m_value; + ValueType m_default_value; +}; + +template +class GncOptionValidatedValue : + public OptionClassifier, + public GncOption> +{ +public: + GncOptionValidatedValue(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, + std::functionvalidator) : + OptionClassifier{section, name, key, doc_string}, + m_value{value}, m_default_value{value}, m_validator{validator} + { + if (!this->validate(value)) + throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); + } + GncOptionValidatedValue(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, + std::functionvalidator, + ValueType val_data) : + OptionClassifier{section, name, key, doc_string}, m_value{value}, + m_default_value{value}, m_validator{validator}, m_validation_data{val_data} + { + if (!this->validate(value)) + throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); + } + ValueType get_value() const { return m_value; } + ValueType get_default_value() const { return m_default_value; } + bool validate(ValueType value) { return m_validator(value); } + void set_value(ValueType value) + { + if (this->validate(value)) + m_value = value; + else + throw std::invalid_argument("Validation failed, value not set."); + } +private: + ValueType m_value; + ValueType m_default_value; + std::function m_validator; //11 + ValueType m_validation_data; +}; + +std::shared_ptr> +gnc_make_string_option(const char* section, const char* name, + const char* key, const char* doc_string, + std::string value); + +std::shared_ptr> +gnc_make_text_option(const char* section, const char* name, + const char* key, const char* doc_string, + std::string value); + +std::shared_ptr> +gnc_make_budget_option(const char* section, const char* name, + const char* key, const char* doc_string, + GncBudget* value); + +std::shared_ptr> +gnc_make_commodity_option(const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity* value); + +std::shared_ptr> +gnc_make_currency_option(const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity* value); + +#endif //GNC_OPTION_HPP_ diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index e92468496b..426f4f2a62 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 @@ -28,6 +29,25 @@ 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 + ${MODULEPATH}/gnc-option.cpp + gtest-gnc-option.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 + gncmod-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 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..ca309f499e --- /dev/null +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -0,0 +1,174 @@ +/******************************************************************** + * 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 + +TEST(GncOption, test_string_ctor) +{ + EXPECT_NO_THROW({ + auto option = gnc_make_string_option("foo", "bar", "baz", + "Phony Option", + std::string{"waldo"}); + }); +} + +TEST(GncOption, test_text_ctor) +{ + EXPECT_NO_THROW({ + auto option = gnc_make_text_option("foo", "bar", "baz", + "Phony Option", + std::string{"waldo"}); + }); +} + +TEST(GncOption, test_string_default_value) +{ + auto option = gnc_make_string_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()); +} + +TEST(GncOption, test_string_value) +{ + auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option", + std::string{"waldo"}); + option->set_value("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_scm_functions) +{ + auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option", + std::string{"waldo"}); + auto scm_value = option->get_scm_value(); + auto str_value = scm_to_utf8_string(scm_value); + EXPECT_STREQ("waldo", str_value); + g_free(str_value); + scm_value = option->get_scm_default_value(); + str_value = scm_to_utf8_string(scm_value); + EXPECT_STREQ("waldo", str_value); + g_free(str_value); +} + +TEST(GNCOption, test_budget_ctor) +{ + auto book = qof_book_new(); + auto budget = gnc_budget_new(book); + EXPECT_NO_THROW({ + auto option = gnc_make_budget_option("foo", "bar", "baz", + "Phony Option", budget); + }); + gnc_budget_destroy(budget); + qof_book_destroy(book); +} + +TEST(GNCOption, test_budget_scm_functions) +{ + auto book = qof_book_new(); + auto budget = gnc_budget_new(book); + auto option = gnc_make_budget_option("foo", "bar", "baz", + "Phony Option", budget); + auto scm_budget = option->get_scm_value(); + auto str_value = scm_to_utf8_string(scm_budget); + auto guid = guid_to_string(qof_instance_get_guid(budget)); + EXPECT_STREQ(guid, str_value); + g_free(guid); + gnc_budget_destroy(budget); + qof_book_destroy(book); +} + +TEST(GNCOption, test_commodity_ctor) +{ + auto book = qof_book_new(); + auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", + "NYSE", "HPE", NULL, 1); + EXPECT_NO_THROW({ + auto option = gnc_make_commodity_option("foo", "bar", "baz", + "Phony Option", hpe); + }); + gnc_commodity_destroy(hpe); + qof_book_destroy(book); +} + +TEST(GNCOption, test_currency_ctor) +{ + auto book = qof_book_new(); + auto table = gnc_commodity_table_new(); + qof_book_set_data(book, GNC_COMMODITY_TABLE, table); + auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", + "NYSE", "HPE", NULL, 1); + EXPECT_THROW({ + auto option = gnc_make_currency_option("foo", "bar", "baz", + "Phony Option", hpe); + }, std::invalid_argument); + gnc_commodity_destroy(hpe); + auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100); + EXPECT_NO_THROW({ + auto option = gnc_make_currency_option("foo", "bar", "baz", + "Phony Option", eur); + }); + gnc_commodity_destroy(eur); + auto usd = gnc_commodity_new(book, "United States Dollar", + "CURRENCY", "USD", NULL, 100); + EXPECT_NO_THROW({ + auto option = gnc_make_currency_option("foo", "bar", "baz", + "Phony Option", usd); + }); + gnc_commodity_destroy(usd); + qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr); + gnc_commodity_table_destroy(table); + qof_book_destroy(book); +} + +TEST(GNCOption, test_currency_setter) +{ + auto book = qof_book_new(); + auto table = gnc_commodity_table_new(); + qof_book_set_data(book, GNC_COMMODITY_TABLE, table); + auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", + "NYSE", "HPE", NULL, 1); + auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100); + auto option = gnc_make_currency_option("foo", "bar", "baz", + "Phony Option", eur); + auto usd = gnc_commodity_new(book, "United States Dollar", + "CURRENCY", "USD", NULL, 100); + EXPECT_NO_THROW({ + option->set_value(QOF_INSTANCE(usd)); + }); + EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option->get_value())); + EXPECT_THROW({ + option->set_value(QOF_INSTANCE(hpe)); + }, std::invalid_argument); + EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option->get_value())); + gnc_commodity_destroy(hpe); + gnc_commodity_destroy(usd); + gnc_commodity_destroy(eur); + qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr); + gnc_commodity_table_destroy(table); + qof_book_destroy(book); +} diff --git a/po/POTFILES.in b/po/POTFILES.in index 1066cf100f..3c28396e23 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -528,6 +528,8 @@ libgnucash/app-utils/gnc-exp-parser.c libgnucash/app-utils/gnc-gsettings.c libgnucash/app-utils/gnc-helpers.c libgnucash/app-utils/gnc-help-utils.c +libgnucash/app-utils/gncmod-app-utils.c +libgnucash/app-utils/gnc-option.cpp libgnucash/app-utils/gnc-prefs-utils.c libgnucash/app-utils/gnc-state.c libgnucash/app-utils/gnc-sx-instance-model.c From b6fd8447743e2d741ebfb8cf49bc24acdf0f470e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 20 Jul 2019 15:09:11 -0700 Subject: [PATCH 004/298] Wrap GncOptionValue/GncOptionValidatedValue in Boost::Variant. To provide a single type for containers. --- libgnucash/app-utils/gnc-option.cpp | 33 +++-- libgnucash/app-utils/gnc-option.hpp | 114 ++++++++++++++++-- .../app-utils/test/gtest-gnc-option.cpp | 24 ++-- 3 files changed, 136 insertions(+), 35 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 447328cee9..e57779025c 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -51,16 +51,18 @@ scm_from_value(QofInstance* value) return scm_guid; } -std::shared_ptr> +GncOption gnc_make_string_option(const char* section, const char* name, const char* key, const char* doc_string, std::string value) { - return std::make_shared>( - section, name, key, doc_string, value); + GncOptionValue retval { + section, name, key, doc_string, value + }; + return retval; } -std::shared_ptr> +GncOption gnc_make_text_option(const char* section, const char* name, const char* key, const char* doc_string, std::string value) @@ -68,36 +70,41 @@ gnc_make_text_option(const char* section, const char* name, return gnc_make_string_option(section, name, key, doc_string, value); } -std::shared_ptr> +GncOption gnc_make_budget_option(const char* section, const char* name, const char* key, const char* doc_string, GncBudget *value) { - return std::make_shared>( - section, name, key, doc_string, QOF_INSTANCE(value)); + GncOptionValue retval { + section, name, key, doc_string, QOF_INSTANCE(value) + }; + return retval; } -std::shared_ptr> +GncOption gnc_make_commodity_option(const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { - return std::make_shared>( - section, name, key, doc_string, QOF_INSTANCE(value)); + GncOptionValue retval { + section, name, key, doc_string, QOF_INSTANCE(value) + }; + return retval; } -std::shared_ptr> +GncOption gnc_make_currency_option(const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { - return std::make_shared>( + GncOptionValidatedValue retval { section, name, key, doc_string, QOF_INSTANCE(value), [](QofInstance* new_value) -> bool { return GNC_IS_COMMODITY (new_value) && gnc_commodity_is_currency(GNC_COMMODITY(new_value)); } - ); + }; + return retval; } diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index f8bcb84aba..8f31681105 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -33,6 +33,7 @@ extern "C" } #include #include +#include /* * Unused base class to document the structure of the current Scheme option @@ -97,7 +98,7 @@ SCM scm_from_value(ValueType); * for a detailed explanation. */ template -class GncOption +class GncOptionBase { public: ValueType get_value() const @@ -112,12 +113,12 @@ public: { return static_cast(*this).get_default_value(); } - SCM get_scm_value() + SCM get_scm_value() const { ValueType value{static_cast(*this).get_value()}; return scm_from_value(value); } - SCM get_scm_default_value() + SCM get_scm_default_value() const { ValueType value{static_cast(*this).get_default_value()}; return scm_from_value(value); @@ -127,7 +128,7 @@ public: template class GncOptionValue : public OptionClassifier, - public GncOption> + public GncOptionBase> { public: GncOptionValue(const char* section, const char* name, @@ -146,7 +147,7 @@ protected: template class GncOptionValidatedValue : public OptionClassifier, - public GncOption> + public GncOptionBase> { public: GncOptionValidatedValue(const char* section, const char* name, @@ -187,29 +188,122 @@ private: ValueType m_validation_data; }; -std::shared_ptr> +using GncOptionVariant = boost::variant, + GncOptionValue, + GncOptionValue, + GncOptionValue, + GncOptionValidatedValue>; +class GncOption +{ +public: + template + GncOption(OptionType option) : m_option{option} {} + template ValueType get_value() const + { + return boost::apply_visitor(GetValueVisitor(), m_option); + } + template ValueType get_default_value() const + { + return boost::apply_visitor(GetDefaultValueVisitor(), m_option); + } + SCM get_scm_value() const + { + return boost::apply_visitor(GetSCMVisitor(), m_option); + } + SCM get_scm_default_value() const + { + return boost::apply_visitor(GetSCMDefaultVisitor(), m_option); + } + template void set_value(ValueType value) + { + boost::apply_visitor(SetValueVisitor(value), m_option); + } +private: + template + struct GetValueVisitor : public boost::static_visitor + { + ValueType operator()(const GncOptionValue& option) const { + return option.get_value(); + } + ValueType operator()(const GncOptionValidatedValue& option) const { + return option.get_value(); + } + template + ValueType operator()(OptionType& option) const { + return ValueType{}; + } + }; + template + struct GetDefaultValueVisitor : public boost::static_visitor + { + ValueType operator()(const GncOptionValue& option) const { + return option.get_default_value(); + } + ValueType operator()(const GncOptionValidatedValue& option) const { + return option.get_default_value(); + } + template + ValueType operator()(OptionType& option) const { + return ValueType(); + } + }; + template + struct SetValueVisitor : public boost::static_visitor<> + { + SetValueVisitor(ValueType value) : m_value{value} {} + void operator()(GncOptionValue& option) const { + option.set_value(m_value); + } + void operator()(GncOptionValidatedValue& option) const { + option.set_value(m_value); + } + template + void operator()(OptionType& option) const { + } + private: + ValueType m_value; + }; + struct GetSCMVisitor : public boost::static_visitor + { + template + SCM operator()(OptionType& option) const { + return option.get_scm_value(); + } + }; + struct GetSCMDefaultVisitor : public boost::static_visitor + { + template + SCM operator()(OptionType& option) const { + return option.get_scm_default_value(); + } + }; + GncOptionVariant m_option; +}; + +GncOption gnc_make_string_option(const char* section, const char* name, const char* key, const char* doc_string, std::string value); -std::shared_ptr> +GncOption gnc_make_text_option(const char* section, const char* name, const char* key, const char* doc_string, std::string value); -std::shared_ptr> +GncOption gnc_make_budget_option(const char* section, const char* name, const char* key, const char* doc_string, GncBudget* value); -std::shared_ptr> +GncOption gnc_make_commodity_option(const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity* value); -std::shared_ptr> +GncOption gnc_make_currency_option(const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity* value); + #endif //GNC_OPTION_HPP_ diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index ca309f499e..be9b3b5603 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -46,18 +46,18 @@ TEST(GncOption, test_string_default_value) { auto option = gnc_make_string_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_STREQ("waldo", option.get_default_value().c_str()); + EXPECT_STREQ("waldo", option.get_value().c_str()); } TEST(GncOption, test_string_value) { auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); - option->set_value("pepper"); - EXPECT_STREQ("waldo", option->get_default_value().c_str()); + 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()); + EXPECT_STREQ("pepper", option.get_value().c_str()); }); } @@ -65,11 +65,11 @@ TEST(GncOption, test_string_scm_functions) { auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); - auto scm_value = option->get_scm_value(); + auto scm_value = option.get_scm_value(); auto str_value = scm_to_utf8_string(scm_value); EXPECT_STREQ("waldo", str_value); g_free(str_value); - scm_value = option->get_scm_default_value(); + scm_value = option.get_scm_default_value(); str_value = scm_to_utf8_string(scm_value); EXPECT_STREQ("waldo", str_value); g_free(str_value); @@ -93,7 +93,7 @@ TEST(GNCOption, test_budget_scm_functions) auto budget = gnc_budget_new(book); auto option = gnc_make_budget_option("foo", "bar", "baz", "Phony Option", budget); - auto scm_budget = option->get_scm_value(); + auto scm_budget = option.get_scm_value(); auto str_value = scm_to_utf8_string(scm_budget); auto guid = guid_to_string(qof_instance_get_guid(budget)); EXPECT_STREQ(guid, str_value); @@ -158,13 +158,13 @@ TEST(GNCOption, test_currency_setter) auto usd = gnc_commodity_new(book, "United States Dollar", "CURRENCY", "USD", NULL, 100); EXPECT_NO_THROW({ - option->set_value(QOF_INSTANCE(usd)); + option.set_value(QOF_INSTANCE(usd)); }); - EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option->get_value())); + EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option.get_value())); EXPECT_THROW({ - option->set_value(QOF_INSTANCE(hpe)); + option.set_value(QOF_INSTANCE(hpe)); }, std::invalid_argument); - EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option->get_value())); + EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option.get_value())); gnc_commodity_destroy(hpe); gnc_commodity_destroy(usd); gnc_commodity_destroy(eur); From 01fcae6ac8aa886db2efdbd6f61e847da1e6a553 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 3 Aug 2019 10:11:49 -0700 Subject: [PATCH 005/298] Make the OptionClassifier members non-const. Constness deletes the default copy assignment operator, making GncOption not copy-assignable. --- libgnucash/app-utils/gnc-option.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 8f31681105..1a91c6138c 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -83,11 +83,11 @@ protected: struct OptionClassifier { - const std::string m_section; - const std::string m_name; - const std::string m_sort_tag; -// const std::type_info m_kvp_type; - const std::string m_doc_string; + std::string m_section; + std::string m_name; + std::string m_sort_tag; +// std::type_info m_kvp_type; + std::string m_doc_string; }; template From 01dc70cc60ae546943200cb726723a419d35928f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 3 Aug 2019 10:38:05 -0700 Subject: [PATCH 006/298] Add GncOption accessors for Classifier strings. --- libgnucash/app-utils/gnc-option.hpp | 46 +++++++++++++++++++ .../app-utils/test/gtest-gnc-option.cpp | 10 ++++ 2 files changed, 56 insertions(+) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 1a91c6138c..15b78876a7 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -198,6 +198,7 @@ class GncOption public: template GncOption(OptionType option) : m_option{option} {} + template ValueType get_value() const { return boost::apply_visitor(GetValueVisitor(), m_option); @@ -218,6 +219,22 @@ public: { boost::apply_visitor(SetValueVisitor(value), m_option); } + const std::string& get_section() const + { + return boost::apply_visitor(GetSectionVisitor(), m_option); + } + const std::string& get_name() const + { + return boost::apply_visitor(GetNameVisitor(), m_option); + } + const std::string& get_key() const + { + return boost::apply_visitor(GetKeyVisitor(), m_option); + } + const std::string& get_docstring() const + { + return boost::apply_visitor(GetDocstringVisitor(), m_option); + } private: template struct GetValueVisitor : public boost::static_visitor @@ -277,6 +294,35 @@ private: return option.get_scm_default_value(); } }; + struct GetSectionVisitor : public boost::static_visitor + { + template + const std::string& operator()(OptionType& option) const { + return option.m_section; + } + }; + struct GetNameVisitor : public boost::static_visitor + { + template + const std::string& operator()(OptionType& option) const { + return option.m_name; + } + }; + struct GetKeyVisitor : public boost::static_visitor + { + template + const std::string& operator()(OptionType& option) const { + return option.m_sort_tag; + } + }; + struct GetDocstringVisitor : + public boost::static_visitor + { + template + const std::string& operator()(OptionType& option) const { + return option.m_doc_string; + } + }; GncOptionVariant m_option; }; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index be9b3b5603..b4a7f187c4 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -42,6 +42,16 @@ TEST(GncOption, test_text_ctor) }); } +TEST(GncOption, test_string_classifier_getters) +{ + auto option = gnc_make_string_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) { auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option", From d88ec0dc1b59e3c15147dcd832d360331582cf8f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 5 Aug 2019 11:03:34 -0700 Subject: [PATCH 007/298] Replace the gnc_make_foo_option free functions with a template ctor. The free functions will reappear for GncOptionDB. This is to avoid having to pass naked pointers to Scheme with the attendant object lifetime issues. --- libgnucash/app-utils/gnc-option.cpp | 57 --------------- libgnucash/app-utils/gnc-option.hpp | 34 +++------ .../app-utils/test/gtest-gnc-option.cpp | 69 ++++++++++--------- 3 files changed, 43 insertions(+), 117 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index e57779025c..485b0d4039 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -51,60 +51,3 @@ scm_from_value(QofInstance* value) return scm_guid; } -GncOption -gnc_make_string_option(const char* section, const char* name, - const char* key, const char* doc_string, - std::string value) -{ - GncOptionValue retval { - section, name, key, doc_string, value - }; - return retval; -} - -GncOption -gnc_make_text_option(const char* section, const char* name, - const char* key, const char* doc_string, - std::string value) -{ - return gnc_make_string_option(section, name, key, doc_string, value); -} - -GncOption -gnc_make_budget_option(const char* section, const char* name, - const char* key, const char* doc_string, - GncBudget *value) -{ - GncOptionValue retval { - section, name, key, doc_string, QOF_INSTANCE(value) - }; - return retval; -} - -GncOption -gnc_make_commodity_option(const char* section, const char* name, - const char* key, const char* doc_string, - gnc_commodity *value) -{ - GncOptionValue retval { - section, name, key, doc_string, QOF_INSTANCE(value) - }; - return retval; -} - - -GncOption -gnc_make_currency_option(const char* section, const char* name, - const char* key, const char* doc_string, - gnc_commodity *value) -{ - GncOptionValidatedValue retval { - section, name, key, doc_string, QOF_INSTANCE(value), - [](QofInstance* new_value) -> bool - { - return GNC_IS_COMMODITY (new_value) && - gnc_commodity_is_currency(GNC_COMMODITY(new_value)); - } - }; - return retval; -} diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 15b78876a7..4b1e17c81c 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -199,6 +199,14 @@ public: template GncOption(OptionType option) : m_option{option} {} + template + GncOption(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value) : + m_option{GncOptionValue { + section, name, key, doc_string, value + }} {} + template ValueType get_value() const { return boost::apply_visitor(GetValueVisitor(), m_option); @@ -326,30 +334,4 @@ private: GncOptionVariant m_option; }; -GncOption -gnc_make_string_option(const char* section, const char* name, - const char* key, const char* doc_string, - std::string value); - -GncOption -gnc_make_text_option(const char* section, const char* name, - const char* key, const char* doc_string, - std::string value); - -GncOption -gnc_make_budget_option(const char* section, const char* name, - const char* key, const char* doc_string, - GncBudget* value); - -GncOption -gnc_make_commodity_option(const char* section, const char* name, - const char* key, const char* doc_string, - gnc_commodity* value); - -GncOption -gnc_make_currency_option(const char* section, const char* name, - const char* key, const char* doc_string, - gnc_commodity* value); - - #endif //GNC_OPTION_HPP_ diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index b4a7f187c4..d552ba8552 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -27,25 +27,14 @@ TEST(GncOption, test_string_ctor) { EXPECT_NO_THROW({ - auto option = gnc_make_string_option("foo", "bar", "baz", - "Phony Option", - std::string{"waldo"}); - }); -} - -TEST(GncOption, test_text_ctor) -{ - EXPECT_NO_THROW({ - auto option = gnc_make_text_option("foo", "bar", "baz", - "Phony Option", - std::string{"waldo"}); + GncOption option("foo", "bar", "baz", "Phony Option", + std::string{"waldo"}); }); } TEST(GncOption, test_string_classifier_getters) { - auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option", - std::string{"waldo"}); + 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()); @@ -54,16 +43,14 @@ TEST(GncOption, test_string_classifier_getters) TEST(GncOption, test_string_default_value) { - auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option", - std::string{"waldo"}); + 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()); } TEST(GncOption, test_string_value) { - auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option", - std::string{"waldo"}); + 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({ @@ -73,8 +60,7 @@ TEST(GncOption, test_string_value) TEST(GncOption, test_string_scm_functions) { - auto option = gnc_make_string_option("foo", "bar", "baz", "Phony Option", - std::string{"waldo"}); + GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); auto scm_value = option.get_scm_value(); auto str_value = scm_to_utf8_string(scm_value); EXPECT_STREQ("waldo", str_value); @@ -90,8 +76,8 @@ TEST(GNCOption, test_budget_ctor) auto book = qof_book_new(); auto budget = gnc_budget_new(book); EXPECT_NO_THROW({ - auto option = gnc_make_budget_option("foo", "bar", "baz", - "Phony Option", budget); + GncOption option("foo", "bar", "baz", "Phony Option", + QOF_INSTANCE(budget)); }); gnc_budget_destroy(budget); qof_book_destroy(book); @@ -101,8 +87,8 @@ TEST(GNCOption, test_budget_scm_functions) { auto book = qof_book_new(); auto budget = gnc_budget_new(book); - auto option = gnc_make_budget_option("foo", "bar", "baz", - "Phony Option", budget); + GncOption option("foo", "bar", "baz", "Phony Option", + QOF_INSTANCE(budget)); auto scm_budget = option.get_scm_value(); auto str_value = scm_to_utf8_string(scm_budget); auto guid = guid_to_string(qof_instance_get_guid(budget)); @@ -118,12 +104,27 @@ TEST(GNCOption, test_commodity_ctor) auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", "NYSE", "HPE", NULL, 1); EXPECT_NO_THROW({ - auto option = gnc_make_commodity_option("foo", "bar", "baz", - "Phony Option", hpe); + GncOption option("foo", "bar", "baz", "Phony Option", + QOF_INSTANCE(hpe)); }); gnc_commodity_destroy(hpe); qof_book_destroy(book); } +static GncOption +make_currency_option (const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity *value) +{ + GncOption option{GncOptionValidatedValue{ + section, name, key, doc_string, QOF_INSTANCE(value), + [](QofInstance* new_value) -> bool + { + return GNC_IS_COMMODITY (new_value) && + gnc_commodity_is_currency(GNC_COMMODITY(new_value)); + } + }}; + return option; +} TEST(GNCOption, test_currency_ctor) { @@ -133,21 +134,21 @@ TEST(GNCOption, test_currency_ctor) auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", "NYSE", "HPE", NULL, 1); EXPECT_THROW({ - auto option = gnc_make_currency_option("foo", "bar", "baz", - "Phony Option", hpe); + auto option = make_currency_option("foo", "bar", "baz", + "Phony Option", hpe); }, std::invalid_argument); gnc_commodity_destroy(hpe); auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100); EXPECT_NO_THROW({ - auto option = gnc_make_currency_option("foo", "bar", "baz", - "Phony Option", eur); + auto option = make_currency_option("foo", "bar", "baz", + "Phony Option", eur); }); gnc_commodity_destroy(eur); auto usd = gnc_commodity_new(book, "United States Dollar", "CURRENCY", "USD", NULL, 100); EXPECT_NO_THROW({ - auto option = gnc_make_currency_option("foo", "bar", "baz", - "Phony Option", usd); + auto option = make_currency_option("foo", "bar", "baz", + "Phony Option",usd); }); gnc_commodity_destroy(usd); qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr); @@ -163,8 +164,8 @@ TEST(GNCOption, test_currency_setter) auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", "NYSE", "HPE", NULL, 1); auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100); - auto option = gnc_make_currency_option("foo", "bar", "baz", - "Phony Option", eur); + auto option = make_currency_option("foo", "bar", "baz", + "Phony Option",eur); auto usd = gnc_commodity_new(book, "United States Dollar", "CURRENCY", "USD", NULL, 100); EXPECT_NO_THROW({ From 13b94d2370b399a5d88cea5a4766ad5aef96c2a6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 6 Aug 2019 10:24:42 -0700 Subject: [PATCH 008/298] Test integer options, calling get_value with wrong type. --- libgnucash/app-utils/test/gtest-gnc-option.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index d552ba8552..bd1712094e 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -46,6 +46,7 @@ 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) @@ -58,6 +59,14 @@ TEST(GncOption, test_string_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_string_scm_functions) { GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); From 16fd632ec85a98358449a25579c5491ded376d4c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 6 Aug 2019 10:17:06 -0700 Subject: [PATCH 009/298] Make the GncOption member variables private. GncOption is intended to be final, so there's no point in protected members. --- libgnucash/app-utils/gnc-option.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 4b1e17c81c..023f8b36e8 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -139,7 +139,7 @@ public: ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } void set_value(ValueType new_value) { m_value = new_value; } -protected: +private: ValueType m_value; ValueType m_default_value; }; From 455d3c2d6015c197c4000b2d48b925c0da498669 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 6 Aug 2019 15:03:48 -0700 Subject: [PATCH 010/298] Add GncOptionDB class. --- libgnucash/app-utils/gnc-optiondb.cpp | 159 ++++++++++++++++++ libgnucash/app-utils/gnc-optiondb.hpp | 71 ++++++++ libgnucash/app-utils/test/CMakeLists.txt | 4 +- .../app-utils/test/gtest-gnc-optiondb.cpp | 58 +++++++ po/POTFILES.in | 1 + 5 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 libgnucash/app-utils/gnc-optiondb.cpp create mode 100644 libgnucash/app-utils/gnc-optiondb.hpp create mode 100644 libgnucash/app-utils/test/gtest-gnc-optiondb.cpp diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp new file mode 100644 index 0000000000..29b140f1a1 --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -0,0 +1,159 @@ +/********************************************************************\ + * 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 "gnc-optiondb.hpp" + +GncOptionDB::GncOptionDB() : m_default_section{nullptr} {} + +GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {} + +void +GncOptionDB::save_to_book(QofBook* book, bool do_clear) const +{ +} + +void +GncOptionDB::register_option(const char* section, GncOption&& option) +{ + auto db_section = std::find_if( + m_sections.begin(), m_sections.end(), + [section](GncOptionSection sect) -> bool + { + return sect.first == std::string{section}; + }); + + if (db_section == m_sections.end()) + { + m_sections.emplace_back(std::make_pair(std::string{section}, + GncOptionVec{})); + db_section = std::prev(m_sections.end()); + } + auto wrapper = std::make_shared(option, nullptr); + db_section->second.emplace_back(wrapper); +} + +void +GncOptionDB::unregister_option(const char* section, const char* name) +{ + auto db_section = std::find_if( + m_sections.begin(), m_sections.end(), + [section](GncOptionSection sect) -> bool + { + return sect.first == std::string{section}; + }); + if (db_section != m_sections.end()) + { + db_section->second.erase( + std::remove_if( + db_section->second.begin(), db_section->second.end(), + [name](GncOptionWrapperPtr option) -> bool + { + return option->m_option.get_name() == std::string{name}; + })); + } +} + +void +GncOptionDB::set_default_section(const char* section) +{ +} + +SCM +GncOptionDB::lookup_option(const char* section, const char* name) const +{ + auto db_section = std::find_if( + m_sections.begin(), m_sections.end(), + [section](GncOptionSection sect) -> bool + { + return sect.first == std::string{section}; + }); + if (db_section == m_sections.end()) + return SCM_BOOL_F; + auto db_opt = std::find_if( + db_section->second.begin(), db_section->second.end(), + [name](GncOptionWrapperPtr option) -> bool + { + return option->m_option.get_name() == std::string{name}; + }); + if (db_opt == db_section->second.end() || !*db_opt) + return SCM_BOOL_F; + return (*db_opt)->m_option.get_scm_value(); +} + +static const std::string empty_string; +std::string +GncOptionDB::lookup_string_option(const char* section, const char* name) const +{ + auto db_section = std::find_if( + m_sections.begin(), m_sections.end(), + [section](GncOptionSection sect) -> bool + { + return sect.first == std::string{section}; + }); + if (db_section == m_sections.end()) + return empty_string; + auto db_opt = std::find_if( + db_section->second.begin(), db_section->second.end(), + [name](GncOptionWrapperPtr option) -> bool + { + return option->m_option.get_name() == std::string{name}; + }); + if (db_opt == db_section->second.end() || !*db_opt) + return empty_string; + return (*db_opt)->m_option.get_value(); +} + +bool +GncOptionDB::set_option(const char* section, const char* name, SCM value) +{ + return false; +} + +void +GncOptionDB::set_selectable(const char* section, const char* name) +{ +} + +void +GncOptionDB::commit() +{ + std::for_each( + m_sections.begin(), m_sections.end(), + [](GncOptionSection section) + { + std::for_each( + section.second.begin(), section.second.end(), + [](GncOptionWrapperPtr option) + { +/* FIXME, not implemented. + if (option->m_option.is_dirty()) + { + option->m_option.commit(); + +* FIXME, no Gtk in gtk_widget_set_value(option->m_widget, +* libgnucash! option->m_option.get_value()); + } +*/ + }); + }); +} diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp new file mode 100644 index 0000000000..07e3846965 --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -0,0 +1,71 @@ +/********************************************************************\ + * 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 * + * * +\********************************************************************/ + +#ifndef GNC_OPTIONDB_HPP_ +#define GNC_OPTIONDB_HPP_ + +#include "gnc-option.hpp" + +class GncOptionDB; +struct GncOptionWrapper +{ + GncOptionWrapper(const GncOption& opt, void* ptr) : m_option{opt}, m_widget{ptr} {} + GncOptionWrapper(GncOption&& opt, void* ptr) : m_option{opt}, m_widget{ptr} {} + GncOption m_option; + void* m_widget; /* Don't want widget code in libgnucash! GObject closure?*/ +}; + +using GncOptionWrapperPtr = std::shared_ptr; +using GncOptionVec = std::vector; +using GncOptionSection = std::pair; +using GncOptionSectionPtr = std::shared_ptr; +class GncOptionDB +{ +public: + GncOptionDB(); + GncOptionDB(QofBook* book); + ~GncOptionDB() = default; + + void save_to_book(QofBook* book, bool do_clear) const; + int 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 unregister_option(const char* section, const char* name); + void set_default_section(const char* section); + const GncOptionSectionPtr get_default_section() const noexcept + { + return m_default_section; + } + SCM lookup_option(const char* section, const char* name) const; + std::string lookup_string_option(const char* section, + const char* name) const; + bool set_option(const char* section, const char* name, SCM value); + void set_selectable(const char* section, const char* name); + void commit(); +private: + GncOptionSectionPtr m_default_section; + std::vector m_sections; + bool m_dirty = false; +}; + +#endif //GNC_OPTIONDB_HPP_ diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index 426f4f2a62..192c6d9d25 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -31,7 +31,9 @@ add_app_utils_test(test-sx test-sx.cpp) set(gtest_gnc_option_SOURCES ${MODULEPATH}/gnc-option.cpp - gtest-gnc-option.cpp) + ${MODULEPATH}/gnc-optiondb.cpp + gtest-gnc-option.cpp + gtest-gnc-optiondb.cpp) set(gtest_gnc_option_INCLUDES ${MODULEPATH} 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..d33311bb18 --- /dev/null +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -0,0 +1,58 @@ +/******************************************************************** + * 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 + +TEST(GncOptionDB, test_ctor) +{ + EXPECT_NO_THROW ({ GncOptionDB optiondb; }); +} + +TEST(GncOptionDB, test_register_option) +{ + GncOptionDB optiondb; + GncOption option1{"foo", "bar", "baz", "Phony Option", + std::string{"waldo"}}; + optiondb.register_option("foo", std::move(option1)); + EXPECT_EQ(optiondb.num_sections(), 1); +} + +TEST(GncOptionDB, test_lookup_string_option) +{ + GncOptionDB optiondb; + GncOption option1{"foo", "bar", "baz", "Phony Option", + std::string{"waldo"}}; + optiondb.register_option("foo", std::move(option1)); + EXPECT_STREQ("waldo", optiondb.lookup_string_option("foo", "bar").c_str()); +} + +TEST(GncOptionDB, test_unregister_option) +{ + GncOptionDB optiondb; + GncOption option1{"foo", "bar", "baz", "Phony Option", + std::string{"waldo"}}; + optiondb.register_option("foo", std::move(option1)); + optiondb.unregister_option("foo", "bar"); + EXPECT_TRUE(optiondb.lookup_string_option("foo", "bar").empty()); +} diff --git a/po/POTFILES.in b/po/POTFILES.in index 3c28396e23..fe2f1ca715 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -530,6 +530,7 @@ libgnucash/app-utils/gnc-helpers.c libgnucash/app-utils/gnc-help-utils.c libgnucash/app-utils/gncmod-app-utils.c libgnucash/app-utils/gnc-option.cpp +libgnucash/app-utils/gnc-optiondb.cpp libgnucash/app-utils/gnc-prefs-utils.c libgnucash/app-utils/gnc-state.c libgnucash/app-utils/gnc-sx-instance-model.c From f3eee511e88d406091151cfd4c8122ca0bbc40c8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 6 Aug 2019 15:29:58 -0700 Subject: [PATCH 011/298] Add free functions to create a new GncOptionDB and to register options. The objective of the free functions is to hide the GncOption from language bindings so that the GncOptions can be moved into the GncOptionDB instead of having shared ptrs splattered around the heap. Nearly all access to the options can then be mediated through the GncOptionDB container. Note that gnc_option_db_new creates the GncOptionDB on the heap and returns a raw ptr, so it's up to the creator of the GncOptionDB to call delete on it when it's no longer needed. --- libgnucash/app-utils/gnc-optiondb.cpp | 58 +++++++++++++++++++ libgnucash/app-utils/gnc-optiondb.hpp | 39 +++++++++++++ .../app-utils/test/gtest-gnc-optiondb.cpp | 9 +++ 3 files changed, 106 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 29b140f1a1..fbc08f410b 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -157,3 +157,61 @@ GncOptionDB::commit() }); }); } + +GncOptionDB* +gnc_option_db_new(void) +{ + return new GncOptionDB; +} + +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}; + 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) +{ + gnc_register_string_option(db, section, name, key, doc_string, value); +} + +void +gnc_register_budget_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, GncBudget *value) +{ + GncOption option{section, name, key, doc_string, QOF_INSTANCE(value)}; + 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{section, name, key, doc_string, QOF_INSTANCE(value)}; + 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{GncOptionValidatedValue{ + section, name, key, doc_string, QOF_INSTANCE(value), + [](QofInstance* new_value) -> bool + { + return GNC_IS_COMMODITY (new_value) && + gnc_commodity_is_currency(GNC_COMMODITY(new_value)); + } + }}; + db->register_option(section, std::move(option)); +} diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 07e3846965..69503c22c6 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -66,6 +66,45 @@ private: GncOptionSectionPtr m_default_section; std::vector m_sections; bool m_dirty = false; + + std::function m_get_ui_value; + std::function m_set_ui_value; }; +/** + * Create an empty option database. + * + * It would be nice to use a std::shared_ptr here but Swig doesn't implement + * that for Guile. + * @return A newly allocated GncOptionDB. Use delete to destroy it. + */ +GncOptionDB *gnc_option_db_new(void); + +void gnc_register_string_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + + +void gnc_register_string_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + +void gnc_register_text_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + +void gnc_register_budget_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, GncBudget* value); + +void gnc_register_commodity_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, + gnc_commodity* value); + +void gnc_register_currency_option(GncOptionDB* db, const char* section, + const char* name, const char* key, + const char* doc_string, gnc_commodity* value); + + #endif //GNC_OPTIONDB_HPP_ diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index d33311bb18..a09659fde7 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -56,3 +56,12 @@ TEST(GncOptionDB, test_unregister_option) optiondb.unregister_option("foo", "bar"); EXPECT_TRUE(optiondb.lookup_string_option("foo", "bar").empty()); } + +TEST(GncOptionDB, test_register_string_option) +{ + GncOptionDB* db = gnc_option_db_new(); + gnc_register_string_option(db, "foo", "bar", "baz", "Phony Option", + std::string{"waldo"}); + EXPECT_STREQ("waldo", db->lookup_string_option("foo", "bar").c_str()); + delete db; +} From ade7fc8b6e29a43d7b47db246066c57e6c6af309 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 6 Aug 2019 15:31:44 -0700 Subject: [PATCH 012/298] Initial SWIG of GncOptionDB and Scheme tests. This class will be heavily used by reports so we need to ensure SWIG and Scheme compatibility from the start. --- libgnucash/app-utils/gnc-optiondb.i | 18 +++++++ libgnucash/app-utils/test/CMakeLists.txt | 47 +++++++++++++++++-- .../app-utils/test/test-gnc-optiondb.scm | 46 ++++++++++++++++++ 3 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 libgnucash/app-utils/gnc-optiondb.i create mode 100644 libgnucash/app-utils/test/test-gnc-optiondb.scm diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i new file mode 100644 index 0000000000..d15c8598db --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -0,0 +1,18 @@ +/* + * Temporary swig interface file while developing C++ options. + */ + +%module sw_gnc_optiondb +%{ +#include +#include "gnc-optiondb.hpp" +extern "C" SCM scm_init_sw_gnc_optiondb_module(void); +%} + +%include + +%ignore OptionClassifier; +%ignore GncOption; +%ignore GncOptionWrapper; + +%include "gnc-optiondb.hpp" diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index 192c6d9d25..17de9412db 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -82,12 +82,49 @@ 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 + "${test_app_utils_scheme_SRFI64_SOURCES}" + "tests" + "${GUILE_DEPENDS};scm-srfi64-extras" + FALSE + ) - gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}") + set(SWIG_ARGS "-c++" "-procdoc" "sw-gnc-option-doc" "-procdocformat" "plain") + gnc_add_swig_guile_command(swig-gnc-optiondb-guile + SWIG_GNC_OPTIONDB_GUILE_CPP swig-gnc-optiondb-guile.cpp + ${MODULEPATH}/gnc-optiondb.i + ) + add_library(swig-gnc-optiondb MODULE + ${MODULEPATH}/gnc-option.cpp + ${MODULEPATH}/gnc-optiondb.cpp + ${SWIG_GNC_OPTIONDB_GUILE_CPP} + ) + set(swig_gnc_optiondb_INCLUDES + ${MODULEPATH} + ${CMAKE_SOURCE_DIR}/libgnucash/engine + ${CMAKE_BINARY_DIR}/common # for config.h + ${GLIB2_INCLUDE_DIRS} + ${GUILE_INCLUDE_DIRS} + ) + + set(swig_gnc_optiondb_LIBS + gncmod-engine + ${GLIB2_LDFLAGS} + ${GUILE_LDFLAGS} + ) + + target_link_libraries(swig-gnc-optiondb ${swig_gnc_optiondb_LIBS}) + target_include_directories(swig-gnc-optiondb + PRIVATE ${swig_gnc_optiondb_INCLUDES}) + + gnc_add_scheme_test_targets(scm-test-gnc-optiondb + "test-gnc-optiondb.scm" + "tests" + "swig-gnc-optiondb;scm-srfi64-extras" + FALSE + ) + gnc_add_scheme_tests("test-gnc-optiondb.scm") + gnc_add_scheme_tests("${test_app_utils_scheme_SRFI64_SOURCES}") endif() # Doesn't work yet: 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..4d26a803f7 --- /dev/null +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -0,0 +1,46 @@ + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; 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)) + +(eval-when + (compile load eval expand) + (load-extension "libswig-gnc-optiondb" "scm_init_sw_gnc_optiondb_module")) +(use-modules (sw_gnc_optiondb)) + +(define (run-test) + (test-runner-factory gnc:test-runner) + (test-begin "test-gnc-optiondb-scheme") + (test-gnc-make-text-option) + (test-end "test-gnc-optiondb-scheme")) + +(define (test-gnc-make-text-option) + (test-begin "test-gnc-test-string-option") + (let* ((option-db (new-GncOptionDB)) + (string-opt (gnc-register-string-option option-db "foo" "bar" "baz" + "Phony Option" "waldo"))) + (test-equal (GncOptionDB-lookup-option option-db "foo" "bar") "waldo") + (delete-GncOptionDB option-db)) + + (test-end "test-gnc-make-string-option")) From d2655d3fb0e4fa78ce0ca3f2f210194ddeac290f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 22 Aug 2019 15:39:39 -0700 Subject: [PATCH 013/298] Remove gnc-option-db business convenience functions. gnc_option_db_lookup_invoice_option used only once, so moved its guts there. The others weren't used at all. --- gnucash/gnome/business-options-gnome.h | 2 +- gnucash/gnome/gnc-plugin-page-report.c | 15 ++-- libgnucash/app-utils/CMakeLists.txt | 2 - libgnucash/app-utils/business-options.c | 92 ------------------------- libgnucash/app-utils/business-options.h | 56 --------------- libgnucash/app-utils/gnc-optiondb.cpp | 19 ----- po/POTFILES.in | 1 - 7 files changed, 11 insertions(+), 176 deletions(-) delete mode 100644 libgnucash/app-utils/business-options.c delete mode 100644 libgnucash/app-utils/business-options.h diff --git a/gnucash/gnome/business-options-gnome.h b/gnucash/gnome/business-options-gnome.h index 32db8740b3..47d518f225 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 diff --git a/gnucash/gnome/gnc-plugin-page-report.c b/gnucash/gnome/gnc-plugin-page-report.c index dff95509de..3c5f4104c6 100644 --- a/gnucash/gnome/gnc-plugin-page-report.c +++ b/gnucash/gnome/gnc-plugin-page-report.c @@ -72,7 +72,6 @@ #include "window-report.h" #include "swig-runtime.h" #include "guile-mappings.h" -#include "business-options.h" #include "gnc-icons.h" #include "print-session.h" @@ -1785,11 +1784,17 @@ 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); + SCM opt_val = gnc_option_db_lookup_option(priv->cur_odb, "General", + "Invoice Number", NULL); + if (opt_val == SCM_UNDEFINED) + return NULL; + +#define FUNC_NAME G_STRFUNC + return SWIG_MustGetPtr(opt_val, SWIG_TypeQuery("_p__gncInvoice"), 1, 0); +#undef FUNC_NAME } #define GNC_PREFS_GROUP_REPORT_PDFEXPORT GNC_PREFS_GROUP_GENERAL_REPORT ".pdf-export" diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt index 6a376b7ea1..7a0df5d351 100644 --- a/libgnucash/app-utils/CMakeLists.txt +++ b/libgnucash/app-utils/CMakeLists.txt @@ -15,7 +15,6 @@ set (app_utils_noinst_HEADERS set (app_utils_HEADERS QuickFill.h - business-options.h file-utils.h gfec.h gnc-basic-gobject.h @@ -52,7 +51,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 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/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index fbc08f410b..641bd1194d 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -137,25 +137,6 @@ GncOptionDB::set_selectable(const char* section, const char* name) void GncOptionDB::commit() { - std::for_each( - m_sections.begin(), m_sections.end(), - [](GncOptionSection section) - { - std::for_each( - section.second.begin(), section.second.end(), - [](GncOptionWrapperPtr option) - { -/* FIXME, not implemented. - if (option->m_option.is_dirty()) - { - option->m_option.commit(); - -* FIXME, no Gtk in gtk_widget_set_value(option->m_widget, -* libgnucash! option->m_option.get_value()); - } -*/ - }); - }); } GncOptionDB* diff --git a/po/POTFILES.in b/po/POTFILES.in index fe2f1ca715..0f92fb3c27 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -509,7 +509,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 From 2ee0edaa163d17d2f2e6bf99b4a246d9e7d6738e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 20 Sep 2019 12:36:45 -0700 Subject: [PATCH 014/298] Use targets instead of variables for GncOption* tests. --- libgnucash/app-utils/test/CMakeLists.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index 17de9412db..9d43b1213b 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -103,15 +103,10 @@ if (HAVE_SRFI64) ${MODULEPATH} ${CMAKE_SOURCE_DIR}/libgnucash/engine ${CMAKE_BINARY_DIR}/common # for config.h - ${GLIB2_INCLUDE_DIRS} - ${GUILE_INCLUDE_DIRS} ) set(swig_gnc_optiondb_LIBS - gncmod-engine - ${GLIB2_LDFLAGS} - ${GUILE_LDFLAGS} - ) + gncmod-engine PkgConfig::GLIB2 PkgConfig::GUILE) target_link_libraries(swig-gnc-optiondb ${swig_gnc_optiondb_LIBS}) target_include_directories(swig-gnc-optiondb From cf0b1da4fa43699b233dd76516ff3971beb366bb Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 20 Sep 2019 12:40:43 -0700 Subject: [PATCH 015/298] Remove GncOptionWrapper. Move the GncOptions into the GncOptionDB. This works with tests but might not with real reports. --- libgnucash/app-utils/gnc-optiondb.cpp | 23 +++++++++++------------ libgnucash/app-utils/gnc-optiondb.hpp | 10 +--------- libgnucash/app-utils/gnc-optiondb.i | 2 +- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 641bd1194d..733691fc2f 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -48,8 +48,7 @@ GncOptionDB::register_option(const char* section, GncOption&& option) GncOptionVec{})); db_section = std::prev(m_sections.end()); } - auto wrapper = std::make_shared(option, nullptr); - db_section->second.emplace_back(wrapper); + db_section->second.emplace_back(std::move(option)); } void @@ -66,9 +65,9 @@ GncOptionDB::unregister_option(const char* section, const char* name) db_section->second.erase( std::remove_if( db_section->second.begin(), db_section->second.end(), - [name](GncOptionWrapperPtr option) -> bool + [name](const GncOption& option) -> bool { - return option->m_option.get_name() == std::string{name}; + return option.get_name() == std::string{name}; })); } } @@ -91,13 +90,13 @@ GncOptionDB::lookup_option(const char* section, const char* name) const return SCM_BOOL_F; auto db_opt = std::find_if( db_section->second.begin(), db_section->second.end(), - [name](GncOptionWrapperPtr option) -> bool + [name](const GncOption& option) -> bool { - return option->m_option.get_name() == std::string{name}; + return option.get_name() == std::string{name}; }); - if (db_opt == db_section->second.end() || !*db_opt) + if (db_opt == db_section->second.end()) return SCM_BOOL_F; - return (*db_opt)->m_option.get_scm_value(); + return db_opt->get_scm_value(); } static const std::string empty_string; @@ -114,13 +113,13 @@ GncOptionDB::lookup_string_option(const char* section, const char* name) const return empty_string; auto db_opt = std::find_if( db_section->second.begin(), db_section->second.end(), - [name](GncOptionWrapperPtr option) -> bool + [name](const GncOption& option) -> bool { - return option->m_option.get_name() == std::string{name}; + return option.get_name() == std::string{name}; }); - if (db_opt == db_section->second.end() || !*db_opt) + if (db_opt == db_section->second.end()) return empty_string; - return (*db_opt)->m_option.get_value(); + return db_opt->get_value(); } bool diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 69503c22c6..9fb25bd097 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -27,16 +27,8 @@ #include "gnc-option.hpp" class GncOptionDB; -struct GncOptionWrapper -{ - GncOptionWrapper(const GncOption& opt, void* ptr) : m_option{opt}, m_widget{ptr} {} - GncOptionWrapper(GncOption&& opt, void* ptr) : m_option{opt}, m_widget{ptr} {} - GncOption m_option; - void* m_widget; /* Don't want widget code in libgnucash! GObject closure?*/ -}; -using GncOptionWrapperPtr = std::shared_ptr; -using GncOptionVec = std::vector; +using GncOptionVec = std::vector; using GncOptionSection = std::pair; using GncOptionSectionPtr = std::shared_ptr; class GncOptionDB diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index d15c8598db..01689ef041 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -12,7 +12,7 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void); %include %ignore OptionClassifier; +%ignore OptionUIItem; %ignore GncOption; -%ignore GncOptionWrapper; %include "gnc-optiondb.hpp" From 083e5b93dfab92ded9487ab34d0c5adf83eb217c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 20 Sep 2019 13:13:53 -0700 Subject: [PATCH 016/298] Replace the CRTP class GncOptionBase with direct calls in its former children. Benefit of CRTP too limited to accept the cost of understanding it. --- libgnucash/app-utils/gnc-option.hpp | 54 ++++++++---------------- libgnucash/app-utils/test/CMakeLists.txt | 7 ++- 2 files changed, 24 insertions(+), 37 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 023f8b36e8..6752c8562e 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -93,42 +93,9 @@ struct OptionClassifier template SCM scm_from_value(ValueType); -/* This design pattern is called the Curiously Recursive Template Pattern, or - * CRTP. See https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern - * for a detailed explanation. - */ -template -class GncOptionBase -{ -public: - ValueType get_value() const - { - return static_cast(*this).get_value(); - } - void set_value(ValueType value) - { - static_cast(*this).set_value(value); - } - ValueType get_default_value() const - { - return static_cast(*this).get_default_value(); - } - SCM get_scm_value() const - { - ValueType value{static_cast(*this).get_value()}; - return scm_from_value(value); - } - SCM get_scm_default_value() const - { - ValueType value{static_cast(*this).get_default_value()}; - return scm_from_value(value); - } -}; - template class GncOptionValue : - public OptionClassifier, - public GncOptionBase> + public OptionClassifier { public: GncOptionValue(const char* section, const char* name, @@ -138,6 +105,14 @@ public: m_value{value}, m_default_value{value} {} ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } + SCM get_scm_value() const + { + return scm_from_value(m_value); + } + SCM get_scm_default_value() const + { + return scm_from_value(m_default_value); + } void set_value(ValueType new_value) { m_value = new_value; } private: ValueType m_value; @@ -146,8 +121,7 @@ private: template class GncOptionValidatedValue : - public OptionClassifier, - public GncOptionBase> + public OptionClassifier { public: GncOptionValidatedValue(const char* section, const char* name, @@ -173,6 +147,14 @@ public: } ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } + SCM get_scm_value() const + { + return scm_from_value(m_value); + } + SCM get_scm_default_value() const + { + return scm_from_value(m_default_value); + } bool validate(ValueType value) { return m_validator(value); } void set_value(ValueType value) { diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index 9d43b1213b..17de9412db 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -103,10 +103,15 @@ if (HAVE_SRFI64) ${MODULEPATH} ${CMAKE_SOURCE_DIR}/libgnucash/engine ${CMAKE_BINARY_DIR}/common # for config.h + ${GLIB2_INCLUDE_DIRS} + ${GUILE_INCLUDE_DIRS} ) set(swig_gnc_optiondb_LIBS - gncmod-engine PkgConfig::GLIB2 PkgConfig::GUILE) + gncmod-engine + ${GLIB2_LDFLAGS} + ${GUILE_LDFLAGS} + ) target_link_libraries(swig-gnc-optiondb ${swig_gnc_optiondb_LIBS}) target_include_directories(swig-gnc-optiondb From 41ef2c5d44ba8a9667fb9393cd89945c22f432c3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 20 Sep 2019 13:56:54 -0700 Subject: [PATCH 017/298] Add OptionUIItem composition class to GncOption. Provides a type and a raw pointer member with accessors. The type is one of enum GncOptionIUType and is either VOID (for internal options that don't get UI items) or one of the widget types specified in dialog-option.c or business-options-gnome.c. --- libgnucash/app-utils/gnc-option.hpp | 126 +++++++++++++++++- .../app-utils/test/gtest-gnc-option.cpp | 33 +++++ 2 files changed, 152 insertions(+), 7 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 6752c8562e..d8400084b7 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -33,6 +33,7 @@ extern "C" } #include #include +#include #include /* @@ -81,6 +82,34 @@ protected: }; */ +enum GncOptionUIType +{ + INTERNAL, + BOOLEAN, + STRING, + TEXT, + CURRENCY, + COMMODITY, + MULTICHOICE, + DATE, + ACCOUNT_LIST, + ACCOUNT_SEL, + LIST, + NUMBER_RANGE, + COLOR, + FONT, + BUDGET, + PIXMAP, + RADIOBUTTON, + DATE_FORMAT, + OWNER, + CUSTOMER, + VENDOR, + EMPLOYEE, + INVOICE, + TAX_TABLE +}; + struct OptionClassifier { std::string m_section; @@ -90,18 +119,59 @@ struct OptionClassifier std::string m_doc_string; }; +/** + * 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 if the ui_item is destroyed elsewhere the ptr will be nulled and + * the type reset to OptionUIType::INTERNAL. + */ +class OptionUIItem +{ +public: + GncOptionUIType get_ui_type() { return m_ui_type; } + const void* get_ui_item() {return m_ui_item; } + void set_ui_item(void* ui_item) + { + if (m_ui_type == GncOptionUIType::INTERNAL) + { + std::string error{"Can't set ui item with void ui type."}; + throw std::logic_error(std::move(error)); + } + m_ui_item = ui_item; + } +protected: + OptionUIItem(GncOptionUIType ui_type) : + m_ui_item{nullptr}, m_ui_type{ui_type} {} + OptionUIItem(const OptionUIItem&) = default; + OptionUIItem(OptionUIItem&&) = default; + ~OptionUIItem() = default; + OptionUIItem& operator=(const OptionUIItem&) = default; + OptionUIItem& operator=(OptionUIItem&&) = default; +private: + void* m_ui_item; + GncOptionUIType m_ui_type; +}; + template SCM scm_from_value(ValueType); template class GncOptionValue : - public OptionClassifier + public OptionClassifier, public OptionUIItem { public: GncOptionValue(const char* section, const char* name, const char* key, const char* doc_string, - ValueType value) : + ValueType value, + GncOptionUIType ui_type = GncOptionUIType::INTERNAL) : OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), m_value{value}, m_default_value{value} {} ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } @@ -121,14 +191,17 @@ private: template class GncOptionValidatedValue : - public OptionClassifier + public OptionClassifier, public OptionUIItem { public: GncOptionValidatedValue(const char* section, const char* name, const char* key, const char* doc_string, ValueType value, - std::functionvalidator) : + std::functionvalidator, + GncOptionUIType ui_type = GncOptionUIType::INTERNAL + ) : OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), m_value{value}, m_default_value{value}, m_validator{validator} { if (!this->validate(value)) @@ -184,10 +257,10 @@ public: template GncOption(const char* section, const char* name, const char* key, const char* doc_string, - ValueType value) : + ValueType value, + GncOptionUIType ui_type = GncOptionUIType::INTERNAL) : m_option{GncOptionValue { - section, name, key, doc_string, value - }} {} + section, name, key, doc_string, value, ui_type}} {} template ValueType get_value() const { @@ -225,6 +298,18 @@ public: { return boost::apply_visitor(GetDocstringVisitor(), m_option); } + void set_ui_item(void* ui_elem) + { + return boost::apply_visitor(SetUIItemVisitor(ui_elem), m_option); + } + const GncOptionUIType get_ui_type() + { + return boost::apply_visitor(GetUITypeVisitor(), m_option); + } + const void* get_ui_item() + { + return boost::apply_visitor(GetUIItemVisitor(), m_option); + } private: template struct GetValueVisitor : public boost::static_visitor @@ -313,6 +398,33 @@ private: return option.m_doc_string; } }; + struct SetUIItemVisitor : public boost::static_visitor<> + { + SetUIItemVisitor(void* ui_item) : m_ui_item{ui_item} {} + template + void operator()(OptionType& option) const { + option.set_ui_item(m_ui_item); + } + private: + void* m_ui_item; + }; + struct GetUITypeVisitor : + public boost::static_visitor + { + template + const GncOptionUIType operator()(OptionType& option) const { + return option.get_ui_type(); + } + }; + struct GetUIItemVisitor : + public boost::static_visitor + { + template + const void* operator()(OptionType& option) const { + return option.get_ui_item(); + } + }; + GncOptionVariant m_option; }; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index bd1712094e..8dffa8afa7 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -192,3 +192,36 @@ TEST(GNCOption, test_currency_setter) gnc_commodity_table_destroy(table); qof_book_destroy(book); } + +class GncUIItem +{ +public: + void set_value(const std::string& value) { m_value = value; } + const std::string& get_value() { return m_value; } +private: + std::string m_value; +}; + +class GncOptionUITest : public ::testing::Test +{ +protected: + GncOptionUITest() : + m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}, + GncOptionUIType::STRING} {} + + 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_set_option_ui_element) +{ + GncUIItem ui_item; + m_option.set_ui_item(&ui_item); + EXPECT_EQ(&ui_item, static_cast(m_option.get_ui_item())); +} From 40361ec854e71220964831356b66ba8c4fb7e304 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 29 Sep 2019 09:56:18 -0700 Subject: [PATCH 018/298] gnc-opption.hpp needs to include . --- libgnucash/app-utils/gnc-option.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index d8400084b7..3260389753 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -34,6 +34,7 @@ extern "C" #include #include #include +#include #include /* From 6deedd441f9fadd6250ee4d1793a4bb05c4b6797 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 29 Sep 2019 09:58:07 -0700 Subject: [PATCH 019/298] Make the ptr returned by GncOption::get_ui_item() const but not the GtkWidget it points to. --- libgnucash/app-utils/gnc-option.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 3260389753..115b43b58e 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -136,7 +136,7 @@ class OptionUIItem { public: GncOptionUIType get_ui_type() { return m_ui_type; } - const void* get_ui_item() {return m_ui_item; } + void* const get_ui_item() {return m_ui_item; } void set_ui_item(void* ui_item) { if (m_ui_type == GncOptionUIType::INTERNAL) @@ -307,7 +307,7 @@ public: { return boost::apply_visitor(GetUITypeVisitor(), m_option); } - const void* get_ui_item() + void* const get_ui_item() { return boost::apply_visitor(GetUIItemVisitor(), m_option); } @@ -418,10 +418,10 @@ private: } }; struct GetUIItemVisitor : - public boost::static_visitor + public boost::static_visitor { template - const void* operator()(OptionType& option) const { + void* const operator()(OptionType& option) const { return option.get_ui_item(); } }; From 3769a356d55d918f22a0a2aac556d82415374676 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 29 Sep 2019 10:06:00 -0700 Subject: [PATCH 020/298] Extract functions find_section and find_option using boost::optional to handle not-found condition. --- libgnucash/app-utils/gnc-optiondb.cpp | 90 +++++++++++++++------------ libgnucash/app-utils/gnc-optiondb.hpp | 16 ++--- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 733691fc2f..4b922363bd 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -23,7 +23,7 @@ #include "gnc-optiondb.hpp" -GncOptionDB::GncOptionDB() : m_default_section{nullptr} {} +GncOptionDB::GncOptionDB() : m_default_section{boost::none} {} GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {} @@ -35,32 +35,25 @@ GncOptionDB::save_to_book(QofBook* book, bool do_clear) const void GncOptionDB::register_option(const char* section, GncOption&& option) { - auto db_section = std::find_if( - m_sections.begin(), m_sections.end(), - [section](GncOptionSection sect) -> bool - { - return sect.first == std::string{section}; - }); + auto db_section = find_section(section); - if (db_section == m_sections.end()) + if (db_section) { - m_sections.emplace_back(std::make_pair(std::string{section}, - GncOptionVec{})); - db_section = std::prev(m_sections.end()); + db_section->second.emplace_back(std::move(option)); + return; } - db_section->second.emplace_back(std::move(option)); + + m_sections.emplace_back(std::make_pair(std::string{section}, + GncOptionVec{})); + auto new_section = std::prev(m_sections.end()); + new_section->second.emplace_back(std::move(option)); } void GncOptionDB::unregister_option(const char* section, const char* name) { - auto db_section = std::find_if( - m_sections.begin(), m_sections.end(), - [section](GncOptionSection sect) -> bool - { - return sect.first == std::string{section}; - }); - if (db_section != m_sections.end()) + auto db_section = find_section(section); + if (db_section) { db_section->second.erase( std::remove_if( @@ -75,10 +68,18 @@ GncOptionDB::unregister_option(const char* section, const char* name) void GncOptionDB::set_default_section(const char* section) { + m_default_section = find_section(section); } -SCM -GncOptionDB::lookup_option(const char* section, const char* name) const +const GncOptionSection* const +GncOptionDB::get_default_section() const noexcept +{ + if (m_default_section) + return &(m_default_section.get()); + return nullptr; +} +boost::optional +GncOptionDB::find_section(const char* section) { auto db_section = std::find_if( m_sections.begin(), m_sections.end(), @@ -87,37 +88,43 @@ GncOptionDB::lookup_option(const char* section, const char* name) const return sect.first == std::string{section}; }); if (db_section == m_sections.end()) - return SCM_BOOL_F; + return boost::none; + return *db_section; +} + +boost::optional +GncOptionDB::find_option(const char* section, const char* name) +{ + auto db_section = find_section(section); + if (!db_section) + return boost::none; auto db_opt = std::find_if( db_section->second.begin(), db_section->second.end(), - [name](const GncOption& option) -> bool + [name](GncOption& option) -> bool { return option.get_name() == std::string{name}; }); if (db_opt == db_section->second.end()) + return boost::none; + return *db_opt; +} + +SCM +GncOptionDB::lookup_option(const char* section, const char* name) +{ + auto db_opt = find_option(section, name); + if (!db_opt) return SCM_BOOL_F; return db_opt->get_scm_value(); } -static const std::string empty_string; std::string -GncOptionDB::lookup_string_option(const char* section, const char* name) const +GncOptionDB::lookup_string_option(const char* section, const char* name) { - auto db_section = std::find_if( - m_sections.begin(), m_sections.end(), - [section](GncOptionSection sect) -> bool - { - return sect.first == std::string{section}; - }); - if (db_section == m_sections.end()) - return empty_string; - auto db_opt = std::find_if( - db_section->second.begin(), db_section->second.end(), - [name](const GncOption& option) -> bool - { - return option.get_name() == std::string{name}; - }); - if (db_opt == db_section->second.end()) + static const std::string empty_string{}; + + auto db_opt = find_option(section, name); + if (!db_opt) return empty_string; return db_opt->get_value(); } @@ -191,7 +198,8 @@ gnc_register_currency_option(GncOptionDB* db, const char* section, { return GNC_IS_COMMODITY (new_value) && gnc_commodity_is_currency(GNC_COMMODITY(new_value)); - } + }, + GncOptionUIType::CURRENCY }}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 9fb25bd097..72dfe0ed0c 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -25,12 +25,13 @@ #define GNC_OPTIONDB_HPP_ #include "gnc-option.hpp" +#include +#include class GncOptionDB; using GncOptionVec = std::vector; using GncOptionSection = std::pair; -using GncOptionSectionPtr = std::shared_ptr; class GncOptionDB { public: @@ -44,18 +45,17 @@ public: 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 GncOptionSectionPtr get_default_section() const noexcept - { - return m_default_section; - } - SCM lookup_option(const char* section, const char* name) const; + const GncOptionSection* const get_default_section() const noexcept; std::string lookup_string_option(const char* section, - const char* name) const; + const char* name); bool set_option(const char* section, const char* name, SCM value); void set_selectable(const char* section, const char* name); void commit(); private: - GncOptionSectionPtr m_default_section; + boost::optional find_section(const char* section); + boost::optional find_option(const char* section, const char* name); + + boost::optional m_default_section; std::vector m_sections; bool m_dirty = false; From 94628097e439e53a86d4df216afff6e4ea2741b9 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 29 Sep 2019 10:11:48 -0700 Subject: [PATCH 021/298] Use GncOptionUIType parameters in gnc_register_option functions. --- libgnucash/app-utils/gnc-optiondb.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 4b922363bd..d4559374db 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -156,7 +156,8 @@ 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}; + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::STRING}; db->register_option(section, std::move(option)); } @@ -165,7 +166,10 @@ gnc_register_text_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { - gnc_register_string_option(db, section, name, key, doc_string, value); + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::TEXT}; + db->register_option(section, std::move(option)); + } void @@ -173,7 +177,8 @@ gnc_register_budget_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncBudget *value) { - GncOption option{section, name, key, doc_string, QOF_INSTANCE(value)}; + GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOptionUIType::BUDGET}; db->register_option(section, std::move(option)); } @@ -182,7 +187,8 @@ gnc_register_commodity_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { - GncOption option{section, name, key, doc_string, QOF_INSTANCE(value)}; + GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOptionUIType::COMMODITY}; db->register_option(section, std::move(option)); } From 6ccb9dbb9e04e251892fe2fd46441e3f7eb4b932 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 29 Sep 2019 10:24:24 -0700 Subject: [PATCH 022/298] Use a std::unique_ptr instead of a raw ptr. Passing references to it to the gnc_register_option functions. Not tested yet with SWIG, might not work. Includes introducing fixtures to gtest-gnc-optiondb.cpp. --- libgnucash/app-utils/gnc-optiondb.hpp | 19 ++++----- .../app-utils/test/gtest-gnc-optiondb.cpp | 41 ++++++++++--------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 72dfe0ed0c..293dc0f638 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -63,6 +63,7 @@ private: std::function m_set_ui_value; }; +using GncOptionDBPtr = std::unique_ptr; /** * Create an empty option database. * @@ -70,33 +71,29 @@ private: * that for Guile. * @return A newly allocated GncOptionDB. Use delete to destroy it. */ -GncOptionDB *gnc_option_db_new(void); +GncOptionDBPtr gnc_option_db_new(void); -void gnc_register_string_option(GncOptionDB* db, const char* section, +void gnc_register_string_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); - -void gnc_register_string_option(GncOptionDB* db, const char* section, +void gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); -void gnc_register_text_option(GncOptionDB* db, const char* section, - const char* name, const char* key, - const char* doc_string, std::string value); - -void gnc_register_budget_option(GncOptionDB* db, const char* section, +void gnc_register_budget_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, GncBudget* value); -void gnc_register_commodity_option(GncOptionDB* db, const char* section, +void gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity* value); -void gnc_register_currency_option(GncOptionDB* db, const char* section, +void gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity* value); + #endif //GNC_OPTIONDB_HPP_ diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index a09659fde7..e65629ee2b 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -24,44 +24,47 @@ #include #include -TEST(GncOptionDB, test_ctor) +class GncOptionDBTest : public ::testing::Test +{ +protected: + GncOptionDBTest() : m_db{gnc_option_db_new()} {} + + GncOptionDBPtr m_db; +}; + +TEST_F(GncOptionDBTest, test_ctor) { EXPECT_NO_THROW ({ GncOptionDB optiondb; }); } -TEST(GncOptionDB, test_register_option) +TEST_F(GncOptionDBTest, test_register_option) { - GncOptionDB optiondb; GncOption option1{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}}; - optiondb.register_option("foo", std::move(option1)); - EXPECT_EQ(optiondb.num_sections(), 1); + m_db->register_option("foo", std::move(option1)); + EXPECT_EQ(m_db->num_sections(), 1); } -TEST(GncOptionDB, test_lookup_string_option) +TEST_F(GncOptionDBTest, test_lookup_string_option) { - GncOptionDB optiondb; GncOption option1{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}}; - optiondb.register_option("foo", std::move(option1)); - EXPECT_STREQ("waldo", optiondb.lookup_string_option("foo", "bar").c_str()); + m_db->register_option("foo", std::move(option1)); + EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str()); } -TEST(GncOptionDB, test_unregister_option) +TEST_F(GncOptionDBTest, test_unregister_option) { - GncOptionDB optiondb; GncOption option1{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}}; - optiondb.register_option("foo", std::move(option1)); - optiondb.unregister_option("foo", "bar"); - EXPECT_TRUE(optiondb.lookup_string_option("foo", "bar").empty()); + 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(GncOptionDB, test_register_string_option) +TEST_F(GncOptionDBTest, test_register_string_option) { - GncOptionDB* db = gnc_option_db_new(); - gnc_register_string_option(db, "foo", "bar", "baz", "Phony Option", + gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option", std::string{"waldo"}); - EXPECT_STREQ("waldo", db->lookup_string_option("foo", "bar").c_str()); - delete db; + EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str()); } From 4146251cc79272df458cbfc509eb2ab5bdc89bc6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 29 Sep 2019 10:28:04 -0700 Subject: [PATCH 023/298] Add GncOptionUIItem manipulation to GncOptionDB. --- libgnucash/app-utils/gnc-optiondb.cpp | 58 +++++++++++++++--- libgnucash/app-utils/gnc-optiondb.hpp | 12 +++- .../app-utils/test/gtest-gnc-optiondb.cpp | 59 +++++++++++++++++++ 3 files changed, 120 insertions(+), 9 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index d4559374db..b7cb5ae74a 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -78,6 +78,50 @@ GncOptionDB::get_default_section() const noexcept return &(m_default_section.get()); return nullptr; } + +void +GncOptionDB::set_ui_item(const char* section, const char* name, void* ui_item) +{ + auto option = find_option(section, name); + if (!option) return; + option->set_ui_item(ui_item); +} + +void* const +GncOptionDB::get_ui_item(const char* section, const char* name) +{ + auto option = find_option(section, name); + if (!option) return nullptr; + return option->get_ui_item(); +} + +GncOptionUIType +GncOptionDB::get_ui_type(const char* section, const char* name) +{ + auto option = find_option(section, name); + if (!option) return GncOptionUIType::INTERNAL; + return option->get_ui_type(); +} + +void +GncOptionDB::set_ui_from_option(const char* section, const char* name, + std::function func) +{ + auto option = find_option(section, name); + if (!option) return; + func(option.get()); +} + +void +GncOptionDB::set_option_from_ui(const char* section, const char* name, + std::function func) +{ + auto option = find_option(section, name); + if (!option) return; + func(option.get()); +} + + boost::optional GncOptionDB::find_section(const char* section) { @@ -145,14 +189,14 @@ GncOptionDB::commit() { } -GncOptionDB* +GncOptionDBPtr gnc_option_db_new(void) { - return new GncOptionDB; + return GncOptionDBPtr{new GncOptionDB}; } void -gnc_register_string_option(GncOptionDB* db, const char* section, +gnc_register_string_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { @@ -162,7 +206,7 @@ gnc_register_string_option(GncOptionDB* db, const char* section, } void -gnc_register_text_option(GncOptionDB* db, const char* section, const char* name, +gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { @@ -173,7 +217,7 @@ gnc_register_text_option(GncOptionDB* db, const char* section, const char* name, } void -gnc_register_budget_option(GncOptionDB* db, const char* section, +gnc_register_budget_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, GncBudget *value) { @@ -183,7 +227,7 @@ gnc_register_budget_option(GncOptionDB* db, const char* section, } void -gnc_register_commodity_option(GncOptionDB* db, const char* section, +gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { @@ -194,7 +238,7 @@ gnc_register_commodity_option(GncOptionDB* db, const char* section, void -gnc_register_currency_option(GncOptionDB* db, const char* section, +gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 293dc0f638..c81fd1cb1b 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -46,6 +46,14 @@ public: void unregister_option(const char* section, const char* name); void set_default_section(const char* section); const GncOptionSection* const get_default_section() const noexcept; + void set_ui_item(const char* section, const char* name, void* ui_item); + void* const get_ui_item(const char* section, const char* name); + GncOptionUIType get_ui_type(const char* section, const char* name); + void set_ui_from_option(const char* section, const char* name, + std::function func); + void set_option_from_ui(const char* section, const char* name, + std::function func); + SCM lookup_option(const char* section, const char* name); std::string lookup_string_option(const char* section, const char* name); bool set_option(const char* section, const char* name, SCM value); @@ -78,8 +86,8 @@ void gnc_register_string_option(const GncOptionDBPtr& db, const char* section, const char* doc_string, std::string value); void gnc_register_text_option(const GncOptionDBPtr& db, const char* section, - const char* name, const char* key, - const char* doc_string, std::string value); + const char* name, const char* key, + const char* doc_string, std::string value); void gnc_register_budget_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index e65629ee2b..86a8390e61 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -68,3 +68,62 @@ TEST_F(GncOptionDBTest, test_register_string_option) std::string{"waldo"}); EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str()); } + +class GncUIType +{ +public: + void set_value(const std::string& value) { m_value = value; } + const std::string& get_value() const { return m_value; } +private: + std::string m_value; +}; + +class GncOptionDBUITest : public ::testing::Test +{ +protected: + GncOptionDBUITest() : m_db{gnc_option_db_new()} + { + 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"}); + } + + GncOptionDBPtr m_db; +}; + +TEST_F(GncOptionDBUITest, test_set_ui_item) +{ + GncUIType entry; + m_db->set_ui_item("foo", "bar", &entry); + EXPECT_EQ(&entry, static_cast(m_db->get_ui_item("foo", "bar"))); +} + +TEST_F(GncOptionDBUITest, test_ui_value_from_option) +{ + GncUIType entry; + const char* value{"waldo"}; + m_db->set_ui_item("foo", "bar", &entry); + m_db->set_ui_from_option("foo", "bar", [](GncOption& option){ + auto ui_item = static_cast(option.get_ui_item()); + ui_item->set_value(option.get_value()); + }); + EXPECT_STREQ(value, entry.get_value().c_str()); +} + +TEST_F(GncOptionDBUITest, test_option_value_from_ui) +{ + GncUIType entry; + const char* value{"pepper"}; + m_db->set_ui_item("foo", "bar", &entry); + entry.set_value(value); + m_db->set_option_from_ui("foo", "bar", [](GncOption& option){ + auto ui_item = static_cast(option.get_ui_item()); + option.set_value(ui_item->get_value()); + }); + EXPECT_STREQ(value, m_db->lookup_string_option("foo", "bar").c_str()); +} From 0a13b4c51868e0995217dec9783aa1a6f966f43c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 29 Sep 2019 12:12:32 -0700 Subject: [PATCH 024/298] Fix up the SWIG wrapper for GncOptionDBPtr. Thanks to Flexo@stackoverflow for https://stackoverflow.com/questions/27693812/how-to-handle-unique-ptrs-with-swig 5 years later, why isn't this in SWIG yet? --- libgnucash/app-utils/gnc-optiondb.i | 37 +++++++++++++++++++ .../app-utils/test/test-gnc-optiondb.scm | 5 +-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 01689ef041..88d690f54d 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -1,7 +1,42 @@ /* * Temporary swig interface file while developing C++ options. + * + * unique_ptr SWIG wrapper from https://stackoverflow.com/questions/27693812/how-to-handle-unique-ptrs-with-swig */ +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(); + }; +} + +%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 + %module sw_gnc_optiondb %{ #include @@ -15,4 +50,6 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void); %ignore OptionUIItem; %ignore GncOption; +wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); + %include "gnc-optiondb.hpp" diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 4d26a803f7..f46b4303e4 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -37,10 +37,9 @@ (define (test-gnc-make-text-option) (test-begin "test-gnc-test-string-option") - (let* ((option-db (new-GncOptionDB)) + (let* ((option-db (gnc-option-db-new)) (string-opt (gnc-register-string-option option-db "foo" "bar" "baz" "Phony Option" "waldo"))) - (test-equal (GncOptionDB-lookup-option option-db "foo" "bar") "waldo") - (delete-GncOptionDB option-db)) + (test-equal (GncOptionDB-lookup-option (GncOptionDBPtr-get option-db) "foo" "bar") "waldo")) (test-end "test-gnc-make-string-option")) From 3296212aef4b87b7a37acf56d460cb6eaf25f1fc Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 1 Oct 2019 16:03:15 -0700 Subject: [PATCH 025/298] Sketch out the rest of the option types. Minimal implentation to get it to compile and pass tests, not functional yet. --- libgnucash/app-utils/gnc-option.cpp | 39 ++++ libgnucash/app-utils/gnc-option.hpp | 89 ++++++- libgnucash/app-utils/gnc-optiondb.cpp | 219 ++++++++++++++++++ libgnucash/app-utils/gnc-optiondb.hpp | 166 ++++++++++++- libgnucash/app-utils/gnc-optiondb.i | 2 +- .../app-utils/test/gtest-gnc-option.cpp | 2 +- 6 files changed, 503 insertions(+), 14 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 485b0d4039..36a5981071 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -23,6 +23,7 @@ //#include "options.h" #include "gnc-option.hpp" +#include template<> SCM scm_from_value(std::string value) @@ -42,6 +43,12 @@ scm_from_value(int64_t value) return scm_from_int64(value); } +template<> SCM +scm_from_value(int value) +{ + return scm_from_int(value); +} + template<> SCM scm_from_value(QofInstance* value) { @@ -51,3 +58,35 @@ scm_from_value(QofInstance* value) return scm_guid; } +template<> SCM +scm_from_value(QofQuery* value) +{ + return SCM_BOOL_F; +} + +template<> SCM +scm_from_value(GncNumeric value) +{ + return SCM_BOOL_F; +} + +template<> SCM +scm_from_value>(std::vector value) +{ + SCM s_list; + for (auto guid : value) + { + auto guid_s = guid_to_string(qof_instance_get_guid(&guid)); + auto scm_guid = scm_from_utf8_string(guid_s); + auto scm_guid_list1 = scm_list_1(scm_guid); + s_list = scm_append(scm_list_2(s_list, scm_guid_list1)); + g_free(guid_s); + } + return s_list; +} + +template<> SCM +scm_from_value(GncMultiChoiceOptionChoices value) +{ + return SCM_BOOL_F; +} diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 115b43b58e..7a8032962b 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -31,6 +31,7 @@ extern "C" #include #include } +#include #include #include #include @@ -108,7 +109,8 @@ enum GncOptionUIType VENDOR, EMPLOYEE, INVOICE, - TAX_TABLE + TAX_TABLE, + QUERY, }; struct OptionClassifier @@ -120,6 +122,9 @@ struct OptionClassifier std::string m_doc_string; }; +using GncMultiChoiceOptionEntry = std::pair; +using GncMultiChoiceOptionChoices = std::vector; + /** * 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 @@ -129,23 +134,32 @@ struct OptionClassifier * 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 if the ui_item is destroyed elsewhere the ptr will be nulled and - * the type reset to OptionUIType::INTERNAL. + * ensure that the ptr will be nulled if the ui_item is destroyed elsewhere. */ class OptionUIItem { public: - GncOptionUIType get_ui_type() { return m_ui_type; } + GncOptionUIType get_ui_type() { return m_ui_type; } void* const get_ui_item() {return m_ui_item; } + void clear_ui_item() { m_ui_item = nullptr; } void set_ui_item(void* ui_item) { if (m_ui_type == GncOptionUIType::INTERNAL) { - std::string error{"Can't set ui item with void ui type."}; + std::string error{"INTERNAL option, setting the UI item forbidden."}; throw std::logic_error(std::move(error)); } m_ui_item = ui_item; } + void make_internal() + { + if (m_ui_item != nullptr) + { + std::string error("Option has a UI Element, can't be INTERNAL."); + throw std::logic_error(std::move(error)); + } + m_ui_type = GncOptionUIType::INTERNAL; + } protected: OptionUIItem(GncOptionUIType ui_type) : m_ui_item{nullptr}, m_ui_type{ui_type} {} @@ -244,11 +258,57 @@ private: ValueType m_validation_data; }; +template +class GncOptionRangeValue : + public OptionClassifier, public OptionUIItem +{ +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}, + OptionUIItem(GncOptionUIType::NUMBER_RANGE), + m_value{value >= min && value <= max ? value : min}, + m_default_value{value >= min && value <= max ? value : min}, + m_min{min}, m_step{step} {} + + ValueType get_value() const { return m_value; } + ValueType get_default_value() const { return m_default_value; } + SCM get_scm_value() const + { + return scm_from_value(m_value); + } + SCM get_scm_default_value() const + { + return scm_from_value(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."); + } +private: + ValueType m_value; + ValueType m_default_value; + ValueType m_min; + ValueType m_max; + ValueType m_step; +}; + using GncOptionVariant = boost::variant, - GncOptionValue, - GncOptionValue, - GncOptionValue, - GncOptionValidatedValue>; + GncOptionValue, + GncOptionValue, + GncOptionValue, + GncOptionValue, + GncOptionValue>, + GncOptionValue, + GncOptionRangeValue, + GncOptionRangeValue, + GncOptionValidatedValue>; class GncOption { public: @@ -311,6 +371,10 @@ public: { return boost::apply_visitor(GetUIItemVisitor(), m_option); } + void make_internal() + { + return boost::apply_visitor(MakeInternalVisitor(), m_option); + } private: template struct GetValueVisitor : public boost::static_visitor @@ -425,6 +489,13 @@ private: return option.get_ui_item(); } }; + struct MakeInternalVisitor : public boost::static_visitor<> + { + template + void operator()(OptionType& option) const { + return option.make_internal(); + } + }; GncOptionVariant m_option; }; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index b7cb5ae74a..250302aa8a 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -184,6 +184,14 @@ GncOptionDB::set_selectable(const char* section, const char* name) { } +void +GncOptionDB::make_internal(const char* section, const char* name) +{ + auto db_opt = find_option(section, name); + if (db_opt) + db_opt->make_internal(); +} + void GncOptionDB::commit() { @@ -216,6 +224,16 @@ gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const ch } +void +gnc_register_font_option(const GncOptionDBPtr& 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(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, @@ -226,6 +244,16 @@ gnc_register_budget_option(const GncOptionDBPtr& db, const char* section, db->register_option(section, std::move(option)); } +void +gnc_register_color_option(const GncOptionDBPtr& 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_commodity_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, @@ -236,6 +264,197 @@ gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section, db->register_option(section, std::move(option)); } +void +gnc_register_simple_boolean_option(const GncOptionDBPtr& 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::INTERNAL}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_complex_boolean_option(const GncOptionDBPtr& 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(const GncOptionDBPtr& 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_liat_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + std::vector value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::ACCOUNT_LIST}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_acount_list_limited_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + std::vector value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::ACCOUNT_LIST}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_account_sel_limited_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + std::vector value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::ACCOUNT_SEL}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + GncMultiChoiceOptionChoices&& value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::MULTICHOICE}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_list_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + GncMultiChoiceOptionChoices&& value) +{ + GncOption option{section, name, key, doc_string, value, + 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. */ +void +gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, int value, int min, + int max, int step) +{ + GncOption option{GncOptionRangeValue{section, name, key, doc_string, + value, min, max, step}}; + db->register_option(section, std::move(option)); +} + +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) +{ + GncOption option{GncOptionRangeValue{section, name, key, doc_string, + value, 100, 20000, 5}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_query_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, QofQuery* value) +{ + GncOption option{section, name, key, doc_string, value, + GncOptionUIType::QUERY}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_internal_option(const GncOptionDBPtr& 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::INTERNAL}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, GncInvoice* value) +{ + GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOptionUIType::INVOICE}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_owner_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, GncOwner* value) +{ + GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOptionUIType::OWNER}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, GncTaxTable* value) +{ + GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOptionUIType::TAX_TABLE}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_counter_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, int value) +{ + GncOption option{GncOptionRangeValue{section, name, key, doc_string, + value, 0, 999999999, 1}}; + db->register_option(section, std::move(option)); +} + +void +gnc_register_counter_format_option(const GncOptionDBPtr& 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(const GncOptionDBPtr& 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(const GncOptionDBPtr& db, const char* section, diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index c81fd1cb1b..3b723dd265 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -27,6 +27,12 @@ #include "gnc-option.hpp" #include #include +extern "C" +{ +#include +#include +#include +} class GncOptionDB; @@ -58,6 +64,7 @@ public: const char* name); bool set_option(const char* section, const char* name, SCM value); void set_selectable(const char* section, const char* name); + void make_internal(const char* section, const char* name); void commit(); private: boost::optional find_section(const char* section); @@ -89,19 +96,172 @@ void gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); +void gnc_register_font_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + void gnc_register_budget_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, GncBudget* value); -void gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section, - const char* name, const char* key, - const char* doc_string, +void gnc_register_commodity_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, gnc_commodity* value); +/* Complex boolean options are the same as simple boolean options 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. + */ +void gnc_register_simple_boolean_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + bool value); + +void gnc_register_complex_boolean_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, + const char* doc_string, + bool value); + +void gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + +/* 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. + */ +void gnc_register_acount_list_limited_option(const GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + std::vector value); + +/* Just like gnc:make-account-list-limited-option except it does not limit the + * types of accounts that are available to the user. + */ +void gnc_register_account_liat_option(const GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + std::vector value); + +/* 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. + */ +void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, + std::vector value); + +/* 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. + * + * 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"). + */ + +void gnc_register_multichoice_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + GncMultiChoiceOptionChoices&& value); + +/* List options use the option-data in the same way as multichoice options. List + * options allow the user to select more than one option. + */ +void gnc_register_list_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + GncMultiChoiceOptionChoices&& value); + +/* Number range options use the option-data as a list whose elements are: + * (lower-bound upper-bound step-size). +*/ +void gnc_register_number_range_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + int value, int min, int max, int step); + +/* Number plot size options are a convenience wrapper on number range options + * with fixed min, max, and step. +*/ +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); + +void gnc_register_query_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, QofQuery* value); + +/* 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. + */ +void gnc_register_color_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + +void gnc_register_internal_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, std::string value); + + void gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity* value); +void gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, GncInvoice* value); + +void gnc_register_owner_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, GncOwner* value); + +void gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, GncTaxTable* value); + +void gnc_register_counter_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, int value); + +void gnc_register_counter_format_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + std::string value); + +void gnc_register_dateformat_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + std::string value); #endif //GNC_OPTIONDB_HPP_ diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 88d690f54d..ade5c73e29 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -21,7 +21,7 @@ namespace std { void reset (pointer __p=pointer()); void swap (unique_ptr &__u); pointer get () const; - operator bool () const; +// operator bool () const; ~unique_ptr(); }; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 8dffa8afa7..6c3a518899 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -219,7 +219,7 @@ TEST_F(GncOptionUI, test_option_ui_type) EXPECT_EQ(GncOptionUIType::STRING, m_option.get_ui_type()); } -TEST_F(GncOptionUI, test_set_option_ui_element) +TEST_F(GncOptionUI, test_set_option_ui_item) { GncUIItem ui_item; m_option.set_ui_item(&ui_item); From c5fac51a8b6bf316d47bd6e613efaf1f8f277e87 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 3 Oct 2019 13:30:49 -0700 Subject: [PATCH 026/298] Change the type of OptionUIItem's m_ui_item from void* to GncOptionUIItem. A locally-opaque class wrapping whatever sort of widget ptr one needs. Thanks, warlord! --- libgnucash/app-utils/gnc-option.hpp | 20 ++++++----- libgnucash/app-utils/gnc-optiondb.cpp | 5 +-- libgnucash/app-utils/gnc-optiondb.hpp | 8 ++--- .../app-utils/test/gtest-gnc-option.cpp | 12 +++++-- .../app-utils/test/gtest-gnc-optiondb.cpp | 36 ++++++++++++++----- 5 files changed, 56 insertions(+), 25 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 7a8032962b..e99c8a2130 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -125,6 +125,8 @@ struct OptionClassifier using GncMultiChoiceOptionEntry = std::pair; using GncMultiChoiceOptionChoices = std::vector; +class GncOptionUIItem; + /** * 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 @@ -140,9 +142,9 @@ class OptionUIItem { public: GncOptionUIType get_ui_type() { return m_ui_type; } - void* const get_ui_item() {return m_ui_item; } + GncOptionUIItem* const get_ui_item() {return m_ui_item; } void clear_ui_item() { m_ui_item = nullptr; } - void set_ui_item(void* ui_item) + void set_ui_item(GncOptionUIItem* ui_item) { if (m_ui_type == GncOptionUIType::INTERNAL) { @@ -169,7 +171,7 @@ protected: OptionUIItem& operator=(const OptionUIItem&) = default; OptionUIItem& operator=(OptionUIItem&&) = default; private: - void* m_ui_item; + GncOptionUIItem* m_ui_item; GncOptionUIType m_ui_type; }; @@ -359,7 +361,7 @@ public: { return boost::apply_visitor(GetDocstringVisitor(), m_option); } - void set_ui_item(void* ui_elem) + void set_ui_item(GncOptionUIItem* ui_elem) { return boost::apply_visitor(SetUIItemVisitor(ui_elem), m_option); } @@ -367,7 +369,7 @@ public: { return boost::apply_visitor(GetUITypeVisitor(), m_option); } - void* const get_ui_item() + GncOptionUIItem* const get_ui_item() { return boost::apply_visitor(GetUIItemVisitor(), m_option); } @@ -465,13 +467,13 @@ private: }; struct SetUIItemVisitor : public boost::static_visitor<> { - SetUIItemVisitor(void* ui_item) : m_ui_item{ui_item} {} + SetUIItemVisitor(GncOptionUIItem* ui_item) : m_ui_item{ui_item} {} template void operator()(OptionType& option) const { option.set_ui_item(m_ui_item); } private: - void* m_ui_item; + GncOptionUIItem* m_ui_item; }; struct GetUITypeVisitor : public boost::static_visitor @@ -482,10 +484,10 @@ private: } }; struct GetUIItemVisitor : - public boost::static_visitor + public boost::static_visitor { template - void* const operator()(OptionType& option) const { + GncOptionUIItem* const operator()(OptionType& option) const { return option.get_ui_item(); } }; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 250302aa8a..3dde5739e1 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -80,14 +80,15 @@ GncOptionDB::get_default_section() const noexcept } void -GncOptionDB::set_ui_item(const char* section, const char* name, void* ui_item) +GncOptionDB::set_ui_item(const char* section, const char* name, + GncOptionUIItem* ui_item) { auto option = find_option(section, name); if (!option) return; option->set_ui_item(ui_item); } -void* const +GncOptionUIItem* const GncOptionDB::get_ui_item(const char* section, const char* name) { auto option = find_option(section, name); diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 3b723dd265..bf0cdade37 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -52,8 +52,8 @@ public: void unregister_option(const char* section, const char* name); void set_default_section(const char* section); const GncOptionSection* const get_default_section() const noexcept; - void set_ui_item(const char* section, const char* name, void* ui_item); - void* const get_ui_item(const char* section, const char* name); + void set_ui_item(const char* section, const char* name, GncOptionUIItem* ui_item); + GncOptionUIItem* const get_ui_item(const char* section, const char* name); GncOptionUIType get_ui_type(const char* section, const char* name); void set_ui_from_option(const char* section, const char* name, std::function func); @@ -74,8 +74,8 @@ private: std::vector m_sections; bool m_dirty = false; - std::function m_get_ui_value; - std::function m_set_ui_value; + std::function m_get_ui_value; + std::function m_set_ui_value; }; using GncOptionDBPtr = std::unique_ptr; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 6c3a518899..9267ba630f 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -202,6 +202,13 @@ private: std::string m_value; }; +class GncOptionUIItem +{ +public: + GncOptionUIItem(GncUIItem* widget) : m_widget{widget} {} + GncUIItem* m_widget; +}; + class GncOptionUITest : public ::testing::Test { protected: @@ -222,6 +229,7 @@ TEST_F(GncOptionUI, test_option_ui_type) TEST_F(GncOptionUI, test_set_option_ui_item) { GncUIItem ui_item; - m_option.set_ui_item(&ui_item); - EXPECT_EQ(&ui_item, static_cast(m_option.get_ui_item())); + GncOptionUIItem option_ui_item{&ui_item}; + m_option.set_ui_item(&option_ui_item); + EXPECT_EQ(&ui_item, m_option.get_ui_item()->m_widget); } diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 86a8390e61..be6221f60b 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -78,6 +78,23 @@ private: std::string m_value; }; +class GncOptionUIItem +{ +public: + GncOptionUIItem(GncUIType* widget) : m_widget{widget} {} + GncUIType* m_widget; +}; + +class GncOptionUITest : public ::testing::Test +{ +protected: + GncOptionUITest() : + m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}, + GncOptionUIType::STRING} {} + + GncOption m_option; +}; + class GncOptionDBUITest : public ::testing::Test { protected: @@ -99,18 +116,20 @@ protected: TEST_F(GncOptionDBUITest, test_set_ui_item) { GncUIType entry; - m_db->set_ui_item("foo", "bar", &entry); - EXPECT_EQ(&entry, static_cast(m_db->get_ui_item("foo", "bar"))); + GncOptionUIItem ui_item(&entry); + m_db->set_ui_item("foo", "bar", &ui_item); + EXPECT_EQ(&entry, m_db->get_ui_item("foo", "bar")->m_widget); } TEST_F(GncOptionDBUITest, test_ui_value_from_option) { GncUIType entry; + GncOptionUIItem ui_item(&entry); const char* value{"waldo"}; - m_db->set_ui_item("foo", "bar", &entry); + m_db->set_ui_item("foo", "bar", &ui_item); m_db->set_ui_from_option("foo", "bar", [](GncOption& option){ - auto ui_item = static_cast(option.get_ui_item()); - ui_item->set_value(option.get_value()); + auto new_ui_item = option.get_ui_item(); + new_ui_item->m_widget->set_value(option.get_value()); }); EXPECT_STREQ(value, entry.get_value().c_str()); } @@ -118,12 +137,13 @@ TEST_F(GncOptionDBUITest, test_ui_value_from_option) TEST_F(GncOptionDBUITest, test_option_value_from_ui) { GncUIType entry; + GncOptionUIItem ui_item(&entry); const char* value{"pepper"}; - m_db->set_ui_item("foo", "bar", &entry); + m_db->set_ui_item("foo", "bar", &ui_item); entry.set_value(value); m_db->set_option_from_ui("foo", "bar", [](GncOption& option){ - auto ui_item = static_cast(option.get_ui_item()); - option.set_value(ui_item->get_value()); + auto new_ui_item = option.get_ui_item()->m_widget; + option.set_value(new_ui_item->get_value()); }); EXPECT_STREQ(value, m_db->lookup_string_option("foo", "bar").c_str()); } From e51faff3e3dd4b4510f8da7edea6ddb047eaf044 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 12 Oct 2019 18:10:43 -0700 Subject: [PATCH 027/298] Throw an exception if one tries to set a GncOption with an unsupported type. --- libgnucash/app-utils/gnc-option.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index e99c8a2130..68d45d3c6b 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -418,6 +418,11 @@ private: } template void operator()(OptionType& option) const { + std::string msg{"Attempt to set option of type "}; + msg += typeid(OptionType).name(); + msg += " with value of type "; + msg += typeid(m_value).name(); + throw std::invalid_argument(msg); } private: ValueType m_value; From 935ce6db99461415bf61e292362ae41118c3c4a3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 12 Oct 2019 18:12:46 -0700 Subject: [PATCH 028/298] Move the SCM option value conversion from the GncOptionValue classes to GncOption. --- libgnucash/app-utils/gnc-option.hpp | 30 ++++------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 68d45d3c6b..9c5daa5440 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -192,14 +192,6 @@ public: m_value{value}, m_default_value{value} {} ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } - SCM get_scm_value() const - { - return scm_from_value(m_value); - } - SCM get_scm_default_value() const - { - return scm_from_value(m_default_value); - } void set_value(ValueType new_value) { m_value = new_value; } private: ValueType m_value; @@ -237,14 +229,6 @@ public: } ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } - SCM get_scm_value() const - { - return scm_from_value(m_value); - } - SCM get_scm_default_value() const - { - return scm_from_value(m_default_value); - } bool validate(ValueType value) { return m_validator(value); } void set_value(ValueType value) { @@ -277,14 +261,6 @@ public: ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } - SCM get_scm_value() const - { - return scm_from_value(m_value); - } - SCM get_scm_default_value() const - { - return scm_from_value(m_default_value); - } bool validate(ValueType value) { return value >= m_min && value <= m_max; } void set_value(ValueType value) { @@ -431,14 +407,16 @@ private: { template SCM operator()(OptionType& option) const { - return option.get_scm_value(); + auto value{option.get_value()}; + return scm_from_value(value); } }; struct GetSCMDefaultVisitor : public boost::static_visitor { template SCM operator()(OptionType& option) const { - return option.get_scm_default_value(); + auto value{option.get_value()}; + return scm_from_value(value); } }; struct GetSectionVisitor : public boost::static_visitor From 9cdcaf0da876dca96630f6e025bdb77843607d29 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 12 Oct 2019 18:14:22 -0700 Subject: [PATCH 029/298] Remove GncOptionDB::set_selectable and convert set_option to a template. --- libgnucash/app-utils/gnc-optiondb.cpp | 11 ----------- libgnucash/app-utils/gnc-optiondb.hpp | 21 +++++++++++++++++++-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 3dde5739e1..80fb768919 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -174,17 +174,6 @@ GncOptionDB::lookup_string_option(const char* section, const char* name) return db_opt->get_value(); } -bool -GncOptionDB::set_option(const char* section, const char* name, SCM value) -{ - return false; -} - -void -GncOptionDB::set_selectable(const char* section, const char* name) -{ -} - void GncOptionDB::make_internal(const char* section, const char* name) { diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index bf0cdade37..abc783e055 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -26,6 +26,7 @@ #include "gnc-option.hpp" #include +#include #include extern "C" { @@ -62,8 +63,24 @@ public: SCM lookup_option(const char* section, const char* name); std::string lookup_string_option(const char* section, const char* name); - bool set_option(const char* section, const char* name, SCM value); - void set_selectable(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(); private: From cee3cdaff9a29a3f64e4d38faad6f96dc5873be4 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 12 Oct 2019 18:16:05 -0700 Subject: [PATCH 030/298] Instantiate GncOption::set_option for guile, initial types string and int. For proof-of-concept. Guile obviously doesn't know about templates. --- libgnucash/app-utils/gnc-optiondb.i | 6 +++++- libgnucash/app-utils/test/test-gnc-optiondb.scm | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index ade5c73e29..3ecee41f14 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -51,5 +51,9 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void); %ignore GncOption; wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); - %include "gnc-optiondb.hpp" + +%extend GncOptionDB { + %template(set_option_string) set_option; + %template(set_option_int) set_option; + }; diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index f46b4303e4..cce651fb00 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -40,6 +40,10 @@ (let* ((option-db (gnc-option-db-new)) (string-opt (gnc-register-string-option option-db "foo" "bar" "baz" "Phony Option" "waldo"))) - (test-equal (GncOptionDB-lookup-option (GncOptionDBPtr-get option-db) "foo" "bar") "waldo")) + (test-equal "waldo" (GncOptionDB-lookup-option + (GncOptionDBPtr-get option-db) "foo" "bar")) + (GncOptionDB-set-option-string (GncOptionDBPtr-get option-db) "foo" "bar" "pepper") + (test-equal "pepper" (GncOptionDB-lookup-option + (GncOptionDBPtr-get option-db) "foo" "bar"))) (test-end "test-gnc-make-string-option")) From 16d1f0655bc69671bce3ed21eeb3a454fee8f3cf Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 12 Oct 2019 18:17:09 -0700 Subject: [PATCH 031/298] Get libswig-gnc-optiondb to install in the right place on Windows. --- libgnucash/app-utils/test/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index 17de9412db..d8fe3e53f8 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -117,6 +117,12 @@ if (HAVE_SRFI64) target_include_directories(swig-gnc-optiondb PRIVATE ${swig_gnc_optiondb_INCLUDES}) + install(TARGETS swig-gnc-optiondb + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + gnc_add_scheme_test_targets(scm-test-gnc-optiondb "test-gnc-optiondb.scm" "tests" From 694a15ed21ff8f21b521ea64c5f901dfc0b539bf Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 13 Oct 2019 09:40:08 -0700 Subject: [PATCH 032/298] Extract SCM GncOptionDB::lookup_option to gnc-optiondb.i. --- libgnucash/app-utils/gnc-optiondb.cpp | 9 --------- libgnucash/app-utils/gnc-optiondb.hpp | 4 +--- libgnucash/app-utils/gnc-optiondb.i | 8 ++++++++ 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 80fb768919..169228c060 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -154,15 +154,6 @@ GncOptionDB::find_option(const char* section, const char* name) return *db_opt; } -SCM -GncOptionDB::lookup_option(const char* section, const char* name) -{ - auto db_opt = find_option(section, name); - if (!db_opt) - return SCM_BOOL_F; - return db_opt->get_scm_value(); -} - std::string GncOptionDB::lookup_string_option(const char* section, const char* name) { diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index abc783e055..1b2de18f39 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -60,7 +60,6 @@ public: std::function func); void set_option_from_ui(const char* section, const char* name, std::function func); - SCM lookup_option(const char* section, const char* name); std::string lookup_string_option(const char* section, const char* name); template @@ -83,10 +82,9 @@ public: // void set_selectable(const char* section, const char* name); void make_internal(const char* section, const char* name); void commit(); -private: boost::optional find_section(const char* section); boost::optional find_option(const char* section, const char* name); - +private: boost::optional m_default_section; std::vector m_sections; bool m_dirty = false; diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 3ecee41f14..ad81c0450d 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -54,6 +54,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %include "gnc-optiondb.hpp" %extend GncOptionDB { + SCM lookup_option(const char* section, const char* name) + { + auto db_opt = $self->find_option(section, name); + if (!db_opt) + return SCM_BOOL_F; + return db_opt->get_scm_value(); + } + %template(set_option_string) set_option; %template(set_option_int) set_option; }; From d544f852568cdaa271980153e699cbd3ec242918 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 15 Oct 2019 14:49:49 -0700 Subject: [PATCH 033/298] Replace boost::variant and boost::optional with the C++17 std equivs. --- libgnucash/app-utils/gnc-option.cpp | 65 ------ libgnucash/app-utils/gnc-option.hpp | 284 ++++++++++++-------------- libgnucash/app-utils/gnc-optiondb.cpp | 38 ++-- libgnucash/app-utils/gnc-optiondb.hpp | 10 +- libgnucash/app-utils/gnc-optiondb.i | 2 +- 5 files changed, 160 insertions(+), 239 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 36a5981071..8543efa20c 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -25,68 +25,3 @@ #include "gnc-option.hpp" #include -template<> SCM -scm_from_value(std::string value) -{ - return scm_from_utf8_string(value.c_str()); -} - -template<> SCM -scm_from_value(bool value) -{ - return value ? SCM_BOOL_T : SCM_BOOL_F; -} - -template<> SCM -scm_from_value(int64_t value) -{ - return scm_from_int64(value); -} - -template<> SCM -scm_from_value(int value) -{ - return scm_from_int(value); -} - -template<> SCM -scm_from_value(QofInstance* value) -{ - auto guid = guid_to_string(qof_instance_get_guid(value)); - auto scm_guid = scm_from_utf8_string(guid); - g_free(guid); - return scm_guid; -} - -template<> SCM -scm_from_value(QofQuery* value) -{ - return SCM_BOOL_F; -} - -template<> SCM -scm_from_value(GncNumeric value) -{ - return SCM_BOOL_F; -} - -template<> SCM -scm_from_value>(std::vector value) -{ - SCM s_list; - for (auto guid : value) - { - auto guid_s = guid_to_string(qof_instance_get_guid(&guid)); - auto scm_guid = scm_from_utf8_string(guid_s); - auto scm_guid_list1 = scm_list_1(scm_guid); - s_list = scm_append(scm_list_2(s_list, scm_guid_list1)); - g_free(guid_s); - } - return s_list; -} - -template<> SCM -scm_from_value(GncMultiChoiceOptionChoices value) -{ - return SCM_BOOL_F; -} diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 9c5daa5440..045bd55413 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -32,11 +32,14 @@ extern "C" #include } #include +#include #include #include +#include +#include #include #include -#include +#include /* * Unused base class to document the structure of the current Scheme option @@ -141,8 +144,8 @@ class GncOptionUIItem; class OptionUIItem { public: - GncOptionUIType get_ui_type() { return m_ui_type; } - GncOptionUIItem* const get_ui_item() {return m_ui_item; } + GncOptionUIType get_ui_type() const { return m_ui_type; } + GncOptionUIItem* const get_ui_item() const {return m_ui_item; } void clear_ui_item() { m_ui_item = nullptr; } void set_ui_item(GncOptionUIItem* ui_item) { @@ -175,8 +178,71 @@ private: GncOptionUIType m_ui_type; }; -template -SCM scm_from_value(ValueType); +inline SCM +scm_from_value(std::string value) +{ + return scm_from_utf8_string(value.c_str()); +} + +inline SCM +scm_from_value(bool value) +{ + return value ? SCM_BOOL_T : SCM_BOOL_F; +} + +inline SCM +scm_from_value(int64_t value) +{ + return scm_from_int64(value); +} + +inline SCM +scm_from_value(int value) +{ + return scm_from_int(value); +} + +inline SCM +scm_from_value(QofInstance* value) +{ + auto guid = guid_to_string(qof_instance_get_guid(value)); + auto scm_guid = scm_from_utf8_string(guid); + g_free(guid); + return scm_guid; +} + +inline SCM +scm_from_value(QofQuery* value) +{ + return SCM_BOOL_F; +} + +inline SCM +scm_from_value(GncNumeric value) +{ + return SCM_BOOL_F; +} + +inline SCM +scm_from_value(std::vector value) +{ + SCM s_list; + for (auto guid : value) + { + auto guid_s = guid_to_string(qof_instance_get_guid(&guid)); + auto scm_guid = scm_from_utf8_string(guid_s); + auto scm_guid_list1 = scm_list_1(scm_guid); + s_list = scm_append(scm_list_2(s_list, scm_guid_list1)); + g_free(guid_s); + } + return s_list; +} + +inline SCM +scm_from_value(GncMultiChoiceOptionChoices value) +{ + return SCM_BOOL_F; +} template class GncOptionValue : @@ -190,6 +256,10 @@ public: OptionClassifier{section, name, key, doc_string}, OptionUIItem(ui_type), m_value{value}, m_default_value{value} {} + GncOptionValue(const GncOptionValue&) = default; + GncOptionValue(GncOptionValue&&) = default; + GncOptionValue& operator=(const GncOptionValue&) = default; + GncOptionValue& operator=(GncOptionValue&&) = default; ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } void set_value(ValueType new_value) { m_value = new_value; } @@ -227,6 +297,10 @@ public: if (!this->validate(value)) throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); } + GncOptionValidatedValue(const GncOptionValidatedValue&) = default; + GncOptionValidatedValue(GncOptionValidatedValue&&) = default; + GncOptionValidatedValue& operator=(const GncOptionValidatedValue&) = default; + GncOptionValidatedValue& operator=(GncOptionValidatedValue&&) = default; ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } bool validate(ValueType value) { return m_validator(value); } @@ -259,6 +333,10 @@ public: m_default_value{value >= min && value <= max ? value : min}, m_min{min}, 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; } @@ -277,7 +355,7 @@ private: ValueType m_step; }; -using GncOptionVariant = boost::variant, +using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue, @@ -303,185 +381,93 @@ public: template ValueType get_value() const { - return boost::apply_visitor(GetValueVisitor(), m_option); + return std::visit([](const auto& option)->ValueType { + if constexpr (std::is_same_v) + return option.get_value(); + else + return ValueType {}; + }, m_option); } template ValueType get_default_value() const { - return boost::apply_visitor(GetDefaultValueVisitor(), m_option); + return std::visit([](const auto& option)->ValueType { + if constexpr (std::is_same_v) + return option.get_default_value(); + else + return ValueType {}; + }, m_option); + } SCM get_scm_value() const { - return boost::apply_visitor(GetSCMVisitor(), m_option); + return std::visit([](const auto& option)->SCM { + auto value{option.get_value()}; + return scm_from_value(value); + }, m_option); } SCM get_scm_default_value() const { - return boost::apply_visitor(GetSCMDefaultVisitor(), m_option); + return std::visit([](const auto& option)->SCM { + auto value{option.get_default_value()}; + return scm_from_value(value); + }, m_option); } template void set_value(ValueType value) { - boost::apply_visitor(SetValueVisitor(value), m_option); + std::visit([value](auto& option) { + if constexpr (std::is_same_v) + option.set_value(value); + }, m_option); } const std::string& get_section() const { - return boost::apply_visitor(GetSectionVisitor(), m_option); + return std::visit([](const auto& option)->const std::string& { + return option.m_section; + }, m_option); } const std::string& get_name() const { - return boost::apply_visitor(GetNameVisitor(), m_option); + return std::visit([](const auto& option)->const std::string& { + return option.m_name; + }, m_option); } const std::string& get_key() const { - return boost::apply_visitor(GetKeyVisitor(), m_option); + return std::visit([](const auto& option)->const std::string& { + return option.m_sort_tag; + }, m_option); } const std::string& get_docstring() const { - return boost::apply_visitor(GetDocstringVisitor(), m_option); + return std::visit([](const auto& option)->const std::string& { + return option.m_doc_string; + }, m_option); } void set_ui_item(GncOptionUIItem* ui_elem) { - return boost::apply_visitor(SetUIItemVisitor(ui_elem), m_option); + std::visit([ui_elem](auto& option) { + option.set_ui_item(ui_elem); + }, m_option); } - const GncOptionUIType get_ui_type() + const GncOptionUIType get_ui_type() const { - return boost::apply_visitor(GetUITypeVisitor(), m_option); + return std::visit([](const auto& option)->GncOptionUIType { + return option.get_ui_type(); + }, m_option); } - GncOptionUIItem* const get_ui_item() + GncOptionUIItem* const get_ui_item() const { - return boost::apply_visitor(GetUIItemVisitor(), m_option); + return std::visit([](const auto& option)->GncOptionUIItem* { + return option.get_ui_item(); + }, m_option); } void make_internal() { - return boost::apply_visitor(MakeInternalVisitor(), m_option); + std::visit([](auto& option) { + option.make_internal(); + }, m_option); } private: - template - struct GetValueVisitor : public boost::static_visitor - { - ValueType operator()(const GncOptionValue& option) const { - return option.get_value(); - } - ValueType operator()(const GncOptionValidatedValue& option) const { - return option.get_value(); - } - template - ValueType operator()(OptionType& option) const { - return ValueType{}; - } - }; - template - struct GetDefaultValueVisitor : public boost::static_visitor - { - ValueType operator()(const GncOptionValue& option) const { - return option.get_default_value(); - } - ValueType operator()(const GncOptionValidatedValue& option) const { - return option.get_default_value(); - } - template - ValueType operator()(OptionType& option) const { - return ValueType(); - } - }; - template - struct SetValueVisitor : public boost::static_visitor<> - { - SetValueVisitor(ValueType value) : m_value{value} {} - void operator()(GncOptionValue& option) const { - option.set_value(m_value); - } - void operator()(GncOptionValidatedValue& option) const { - option.set_value(m_value); - } - template - void operator()(OptionType& option) const { - std::string msg{"Attempt to set option of type "}; - msg += typeid(OptionType).name(); - msg += " with value of type "; - msg += typeid(m_value).name(); - throw std::invalid_argument(msg); - } - private: - ValueType m_value; - }; - struct GetSCMVisitor : public boost::static_visitor - { - template - SCM operator()(OptionType& option) const { - auto value{option.get_value()}; - return scm_from_value(value); - } - }; - struct GetSCMDefaultVisitor : public boost::static_visitor - { - template - SCM operator()(OptionType& option) const { - auto value{option.get_value()}; - return scm_from_value(value); - } - }; - struct GetSectionVisitor : public boost::static_visitor - { - template - const std::string& operator()(OptionType& option) const { - return option.m_section; - } - }; - struct GetNameVisitor : public boost::static_visitor - { - template - const std::string& operator()(OptionType& option) const { - return option.m_name; - } - }; - struct GetKeyVisitor : public boost::static_visitor - { - template - const std::string& operator()(OptionType& option) const { - return option.m_sort_tag; - } - }; - struct GetDocstringVisitor : - public boost::static_visitor - { - template - const std::string& operator()(OptionType& option) const { - return option.m_doc_string; - } - }; - struct SetUIItemVisitor : public boost::static_visitor<> - { - SetUIItemVisitor(GncOptionUIItem* ui_item) : m_ui_item{ui_item} {} - template - void operator()(OptionType& option) const { - option.set_ui_item(m_ui_item); - } - private: - GncOptionUIItem* m_ui_item; - }; - struct GetUITypeVisitor : - public boost::static_visitor - { - template - const GncOptionUIType operator()(OptionType& option) const { - return option.get_ui_type(); - } - }; - struct GetUIItemVisitor : - public boost::static_visitor - { - template - GncOptionUIItem* const operator()(OptionType& option) const { - return option.get_ui_item(); - } - }; - struct MakeInternalVisitor : public boost::static_visitor<> - { - template - void operator()(OptionType& option) const { - return option.make_internal(); - } - }; - GncOptionVariant m_option; }; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 169228c060..9276513ac1 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -23,7 +23,7 @@ #include "gnc-optiondb.hpp" -GncOptionDB::GncOptionDB() : m_default_section{boost::none} {} +GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {} GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {} @@ -39,7 +39,7 @@ GncOptionDB::register_option(const char* section, GncOption&& option) if (db_section) { - db_section->second.emplace_back(std::move(option)); + db_section->get().second.emplace_back(std::move(option)); return; } @@ -55,9 +55,9 @@ GncOptionDB::unregister_option(const char* section, const char* name) auto db_section = find_section(section); if (db_section) { - db_section->second.erase( + db_section->get().second.erase( std::remove_if( - db_section->second.begin(), db_section->second.end(), + db_section->get().second.begin(), db_section->get().second.end(), [name](const GncOption& option) -> bool { return option.get_name() == std::string{name}; @@ -75,7 +75,7 @@ const GncOptionSection* const GncOptionDB::get_default_section() const noexcept { if (m_default_section) - return &(m_default_section.get()); + return &(m_default_section.value().get()); return nullptr; } @@ -85,7 +85,7 @@ GncOptionDB::set_ui_item(const char* section, const char* name, { auto option = find_option(section, name); if (!option) return; - option->set_ui_item(ui_item); + option->get().set_ui_item(ui_item); } GncOptionUIItem* const @@ -93,7 +93,7 @@ GncOptionDB::get_ui_item(const char* section, const char* name) { auto option = find_option(section, name); if (!option) return nullptr; - return option->get_ui_item(); + return option->get().get_ui_item(); } GncOptionUIType @@ -101,7 +101,7 @@ GncOptionDB::get_ui_type(const char* section, const char* name) { auto option = find_option(section, name); if (!option) return GncOptionUIType::INTERNAL; - return option->get_ui_type(); + return option->get().get_ui_type(); } void @@ -110,7 +110,7 @@ GncOptionDB::set_ui_from_option(const char* section, const char* name, { auto option = find_option(section, name); if (!option) return; - func(option.get()); + func(option->get()); } void @@ -119,11 +119,11 @@ GncOptionDB::set_option_from_ui(const char* section, const char* name, { auto option = find_option(section, name); if (!option) return; - func(option.get()); + func(option->get()); } -boost::optional +std::optional> GncOptionDB::find_section(const char* section) { auto db_section = std::find_if( @@ -133,24 +133,24 @@ GncOptionDB::find_section(const char* section) return sect.first == std::string{section}; }); if (db_section == m_sections.end()) - return boost::none; + return std::nullopt; return *db_section; } -boost::optional +std::optional> GncOptionDB::find_option(const char* section, const char* name) { auto db_section = find_section(section); if (!db_section) - return boost::none; + return std::nullopt; auto db_opt = std::find_if( - db_section->second.begin(), db_section->second.end(), + db_section->get().second.begin(), db_section->get().second.end(), [name](GncOption& option) -> bool { return option.get_name() == std::string{name}; }); - if (db_opt == db_section->second.end()) - return boost::none; + if (db_opt == db_section->get().second.end()) + return std::nullopt; return *db_opt; } @@ -162,7 +162,7 @@ GncOptionDB::lookup_string_option(const char* section, const char* name) auto db_opt = find_option(section, name); if (!db_opt) return empty_string; - return db_opt->get_value(); + return db_opt->get().get_value(); } void @@ -170,7 +170,7 @@ GncOptionDB::make_internal(const char* section, const char* name) { auto db_opt = find_option(section, name); if (db_opt) - db_opt->make_internal(); + db_opt->get().make_internal(); } void diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 1b2de18f39..2a9c597a2d 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -27,7 +27,7 @@ #include "gnc-option.hpp" #include #include -#include +#include extern "C" { #include @@ -70,7 +70,7 @@ public: auto option{find_option(section, name)}; if (!option) return false; - option->set_value(value); + option->get().set_value(value); return true; } catch(const std::invalid_argument& err) @@ -82,10 +82,10 @@ public: // void set_selectable(const char* section, const char* name); void make_internal(const char* section, const char* name); void commit(); - boost::optional find_section(const char* section); - boost::optional find_option(const char* section, const char* name); + std::optional> find_section(const char* section); + std::optional> find_option(const char* section, const char* name); private: - boost::optional m_default_section; + std::optional> m_default_section; std::vector m_sections; bool m_dirty = false; diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index ad81c0450d..7d8af2f715 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -59,7 +59,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); auto db_opt = $self->find_option(section, name); if (!db_opt) return SCM_BOOL_F; - return db_opt->get_scm_value(); + return db_opt->get().get_scm_value(); } %template(set_option_string) set_option; From 8eedcb6d6dcb18b2e536369ee2f4325d21877957 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 15 Oct 2019 16:33:55 -0700 Subject: [PATCH 034/298] Extract all SCM functions to gnc-optiondb.i. So that it can be moved to bindings/ when gnc-module-load-begone is ready. --- libgnucash/app-utils/gnc-option.hpp | 83 +----------- libgnucash/app-utils/gnc-optiondb.i | 121 +++++++++++++++++- .../app-utils/test/gtest-gnc-option.cpp | 28 ---- 3 files changed, 122 insertions(+), 110 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 045bd55413..59cda4bb51 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -178,72 +178,6 @@ private: GncOptionUIType m_ui_type; }; -inline SCM -scm_from_value(std::string value) -{ - return scm_from_utf8_string(value.c_str()); -} - -inline SCM -scm_from_value(bool value) -{ - return value ? SCM_BOOL_T : SCM_BOOL_F; -} - -inline SCM -scm_from_value(int64_t value) -{ - return scm_from_int64(value); -} - -inline SCM -scm_from_value(int value) -{ - return scm_from_int(value); -} - -inline SCM -scm_from_value(QofInstance* value) -{ - auto guid = guid_to_string(qof_instance_get_guid(value)); - auto scm_guid = scm_from_utf8_string(guid); - g_free(guid); - return scm_guid; -} - -inline SCM -scm_from_value(QofQuery* value) -{ - return SCM_BOOL_F; -} - -inline SCM -scm_from_value(GncNumeric value) -{ - return SCM_BOOL_F; -} - -inline SCM -scm_from_value(std::vector value) -{ - SCM s_list; - for (auto guid : value) - { - auto guid_s = guid_to_string(qof_instance_get_guid(&guid)); - auto scm_guid = scm_from_utf8_string(guid_s); - auto scm_guid_list1 = scm_list_1(scm_guid); - s_list = scm_append(scm_list_2(s_list, scm_guid_list1)); - g_free(guid_s); - } - return s_list; -} - -inline SCM -scm_from_value(GncMultiChoiceOptionChoices value) -{ - return SCM_BOOL_F; -} - template class GncOptionValue : public OptionClassifier, public OptionUIItem @@ -388,6 +322,7 @@ public: return ValueType {}; }, m_option); } + template ValueType get_default_value() const { return std::visit([](const auto& option)->ValueType { @@ -398,20 +333,7 @@ public: }, m_option); } - SCM get_scm_value() const - { - return std::visit([](const auto& option)->SCM { - auto value{option.get_value()}; - return scm_from_value(value); - }, m_option); - } - SCM get_scm_default_value() const - { - return std::visit([](const auto& option)->SCM { - auto value{option.get_default_value()}; - return scm_from_value(value); - }, m_option); - } + template void set_value(ValueType value) { std::visit([value](auto& option) { @@ -467,6 +389,7 @@ public: option.make_internal(); }, m_option); } + const GncOptionVariant& _get_option() const { return m_option; } private: GncOptionVariant m_option; }; diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 7d8af2f715..157d8b7f8c 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -46,22 +46,139 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void); %include + +%inline %{ +inline SCM +scm_from_value(std::string value) +{ + return scm_from_utf8_string(value.c_str()); +} + +inline SCM +scm_from_value(bool value) +{ + return value ? SCM_BOOL_T : SCM_BOOL_F; +} + +inline SCM +scm_from_value(int64_t value) +{ + return scm_from_int64(value); +} + +inline SCM +scm_from_value(int value) +{ + return scm_from_int(value); +} + +inline SCM +scm_from_value(QofInstance* value) +{ + auto guid = guid_to_string(qof_instance_get_guid(value)); + auto scm_guid = scm_from_utf8_string(guid); + g_free(guid); + return scm_guid; +} + +inline SCM +scm_from_value(QofQuery* value) +{ + return SCM_BOOL_F; +} + +inline SCM +scm_from_value(GncNumeric value) +{ + return SCM_BOOL_F; +} + +inline SCM +scm_from_value(std::vector value) +{ + SCM s_list; + for (auto guid : value) + { + auto guid_s = guid_to_string(qof_instance_get_guid(&guid)); + auto scm_guid = scm_from_utf8_string(guid_s); + auto scm_guid_list1 = scm_list_1(scm_guid); + s_list = scm_append(scm_list_2(s_list, scm_guid_list1)); + g_free(guid_s); + } + return s_list; +} + +inline SCM + scm_from_value(std::vector>) +{ + return SCM_BOOL_F; +} +%} %ignore OptionClassifier; %ignore OptionUIItem; -%ignore GncOption; +%nodefaultctor GncOption; wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); +%include "gnc-option.hpp" %include "gnc-optiondb.hpp" +%extend GncOption { + SCM get_scm_value() const + { + return std::visit([](const auto& option)->SCM { + auto value{option.get_value()}; + return scm_from_value(value); + }, $self->_get_option()); + } + SCM get_scm_default_value() const + { + return std::visit([](const auto& option)->SCM { + auto value{option.get_default_value()}; + return scm_from_value(value); + }, $self->_get_option()); + } +}; + %extend GncOptionDB { SCM lookup_option(const char* section, const char* name) { auto db_opt = $self->find_option(section, name); if (!db_opt) return SCM_BOOL_F; - return db_opt->get().get_scm_value(); + return GncOption_get_scm_value(&(db_opt->get())); } %template(set_option_string) set_option; %template(set_option_int) set_option; }; + +/* +TEST(GncOption, test_string_scm_functions) +{ + GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); + auto scm_value = option.get_scm_value(); + auto str_value = scm_to_utf8_string(scm_value); + EXPECT_STREQ("waldo", str_value); + g_free(str_value); + scm_value = option.get_scm_default_value(); + str_value = scm_to_utf8_string(scm_value); + EXPECT_STREQ("waldo", str_value); + g_free(str_value); +} + +TEST(GNCOption, test_budget_scm_functions) +{ + auto book = qof_book_new(); + auto budget = gnc_budget_new(book); + GncOption option("foo", "bar", "baz", "Phony Option", + QOF_INSTANCE(budget)); + auto scm_budget = option.get_scm_value(); + auto str_value = scm_to_utf8_string(scm_budget); + auto guid = guid_to_string(qof_instance_get_guid(budget)); + EXPECT_STREQ(guid, str_value); + g_free(guid); + gnc_budget_destroy(budget); + qof_book_destroy(book); +} + +*/ diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 9267ba630f..ec52438f97 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -67,19 +67,6 @@ TEST(GncOption, test_int64_t_value) EXPECT_EQ(INT64_C(987654321), option.get_value()); } -TEST(GncOption, test_string_scm_functions) -{ - GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); - auto scm_value = option.get_scm_value(); - auto str_value = scm_to_utf8_string(scm_value); - EXPECT_STREQ("waldo", str_value); - g_free(str_value); - scm_value = option.get_scm_default_value(); - str_value = scm_to_utf8_string(scm_value); - EXPECT_STREQ("waldo", str_value); - g_free(str_value); -} - TEST(GNCOption, test_budget_ctor) { auto book = qof_book_new(); @@ -92,21 +79,6 @@ TEST(GNCOption, test_budget_ctor) qof_book_destroy(book); } -TEST(GNCOption, test_budget_scm_functions) -{ - auto book = qof_book_new(); - auto budget = gnc_budget_new(book); - GncOption option("foo", "bar", "baz", "Phony Option", - QOF_INSTANCE(budget)); - auto scm_budget = option.get_scm_value(); - auto str_value = scm_to_utf8_string(scm_budget); - auto guid = guid_to_string(qof_instance_get_guid(budget)); - EXPECT_STREQ(guid, str_value); - g_free(guid); - gnc_budget_destroy(budget); - qof_book_destroy(book); -} - TEST(GNCOption, test_commodity_ctor) { auto book = qof_book_new(); From 435667e8fe16c9a66b1e765ac417135415a6bab3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 20 Oct 2019 15:13:33 -0700 Subject: [PATCH 035/298] Implement GncOptionMultichoiceValue Replaces GncOptionValue because having the vector as the value obviously wouldn't work and besides it needs additional functions. --- libgnucash/app-utils/gnc-option.hpp | 106 ++++++++++++++++-- libgnucash/app-utils/gnc-optiondb.cpp | 8 +- libgnucash/app-utils/gnc-optiondb.i | 19 ++++ .../app-utils/test/gtest-gnc-option.cpp | 62 ++++++++++ .../app-utils/test/gtest-gnc-optiondb.cpp | 13 +++ .../app-utils/test/test-gnc-optiondb.scm | 34 ++++++ 6 files changed, 229 insertions(+), 13 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 59cda4bb51..b80fb87187 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -125,9 +125,6 @@ struct OptionClassifier std::string m_doc_string; }; -using GncMultiChoiceOptionEntry = std::pair; -using GncMultiChoiceOptionChoices = std::vector; - class GncOptionUIItem; /** @@ -289,16 +286,109 @@ private: ValueType m_step; }; +/** 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. The + * tuple contains three strings, a key, a display + * name and a brief description for the tooltip. Both name and description + * should be localized at the point of use. + * + * + */ + +using GncMultiChoiceOptionEntry = std::tuple; +using GncMultiChoiceOptionChoices = std::vector; + +class GncOptionMultichoiceValue : + public OptionClassifier, public OptionUIItem +{ +public: + GncOptionMultichoiceValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncMultiChoiceOptionChoices&& choices, + GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), + m_value{}, m_default_value{}, 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 + { + return std::get<0>(m_choices.at(m_value)); + } + const std::string& get_default_value() const + { + return std::get<0>(m_choices.at(m_default_value)); + } + bool validate(const std::string& value) const noexcept + { + auto index = find_key(value); + return index != std::numeric_limits::max(); + + } + void set_value(const std::string& value) + { + auto index = find_key(value); + if (index != std::numeric_limits::max()) + m_value = index; + else + throw std::invalid_argument("Value not a valid choice."); + + } + std::size_t num_permissible_values() const noexcept + { + return m_choices.size(); + } + std::size_t permissible_value_index(const std::string& key) const noexcept + { + return find_key(key); + } + const std::string& permissible_value(std::size_t index) const + { + return std::get<0>(m_choices.at(index)); + } + const std::string& permissible_value_name(std::size_t index) const + { + return std::get<1>(m_choices.at(index)); + } + const std::string& permissible_value_description(std::size_t index) const + { + return std::get<2>(m_choices.at(index)); + } +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 std::numeric_limits::max(); + + } + std::size_t m_value; + std::size_t m_default_value; + GncMultiChoiceOptionChoices m_choices; +}; + using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue, GncOptionValue, GncOptionValue>, - GncOptionValue, + GncOptionMultichoiceValue, GncOptionRangeValue, GncOptionRangeValue, GncOptionValidatedValue>; + class GncOption { public: @@ -316,9 +406,8 @@ public: template ValueType get_value() const { return std::visit([](const auto& option)->ValueType { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v, std::decay_t>) return option.get_value(); - else return ValueType {}; }, m_option); } @@ -326,9 +415,8 @@ public: template ValueType get_default_value() const { return std::visit([](const auto& option)->ValueType { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v, std::decay_t>) return option.get_default_value(); - else return ValueType {}; }, m_option); @@ -337,7 +425,7 @@ public: template void set_value(ValueType value) { std::visit([value](auto& option) { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v, std::decay_t>) option.set_value(value); }, m_option); } diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 9276513ac1..46853a349d 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -316,8 +316,8 @@ gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section, const char* doc_string, GncMultiChoiceOptionChoices&& value) { - GncOption option{section, name, key, doc_string, value, - GncOptionUIType::MULTICHOICE}; + GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string, + std::move(value)}}; db->register_option(section, std::move(option)); } @@ -327,8 +327,8 @@ gnc_register_list_option(const GncOptionDBPtr& db, const char* section, const char* doc_string, GncMultiChoiceOptionChoices&& value) { - GncOption option{section, name, key, doc_string, value, - GncOptionUIType::LIST}; + GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string, + std::move(value), GncOptionUIType::LIST}}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 157d8b7f8c..b7314ac17f 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -45,6 +45,7 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void); %} %include +%import %inline %{ @@ -117,6 +118,23 @@ inline SCM %ignore OptionClassifier; %ignore OptionUIItem; %nodefaultctor GncOption; +%ignore GncOptionMultichoiceValue(GncOptionMultichoiceValue&&); +%ignore GncOptionMultichoiceValue::operator=(const GncOptionMultichoiceValue&); +%ignore GncOptionMultichoiceValue::operator=(GncOptionMultichoiceValue&&); + +%typemap(in) GncMultiChoiceOptionChoices&& (GncMultiChoiceOptionChoices choices) +{ + auto len = scm_to_size_t(scm_length($input)); + for (std::size_t i = 0; i < len; ++i) + { + SCM vec = scm_list_ref($input, scm_from_size_t(i)); + std::string key{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 0))}; + std::string name{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 1))}; + std::string desc{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 2))}; + choices.push_back({std::move(key), std::move(name), std::move(desc)}); + } + $1 = &choices; + } wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %include "gnc-option.hpp" @@ -152,6 +170,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %template(set_option_int) set_option; }; + /* TEST(GncOption, test_string_scm_functions) { diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index ec52438f97..a209940983 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -91,6 +91,7 @@ TEST(GNCOption, test_commodity_ctor) gnc_commodity_destroy(hpe); qof_book_destroy(book); } + static GncOption make_currency_option (const char* section, const char* name, const char* key, const char* doc_string, @@ -205,3 +206,64 @@ TEST_F(GncOptionUI, test_set_option_ui_item) m_option.set_ui_item(&option_ui_item); EXPECT_EQ(&ui_item, m_option.get_ui_item()->m_widget); } + +class GncOptionMultichoiceTest : public ::testing::Test +{ +protected: + GncOptionMultichoiceTest() : + m_option{"foo", "bar", "baz", "Phony Option", + { + {"plugh", "xyzzy", "thud"}, + {"waldo", "pepper", "salt"}, + {"pork", "sausage", "links"}, + {"corge", "grault", "garply"} + }} {} + GncOptionMultichoiceValue 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("waldo")); + EXPECT_FALSE(m_option.validate("grault")); +} + +TEST_F(GncMultichoiceOption, test_set_value) +{ + EXPECT_NO_THROW({ + m_option.set_value("pork"); + EXPECT_STREQ("pork", m_option.get_value().c_str()); + }); + EXPECT_THROW({ m_option.set_value("salt"); }, std::invalid_argument); + EXPECT_STREQ("pork", m_option.get_value().c_str()); +} + +TEST_F(GncMultichoiceOption, test_num_permissible) +{ + EXPECT_EQ(4, m_option.num_permissible_values()); +} + +TEST_F(GncMultichoiceOption, test_permissible_value_stuff) +{ + EXPECT_NO_THROW({ + EXPECT_EQ(3, m_option.permissible_value_index("corge")); + EXPECT_STREQ("waldo", m_option.permissible_value(1).c_str()); + EXPECT_STREQ("sausage", m_option.permissible_value_name(2).c_str()); + EXPECT_STREQ("thud", + m_option.permissible_value_description(0).c_str()); + }); + 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_THROW({ auto result = m_option.permissible_value_description(4); }, + std::out_of_range); + EXPECT_EQ(std::numeric_limits::max(), + m_option.permissible_value_index("xyzzy")); +} diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index be6221f60b..917e07fc5f 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -69,6 +69,19 @@ TEST_F(GncOptionDBTest, test_register_string_option) EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str()); } +TEST_F(GncOptionDBTest, test_register_multichoice_option) +{ + GncMultiChoiceOptionChoices choices{ + { "plugh", "xyzzy", "thud"}, + { "waldo", "pepper", "salt"}, + { "pork", "sausage", "links"}, + { "corge", "grault", "garply"}}; + gnc_register_multichoice_option(m_db, "foo", "bar", "baz", "Phony Option", + std::move(choices)); + ASSERT_TRUE(m_db->set_option("foo", "bar", std::string{"corge"})); + EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str()); +} + class GncUIType { public: diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index cce651fb00..d9c8450c8d 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -33,6 +33,7 @@ (test-runner-factory gnc:test-runner) (test-begin "test-gnc-optiondb-scheme") (test-gnc-make-text-option) + (test-gnc-make-multichoice-option) (test-end "test-gnc-optiondb-scheme")) (define (test-gnc-make-text-option) @@ -47,3 +48,36 @@ (test-equal "pepper" (GncOptionDB-lookup-option (GncOptionDBPtr-get option-db) "foo" "bar"))) (test-end "test-gnc-make-string-option")) + +(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 (gnc-option-db-new)) + (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" multichoice))) + + (GncOptionDB-set-option-string + (GncOptionDBPtr-get option-db) "foo" "bar" "corge") + (test-equal "corge" (GncOptionDB-lookup-option + (GncOptionDBPtr-get option-db) "foo" "bar"))) + (test-end "test-gnc-test-multichoice-option")) + (GncOptionDBPtr-get option-db) "foo" "bar")) + (test-end "test-gnc-test-multichoice-option"))) From 3dc4bc237730adecd9b375c53c48a5e4958291d6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 29 Oct 2019 16:34:44 -0700 Subject: [PATCH 036/298] Implement GncOptionDateValue. --- libgnucash/app-utils/gnc-option.cpp | 96 +++++++++- libgnucash/app-utils/gnc-option.hpp | 87 +++++++-- libgnucash/app-utils/gnc-optiondb.cpp | 9 + libgnucash/app-utils/gnc-optiondb.hpp | 4 +- libgnucash/app-utils/gnc-optiondb.i | 8 + libgnucash/app-utils/test/CMakeLists.txt | 2 + .../app-utils/test/gtest-gnc-option.cpp | 166 ++++++++++++++++++ .../app-utils/test/gtest-gnc-optiondb.cpp | 8 + .../app-utils/test/test-gnc-optiondb.scm | 20 ++- 9 files changed, 381 insertions(+), 19 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 8543efa20c..278ca112c7 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -23,5 +23,99 @@ //#include "options.h" #include "gnc-option.hpp" -#include +#include +extern "C" +{ +#include "gnc-accounting-period.h" +} +static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +static void +normalize_month(struct tm& now) +{ + if (now.tm_mon < 0) + { + now.tm_mon += 12; + --now.tm_year; + } + else if (now.tm_mon > 11) + { + now.tm_mon -= 12; + ++now.tm_year; + } +} + +static void +set_day_and_time(struct tm& now, bool starting) +{ + if (starting) + { + now.tm_hour = now.tm_min = now.tm_sec = 0; + now.tm_mday = 1; + } + else + { + now.tm_min = now.tm_sec = 59; + now.tm_hour = 23; + now.tm_mday = days_in_month[now.tm_mon]; + // Check for Februrary in a leap year + if (int year = now.tm_year + 1900; now.tm_mon == 1 && + year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) + ++now.tm_mday; + } +}; + +time64 +GncOptionDateValue::get_value() const +{ + if (m_type == DateType::ABSOLUTE) + return m_date; + if (m_period == RelativeDatePeriod::TODAY) + return static_cast(GncDateTime()); + if (m_period == RelativeDatePeriod::ACCOUNTING_PERIOD) + return m_type == DateType::STARTING ? + gnc_accounting_period_fiscal_start() : + gnc_accounting_period_fiscal_end(); + + struct tm now{static_cast(GncDateTime())}; + struct tm period{static_cast(GncDateTime(gnc_accounting_period_fiscal_start()))}; + + if (m_period == RelativeDatePeriod::CAL_YEAR || + m_period == RelativeDatePeriod::PREV_YEAR) + { + if (m_period == RelativeDatePeriod::PREV_YEAR) + --now.tm_year; + now.tm_mon = m_type == DateType::STARTING ? 0 : 11; + } + else if (m_period == RelativeDatePeriod::PREV_QUARTER || + m_period == RelativeDatePeriod::CURRENT_QUARTER) + { + now.tm_mon = now.tm_mon - (now.tm_mon - period.tm_mon) % 3; + if (m_period == RelativeDatePeriod::PREV_QUARTER) + now.tm_mon -= 3; + if (m_type == DateType::ENDING) + now.tm_mon += 2; + } + else if (m_period == RelativeDatePeriod::PREV_MONTH) + --now.tm_mon; + normalize_month(now); + set_day_and_time(now, m_type == DateType::STARTING); + return static_cast(GncDateTime(now)); +} + +void +GncOptionDateValue::set_value(DateSetterValue value) +{ + auto [type, val] = value; + m_type = type; + if (type == DateType::ABSOLUTE) + { + m_period = RelativeDatePeriod::TODAY; + m_date = static_cast(val); + return; + } + + m_period = static_cast(val); + m_date = 0; +} diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index b80fb87187..522c2870be 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -31,6 +31,7 @@ extern "C" #include #include } +#include #include #include #include @@ -295,7 +296,6 @@ private: * * */ - using GncMultiChoiceOptionEntry = std::tuple; @@ -306,7 +306,7 @@ class GncOptionMultichoiceValue : { public: GncOptionMultichoiceValue(const char* section, const char* name, - const char* key, const char* doc_string, + const char* key, const char* doc_string, GncMultiChoiceOptionChoices&& choices, GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) : OptionClassifier{section, name, key, doc_string}, @@ -378,16 +378,75 @@ private: GncMultiChoiceOptionChoices m_choices; }; +/** Date options + * A legal date value is a pair of either and a RelativeDatePeriod, the absolute flag and a time64, or for legacy purposes the absolute flag and a timespec. + * The original design allowed custom RelativeDatePeriods, but that facility is unused so we'll go with compiled-in enums. + +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 +gnc-date-option-relative-time + */ + +enum class DateType +{ + ABSOLUTE, + STARTING, + ENDING, +}; + +enum class RelativeDatePeriod : int64_t +{ + TODAY, + THIS_MONTH, + PREV_MONTH, + CURRENT_QUARTER, + PREV_QUARTER, + CAL_YEAR, + PREV_YEAR, + ACCOUNTING_PERIOD +}; + +using DateSetterValue = std::pair; +class GncOptionDateValue : public OptionClassifier, public OptionUIItem +{ +public: + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(GncOptionUIType::DATE), + m_type{DateType::ABSOLUTE}, m_period{RelativeDatePeriod::TODAY}, + m_date{static_cast(GncDateTime())} {} + GncOptionDateValue(const GncOptionDateValue&) = default; + GncOptionDateValue(GncOptionDateValue&&) = default; + GncOptionDateValue& operator=(const GncOptionDateValue&) = default; + GncOptionDateValue& operator=(GncOptionDateValue&&) = default; + time64 get_value() const; + time64 get_default_value() const { return static_cast(GncDateTime()); } + void set_value(DateSetterValue); + void set_value(time64 time) { + m_type = DateType::ABSOLUTE; + m_period = RelativeDatePeriod::TODAY; + m_date = time; + } +private: + DateType m_type; + RelativeDatePeriod m_period; + time64 m_date; +}; + using GncOptionVariant = std::variant, - GncOptionValue, - GncOptionValue, - GncOptionValue, - GncOptionValue, - GncOptionValue>, - GncOptionMultichoiceValue, - GncOptionRangeValue, - GncOptionRangeValue, - GncOptionValidatedValue>; + GncOptionValue, + GncOptionValue, + GncOptionValue, + GncOptionValue, + GncOptionValue>, + GncOptionMultichoiceValue, + GncOptionRangeValue, + GncOptionRangeValue, + GncOptionValidatedValue, + GncOptionDateValue>; class GncOption { @@ -408,7 +467,7 @@ public: return std::visit([](const auto& option)->ValueType { if constexpr (std::is_same_v, std::decay_t>) return option.get_value(); - return ValueType {}; + return ValueType {}; }, m_option); } @@ -417,7 +476,7 @@ public: return std::visit([](const auto& option)->ValueType { if constexpr (std::is_same_v, std::decay_t>) return option.get_default_value(); - return ValueType {}; + return ValueType {}; }, m_option); } @@ -426,7 +485,7 @@ public: { std::visit([value](auto& option) { if constexpr (std::is_same_v, std::decay_t>) - option.set_value(value); + option.set_value(value); }, m_option); } const std::string& get_section() const diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 46853a349d..814dccd9ee 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -453,3 +453,12 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, }}; db->register_option(section, std::move(option)); } + +void +gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string) +{ + GncOption option{GncOptionDateValue(section, name, key, doc_string)}; + db->register_option(section, std::move(option)); +} diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 2a9c597a2d..95319447fb 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -278,5 +278,7 @@ void gnc_register_dateformat_option(const GncOptionDBPtr& db, const char* key, const char* doc_string, std::string value); - +void gnc_register_date_interval_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string); #endif //GNC_OPTIONDB_HPP_ diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index b7314ac17f..33df360cfc 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -121,6 +121,13 @@ inline SCM %ignore GncOptionMultichoiceValue(GncOptionMultichoiceValue&&); %ignore GncOptionMultichoiceValue::operator=(const GncOptionMultichoiceValue&); %ignore GncOptionMultichoiceValue::operator=(GncOptionMultichoiceValue&&); +%ignore GncOptionDateValue(GncOptionDateValue&&); +%ignore GncOptionDateValue::operator=(const GncOptionDateValue&); +%ignore GncOptionDateValue::operator=(GncOptionDateValue&&); + +%typemap(typecheck, precedence=SWIG_TYPECHECK_INT64) time64 { + $1 = scm_is_signed_integer($input, INT64_MAX, INT64_MIN); +} %typemap(in) GncMultiChoiceOptionChoices&& (GncMultiChoiceOptionChoices choices) { @@ -168,6 +175,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %template(set_option_string) set_option; %template(set_option_int) set_option; + %template(set_option_time64) set_option; }; diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index d8fe3e53f8..dc41f34715 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -43,6 +43,7 @@ set(gtest_gnc_option_INCLUDES ${GUILE_INCLUDE_DIRS}) set(gtest_gnc_option_LIBS + gncmod-app-utils gncmod-engine ${GLIB2_LDFLAGS} ${GUILE_LDFLAGS} @@ -109,6 +110,7 @@ if (HAVE_SRFI64) set(swig_gnc_optiondb_LIBS gncmod-engine + gncmod-app-utils ${GLIB2_LDFLAGS} ${GUILE_LDFLAGS} ) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index a209940983..59b04aaee9 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -23,6 +23,11 @@ #include #include +extern "C" +{ +#include +#include +} TEST(GncOption, test_string_ctor) { @@ -267,3 +272,164 @@ TEST_F(GncMultichoiceOption, test_permissible_value_stuff) EXPECT_EQ(std::numeric_limits::max(), m_option.permissible_value_index("xyzzy")); } + +class GncOptionDateOptionTest : public ::testing::Test +{ +protected: + GncOptionDateOptionTest() : + m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option"}} {} + + GncOptionDateValue m_option; +}; + +using GncDateOption = GncOptionDateOptionTest; + +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_F(GncDateOption, test_set_and_get_absolute) +{ + time64 time1{static_cast(GncDateTime("2019-07-19 15:32:26 +05:00"))}; + DateSetterValue value1{DateType::ABSOLUTE, time1}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_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)}; + DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::THIS_MONTH)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_month_end) +{ + GDate month_end; + g_date_set_time_t(&month_end, time(nullptr)); + gnc_gdate_set_month_end(&month_end); + time64 time1{time64_from_gdate(&month_end, DayPart::end)}; + DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::THIS_MONTH)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_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)}; + DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_MONTH)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_prev_month_end) +{ + GDate prev_month_end; + g_date_set_time_t(&prev_month_end, time(nullptr)); + gnc_gdate_set_prev_month_end(&prev_month_end); + time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)}; + DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_MONTH)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_quarter_start) +{ + GDate quarter_start; + g_date_set_time_t(&quarter_start, time(nullptr)); + gnc_gdate_set_quarter_start(&quarter_start); + time64 time1{time64_from_gdate(&quarter_start, DayPart::start)}; + DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::CURRENT_QUARTER)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_quarter_end) +{ + GDate quarter_end; + g_date_set_time_t(&quarter_end, time(nullptr)); + gnc_gdate_set_quarter_end(&quarter_end); + time64 time1{time64_from_gdate(&quarter_end, DayPart::end)}; + DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::CURRENT_QUARTER)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_prev_quarter_start) +{ + GDate prev_quarter_start; + g_date_set_time_t(&prev_quarter_start, time(nullptr)); + gnc_gdate_set_prev_quarter_start(&prev_quarter_start); + time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)}; + DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_QUARTER)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_prev_quarter_end) +{ + GDate prev_quarter_end; + g_date_set_time_t(&prev_quarter_end, time(nullptr)); + gnc_gdate_set_prev_quarter_end(&prev_quarter_end); + time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)}; + DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_QUARTER)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_year_start) +{ + GDate year_start; + g_date_set_time_t(&year_start, time(nullptr)); + gnc_gdate_set_year_start(&year_start); + time64 time1{time64_from_gdate(&year_start, DayPart::start)}; + DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::CAL_YEAR)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_year_end) +{ + GDate year_end; + g_date_set_time_t(&year_end, time(nullptr)); + gnc_gdate_set_year_end(&year_end); + time64 time1{time64_from_gdate(&year_end, DayPart::end)}; + DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::CAL_YEAR)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_prev_year_start) +{ + 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)}; + DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_YEAR)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + +TEST_F(GncDateOption, test_set_and_get_prev_year_end) +{ + GDate prev_year_end; + g_date_set_time_t(&prev_year_end, time(nullptr)); + gnc_gdate_set_prev_year_end(&prev_year_end); + time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)}; + DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_YEAR)}; + m_option.set_value(value1); + EXPECT_EQ(time1, m_option.get_value()); +} + diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 917e07fc5f..447fcda450 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -82,6 +82,14 @@ TEST_F(GncOptionDBTest, test_register_multichoice_option) EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str()); } +TEST_F(GncOptionDBTest, test_register_date_interval_option) +{ + gnc_register_date_interval_option(m_db, "foo", "bar", "baz", "Phony Option"); + auto time{gnc_dmy2time64(11, 7, 2019)}; + ASSERT_TRUE(m_db->set_option("foo", "bar", time)); + EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value()); +} + class GncUIType { public: diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index d9c8450c8d..5916311b64 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -23,10 +23,12 @@ (use-modules (srfi srfi-64)) (use-modules (tests srfi64-extras)) - +(use-modules (gnucash gnc-module)) (eval-when (compile load eval expand) (load-extension "libswig-gnc-optiondb" "scm_init_sw_gnc_optiondb_module")) + +(gnc:module-load "gnucash/engine" 0) (use-modules (sw_gnc_optiondb)) (define (run-test) @@ -34,6 +36,7 @@ (test-begin "test-gnc-optiondb-scheme") (test-gnc-make-text-option) (test-gnc-make-multichoice-option) + (test-gnc-make-date-option) (test-end "test-gnc-optiondb-scheme")) (define (test-gnc-make-text-option) @@ -79,5 +82,16 @@ (test-equal "corge" (GncOptionDB-lookup-option (GncOptionDBPtr-get option-db) "foo" "bar"))) (test-end "test-gnc-test-multichoice-option")) - (GncOptionDBPtr-get option-db) "foo" "bar")) - (test-end "test-gnc-test-multichoice-option"))) + +(define (test-gnc-make-date-option) + (test-begin "test-gnc-test-date-option") + (let* ((option-db (gnc-option-db-new)) + (date-opt (gnc-register-date-interval-option option-db "foo" "bar" + "baz" "Phony Option")) + (a-time (gnc-dmy2time64 2019 07 11))) + (test-equal (current-time) (GncOptionDB-lookup-option + (GncOptionDBPtr-get option-db) "foo" "bar")) + (GncOptionDB-set-option-time64 (GncOptionDBPtr-get option-db) "foo" "bar" a-time) + (test-equal a-time (GncOptionDB-lookup-option + (GncOptionDBPtr-get option-db) "foo" "bar")) + (test-end "test-gnc-test-date-option"))) From 39b7c9c74da0c903a6a59292c3d99fee416b839c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 31 Oct 2019 18:14:41 -0700 Subject: [PATCH 037/298] Convert scm_from_value() to templates for stricter overload resolution. --- libgnucash/app-utils/gnc-optiondb.i | 67 ++++++++++++++++++----------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 33df360cfc..b382c3d067 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -47,34 +47,52 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void); %include %import - + /* 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. + */ %inline %{ -inline SCM -scm_from_value(std::string value) +template inline SCM +scm_from_value(ValueType value) +{ + return SCM_BOOL_F; +} +template <> inline SCM +scm_from_value(std::string value) { return scm_from_utf8_string(value.c_str()); } -inline SCM -scm_from_value(bool value) +template <> inline SCM +scm_from_value(bool value) { return value ? SCM_BOOL_T : SCM_BOOL_F; } -inline SCM -scm_from_value(int64_t value) +template <> inline SCM +scm_from_value(int64_t value) { return scm_from_int64(value); } -inline SCM -scm_from_value(int value) +template <> inline SCM +scm_from_value(int value) { return scm_from_int(value); } -inline SCM -scm_from_value(QofInstance* value) +template <> inline SCM +scm_from_value(double value) +{ + return scm_from_double(value); +} + +template <> inline SCM +scm_from_value(const QofInstance* value) { auto guid = guid_to_string(qof_instance_get_guid(value)); auto scm_guid = scm_from_utf8_string(guid); @@ -82,20 +100,16 @@ scm_from_value(QofInstance* value) return scm_guid; } -inline SCM -scm_from_value(QofQuery* value) +/* Not needed now, the default template will do this +template <> inline SCM +scm_from_value(const QofQuery* value) { return SCM_BOOL_F; } +*/ -inline SCM -scm_from_value(GncNumeric value) -{ - return SCM_BOOL_F; -} - -inline SCM -scm_from_value(std::vector value) +template <>inline SCM +scm_from_value&>(const std::vector& value) { SCM s_list; for (auto guid : value) @@ -108,12 +122,13 @@ scm_from_value(std::vector value) } return s_list; } - -inline SCM - scm_from_value(std::vector>) +/* default template +template <>inline SCM + scm_from_value<(const std::vector>&) { return SCM_BOOL_F; } +*/ %} %ignore OptionClassifier; %ignore OptionUIItem; @@ -152,14 +167,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); { return std::visit([](const auto& option)->SCM { auto value{option.get_value()}; - return scm_from_value(value); + return scm_from_value(static_cast(value)); }, $self->_get_option()); } SCM get_scm_default_value() const { return std::visit([](const auto& option)->SCM { auto value{option.get_default_value()}; - return scm_from_value(value); + return scm_from_value(static_cast(value)); }, $self->_get_option()); } }; From ff7b263a5f98cc7e48765a571b86f39ac3360aaf Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 31 Oct 2019 18:17:03 -0700 Subject: [PATCH 038/298] Fix up and test the int specialization of GncOptionRangeValue. Not sure yet that a double one is really needed. --- libgnucash/app-utils/gnc-option.hpp | 8 +++-- .../app-utils/test/gtest-gnc-option.cpp | 35 +++++++++++++++++++ .../app-utils/test/test-gnc-optiondb.scm | 16 ++++++++- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 522c2870be..463ec762dd 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -250,6 +250,10 @@ private: ValueType m_validation_data; }; +/** + * Used for numeric ranges and plot sizes. + */ + template class GncOptionRangeValue : public OptionClassifier, public OptionUIItem @@ -263,7 +267,7 @@ public: OptionUIItem(GncOptionUIType::NUMBER_RANGE), m_value{value >= min && value <= max ? value : min}, m_default_value{value >= min && value <= max ? value : min}, - m_min{min}, m_step{step} {} + m_min{min}, m_max{max}, m_step{step} {} GncOptionRangeValue(const GncOptionRangeValue&) = default; GncOptionRangeValue(GncOptionRangeValue&&) = default; @@ -444,7 +448,7 @@ using GncOptionVariant = std::variant, GncOptionValue>, GncOptionMultichoiceValue, GncOptionRangeValue, - GncOptionRangeValue, + GncOptionRangeValue, GncOptionValidatedValue, GncOptionDateValue>; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 59b04aaee9..c505612902 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -212,6 +212,41 @@ TEST_F(GncOptionUI, test_set_option_ui_item) EXPECT_EQ(&ui_item, m_option.get_ui_item()->m_widget); } +class GncOptionRangeTest : public ::testing::Test +{ +protected: + GncOptionRangeTest() : + m_intoption{"foo", "bar", "baz", "Phony Option", 15, 1, 30, 1}, + m_doubleoption{"waldo", "pepper", "salt", "Phonier Option", + 1.5, 1.0, 3.0, 0.1} {} + + GncOptionRangeValue m_intoption; + GncOptionRangeValue 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()); +} + + class GncOptionMultichoiceTest : public ::testing::Test { protected: diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 5916311b64..683432bfb6 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -37,6 +37,7 @@ (test-gnc-make-text-option) (test-gnc-make-multichoice-option) (test-gnc-make-date-option) + (test-gnc-make-number-range-option) (test-end "test-gnc-optiondb-scheme")) (define (test-gnc-make-text-option) @@ -88,10 +89,23 @@ (let* ((option-db (gnc-option-db-new)) (date-opt (gnc-register-date-interval-option option-db "foo" "bar" "baz" "Phony Option")) - (a-time (gnc-dmy2time64 2019 07 11))) + (a-time (gnc-dmy2time64 11 07 2019))) (test-equal (current-time) (GncOptionDB-lookup-option (GncOptionDBPtr-get option-db) "foo" "bar")) (GncOptionDB-set-option-time64 (GncOptionDBPtr-get option-db) "foo" "bar" a-time) (test-equal a-time (GncOptionDB-lookup-option (GncOptionDBPtr-get option-db) "foo" "bar")) (test-end "test-gnc-test-date-option"))) + +(define (test-gnc-make-number-range-option) + (test-begin "test-gnc-number-range-option") + (let* ((option-db (gnc-option-db-new)) + (number-opt (gnc-register-number-range-option option-db "foo" "bar" + "baz" "Phony Option" + 15 5 30 1))) + (test-equal 15 (GncOptionDB-lookup-option + (GncOptionDBPtr-get option-db) "foo" "bar")) + (GncOptionDB-set-option-int (GncOptionDBPtr-get option-db) "foo" "bar" 20) + (test-equal 20 (GncOptionDB-lookup-option + (GncOptionDBPtr-get option-db) "foo" "bar"))) + (test-end "test-gnc-number-range-option")) From 252ba9b4779b9b6a3c66d2e54368a93b89394ddf Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 1 Nov 2019 14:59:49 -0700 Subject: [PATCH 039/298] Change GncOptionDB::lookup_option to gnc_option_value(). The old gnc:lookup-option returned the option object so that it could be manipulated rather than getting its value; gnc_option_value replicates the behavior of (gnc:option-value (gnc:lookup-option)). --- libgnucash/app-utils/gnc-optiondb.i | 18 +++++++++------- .../app-utils/test/test-gnc-optiondb.scm | 21 +++++++------------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index b382c3d067..844ce542ef 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -180,20 +180,22 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); }; %extend GncOptionDB { - SCM lookup_option(const char* section, const char* name) - { - auto db_opt = $self->find_option(section, name); - if (!db_opt) - return SCM_BOOL_F; - return GncOption_get_scm_value(&(db_opt->get())); - } - %template(set_option_string) set_option; %template(set_option_int) set_option; %template(set_option_time64) set_option; }; +%inline %{ + 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->get())); + } +%} /* TEST(GncOption, test_string_scm_functions) { diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 683432bfb6..fc523001f4 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -45,12 +45,10 @@ (let* ((option-db (gnc-option-db-new)) (string-opt (gnc-register-string-option option-db "foo" "bar" "baz" "Phony Option" "waldo"))) - (test-equal "waldo" (GncOptionDB-lookup-option - (GncOptionDBPtr-get option-db) "foo" "bar")) + (test-equal "waldo" (gnc-option-value option-db "foo" "bar")) (GncOptionDB-set-option-string (GncOptionDBPtr-get option-db) "foo" "bar" "pepper") - (test-equal "pepper" (GncOptionDB-lookup-option - (GncOptionDBPtr-get option-db) "foo" "bar"))) + (test-equal "pepper" (gnc-option-value option-db "foo" "bar"))) (test-end "test-gnc-make-string-option")) (define (test-gnc-make-multichoice-option) @@ -80,8 +78,7 @@ (GncOptionDB-set-option-string (GncOptionDBPtr-get option-db) "foo" "bar" "corge") - (test-equal "corge" (GncOptionDB-lookup-option - (GncOptionDBPtr-get option-db) "foo" "bar"))) + (test-equal "corge" (gnc-option-value option-db "foo" "bar"))) (test-end "test-gnc-test-multichoice-option")) (define (test-gnc-make-date-option) @@ -90,11 +87,9 @@ (date-opt (gnc-register-date-interval-option option-db "foo" "bar" "baz" "Phony Option")) (a-time (gnc-dmy2time64 11 07 2019))) - (test-equal (current-time) (GncOptionDB-lookup-option - (GncOptionDBPtr-get option-db) "foo" "bar")) + (test-equal (current-time) (gnc-option-value option-db "foo" "bar")) (GncOptionDB-set-option-time64 (GncOptionDBPtr-get option-db) "foo" "bar" a-time) - (test-equal a-time (GncOptionDB-lookup-option - (GncOptionDBPtr-get option-db) "foo" "bar")) + (test-equal a-time (gnc-option-value option-db "foo" "bar")) (test-end "test-gnc-test-date-option"))) (define (test-gnc-make-number-range-option) @@ -103,9 +98,7 @@ (number-opt (gnc-register-number-range-option option-db "foo" "bar" "baz" "Phony Option" 15 5 30 1))) - (test-equal 15 (GncOptionDB-lookup-option - (GncOptionDBPtr-get option-db) "foo" "bar")) + (test-equal 15 (gnc-option-value option-db "foo" "bar")) (GncOptionDB-set-option-int (GncOptionDBPtr-get option-db) "foo" "bar" 20) - (test-equal 20 (GncOptionDB-lookup-option - (GncOptionDBPtr-get option-db) "foo" "bar"))) + (test-equal 20 (gnc-option-value option-db "foo" "bar"))) (test-end "test-gnc-number-range-option")) From 2f2ac99944e8326168ce8e086bfe1d0c6975861a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 1 Nov 2019 16:14:30 -0700 Subject: [PATCH 040/298] Replace the direct wrapping of GncOptionDB-set-option-string-foo. With a simple type-free function gnc_option_set. This mirrors the current (gnc:option-set(gnc:lookup-option... and takes care of SCM type conversion. --- libgnucash/app-utils/gnc-option.hpp | 4 +- libgnucash/app-utils/gnc-optiondb.i | 56 +++++++++++++++++++ .../app-utils/test/test-gnc-optiondb.scm | 9 ++- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 463ec762dd..b4710719cf 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -540,9 +540,9 @@ public: option.make_internal(); }, m_option); } - const GncOptionVariant& _get_option() const { return m_option; } + GncOptionVariant& _get_option() const { return m_option; } private: - GncOptionVariant m_option; + mutable GncOptionVariant m_option; }; #endif //GNC_OPTION_HPP_ diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 844ce542ef..2971ec9d45 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -100,6 +100,7 @@ scm_from_value(const QofInstance* value) return scm_guid; } + /* Not needed now, the default template will do this template <> inline SCM scm_from_value(const QofQuery* value) @@ -129,6 +130,44 @@ template <>inline SCM return SCM_BOOL_F; } */ + +template inline ValueType +scm_to_value(SCM new_value) +{ + return ValueType{}; +} + +template <> inline std::string +scm_to_value(SCM new_value) +{ + auto strval = scm_to_utf8_stringn(new_value, nullptr); + std::string retval{strval}; + free(strval); + return retval; +} +/* +template <> inline std::string +scm_to_value(SCM 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); +} + + %} %ignore OptionClassifier; %ignore OptionUIItem; @@ -177,6 +216,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return scm_from_value(static_cast(value)); }, $self->_get_option()); } + void set_value_from_scm(SCM new_value) + { + std::visit([new_value](auto& option) { + option.set_value(scm_to_value>(new_value)); + }, $self->_get_option()); + } }; %extend GncOptionDB { @@ -195,6 +240,17 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return GncOption_get_scm_value(&(db_opt->get())); } + 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) + { +// PWARN("Attempt to write non-existent option %s/%s", section, name); + return; + } + GncOption_set_value_from_scm(&(db_opt->get()), new_value); + } %} /* TEST(GncOption, test_string_scm_functions) diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index fc523001f4..09fc1e6d93 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -47,7 +47,7 @@ "Phony Option" "waldo"))) (test-equal "waldo" (gnc-option-value option-db "foo" "bar")) - (GncOptionDB-set-option-string (GncOptionDBPtr-get option-db) "foo" "bar" "pepper") + (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")) @@ -76,8 +76,7 @@ (multi-opt (gnc-register-multichoice-option option-db "foo" "bar" "baz" "Phony Option" multichoice))) - (GncOptionDB-set-option-string - (GncOptionDBPtr-get option-db) "foo" "bar" "corge") + (gnc-set-option option-db "foo" "bar" "corge") (test-equal "corge" (gnc-option-value option-db "foo" "bar"))) (test-end "test-gnc-test-multichoice-option")) @@ -88,7 +87,7 @@ "baz" "Phony Option")) (a-time (gnc-dmy2time64 11 07 2019))) (test-equal (current-time) (gnc-option-value option-db "foo" "bar")) - (GncOptionDB-set-option-time64 (GncOptionDBPtr-get option-db) "foo" "bar" a-time) + (gnc-set-option option-db "foo" "bar" a-time) (test-equal a-time (gnc-option-value option-db "foo" "bar")) (test-end "test-gnc-test-date-option"))) @@ -99,6 +98,6 @@ "baz" "Phony Option" 15 5 30 1))) (test-equal 15 (gnc-option-value option-db "foo" "bar")) - (GncOptionDB-set-option-int (GncOptionDBPtr-get option-db) "foo" "bar" 20) + (gnc-set-option option-db "foo" "bar" 20) (test-equal 20 (gnc-option-value option-db "foo" "bar"))) (test-end "test-gnc-number-range-option")) From 3f576671aa6246fc8d9d394400ce9f2852a0f0ba Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 2 Nov 2019 11:06:12 -0700 Subject: [PATCH 041/298] Adapt GncOptionMultiChoiceValue to support list options. Main change adds a value field to the constructor to set the value and default value. --- libgnucash/app-utils/gnc-option.hpp | 13 ++++++++++++- libgnucash/app-utils/gnc-optiondb.cpp | 12 ++++++------ libgnucash/app-utils/gnc-optiondb.hpp | 4 ++-- libgnucash/app-utils/test/test-gnc-optiondb.scm | 15 +++++++++++++++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index b4710719cf..61171c4b89 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -311,11 +311,22 @@ class GncOptionMultichoiceValue : 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}, OptionUIItem(ui_type), - m_value{}, m_default_value{}, m_choices{std::move(choices)} {} + m_value{}, m_default_value{}, m_choices{std::move(choices)} { + if (value) + { + if (auto index = find_key(value); + index != std::numeric_limits::max()) + { + m_value = index; + m_default_value = index; + } + } + } GncOptionMultichoiceValue(const GncOptionMultichoiceValue&) = default; GncOptionMultichoiceValue(GncOptionMultichoiceValue&&) = default; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 814dccd9ee..f7fd62e734 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -314,21 +314,21 @@ void gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, - GncMultiChoiceOptionChoices&& value) + GncMultiChoiceOptionChoices&& choices) { GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string, - std::move(value)}}; + std::get<0>(choices.at(0)).c_str(), std::move(choices)}}; db->register_option(section, std::move(option)); } void gnc_register_list_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, - const char* doc_string, - GncMultiChoiceOptionChoices&& value) + const char* doc_string, const char* value, + GncMultiChoiceOptionChoices&& list) { GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string, - std::move(value), GncOptionUIType::LIST}}; + value, std::move(list), GncOptionUIType::LIST}}; db->register_option(section, std::move(option)); } @@ -362,7 +362,7 @@ gnc_register_query_option(const GncOptionDBPtr& db, const char* section, const char* doc_string, QofQuery* value) { GncOption option{section, name, key, doc_string, value, - GncOptionUIType::QUERY}; + GncOptionUIType::INTERNAL}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 95319447fb..72936f23fe 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -211,8 +211,8 @@ void gnc_register_multichoice_option(const GncOptionDBPtr& db, */ void gnc_register_list_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, - const char* doc_string, - GncMultiChoiceOptionChoices&& value); + const char* doc_string, const char* value, + GncMultiChoiceOptionChoices&& list); /* Number range options use the option-data as a list whose elements are: * (lower-bound upper-bound step-size). diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 09fc1e6d93..78ab8b94d5 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -36,6 +36,7 @@ (test-begin "test-gnc-optiondb-scheme") (test-gnc-make-text-option) (test-gnc-make-multichoice-option) + (test-gnc-make-list-option) (test-gnc-make-date-option) (test-gnc-make-number-range-option) (test-end "test-gnc-optiondb-scheme")) @@ -80,6 +81,20 @@ (test-equal "corge" (gnc-option-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 (gnc-option-db-new)) + (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" "GLPlot") + (test-equal "GLPlot" (gnc-option-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 (gnc-option-db-new)) From 12c5b9443000aafcd21214175762d04a49365ce5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 2 Nov 2019 11:06:49 -0700 Subject: [PATCH 042/298] New scheme function gnc_option_default_value. --- libgnucash/app-utils/gnc-optiondb.i | 9 +++++++++ libgnucash/app-utils/test/test-gnc-optiondb.scm | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 2971ec9d45..a9e8729165 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -240,6 +240,15 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return GncOption_get_scm_value(&(db_opt->get())); } + 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->get())); + } + void gnc_set_option(const GncOptionDBPtr& optiondb, const char* section, const char* name, SCM new_value) { diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 78ab8b94d5..c27f21a743 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -77,8 +77,10 @@ (multi-opt (gnc-register-multichoice-option option-db "foo" "bar" "baz" "Phony Option" multichoice))) + (test-equal "plugh" (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 "corge" (gnc-option-value option-db "foo" "bar")) + (test-equal "plugh" (gnc-option-default-value option-db "foo" "bar"))) (test-end "test-gnc-test-multichoice-option")) (define (test-gnc-make-list-option) @@ -93,6 +95,7 @@ (test-equal "AvgBalPlot" (gnc-option-value option-db "foo" "bar")) (gnc-set-option option-db "foo" "bar" "GLPlot") (test-equal "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) From 399573a89ddf508ee86214e480d974ce11043461 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 8 Nov 2019 16:31:36 -0800 Subject: [PATCH 043/298] Fix multichoice test construction. --- libgnucash/app-utils/test/gtest-gnc-option.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index c505612902..4a1345c8a6 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -251,7 +251,7 @@ class GncOptionMultichoiceTest : public ::testing::Test { protected: GncOptionMultichoiceTest() : - m_option{"foo", "bar", "baz", "Phony Option", + m_option{"foo", "bar", "baz", "Phony Option", "plugh", { {"plugh", "xyzzy", "thud"}, {"waldo", "pepper", "salt"}, From 7183e7c43ab47d0d334d6433325b38eff9c1fa9f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 10 Nov 2019 15:34:21 -0800 Subject: [PATCH 044/298] Fix begin/end of quarter calculations. Set the period to the calendar year if it's not set in prefs. Handle correctly the FY start month being after the current month. --- libgnucash/app-utils/gnc-option.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 278ca112c7..87c4eb6838 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -81,6 +81,13 @@ GncOptionDateValue::get_value() const struct tm now{static_cast(GncDateTime())}; struct tm period{static_cast(GncDateTime(gnc_accounting_period_fiscal_start()))}; + if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday) + { + //No set accounting period, use the calendar year + period.tm_mon = 0; + period.tm_mday = 0; + } + if (m_period == RelativeDatePeriod::CAL_YEAR || m_period == RelativeDatePeriod::PREV_YEAR) { @@ -91,7 +98,9 @@ GncOptionDateValue::get_value() const else if (m_period == RelativeDatePeriod::PREV_QUARTER || m_period == RelativeDatePeriod::CURRENT_QUARTER) { - now.tm_mon = now.tm_mon - (now.tm_mon - period.tm_mon) % 3; + auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon : + period.tm_mon - now.tm_mon) % 3; + now.tm_mon = now.tm_mon - offset; if (m_period == RelativeDatePeriod::PREV_QUARTER) now.tm_mon -= 3; if (m_type == DateType::ENDING) From b95ea2c4aa4518ae7babc4cb1a237f107bccf670 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 17 Nov 2019 11:15:56 -0800 Subject: [PATCH 045/298] Add Account-list options. --- libgnucash/app-utils/gnc-option.cpp | 17 +++ libgnucash/app-utils/gnc-option.hpp | 78 ++++++++++ libgnucash/app-utils/gnc-optiondb.cpp | 74 +++++++-- libgnucash/app-utils/gnc-optiondb.hpp | 32 +++- libgnucash/app-utils/gnc-optiondb.i | 129 ++++++++++++++-- .../app-utils/test/gtest-gnc-option.cpp | 143 ++++++++++++++++++ .../app-utils/test/gtest-gnc-optiondb.cpp | 94 ++++++++++++ .../app-utils/test/test-gnc-optiondb.scm | 81 ++++++++++ 8 files changed, 617 insertions(+), 31 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 87c4eb6838..06f2abf9bd 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -29,6 +29,23 @@ extern "C" #include "gnc-accounting-period.h" } +bool +GncOptionAccountValue::validate(const GncOptionAccountList& values) const +{ + if (values.empty()) + return false; + if (get_ui_type() == GncOptionUIType::ACCOUNT_SEL && values.size() != 1) + return false; + if (m_allowed.empty()) + return true; + for(auto account : values) { + if (std::find(m_allowed.begin(), m_allowed.end(), + xaccAccountGetType(account)) == m_allowed.end()) + return false; + } + return true; +} + static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static void diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 61171c4b89..c48d9f23fa 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -28,6 +28,7 @@ extern "C" { #include #include +#include #include #include } @@ -393,6 +394,82 @@ private: GncMultiChoiceOptionChoices m_choices; }; +/** Account options + * + * 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. + * + + */ + +using GncOptionAccountList = std::vector; +using GncOptionAccountTypeList = std::vector; + +class GncOptionAccountValue : + public OptionClassifier, public OptionUIItem +{ +public: + GncOptionAccountValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), m_value{}, m_default_value{}, m_allowed{} {} + + GncOptionAccountValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + const GncOptionAccountList& value) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), + m_value{value}, + m_default_value{std::move(value)}, m_allowed{} {} + GncOptionAccountValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + GncOptionAccountTypeList&& allowed) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), + m_value{}, + m_default_value{}, m_allowed{std::move(allowed)} {} + GncOptionAccountValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), + m_value{}, + m_default_value{}, m_allowed{std::move(allowed)} { + if (!validate(value)) + throw std::invalid_argument("Supplied Value not in allowed set."); + m_value = value; + m_default_value = std::move(value); + } + + const GncOptionAccountList& get_value() const { return m_value; } + const GncOptionAccountList& get_default_value() const { return m_default_value; } + bool validate (const GncOptionAccountList& values) const; + void set_value (const GncOptionAccountList& values) { + if (validate(values)) + //throw! + m_value = values; + } + +private: + GncOptionAccountList m_value; + GncOptionAccountList m_default_value; + GncOptionAccountTypeList m_allowed; +}; + /** Date options * A legal date value is a pair of either and a RelativeDatePeriod, the absolute flag and a time64, or for legacy purposes the absolute flag and a timespec. * The original design allowed custom RelativeDatePeriods, but that facility is unused so we'll go with compiled-in enums. @@ -457,6 +534,7 @@ using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue>, + GncOptionAccountValue, GncOptionMultichoiceValue, GncOptionRangeValue, GncOptionRangeValue, diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index f7fd62e734..8fac00eb83 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -231,7 +231,7 @@ gnc_register_color_option(const GncOptionDBPtr& db, const char* section, const char* doc_string, std::string value) { GncOption option{section, name, key, doc_string, value, - GncOptionUIType::FONT}; + GncOptionUIType::COLOR}; db->register_option(section, std::move(option)); } @@ -278,36 +278,80 @@ gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_account_liat_option(const GncOptionDBPtr& db, const char* section, +gnc_register_account_list_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, - std::vector value) + const GncOptionAccountList& value) { - GncOption option{section, name, key, doc_string, value, - GncOptionUIType::ACCOUNT_LIST}; + GncOption option{GncOptionAccountValue{section, name, key, doc_string, + GncOptionUIType::ACCOUNT_LIST, value}}; db->register_option(section, std::move(option)); } void -gnc_register_acount_list_limited_option(const GncOptionDBPtr& db, - const char* section, const char* name, - const char* key, const char* doc_string, - std::vector value) +gnc_register_account_list_limited_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, + const char* doc_string, + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed) { - GncOption option{section, name, key, doc_string, value, - GncOptionUIType::ACCOUNT_LIST}; - db->register_option(section, std::move(option)); + try + { + GncOption option{GncOptionAccountValue{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 registerd.\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(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(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, - std::vector value) + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed) { - GncOption option{section, name, key, doc_string, value, - GncOptionUIType::ACCOUNT_SEL}; + try + { + GncOption option{GncOptionAccountValue{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 diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 72936f23fe..f094a9656f 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -93,6 +93,26 @@ private: std::function m_set_ui_value; }; +/** + * 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); + + using GncOptionDBPtr = std::unique_ptr; /** * Create an empty option database. @@ -160,20 +180,21 @@ void gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section, * 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. */ -void gnc_register_acount_list_limited_option(const GncOptionDBPtr& db, +void gnc_register_account_list_limited_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, - std::vector value); + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed); /* Just like gnc:make-account-list-limited-option except it does not limit the * types of accounts that are available to the user. */ -void gnc_register_account_liat_option(const GncOptionDBPtr& db, +void gnc_register_account_list_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, - std::vector value); + const GncOptionAccountList& value); /* 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 @@ -185,7 +206,8 @@ void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, - std::vector value); + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed); /* Multichoice options use the option-data as a list of vectors. Each vector * contains a permissible value (scheme symbol), a name, and a description diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index a9e8729165..a22b3bf98e 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -57,10 +57,27 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void); */ %inline %{ template inline SCM -scm_from_value(ValueType value) -{ + scm_from_value(ValueType value); +/*{ return SCM_BOOL_F; + }*/ +template <> inline SCM +scm_from_value(QofQuery* value) +{ + return SCM_BOOL_F; } + +template <> inline SCM +scm_from_value(QofInstance* value) +{ + return SCM_BOOL_F; +} +template <> inline SCM + scm_from_value>(std::vector value) +{ + return SCM_BOOL_F; +} + template <> inline SCM scm_from_value(std::string value) { @@ -103,24 +120,30 @@ scm_from_value(const QofInstance* value) /* Not needed now, the default template will do this template <> inline SCM -scm_from_value(const QofQuery* value) +scm_from_value(const QofQuery* value) { return SCM_BOOL_F; } */ +/* Account is actually a typedef for struct account_s and SWIG insists on using + * the struct name (i.e. account_s) in C++ and the alias (i.e. Account) in + * C. Oddly the compiler's type resolution also fails to consider them the same + * so we have to use the struct name here to get the template to resolve + * correctly. + */ +using GncOptionAccount_sList = std::vector; + template <>inline SCM -scm_from_value&>(const std::vector& value) +scm_from_value(GncOptionAccount_sList value) { - SCM s_list; - for (auto guid : value) + SCM s_list = SCM_EOL; + for (auto acct : value) { - auto guid_s = guid_to_string(qof_instance_get_guid(&guid)); - auto scm_guid = scm_from_utf8_string(guid_s); - auto scm_guid_list1 = scm_list_1(scm_guid); - s_list = scm_append(scm_list_2(s_list, scm_guid_list1)); - g_free(guid_s); + SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0)); + s_list = scm_append(scm_list_2(s_list, elem)); } + return s_list; } /* default template @@ -131,6 +154,7 @@ template <>inline SCM } */ + template inline ValueType scm_to_value(SCM new_value) { @@ -167,8 +191,21 @@ scm_to_value(SCM new_value) return scm_to_int64(new_value); } +QofBook* +qof_book_new() +{ + return static_cast(g_object_new(QOF_TYPE_BOOK, nullptr)); +} +void +qof_book_destroy(QofBook* book) +{ + g_object_unref(book); +} + +using Account = struct account_s; %} + %ignore OptionClassifier; %ignore OptionUIItem; %nodefaultctor GncOption; @@ -197,7 +234,77 @@ scm_to_value(SCM new_value) $1 = &choices; } +%typemap(in) GncOptionAccountTypeList& (GncOptionAccountTypeList types) +{ + auto len = scm_to_size_t(scm_length($input)); + 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_to_size_t(scm_length($input)); + 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 +{ + auto len = scm_to_size_t(scm_length($input)); + 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_s, 1, 0); + $1.push_back(acct); + } +} + +%typemap(in) GncOptionAccountList& (GncOptionAccountList acclist) +{ + auto len = scm_to_size_t(scm_length($input)); + 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_s, 1, 0); + acclist.push_back(acct); + } + $1 = &acclist; +} + +%typemap(out) GncOptionAccountList +{ + $result = SCM_EOL; + for (auto acct : $1) + { + SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0)); + $result = scm_append(scm_list_2($result, elem)); + } +} + +%typemap(out) GncOptionAccountList& +{ + $result = SCM_EOL; + for (auto acct : *$1) + { + SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0)); + $result = scm_append(scm_list_2($result, elem)); + } +} + wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); + %include "gnc-option.hpp" %include "gnc-optiondb.hpp" diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 4a1345c8a6..7491b4333c 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -246,6 +246,149 @@ TEST_F(GncRangeOption, test_setter) EXPECT_EQ(1.5, m_doubleoption.get_default_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(account); +} + +class GncOptionAccountTest : public ::testing::Test +{ +protected: + GncOptionAccountTest() : + m_book{qof_book_new()}, 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 + qof_book_destroy(m_book); + } + 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; + } + + QofBook* m_book; + Account* m_root; +}; + +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(2, list.size()); + list = list_of_types({ACCT_TYPE_ASSET, ACCT_TYPE_STOCK}); + EXPECT_EQ(6, list.size()); +} + +TEST_F(GncOptionAccountTest, test_option_no_value_constructor) +{ + GncOptionAccountValue 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})}; + GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, acclist}; + EXPECT_EQ(2, option.get_value().size()); + EXPECT_EQ(2, 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})}; + GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, {ACCT_TYPE_BANK}}; + EXPECT_TRUE(option.get_value().empty()); + EXPECT_TRUE(option.get_default_value().empty()); + 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({ + GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclistbad, {ACCT_TYPE_BANK}); + }, std::invalid_argument); + + EXPECT_THROW({ + GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_SEL, + acclistgood, {ACCT_TYPE_BANK}); + }, std::invalid_argument); + + EXPECT_NO_THROW({ + GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclistgood, {ACCT_TYPE_BANK}); + }); + + EXPECT_NO_THROW({ + GncOptionAccountList accsel{acclistgood[0]}; + GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + accsel, {ACCT_TYPE_BANK}); + }); + GncOptionAccountValue option {"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, acclistgood, {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)); +} + class GncOptionMultichoiceTest : public ::testing::Test { diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 447fcda450..fd0b709f80 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -69,6 +69,100 @@ TEST_F(GncOptionDBTest, test_register_string_option) EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str()); } +/* Note: The following test-fixture code is also present in slightly different + * form in gtest-gnc-option.cpp. + */ + + +struct AccountTestBook +{ + AccountTestBook() : + m_book{qof_book_new()}, 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"); + } + ~AccountTestBook() + { + xaccAccountBeginEdit(m_root); + xaccAccountDestroy(m_root); //It does the commit + qof_book_destroy(m_book); + } + + QofBook* m_book; + Account* m_root; +}; + +TEST_F(GncOptionDBTest, test_register_account_list_option) +{ + AccountTestBook book; + auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; + gnc_register_account_list_option(m_db, "foo", "bar", "baz", "Phony Option", + acclist); + EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value().size()); + EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value().at(3)); +} + +TEST_F(GncOptionDBTest, test_register_account_list_limited_option) +{ + AccountTestBook book; + auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; + gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", + "Phony Option", acclist, + {ACCT_TYPE_STOCK}); + EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value().size()); + EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value().at(3)); +} + +TEST_F(GncOptionDBTest, test_register_account_sel_limited_option) +{ + AccountTestBook book; + auto acclist{gnc_account_list_from_types(book.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(1, m_db->find_option("foo", "bar")->get().get_value().size()); + EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value().at(0)); +} + +TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct) +{ + AccountTestBook book; + auto acclist{gnc_account_list_from_types(book.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")); +} + TEST_F(GncOptionDBTest, test_register_multichoice_option) { GncMultiChoiceOptionChoices choices{ diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index c27f21a743..dde592513b 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -35,6 +35,7 @@ (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) @@ -52,6 +53,86 @@ (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) + (xaccAccountBeginEdit root) + (xaccAccountDestroy root) + (qof-book-destroy book)) + + (define (test-make-account-list-option book) + (test-group "test-make-account-list-option" + (let ((optiondb (gnc-option-db-new)) + (acctlist (gnc-account-list-from-types book + (list ACCT-TYPE-STOCK)))) + (gnc-register-account-list-option optiondb "foo" "bar" "baz" + "Phony Option" acctlist) + (let ((acct-list (gnc-option-value optiondb "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-option" + (let ((optiondb (gnc-option-db-new)) + (acctlist (gnc-account-list-from-types book + (list ACCT-TYPE-STOCK)))) + (gnc-register-account-list-limited-option optiondb "foo" "bar" "baz" + "Phony Option" acctlist (list ACCT-TYPE-STOCK)) + (let ((acct-list (gnc-option-value optiondb "foo" "bar"))) + (test-equal (length acctlist) (length acct-list)) + (test-equal (cadr acctlist) (cadr acct-list))) + (gnc-register-account-list-limited-option optiondb "waldo" "pepper" "baz" + "Phony Option" acctlist (list ACCT-TYPE-BANK)) + (let ((acct-list (gnc-option-value optiondb "waldo" "pepper"))) + (test-equal #f (length acct-list)))))) + + (define (test-make-account-sel-limited-option book) + (test-group "test-make-account-list-option" + (let ((optiondb (gnc-option-db-new)) + (acctlist (gnc-account-list-from-types book + (list ACCT-TYPE-STOCK)))) + (gnc-register-account-sel-limited-option optiondb "salt" "pork" "baz" + "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK)) + (let ((acct (gnc-option-value optiondb "salt" "pork"))) + (test-equal (list (cadr acctlist)) acct))))) + + (let* ((book (qof-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) From 5a9c4ccaf88174973cfc848d263a7cba1e2eaa04 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 17 Nov 2019 12:48:10 -0800 Subject: [PATCH 046/298] Clean out copies of the option.scm comments used as a development guide. --- libgnucash/app-utils/gnc-optiondb.hpp | 57 --------------------------- 1 file changed, 57 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index f094a9656f..34a1af78df 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -144,20 +144,6 @@ void gnc_register_commodity_option(const GncOptionDBPtr& db, const char* key, const char* doc_string, gnc_commodity* value); -/* Complex boolean options are the same as simple boolean options 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. - */ void gnc_register_simple_boolean_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, @@ -173,13 +159,6 @@ void gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); -/* 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. - */ void gnc_register_account_list_limited_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, @@ -187,21 +166,12 @@ void gnc_register_account_list_limited_option(const GncOptionDBPtr& db, const GncOptionAccountList& value, GncOptionAccountTypeList&& allowed); -/* Just like gnc:make-account-list-limited-option except it does not limit the - * types of accounts that are available to the user. - */ void gnc_register_account_list_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, const GncOptionAccountList& value); -/* 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. - */ void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, @@ -209,44 +179,21 @@ void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db, const GncOptionAccountList& value, GncOptionAccountTypeList&& allowed); -/* 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. - * - * 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"). - */ - void gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, GncMultiChoiceOptionChoices&& value); -/* List options use the option-data in the same way as multichoice options. List - * options allow the user to select more than one option. - */ void gnc_register_list_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, const char* value, GncMultiChoiceOptionChoices&& list); -/* Number range options use the option-data as a list whose elements are: - * (lower-bound upper-bound step-size). -*/ void gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, int value, int min, int max, int step); -/* Number plot size options are a convenience wrapper on number range options - * with fixed min, max, and step. -*/ void gnc_register_number_plot_size_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, @@ -257,10 +204,6 @@ void gnc_register_query_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, QofQuery* value); -/* 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. - */ void gnc_register_color_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); From 1b00399b14929961a532fd13a97c1f49712bed07 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 17 Nov 2019 12:50:57 -0800 Subject: [PATCH 047/298] Remove GncOptionValue>. This going to be the Account List option implementation, but a more customized approach is better. --- libgnucash/app-utils/gnc-option.hpp | 2 -- libgnucash/app-utils/gnc-optiondb.i | 5 ----- 2 files changed, 7 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index c48d9f23fa..110d633cbe 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -34,7 +34,6 @@ extern "C" } #include #include -#include #include #include #include @@ -533,7 +532,6 @@ using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue, - GncOptionValue>, GncOptionAccountValue, GncOptionMultichoiceValue, GncOptionRangeValue, diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index a22b3bf98e..92ff7caa14 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -72,11 +72,6 @@ scm_from_value(QofInstance* value) { return SCM_BOOL_F; } -template <> inline SCM - scm_from_value>(std::vector value) -{ - return SCM_BOOL_F; -} template <> inline SCM scm_from_value(std::string value) From aaf6b14c67d158aa3bfd997a1973181ea9a2fb67 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 17 Nov 2019 15:47:17 -0800 Subject: [PATCH 048/298] Remove commented-out code. --- libgnucash/app-utils/gnc-optiondb.i | 56 +---------------------------- 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 92ff7caa14..e92ea84edb 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -112,15 +112,6 @@ scm_from_value(const QofInstance* value) return scm_guid; } - -/* Not needed now, the default template will do this -template <> inline SCM -scm_from_value(const QofQuery* value) -{ - return SCM_BOOL_F; -} -*/ - /* Account is actually a typedef for struct account_s and SWIG insists on using * the struct name (i.e. account_s) in C++ and the alias (i.e. Account) in * C. Oddly the compiler's type resolution also fails to consider them the same @@ -141,14 +132,6 @@ scm_from_value(GncOptionAccount_sList value) return s_list; } -/* default template -template <>inline SCM - scm_from_value<(const std::vector>&) -{ - return SCM_BOOL_F; -} -*/ - template inline ValueType scm_to_value(SCM new_value) @@ -164,16 +147,7 @@ scm_to_value(SCM new_value) free(strval); return retval; } -/* -template <> inline std::string -scm_to_value(SCM 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) { @@ -363,33 +337,5 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); GncOption_set_value_from_scm(&(db_opt->get()), new_value); } %} -/* -TEST(GncOption, test_string_scm_functions) -{ - GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); - auto scm_value = option.get_scm_value(); - auto str_value = scm_to_utf8_string(scm_value); - EXPECT_STREQ("waldo", str_value); - g_free(str_value); - scm_value = option.get_scm_default_value(); - str_value = scm_to_utf8_string(scm_value); - EXPECT_STREQ("waldo", str_value); - g_free(str_value); -} - -TEST(GNCOption, test_budget_scm_functions) -{ - auto book = qof_book_new(); - auto budget = gnc_budget_new(book); - GncOption option("foo", "bar", "baz", "Phony Option", - QOF_INSTANCE(budget)); - auto scm_budget = option.get_scm_value(); - auto str_value = scm_to_utf8_string(scm_budget); - auto guid = guid_to_string(qof_instance_get_guid(budget)); - EXPECT_STREQ(guid, str_value); - g_free(guid); - gnc_budget_destroy(budget); - qof_book_destroy(book); -} */ From e583c84f4e0291077eb04c673afd69b41b1fe0f2 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 17 Nov 2019 15:47:42 -0800 Subject: [PATCH 049/298] Clean up comments for GncOptionDateValue. --- libgnucash/app-utils/gnc-option.hpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 110d633cbe..d8e483943e 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -470,14 +470,18 @@ private: }; /** Date options - * A legal date value is a pair of either and a RelativeDatePeriod, the absolute flag and a time64, or for legacy purposes the absolute flag and a timespec. - * The original design allowed custom RelativeDatePeriods, but that facility is unused so we'll go with compiled-in enums. - + * A legal date value is a pair of either and a RelativeDatePeriod, the absolute + * flag and a time64, or for legacy purposes the absolute flag and a timespec. + * + * The original design allowed custom RelativeDatePeriods, but that facility is + * unused so we'll go with compiled-in enums. + */ +/* 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 -gnc-date-option-relative-time +gnc-date-option-absolute-time m_type == DateTyupe::Absolute +gnc-date-option-relative-time m_type != DateTyupe::Absolute */ enum class DateType From 883127a59d7081ac2a791331ae9e9aa1af3959dc Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 22 Nov 2019 15:45:42 -0800 Subject: [PATCH 050/298] Implement operators >> and << on GncOption. --- libgnucash/app-utils/gnc-option.cpp | 122 +++++ libgnucash/app-utils/gnc-option.hpp | 171 +++++- libgnucash/app-utils/gnc-optiondb.cpp | 38 +- libgnucash/app-utils/gnc-optiondb.hpp | 22 +- libgnucash/app-utils/gnc-optiondb.i | 2 - .../app-utils/test/gtest-gnc-option.cpp | 507 ++++++++++++++++-- 6 files changed, 817 insertions(+), 45 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 06f2abf9bd..3587c93726 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -24,9 +24,11 @@ //#include "options.h" #include "gnc-option.hpp" #include +#include extern "C" { #include "gnc-accounting-period.h" +#include "gnc-ui-util.h" } bool @@ -129,6 +131,72 @@ GncOptionDateValue::get_value() const set_day_and_time(now, m_type == DateType::STARTING); return static_cast(GncDateTime(now)); } +static const char* date_type_str[] {"absolute", "relative"}; +static const std::array date_period_str +{ + "today", "today", + "start-this-month", "end-this-month", + "start-prev-month", "end-prev-month", + "start-current-quarter", "end-current-quarter", + "start-prev-quarter", "end-prev-quarter", + "start-cal-year", "end-cal-year", + "start-prev-year", "end-prev-year", + "start-prev-fin-year", "end-prev-fin-year" +}; + + +std::ostream& +GncOptionDateValue::out_stream(std::ostream& oss) const noexcept +{ + if (m_type == DateType::ABSOLUTE) + oss << date_type_str[0] << " . " << m_date; + else + { + int n{ m_type == DateType::STARTING ? 0 : 1}; + n += 2 * static_cast(m_period); + oss << date_type_str[1] << " . " << date_period_str[n]; + } + return oss; +} + +std::istream& +GncOptionDateValue::in_stream(std::istream& iss) +{ + std::string type_str; + std::getline(iss, type_str, '.'); + if (type_str == "absolute ") + { + time64 time; + iss >> time; + set_value(time); + } + else if (type_str == "relative ") + { + std::string period_str; + iss >> period_str; + auto period = std::find(date_period_str.begin(), date_period_str.end(), + period_str); + if (period == date_period_str.end()) + { + std::string err{"Unknown period string in date option: '"}; + err += period_str; + err += "'"; + throw std::invalid_argument(err); + } + + int64_t index = period - date_period_str.begin(); + DateType type = index % 2 ? DateType::ENDING : DateType::STARTING; + set_value(std::make_pair(type, index / 2)); + } + else + { + std::string err{"Unknown date type string in date option: '"}; + err += type_str; + err += "'"; + throw std::invalid_argument{err}; + } + return iss; +} void GncOptionDateValue::set_value(DateSetterValue value) @@ -145,3 +213,57 @@ GncOptionDateValue::set_value(DateSetterValue value) m_period = static_cast(val); m_date = 0; } + + +QofInstance* +qof_instance_from_string(const std::string& str, GncOptionUIType type) +{ + auto guid{static_cast(gnc::GUID::from_string(str))}; + QofIdType qof_type; + switch(type) + { + case GncOptionUIType::CURRENCY: + case GncOptionUIType::COMMODITY: + qof_type = "Commodity"; + break; + case GncOptionUIType::BUDGET: + qof_type = "Budget"; + break; + case GncOptionUIType::OWNER: + qof_type = "gncOwner"; + 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::QUERY: + qof_type = "gncQuery"; + 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)); +} + +std::string +qof_instance_to_string(const QofInstance* inst) +{ + gnc::GUID guid{*qof_instance_get_guid(inst)}; + return guid.to_string(); +} diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index d8e483943e..ece53edc34 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -33,7 +33,6 @@ extern "C" #include } #include -#include #include #include #include @@ -41,6 +40,7 @@ extern "C" #include #include #include +#include /* * Unused base class to document the structure of the current Scheme option @@ -176,6 +176,28 @@ private: GncOptionUIType m_ui_type; }; +/* 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 the typename = std::enable_if_t<> form so we + * have to use the non-type parameter form. + */ +template>, int> = 0> +std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt) +{ + oss << opt.get_value(); + return oss; +} + +template>, int> = 0> +std::istream& operator>>(std::istream& iss, OptionValueClass& opt) +{ + std::decay_t value; + iss >> value; + opt.set_value(value); + return iss; +} + template class GncOptionValue : public OptionClassifier, public OptionUIItem @@ -195,11 +217,52 @@ public: ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } void set_value(ValueType new_value) { m_value = new_value; } + bool is_changed() const noexcept { return m_value != m_default_value; } private: ValueType m_value; ValueType m_default_value; }; +QofInstance* qof_instance_from_string(const std::string& str, + GncOptionUIType type); +std::string qof_instance_to_string(const QofInstance* inst); + +template<> inline std::ostream& +operator<< >(std::ostream& oss, + const GncOptionValue& opt) +{ + oss << (opt.get_value() ? "#t" : "#f"); + return oss; +} + +template<> inline std::ostream& +operator<< >(std::ostream& oss, + const GncOptionValue& opt) +{ + oss << qof_instance_to_string(opt.get_value()); + return oss; +} + +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) +{ + std::string instr; + iss >> instr; + opt.set_value(qof_instance_from_string(instr, opt.get_ui_type())); + return iss; +} + template class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem @@ -243,6 +306,7 @@ public: else throw std::invalid_argument("Validation failed, value not set."); } + bool is_changed() const noexcept { return m_value != m_default_value; } private: ValueType m_value; ValueType m_default_value; @@ -250,6 +314,25 @@ private: ValueType m_validation_data; }; +template<> inline std::ostream& +operator<< >(std::ostream& oss, + const GncOptionValidatedValue& opt) +{ + oss << qof_instance_to_string(opt.get_value()); + std::cerr << qof_instance_to_string(opt.get_value()); + return oss; +} + +template<> inline std::istream& +operator>> >(std::istream& iss, + GncOptionValidatedValue& opt) +{ + std::string instr; + iss >> instr; + opt.set_value(qof_instance_from_string(instr, opt.get_ui_type())); + return iss; +} + /** * Used for numeric ranges and plot sizes. */ @@ -283,6 +366,7 @@ public: else throw std::invalid_argument("Validation failed, value not set."); } + bool is_changed() const noexcept { return m_value != m_default_value; } private: ValueType m_value; ValueType m_default_value; @@ -376,6 +460,7 @@ public: { return std::get<2>(m_choices.at(index)); } + bool is_changed() const noexcept { return m_value != m_default_value; } private: std::size_t find_key (const std::string& key) const noexcept { @@ -462,13 +547,41 @@ public: //throw! m_value = values; } - + bool is_changed() const noexcept { return m_value != m_default_value; } private: GncOptionAccountList m_value; GncOptionAccountList m_default_value; GncOptionAccountTypeList m_allowed; }; +template<> inline std::ostream& +operator<< (std::ostream& oss, + const GncOptionAccountValue& opt) +{ + auto values{opt.get_value()}; + for (auto value : values) + oss << qof_instance_to_string(QOF_INSTANCE(value)) << " "; + return oss; +} + +template<> inline std::istream& +operator>> (std::istream& iss, + GncOptionAccountValue& opt) +{ + GncOptionAccountList values; + while (true) + { + std::string str; + std::getline(iss, str, ' '); + if (!str.empty()) + values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type())); + else + break; + } + opt.set_value(values); + iss.clear(); + return iss; +} /** Date options * A legal date value is a pair of either and a RelativeDatePeriod, the absolute * flag and a time64, or for legacy purposes the absolute flag and a timespec. @@ -503,7 +616,7 @@ enum class RelativeDatePeriod : int64_t ACCOUNTING_PERIOD }; -using DateSetterValue = std::pair; +using DateSetterValue = std::tuple; class GncOptionDateValue : public OptionClassifier, public OptionUIItem { public: @@ -519,23 +632,39 @@ public: GncOptionDateValue& operator=(GncOptionDateValue&&) = default; time64 get_value() const; time64 get_default_value() const { return static_cast(GncDateTime()); } + std::ostream& out_stream(std::ostream& oss) const noexcept; + std::istream& in_stream(std::istream& iss); void set_value(DateSetterValue); void set_value(time64 time) { m_type = DateType::ABSOLUTE; m_period = RelativeDatePeriod::TODAY; m_date = time; } + bool is_changed() const noexcept { return true; } private: DateType m_type; RelativeDatePeriod m_period; time64 m_date; }; +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); +} + using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue, - GncOptionValue, GncOptionAccountValue, GncOptionMultichoiceValue, GncOptionRangeValue, @@ -631,9 +760,43 @@ public: option.make_internal(); }, m_option); } + bool is_changed() + { + return std::visit([](const auto& option)->bool { + return option.is_changed(); + }, m_option); + } + + std::ostream& out_stream(std::ostream& oss) const + { + return std::visit([&oss](auto& option) -> std::ostream& { + oss << option; + return oss; + }, m_option); + } + std::istream& in_stream(std::istream& iss) + { + return std::visit([&iss](auto& option) -> std::istream& { + iss >> option; + return iss; + }, m_option); + } + GncOptionVariant& _get_option() const { return m_option; } private: mutable GncOptionVariant m_option; }; +inline std::ostream& +operator<<(std::ostream& oss, const GncOption& opt) +{ + return opt.out_stream(oss); +} + +inline std::istream& +operator>>(std::istream& iss, GncOption& opt) +{ + return opt.in_stream(iss); +} + #endif //GNC_OPTION_HPP_ diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 8fac00eb83..e93e7f2490 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -138,9 +138,9 @@ GncOptionDB::find_section(const char* section) } std::optional> -GncOptionDB::find_option(const char* section, const char* name) +GncOptionDB::find_option(const char* section, const char* name) const { - auto db_section = find_section(section); + auto db_section = const_cast(this)->find_section(section); if (!db_section) return std::nullopt; auto db_opt = std::find_if( @@ -173,6 +173,36 @@ GncOptionDB::make_internal(const char* section, const char* name) db_opt->get().make_internal(); } +std::ostream& +GncOptionDB::serialize_option_scheme(std::ostream& oss, + const char* option_prolog, + const char* section, const char* name) const noexcept +{ + auto db_opt = find_option(section, name); + if (!db_opt || !db_opt->get().is_changed()) + return oss; + oss << c_scheme_serialization_elements[0] << option_prolog; + oss << c_scheme_serialization_elements[1] << section; + oss << c_scheme_serialization_elements[1] << name; //repeats, not an error! +// oss << c_scheme_serialization_elements[2] << db_opt->get(); + oss << c_scheme_serialization_elements[3]; + + return oss; +} + +std::ostream& +GncOptionDB::serialize_option_key_value(std::ostream& oss, + const char* section, + const char* name) const noexcept +{ + + auto db_opt = find_option(section, name); + if (!db_opt || !db_opt->get().is_changed()) + return oss; + oss << section << ":" << name << "=" /* << db_opt->get() */ << ";"; + return oss; +} + void GncOptionDB::commit() { @@ -304,7 +334,7 @@ gnc_register_account_list_limited_option(const GncOptionDBPtr& db, } catch (const std::invalid_argument& err) { - std::cerr << "Account List Limited Option, value failed validation, option not registerd.\n"; + std::cerr << "Account List Limited Option, value failed validation, option not registered.\n"; } } @@ -405,7 +435,7 @@ gnc_register_query_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, QofQuery* value) { - GncOption option{section, name, key, doc_string, value, + GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), GncOptionUIType::INTERNAL}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 34a1af78df..81564448cf 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -28,6 +28,7 @@ #include #include #include +#include extern "C" { #include @@ -83,14 +84,33 @@ public: void make_internal(const char* section, const char* name); void commit(); std::optional> find_section(const char* section); - std::optional> find_option(const char* section, const char* name); + std::optional> find_option(const char* section, const char* name) { + return static_cast(*this).find_option(section, name); + } + std::optional> find_option(const char* section, const char* name) const; private: + std::ostream& serialize_option_scheme(std::ostream& oss, + const char* option_prolog, + const char* section, const char* name) const noexcept; + std::ostream& serialize_option_key_value(std::ostream& oss, + const char* section, + const char* name) const noexcept; + void load_option_scheme(std::istream iss); + void load_option_key_value(std::istream iss); std::optional> m_default_section; std::vector m_sections; bool m_dirty = false; std::function m_get_ui_value; std::function m_set_ui_value; + static constexpr char const* const c_scheme_serialization_elements[] + { + "(let ((option (gnc:lookup-option ", + "\n ", + ")))\n ((lambda (o) (if o (gnc:option-set-value o", + "))) option))\n\n" + }; + }; /** diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index e92ea84edb..f3e58f2260 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -337,5 +337,3 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); GncOption_set_value_from_scm(&(db_opt->get()), new_value); } %} - -*/ diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 7491b4333c..9876cb81a8 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -23,10 +23,13 @@ #include #include +#include extern "C" { #include #include +#include +#include } TEST(GncOption, test_string_ctor) @@ -64,6 +67,23 @@ TEST(GncOption, test_string_value) }); } +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)); @@ -72,35 +92,81 @@ TEST(GncOption, test_int64_t_value) EXPECT_EQ(INT64_C(987654321), option.get_value()); } -TEST(GNCOption, test_budget_ctor) +TEST(GncOption, test_int64_stream_out) { - auto book = qof_book_new(); - auto budget = gnc_budget_new(book); + 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(), "#f"); + oss.str(""); + option.set_value(true); + oss << option; + EXPECT_STREQ(oss.str().c_str(), "#t"); +} + +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("foo", "bar", "baz", "Phony Option", QOF_INSTANCE(budget)); }); gnc_budget_destroy(budget); - qof_book_destroy(book); } -TEST(GNCOption, test_commodity_ctor) +TEST_F(GncOptionTest, test_commodity_ctor) { - auto book = qof_book_new(); - auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", + auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.", "NYSE", "HPE", NULL, 1); EXPECT_NO_THROW({ GncOption option("foo", "bar", "baz", "Phony Option", QOF_INSTANCE(hpe)); }); gnc_commodity_destroy(hpe); - qof_book_destroy(book); } static GncOption make_currency_option (const char* section, const char* name, const char* key, const char* doc_string, - gnc_commodity *value) + gnc_commodity *value, bool is_currency=false) { GncOption option{GncOptionValidatedValue{ section, name, key, doc_string, QOF_INSTANCE(value), @@ -108,52 +174,48 @@ make_currency_option (const char* section, const char* name, { return GNC_IS_COMMODITY (new_value) && gnc_commodity_is_currency(GNC_COMMODITY(new_value)); - } - }}; + }, is_currency ? GncOptionUIType::CURRENCY : GncOptionUIType::COMMODITY} + }; return option; } -TEST(GNCOption, test_currency_ctor) +TEST_F(GncOptionTest, test_currency_ctor) { - auto book = qof_book_new(); auto table = gnc_commodity_table_new(); - qof_book_set_data(book, GNC_COMMODITY_TABLE, table); - auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table); + auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.", "NYSE", "HPE", NULL, 1); EXPECT_THROW({ auto option = make_currency_option("foo", "bar", "baz", - "Phony Option", hpe); + "Phony Option", hpe, false); }, std::invalid_argument); gnc_commodity_destroy(hpe); - auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100); + auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100); EXPECT_NO_THROW({ auto option = make_currency_option("foo", "bar", "baz", - "Phony Option", eur); + "Phony Option", eur, true); }); gnc_commodity_destroy(eur); - auto usd = gnc_commodity_new(book, "United States Dollar", + auto usd = gnc_commodity_new(m_book, "United States Dollar", "CURRENCY", "USD", NULL, 100); EXPECT_NO_THROW({ auto option = make_currency_option("foo", "bar", "baz", - "Phony Option",usd); + "Phony Option", usd, true); }); gnc_commodity_destroy(usd); - qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr); + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr); gnc_commodity_table_destroy(table); - qof_book_destroy(book); } -TEST(GNCOption, test_currency_setter) +TEST_F(GncOptionTest, test_currency_setter) { - auto book = qof_book_new(); auto table = gnc_commodity_table_new(); - qof_book_set_data(book, GNC_COMMODITY_TABLE, table); - auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table); + auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.", "NYSE", "HPE", NULL, 1); - auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100); - auto option = make_currency_option("foo", "bar", "baz", - "Phony Option",eur); - auto usd = gnc_commodity_new(book, "United States Dollar", + auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100); + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true); + auto usd = gnc_commodity_new(m_book, "United States Dollar", "CURRENCY", "USD", NULL, 100); EXPECT_NO_THROW({ option.set_value(QOF_INSTANCE(usd)); @@ -166,9 +228,52 @@ TEST(GNCOption, test_currency_setter) gnc_commodity_destroy(hpe); gnc_commodity_destroy(usd); gnc_commodity_destroy(eur); - qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr); + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr); + gnc_commodity_table_destroy(table); +} + +TEST_F(GncOptionTest, test_qofinstance_out) +{ + auto table = gnc_commodity_table_new(); + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table); + auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100); + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true); + + std::string eur_guid{gnc::GUID{*qof_instance_get_guid(eur)}.to_string()}; + std::ostringstream oss; + oss << option; + EXPECT_EQ(eur_guid, oss.str()); + gnc_commodity_destroy(eur); + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr); + gnc_commodity_table_destroy(table); +} + +TEST_F(GncOptionTest, test_qofinstance_in) +{ + auto table = gnc_commodity_table_new(); + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table); + auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100); + auto usd = gnc_commodity_new(m_book, "United States Dollar", + "CURRENCY", "USD", NULL, 100); + auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.", + "NYSE", "HPE", NULL, 1); + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true); + + EXPECT_THROW({ + std::string hpe_guid{gnc::GUID{*qof_instance_get_guid(hpe)}.to_string()}; + std::istringstream iss{hpe_guid}; + iss >> option; + }, std::invalid_argument); + EXPECT_NO_THROW({ + std::string usd_guid{gnc::GUID{*qof_instance_get_guid(usd)}.to_string()}; + std::istringstream iss{usd_guid}; + iss >> option; + EXPECT_EQ(QOF_INSTANCE(usd), option.get_value()); + }); + gnc_commodity_destroy(eur); + gnc_commodity_destroy(usd); + qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr); gnc_commodity_table_destroy(table); - qof_book_destroy(book); } class GncUIItem @@ -246,6 +351,24 @@ TEST_F(GncRangeOption, test_setter) EXPECT_EQ(1.5, m_doubleoption.get_default_value()); } +TEST_F(GncRangeOption, test_range_out) +{ + std::ostringstream oss; + oss << "Integer " << m_intoption << " Double " << m_doubleoption << "."; + EXPECT_STREQ("Integer 15 Double 1.5.", 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 @@ -264,7 +387,8 @@ class GncOptionAccountTest : public ::testing::Test { protected: GncOptionAccountTest() : - m_book{qof_book_new()}, m_root{gnc_account_create_root(m_book)} + 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* { @@ -296,7 +420,7 @@ protected: { xaccAccountBeginEdit(m_root); xaccAccountDestroy(m_root); //It does the commit - qof_book_destroy(m_book); + gnc_clear_current_session(); } GncOptionAccountList list_of_types(const GncOptionAccountTypeList& types) { @@ -307,6 +431,7 @@ protected: return list; } + QofSession* m_session; QofBook* m_book; Account* m_root; }; @@ -389,6 +514,65 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor) EXPECT_EQ(false, option.validate(acclistbad)); } +TEST_F(GncOptionAccountTest, test_account_list_out) +{ + GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; + GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, acclist}; + std::ostringstream oss; + std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()}; + acc_guids += " "; + acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string(); + acc_guids += " "; + + oss << option; + EXPECT_EQ(acc_guids, oss.str()); + + GncOptionAccountList accsel{acclist[0]}; + GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + accsel, {ACCT_TYPE_BANK}); + acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string(); + acc_guids += " "; + + 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})}; + GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, acclist}; + std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()}; + acc_guids += " "; + acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string(); + acc_guids += " "; + + std::istringstream iss{acc_guids}; + iss >> option; + EXPECT_EQ(acclist, option.get_value()); + + GncOptionAccountList accsel{acclist[0]}; + GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + accsel, {ACCT_TYPE_BANK}); + GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; + acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string(); + acc_guids += " "; + + iss.str(acc_guids); + iss >> sel_option; + EXPECT_EQ(accsel, sel_option.get_value()); + + acc_guids = gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string(); + EXPECT_NO_THROW({ + iss.str(acc_guids); + iss >> sel_option; + }); + EXPECT_EQ(acclist[1], sel_option.get_value()[0]); +} class GncOptionMultichoiceTest : public ::testing::Test { @@ -611,3 +795,258 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_end) EXPECT_EQ(time1, m_option.get_value()); } +TEST_F(GncDateOption, test_stream_out) +{ + time64 time1{static_cast(GncDateTime("2019-07-19 15:32:26 +05:00"))}; + DateSetterValue value1{DateType::ABSOLUTE, time1}; + m_option.set_value(value1); + std::ostringstream oss; + oss << time1; + std::string timestr{"absolute . "}; + timestr += oss.str(); + oss.str(""); + oss << m_option; + EXPECT_EQ(oss.str(), timestr); + + DateSetterValue value2{DateType::STARTING, static_cast(RelativeDatePeriod::TODAY)}; + m_option.set_value(value2); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . today"); + + DateSetterValue value3{DateType::ENDING, static_cast(RelativeDatePeriod::TODAY)}; + m_option.set_value(value3); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . today"); + + DateSetterValue value4{DateType::STARTING, static_cast(RelativeDatePeriod::THIS_MONTH)}; + m_option.set_value(value4); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . start-this-month"); + + DateSetterValue value5{DateType::ENDING, static_cast(RelativeDatePeriod::THIS_MONTH)}; + m_option.set_value(value5); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . end-this-month"); + + DateSetterValue value6{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_MONTH)}; + m_option.set_value(value6); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-month"); + + DateSetterValue value7{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_MONTH)}; + m_option.set_value(value7); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-month"); + + DateSetterValue value8{DateType::STARTING, static_cast(RelativeDatePeriod::CURRENT_QUARTER)}; + m_option.set_value(value8); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . start-current-quarter"); + + DateSetterValue value9{DateType::ENDING, static_cast(RelativeDatePeriod::CURRENT_QUARTER)}; + m_option.set_value(value9); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . end-current-quarter"); + + DateSetterValue value10{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_QUARTER)}; + m_option.set_value(value10); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-quarter"); + + DateSetterValue value11{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_QUARTER)}; + m_option.set_value(value11); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-quarter"); + + DateSetterValue value12{DateType::STARTING, static_cast(RelativeDatePeriod::CAL_YEAR)}; + m_option.set_value(value12); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . start-cal-year"); + + DateSetterValue value13{DateType::ENDING, static_cast(RelativeDatePeriod::CAL_YEAR)}; + m_option.set_value(value13); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . end-cal-year"); + + DateSetterValue value14{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_YEAR)}; + m_option.set_value(value14); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-year"); + + DateSetterValue value15{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_YEAR)}; + m_option.set_value(value15); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-year"); + + DateSetterValue value16{DateType::STARTING, static_cast(RelativeDatePeriod::ACCOUNTING_PERIOD)}; + m_option.set_value(value16); + oss.str(""); + oss << m_option; + EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-fin-year"); + + DateSetterValue value17{DateType::ENDING, static_cast(RelativeDatePeriod::ACCOUNTING_PERIOD)}; + m_option.set_value(value17); + 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 month_end; + g_date_set_time_t(&month_end, time(nullptr)); + gnc_gdate_set_month_end(&month_end); + time64 time1{time64_from_gdate(&month_end, 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 prev_month_end; + g_date_set_time_t(&prev_month_end, time(nullptr)); + gnc_gdate_set_prev_month_end(&prev_month_end); + time64 time1{time64_from_gdate(&prev_month_end, 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 quarter_start; + g_date_set_time_t(&quarter_start, time(nullptr)); + gnc_gdate_set_quarter_start(&quarter_start); + time64 time1{time64_from_gdate(&quarter_start, 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 quarter_end; + g_date_set_time_t(&quarter_end, time(nullptr)); + gnc_gdate_set_quarter_end(&quarter_end); + time64 time1{time64_from_gdate(&quarter_end, 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 prev_quarter_start; + g_date_set_time_t(&prev_quarter_start, time(nullptr)); + gnc_gdate_set_prev_quarter_start(&prev_quarter_start); + time64 time1{time64_from_gdate(&prev_quarter_start, 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 prev_quarter_end; + g_date_set_time_t(&prev_quarter_end, time(nullptr)); + gnc_gdate_set_prev_quarter_end(&prev_quarter_end); + time64 time1{time64_from_gdate(&prev_quarter_end, 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 year_start; + g_date_set_time_t(&year_start, time(nullptr)); + gnc_gdate_set_year_start(&year_start); + time64 time1{time64_from_gdate(&year_start, 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 year_end; + g_date_set_time_t(&year_end, time(nullptr)); + gnc_gdate_set_year_end(&year_end); + time64 time1{time64_from_gdate(&year_end, 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 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)}; + 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 prev_year_end; + g_date_set_time_t(&prev_year_end, time(nullptr)); + gnc_gdate_set_prev_year_end(&prev_year_end); + time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)}; + std::istringstream iss{"relative . end-prev-year"}; + iss >> m_option; + EXPECT_EQ(time1, m_option.get_value()); +} From 52d0ec52901fca6919e4d9ce09a525d992d15361 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 25 Nov 2019 11:20:44 -0800 Subject: [PATCH 051/298] Change GncOptionDateValue design to better match usage. Added advantage that it handles (not correctly, but in the same way as the old code) the ambiguity between a period starting or ending with the stored value "relative . today". --- libgnucash/app-utils/gnc-option.cpp | 83 ++++++++--------- libgnucash/app-utils/gnc-option.hpp | 66 ++++++++----- libgnucash/app-utils/gnc-optiondb.cpp | 7 +- libgnucash/app-utils/gnc-optiondb.hpp | 3 +- .../app-utils/test/gtest-gnc-option.cpp | 93 ++++++------------- .../app-utils/test/gtest-gnc-optiondb.cpp | 18 +++- 6 files changed, 135 insertions(+), 135 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 3587c93726..55c52bb602 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -88,18 +88,32 @@ set_day_and_time(struct tm& now, bool starting) time64 GncOptionDateValue::get_value() const { - if (m_type == DateType::ABSOLUTE) + if (m_period == RelativeDatePeriod::ABSOLUTE) return m_date; if (m_period == RelativeDatePeriod::TODAY) return static_cast(GncDateTime()); - if (m_period == RelativeDatePeriod::ACCOUNTING_PERIOD) - return m_type == DateType::STARTING ? - gnc_accounting_period_fiscal_start() : - gnc_accounting_period_fiscal_end(); + if (m_period == RelativeDatePeriod::START_ACCOUNTING_PERIOD) + return gnc_accounting_period_fiscal_start(); + if (m_period == RelativeDatePeriod::END_ACCOUNTING_PERIOD) + return gnc_accounting_period_fiscal_end(); - struct tm now{static_cast(GncDateTime())}; + GncDateTime now_t; + if (m_period == RelativeDatePeriod::TODAY) + return static_cast(now_t); + struct tm now{static_cast(now_t)}; struct tm period{static_cast(GncDateTime(gnc_accounting_period_fiscal_start()))}; + bool starting = m_period == RelativeDatePeriod::START_PREV_MONTH || + m_period == RelativeDatePeriod::START_THIS_MONTH || + m_period == RelativeDatePeriod::START_CAL_YEAR || + m_period == RelativeDatePeriod::START_PREV_YEAR || + m_period == RelativeDatePeriod::START_CURRENT_QUARTER || + m_period == RelativeDatePeriod::START_PREV_QUARTER; + bool prev = m_period == RelativeDatePeriod::START_PREV_YEAR || + m_period == RelativeDatePeriod::END_PREV_YEAR || + m_period == RelativeDatePeriod::START_PREV_QUARTER || + m_period == RelativeDatePeriod::END_PREV_QUARTER; + if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday) { //No set accounting period, use the calendar year @@ -107,34 +121,40 @@ GncOptionDateValue::get_value() const period.tm_mday = 0; } - if (m_period == RelativeDatePeriod::CAL_YEAR || - m_period == RelativeDatePeriod::PREV_YEAR) + if (m_period == RelativeDatePeriod::START_CAL_YEAR || + m_period == RelativeDatePeriod::END_CAL_YEAR || + m_period == RelativeDatePeriod::START_PREV_YEAR || + m_period == RelativeDatePeriod::END_PREV_YEAR) { - if (m_period == RelativeDatePeriod::PREV_YEAR) + + if (prev) --now.tm_year; - now.tm_mon = m_type == DateType::STARTING ? 0 : 11; + now.tm_mon = starting ? 0 : 11; } - else if (m_period == RelativeDatePeriod::PREV_QUARTER || - m_period == RelativeDatePeriod::CURRENT_QUARTER) + else if (m_period == RelativeDatePeriod::START_PREV_QUARTER || + m_period == RelativeDatePeriod::END_PREV_QUARTER || + m_period == RelativeDatePeriod::START_CURRENT_QUARTER || + m_period == RelativeDatePeriod::END_CURRENT_QUARTER) { auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon : period.tm_mon - now.tm_mon) % 3; now.tm_mon = now.tm_mon - offset; - if (m_period == RelativeDatePeriod::PREV_QUARTER) + if (prev) now.tm_mon -= 3; - if (m_type == DateType::ENDING) + if (!starting) now.tm_mon += 2; } - else if (m_period == RelativeDatePeriod::PREV_MONTH) + else if (m_period == RelativeDatePeriod::START_PREV_MONTH || + m_period == RelativeDatePeriod::END_PREV_MONTH) --now.tm_mon; normalize_month(now); - set_day_and_time(now, m_type == DateType::STARTING); + set_day_and_time(now, starting); return static_cast(GncDateTime(now)); } static const char* date_type_str[] {"absolute", "relative"}; static const std::array date_period_str { - "today", "today", + "today", "start-this-month", "end-this-month", "start-prev-month", "end-prev-month", "start-current-quarter", "end-current-quarter", @@ -148,14 +168,11 @@ static const std::array date_period_str std::ostream& GncOptionDateValue::out_stream(std::ostream& oss) const noexcept { - if (m_type == DateType::ABSOLUTE) + if (m_period == RelativeDatePeriod::ABSOLUTE) oss << date_type_str[0] << " . " << m_date; else - { - int n{ m_type == DateType::STARTING ? 0 : 1}; - n += 2 * static_cast(m_period); - oss << date_type_str[1] << " . " << date_period_str[n]; - } + oss << date_type_str[1] << " . " << + date_period_str[static_cast(m_period)]; return oss; } @@ -185,8 +202,7 @@ GncOptionDateValue::in_stream(std::istream& iss) } int64_t index = period - date_period_str.begin(); - DateType type = index % 2 ? DateType::ENDING : DateType::STARTING; - set_value(std::make_pair(type, index / 2)); + set_value(static_cast(index)); } else { @@ -198,23 +214,6 @@ GncOptionDateValue::in_stream(std::istream& iss) return iss; } -void -GncOptionDateValue::set_value(DateSetterValue value) -{ - auto [type, val] = value; - m_type = type; - if (type == DateType::ABSOLUTE) - { - m_period = RelativeDatePeriod::TODAY; - m_date = static_cast(val); - return; - } - - m_period = static_cast(val); - m_date = 0; -} - - QofInstance* qof_instance_from_string(const std::string& str, GncOptionUIType type) { diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index ece53edc34..c85559f058 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -597,26 +597,26 @@ gnc-date-option-absolute-time m_type == DateTyupe::Absolute gnc-date-option-relative-time m_type != DateTyupe::Absolute */ -enum class DateType -{ - ABSOLUTE, - STARTING, - ENDING, -}; - -enum class RelativeDatePeriod : int64_t +enum class RelativeDatePeriod : int { + ABSOLUTE = -1, TODAY, - THIS_MONTH, - PREV_MONTH, - CURRENT_QUARTER, - PREV_QUARTER, - CAL_YEAR, - PREV_YEAR, - ACCOUNTING_PERIOD + START_THIS_MONTH, + END_THIS_MONTH, + START_PREV_MONTH, + END_PREV_MONTH, + START_CURRENT_QUARTER, + END_CURRENT_QUARTER, + START_PREV_QUARTER, + END_PREV_QUARTER, + START_CAL_YEAR, + END_CAL_YEAR, + START_PREV_YEAR, + END_PREV_YEAR, + START_ACCOUNTING_PERIOD, + END_ACCOUNTING_PERIOD }; -using DateSetterValue = std::tuple; class GncOptionDateValue : public OptionClassifier, public OptionUIItem { public: @@ -624,8 +624,24 @@ public: const char* key, const char* doc_string) : OptionClassifier{section, name, key, doc_string}, OptionUIItem(GncOptionUIType::DATE), - m_type{DateType::ABSOLUTE}, m_period{RelativeDatePeriod::TODAY}, - m_date{static_cast(GncDateTime())} {} + m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, + m_date{INT64_MAX}, + m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, + m_default_date{INT64_MAX} {} + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string, + time64 time) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(GncOptionUIType::DATE), + m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time}, + m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {} + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string, + const RelativeDatePeriod period) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(GncOptionUIType::DATE), + m_period{period}, m_date{INT64_MAX}, + m_default_period{period}, m_default_date{INT64_MAX} {} GncOptionDateValue(const GncOptionDateValue&) = default; GncOptionDateValue(GncOptionDateValue&&) = default; GncOptionDateValue& operator=(const GncOptionDateValue&) = default; @@ -634,17 +650,21 @@ public: time64 get_default_value() const { return static_cast(GncDateTime()); } std::ostream& out_stream(std::ostream& oss) const noexcept; std::istream& in_stream(std::istream& iss); - void set_value(DateSetterValue); + void set_value(RelativeDatePeriod value) { + m_period = value; + m_date = INT64_MAX; + } void set_value(time64 time) { - m_type = DateType::ABSOLUTE; - m_period = RelativeDatePeriod::TODAY; + m_period = RelativeDatePeriod::ABSOLUTE; m_date = time; } - bool is_changed() const noexcept { return true; } + bool is_changed() const noexcept { return m_period != m_default_period && + m_date != m_default_date; } private: - DateType m_type; RelativeDatePeriod m_period; time64 m_date; + RelativeDatePeriod m_default_period; + time64 m_default_date; }; template<> inline std::ostream& diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index e93e7f2490..c4c7fe4d4e 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -530,9 +530,10 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, void gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section, - const char* name, const char* key, - const char* doc_string) + const char* name, const char* key, + const char* doc_string, + RelativeDatePeriod period) { - GncOption option{GncOptionDateValue(section, name, key, doc_string)}; + GncOption option{GncOptionDateValue(section, name, key, doc_string, period)}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 81564448cf..b8c515133d 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -265,5 +265,6 @@ void gnc_register_dateformat_option(const GncOptionDBPtr& db, void gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section, const char* name, - const char* key, const char* doc_string); + const char* key, const char* doc_string, + RelativeDatePeriod period); #endif //GNC_OPTIONDB_HPP_ diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 9876cb81a8..2d2ed45c23 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -658,8 +658,7 @@ time64_from_gdate(const GDate* g_date, DayPart when) TEST_F(GncDateOption, test_set_and_get_absolute) { time64 time1{static_cast(GncDateTime("2019-07-19 15:32:26 +05:00"))}; - DateSetterValue value1{DateType::ABSOLUTE, time1}; - m_option.set_value(value1); + m_option.set_value(time1); EXPECT_EQ(time1, m_option.get_value()); } @@ -669,8 +668,7 @@ TEST_F(GncDateOption, test_set_and_get_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)}; - DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::THIS_MONTH)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::START_THIS_MONTH); EXPECT_EQ(time1, m_option.get_value()); } @@ -680,8 +678,7 @@ TEST_F(GncDateOption, test_set_and_get_month_end) g_date_set_time_t(&month_end, time(nullptr)); gnc_gdate_set_month_end(&month_end); time64 time1{time64_from_gdate(&month_end, DayPart::end)}; - DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::THIS_MONTH)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::END_THIS_MONTH); EXPECT_EQ(time1, m_option.get_value()); } @@ -691,8 +688,7 @@ TEST_F(GncDateOption, test_set_and_get_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)}; - DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_MONTH)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::START_PREV_MONTH); EXPECT_EQ(time1, m_option.get_value()); } @@ -702,8 +698,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_month_end) g_date_set_time_t(&prev_month_end, time(nullptr)); gnc_gdate_set_prev_month_end(&prev_month_end); time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)}; - DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_MONTH)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::END_PREV_MONTH); EXPECT_EQ(time1, m_option.get_value()); } @@ -713,8 +708,7 @@ TEST_F(GncDateOption, test_set_and_get_quarter_start) g_date_set_time_t(&quarter_start, time(nullptr)); gnc_gdate_set_quarter_start(&quarter_start); time64 time1{time64_from_gdate(&quarter_start, DayPart::start)}; - DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::CURRENT_QUARTER)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER); EXPECT_EQ(time1, m_option.get_value()); } @@ -724,8 +718,7 @@ TEST_F(GncDateOption, test_set_and_get_quarter_end) g_date_set_time_t(&quarter_end, time(nullptr)); gnc_gdate_set_quarter_end(&quarter_end); time64 time1{time64_from_gdate(&quarter_end, DayPart::end)}; - DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::CURRENT_QUARTER)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER); EXPECT_EQ(time1, m_option.get_value()); } @@ -735,8 +728,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_quarter_start) g_date_set_time_t(&prev_quarter_start, time(nullptr)); gnc_gdate_set_prev_quarter_start(&prev_quarter_start); time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)}; - DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_QUARTER)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER); EXPECT_EQ(time1, m_option.get_value()); } @@ -746,8 +738,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_quarter_end) g_date_set_time_t(&prev_quarter_end, time(nullptr)); gnc_gdate_set_prev_quarter_end(&prev_quarter_end); time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)}; - DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_QUARTER)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER); EXPECT_EQ(time1, m_option.get_value()); } @@ -757,8 +748,7 @@ TEST_F(GncDateOption, test_set_and_get_year_start) g_date_set_time_t(&year_start, time(nullptr)); gnc_gdate_set_year_start(&year_start); time64 time1{time64_from_gdate(&year_start, DayPart::start)}; - DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::CAL_YEAR)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::START_CAL_YEAR); EXPECT_EQ(time1, m_option.get_value()); } @@ -768,8 +758,7 @@ TEST_F(GncDateOption, test_set_and_get_year_end) g_date_set_time_t(&year_end, time(nullptr)); gnc_gdate_set_year_end(&year_end); time64 time1{time64_from_gdate(&year_end, DayPart::end)}; - DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::CAL_YEAR)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::END_CAL_YEAR); EXPECT_EQ(time1, m_option.get_value()); } @@ -779,8 +768,7 @@ TEST_F(GncDateOption, test_set_and_get_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)}; - DateSetterValue value1{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_YEAR)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::START_PREV_YEAR); EXPECT_EQ(time1, m_option.get_value()); } @@ -790,16 +778,14 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_end) g_date_set_time_t(&prev_year_end, time(nullptr)); gnc_gdate_set_prev_year_end(&prev_year_end); time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)}; - DateSetterValue value1{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_YEAR)}; - m_option.set_value(value1); + m_option.set_value(RelativeDatePeriod::END_PREV_YEAR); EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_out) { time64 time1{static_cast(GncDateTime("2019-07-19 15:32:26 +05:00"))}; - DateSetterValue value1{DateType::ABSOLUTE, time1}; - m_option.set_value(value1); + m_option.set_value(time1); std::ostringstream oss; oss << time1; std::string timestr{"absolute . "}; @@ -808,98 +794,77 @@ TEST_F(GncDateOption, test_stream_out) oss << m_option; EXPECT_EQ(oss.str(), timestr); - DateSetterValue value2{DateType::STARTING, static_cast(RelativeDatePeriod::TODAY)}; - m_option.set_value(value2); + m_option.set_value(RelativeDatePeriod::TODAY); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . today"); - DateSetterValue value3{DateType::ENDING, static_cast(RelativeDatePeriod::TODAY)}; - m_option.set_value(value3); - oss.str(""); - oss << m_option; - EXPECT_STREQ(oss.str().c_str(), "relative . today"); - - DateSetterValue value4{DateType::STARTING, static_cast(RelativeDatePeriod::THIS_MONTH)}; - m_option.set_value(value4); + m_option.set_value(RelativeDatePeriod::START_THIS_MONTH); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . start-this-month"); - DateSetterValue value5{DateType::ENDING, static_cast(RelativeDatePeriod::THIS_MONTH)}; - m_option.set_value(value5); + m_option.set_value(RelativeDatePeriod::END_THIS_MONTH); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . end-this-month"); - DateSetterValue value6{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_MONTH)}; - m_option.set_value(value6); + m_option.set_value(RelativeDatePeriod::START_PREV_MONTH); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-month"); - DateSetterValue value7{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_MONTH)}; - m_option.set_value(value7); + m_option.set_value(RelativeDatePeriod::END_PREV_MONTH); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-month"); - DateSetterValue value8{DateType::STARTING, static_cast(RelativeDatePeriod::CURRENT_QUARTER)}; - m_option.set_value(value8); + m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . start-current-quarter"); - DateSetterValue value9{DateType::ENDING, static_cast(RelativeDatePeriod::CURRENT_QUARTER)}; - m_option.set_value(value9); + m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . end-current-quarter"); - DateSetterValue value10{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_QUARTER)}; - m_option.set_value(value10); + m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-quarter"); - DateSetterValue value11{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_QUARTER)}; - m_option.set_value(value11); + m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-quarter"); - DateSetterValue value12{DateType::STARTING, static_cast(RelativeDatePeriod::CAL_YEAR)}; - m_option.set_value(value12); + m_option.set_value(RelativeDatePeriod::START_CAL_YEAR); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . start-cal-year"); - DateSetterValue value13{DateType::ENDING, static_cast(RelativeDatePeriod::CAL_YEAR)}; - m_option.set_value(value13); + m_option.set_value(RelativeDatePeriod::END_CAL_YEAR); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . end-cal-year"); - DateSetterValue value14{DateType::STARTING, static_cast(RelativeDatePeriod::PREV_YEAR)}; - m_option.set_value(value14); + m_option.set_value(RelativeDatePeriod::START_PREV_YEAR); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-year"); - DateSetterValue value15{DateType::ENDING, static_cast(RelativeDatePeriod::PREV_YEAR)}; - m_option.set_value(value15); + m_option.set_value(RelativeDatePeriod::END_PREV_YEAR); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-year"); - DateSetterValue value16{DateType::STARTING, static_cast(RelativeDatePeriod::ACCOUNTING_PERIOD)}; - m_option.set_value(value16); + m_option.set_value(RelativeDatePeriod::START_ACCOUNTING_PERIOD); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-fin-year"); - DateSetterValue value17{DateType::ENDING, static_cast(RelativeDatePeriod::ACCOUNTING_PERIOD)}; - m_option.set_value(value17); + m_option.set_value(RelativeDatePeriod::END_ACCOUNTING_PERIOD); oss.str(""); oss << m_option; EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-fin-year"); diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index fd0b709f80..1dd088dc12 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -176,10 +176,24 @@ TEST_F(GncOptionDBTest, test_register_multichoice_option) 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 time{date, when}; + return static_cast(time); +} + + TEST_F(GncOptionDBTest, test_register_date_interval_option) { - gnc_register_date_interval_option(m_db, "foo", "bar", "baz", "Phony Option"); - auto time{gnc_dmy2time64(11, 7, 2019)}; + gnc_register_date_interval_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 time{time64_from_gdate(&prev_year_start, DayPart::start)}; ASSERT_TRUE(m_db->set_option("foo", "bar", time)); EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value()); } From 6df516dfcdbefe59ba60914a98f14ae2832dd8b6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 1 Dec 2019 15:00:04 -0800 Subject: [PATCH 052/298] Clean up the commodity/currency tests with a better fixture class. --- .../app-utils/test/gtest-gnc-option.cpp | 90 ++++++++++++------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 2d2ed45c23..dbf93a79d1 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -163,6 +163,48 @@ TEST_F(GncOptionTest, test_commodity_ctor) 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, @@ -179,57 +221,37 @@ make_currency_option (const char* section, const char* name, return option; } -TEST_F(GncOptionTest, test_currency_ctor) +TEST_F(GncOptionCommodityTest, test_currency_ctor) { - auto table = gnc_commodity_table_new(); - qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table); - auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.", - "NYSE", "HPE", NULL, 1); EXPECT_THROW({ auto option = make_currency_option("foo", "bar", "baz", - "Phony Option", hpe, false); + "Phony Option", m_hpe, false); }, std::invalid_argument); - gnc_commodity_destroy(hpe); - auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100); EXPECT_NO_THROW({ auto option = make_currency_option("foo", "bar", "baz", - "Phony Option", eur, true); + "Phony Option", m_eur, true); }); - gnc_commodity_destroy(eur); - auto usd = gnc_commodity_new(m_book, "United States Dollar", - "CURRENCY", "USD", NULL, 100); EXPECT_NO_THROW({ auto option = make_currency_option("foo", "bar", "baz", - "Phony Option", usd, true); + "Phony Option", m_usd, true); }); - gnc_commodity_destroy(usd); - qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr); - gnc_commodity_table_destroy(table); } -TEST_F(GncOptionTest, test_currency_setter) +TEST_F(GncOptionCommodityTest, test_currency_setter) { - auto table = gnc_commodity_table_new(); - qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table); - auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.", - "NYSE", "HPE", NULL, 1); - auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100); - auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true); - auto usd = gnc_commodity_new(m_book, "United States Dollar", - "CURRENCY", "USD", NULL, 100); + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", m_eur, true); EXPECT_NO_THROW({ - option.set_value(QOF_INSTANCE(usd)); + option.set_value(QOF_INSTANCE(m_usd)); }); - EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option.get_value())); + EXPECT_PRED2(gnc_commodity_equal, m_usd, + GNC_COMMODITY(option.get_value())); EXPECT_THROW({ - option.set_value(QOF_INSTANCE(hpe)); + option.set_value(QOF_INSTANCE(m_hpe)); }, std::invalid_argument); - EXPECT_PRED2(gnc_commodity_equal, usd, GNC_COMMODITY(option.get_value())); - gnc_commodity_destroy(hpe); - gnc_commodity_destroy(usd); - gnc_commodity_destroy(eur); - qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr); - gnc_commodity_table_destroy(table); + EXPECT_PRED2(gnc_commodity_equal, m_usd, + GNC_COMMODITY(option.get_value())); +} + } TEST_F(GncOptionTest, test_qofinstance_out) From e2a36a8be3221a5f7a7584697e712bfe45d06af0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 1 Dec 2019 15:05:18 -0800 Subject: [PATCH 053/298] Expose function validate to GncOption and test it on GncOptionValidatedValue. --- libgnucash/app-utils/gnc-option.hpp | 16 +++++++++++++++- libgnucash/app-utils/test/gtest-gnc-option.cpp | 8 ++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index c85559f058..e953c3d8c4 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -298,7 +298,7 @@ public: GncOptionValidatedValue& operator=(GncOptionValidatedValue&&) = default; ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } - bool validate(ValueType value) { return m_validator(value); } + bool validate(ValueType value) const { return m_validator(value); } void set_value(ValueType value) { if (this->validate(value)) @@ -787,6 +787,20 @@ public: }, m_option); } + template + bool validate(ValueType value) const { + return std::visit([value] (const auto& option) -> bool { + if constexpr ((std::is_same_v, + GncOptionMultichoiceValue> && + std::is_same_v, + std::string>) || + std::is_same_v, + GncOptionValidatedValue>) + return option.validate(value); + else + return false; + }, m_option); + } std::ostream& out_stream(std::ostream& oss) const { return std::visit([&oss](auto& option) -> std::ostream& { diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index dbf93a79d1..10e97297bd 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -252,6 +252,14 @@ TEST_F(GncOptionCommodityTest, test_currency_setter) 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(QOF_INSTANCE(m_usd))); + EXPECT_FALSE(option.validate(QOF_INSTANCE(m_aapl))); +} + } TEST_F(GncOptionTest, test_qofinstance_out) From 4cabd6c0529fd85b30c7df1425033e94b0fc5936 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 1 Dec 2019 15:10:03 -0800 Subject: [PATCH 054/298] Change the GncOptionMultichoiceValue test to use a GncOption. Requires exposing the permissible values functions to GncOption. --- libgnucash/app-utils/gnc-option.hpp | 52 +++++++++++++++++++ .../app-utils/test/gtest-gnc-option.cpp | 21 ++++---- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index e953c3d8c4..2102930494 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -801,6 +801,58 @@ public: return false; }, m_option); } + + std::size_t num_permissible_values() const { + return std::visit([] (const auto& option) -> size_t { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.num_permissible_values(); + else + return std::numeric_limits::max(); + }, m_option); + } + + std::size_t permissible_value_index(const std::string& value) const { + return std::visit([&value] (const auto& option) -> size_t { + std::cerr << typeid(option).name() << std::endl; + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.permissible_value_index(value); + else + return std::numeric_limits::max();; + }, m_option); + } + + const std::string& permissible_value(std::size_t index) const { + return std::visit([index] (const auto& option) -> const std::string& { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.permissible_value(index); + else + return c_empty_string; + }, m_option); + } + + const std::string& permissible_value_name(std::size_t index) const { + return std::visit([index] (const auto& option) -> const std::string& { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.permissible_value_name(index); + else + return c_empty_string; + }, m_option); + } + + const std::string& permissible_value_description(std::size_t index) const { + return std::visit([index] (const auto& option) -> const std::string& { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.permissible_value_description(index); + else + return c_empty_string; + }, m_option); + } + std::ostream& out_stream(std::ostream& oss) const { return std::visit([&oss](auto& option) -> std::ostream& { diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 10e97297bd..23a0f1d99c 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -608,14 +608,15 @@ class GncOptionMultichoiceTest : public ::testing::Test { protected: GncOptionMultichoiceTest() : - m_option{"foo", "bar", "baz", "Phony Option", "plugh", + m_option{GncOptionMultichoiceValue{"foo", "bar", "baz", + "Phony Option", "plugh", { {"plugh", "xyzzy", "thud"}, {"waldo", "pepper", "salt"}, {"pork", "sausage", "links"}, {"corge", "grault", "garply"} - }} {} - GncOptionMultichoiceValue m_option; + }}} {} + GncOption m_option; }; using GncMultichoiceOption = GncOptionMultichoiceTest; @@ -627,18 +628,20 @@ TEST_F(GncMultichoiceOption, test_option_ui_type) TEST_F(GncMultichoiceOption, test_validate) { - EXPECT_TRUE(m_option.validate("waldo")); - EXPECT_FALSE(m_option.validate("grault")); + 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("pork"); - EXPECT_STREQ("pork", m_option.get_value().c_str()); + m_option.set_value(std::string{"pork"}); + EXPECT_STREQ("pork", m_option.get_value().c_str()); }); - EXPECT_THROW({ m_option.set_value("salt"); }, std::invalid_argument); - 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) From 4dcf4a0e6a4df3360017a2750cac75037db8046c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 1 Dec 2019 15:20:46 -0800 Subject: [PATCH 055/298] Test GncOptionRangeValue as a GncOption. Better representation of how it will be used. --- .../app-utils/test/gtest-gnc-option.cpp | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 23a0f1d99c..20bc434415 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -351,34 +351,36 @@ class GncOptionRangeTest : public ::testing::Test { protected: GncOptionRangeTest() : - m_intoption{"foo", "bar", "baz", "Phony Option", 15, 1, 30, 1}, - m_doubleoption{"waldo", "pepper", "salt", "Phonier Option", - 1.5, 1.0, 3.0, 0.1} {} + 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}} {} - GncOptionRangeValue m_intoption; - GncOptionRangeValue m_doubleoption; + 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()); + 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_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()); + EXPECT_EQ(2.0, m_doubleoption.get_value()); + EXPECT_EQ(1.5, m_doubleoption.get_default_value()); } TEST_F(GncRangeOption, test_range_out) From b2fb57d39edb6798eaad084e168363dc76438e2a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 1 Dec 2019 15:58:52 -0800 Subject: [PATCH 056/298] Fix GncOption::set_value() to work with GncOptionDateValue::set_value(RelativeDatePeriod). Adjusting tests as necessary. --- libgnucash/app-utils/gnc-option.hpp | 8 ++- .../app-utils/test/gtest-gnc-option.cpp | 54 +++++++++---------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 2102930494..b3a750b25e 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -728,7 +728,13 @@ public: template void set_value(ValueType value) { std::visit([value](auto& option) { - if constexpr (std::is_same_v, std::decay_t>) + if constexpr + (std::is_same_v, + std::decay_t> || + (std::is_same_v, + GncOptionDateValue> && + std::is_same_v, + RelativeDatePeriod>)) option.set_value(value); }, m_option); } diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 20bc434415..fb2038d9fd 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -676,7 +676,7 @@ protected: GncOptionDateOptionTest() : m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option"}} {} - GncOptionDateValue m_option; + GncOption m_option; }; using GncDateOption = GncOptionDateOptionTest; @@ -694,7 +694,7 @@ 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()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_month_start) @@ -704,7 +704,7 @@ TEST_F(GncDateOption, test_set_and_get_month_start) gnc_gdate_set_month_start(&month_start); time64 time1{time64_from_gdate(&month_start, DayPart::start)}; m_option.set_value(RelativeDatePeriod::START_THIS_MONTH); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_month_end) @@ -714,7 +714,7 @@ TEST_F(GncDateOption, test_set_and_get_month_end) gnc_gdate_set_month_end(&month_end); time64 time1{time64_from_gdate(&month_end, DayPart::end)}; m_option.set_value(RelativeDatePeriod::END_THIS_MONTH); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_prev_month_start) @@ -724,7 +724,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_month_start) gnc_gdate_set_prev_month_start(&prev_month_start); time64 time1{time64_from_gdate(&prev_month_start, DayPart::start)}; m_option.set_value(RelativeDatePeriod::START_PREV_MONTH); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_prev_month_end) @@ -734,7 +734,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_month_end) gnc_gdate_set_prev_month_end(&prev_month_end); time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)}; m_option.set_value(RelativeDatePeriod::END_PREV_MONTH); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_quarter_start) @@ -744,7 +744,7 @@ TEST_F(GncDateOption, test_set_and_get_quarter_start) gnc_gdate_set_quarter_start(&quarter_start); time64 time1{time64_from_gdate(&quarter_start, DayPart::start)}; m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_quarter_end) @@ -754,7 +754,7 @@ TEST_F(GncDateOption, test_set_and_get_quarter_end) gnc_gdate_set_quarter_end(&quarter_end); time64 time1{time64_from_gdate(&quarter_end, DayPart::end)}; m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_prev_quarter_start) @@ -764,7 +764,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_quarter_start) gnc_gdate_set_prev_quarter_start(&prev_quarter_start); time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)}; m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_prev_quarter_end) @@ -774,7 +774,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_quarter_end) gnc_gdate_set_prev_quarter_end(&prev_quarter_end); time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)}; m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_year_start) @@ -784,7 +784,7 @@ TEST_F(GncDateOption, test_set_and_get_year_start) gnc_gdate_set_year_start(&year_start); time64 time1{time64_from_gdate(&year_start, DayPart::start)}; m_option.set_value(RelativeDatePeriod::START_CAL_YEAR); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_year_end) @@ -794,7 +794,7 @@ TEST_F(GncDateOption, test_set_and_get_year_end) gnc_gdate_set_year_end(&year_end); time64 time1{time64_from_gdate(&year_end, DayPart::end)}; m_option.set_value(RelativeDatePeriod::END_CAL_YEAR); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_prev_year_start) @@ -804,7 +804,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_start) gnc_gdate_set_prev_year_start(&prev_year_start); time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)}; m_option.set_value(RelativeDatePeriod::START_PREV_YEAR); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_set_and_get_prev_year_end) @@ -814,7 +814,7 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_end) gnc_gdate_set_prev_year_end(&prev_year_end); time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)}; m_option.set_value(RelativeDatePeriod::END_PREV_YEAR); - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_out) @@ -915,7 +915,7 @@ TEST_F(GncDateOption, test_stream_in_absolute) std::istringstream iss{timestr}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_month_start) @@ -926,7 +926,7 @@ TEST_F(GncDateOption, test_stream_in_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()); + EXPECT_EQ(time1, m_option.get_value()); } @@ -938,7 +938,7 @@ TEST_F(GncDateOption, test_stream_in_month_end) time64 time1{time64_from_gdate(&month_end, DayPart::end)}; std::istringstream iss{"relative . end-this-month"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_prev_month_start) @@ -949,7 +949,7 @@ TEST_F(GncDateOption, test_stream_in_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()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_prev_month_end) @@ -960,7 +960,7 @@ TEST_F(GncDateOption, test_stream_in_prev_month_end) time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)}; std::istringstream iss{"relative . end-prev-month"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_quarter_start) @@ -971,7 +971,7 @@ TEST_F(GncDateOption, test_stream_in_quarter_start) time64 time1{time64_from_gdate(&quarter_start, DayPart::start)}; std::istringstream iss{"relative . start-current-quarter"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_quarter_end) @@ -982,7 +982,7 @@ TEST_F(GncDateOption, test_stream_in_quarter_end) time64 time1{time64_from_gdate(&quarter_end, DayPart::end)}; std::istringstream iss{"relative . end-current-quarter"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_prev_quarter_start) @@ -993,7 +993,7 @@ TEST_F(GncDateOption, test_stream_in_prev_quarter_start) time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)}; std::istringstream iss{"relative . start-prev-quarter"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_prev_quarter_end) @@ -1004,7 +1004,7 @@ TEST_F(GncDateOption, test_stream_in_prev_quarter_end) time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)}; std::istringstream iss{"relative . end-prev-quarter"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_year_start) @@ -1015,7 +1015,7 @@ TEST_F(GncDateOption, test_stream_in_year_start) time64 time1{time64_from_gdate(&year_start, DayPart::start)}; std::istringstream iss{"relative . start-cal-year"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_year_end) @@ -1026,7 +1026,7 @@ TEST_F(GncDateOption, test_stream_in_year_end) time64 time1{time64_from_gdate(&year_end, DayPart::end)}; std::istringstream iss{"relative . end-cal-year"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_prev_year_start) @@ -1037,7 +1037,7 @@ TEST_F(GncDateOption, test_stream_in_prev_year_start) time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)}; std::istringstream iss{"relative . start-prev-year"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } TEST_F(GncDateOption, test_stream_in_prev_year_end) @@ -1048,5 +1048,5 @@ TEST_F(GncDateOption, test_stream_in_prev_year_end) time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)}; std::istringstream iss{"relative . end-prev-year"}; iss >> m_option; - EXPECT_EQ(time1, m_option.get_value()); + EXPECT_EQ(time1, m_option.get_value()); } From 4b997cd025b9a65a3a7ee354f52fb787e65f7cb8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 3 Dec 2019 10:43:35 -0800 Subject: [PATCH 057/298] Fixups for GncOptionDateValue better design. --- libgnucash/app-utils/gnc-option.cpp | 2 +- libgnucash/app-utils/gnc-optiondb.i | 16 ++++++++++++++++ libgnucash/app-utils/test/test-gnc-optiondb.scm | 7 ++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 55c52bb602..9492d7d313 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -152,7 +152,7 @@ GncOptionDateValue::get_value() const return static_cast(GncDateTime(now)); } static const char* date_type_str[] {"absolute", "relative"}; -static const std::array date_period_str +static const std::array date_period_str { "today", "start-this-month", "end-this-month", diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index f3e58f2260..98cbc908a1 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -184,6 +184,22 @@ using Account = struct account_s; %ignore GncOptionDateValue(GncOptionDateValue&&); %ignore GncOptionDateValue::operator=(const GncOptionDateValue&); %ignore GncOptionDateValue::operator=(GncOptionDateValue&&); +%rename(absolute) RelativeDatePeriod::ABSOLUTE; +%rename(today) RelativeDatePeriod::TODAY; +%rename(start_this_month) RelativeDatePeriod::START_THIS_MONTH; +%rename(end_this_month) RelativeDatePeriod::END_THIS_MONTH; +%rename(start_prev_month) RelativeDatePeriod::START_PREV_MONTH; +%rename(end_prev_month) RelativeDatePeriod::END_PREV_MONTH; +%rename(start_current_quarter) RelativeDatePeriod::START_CURRENT_QUARTER; +%rename(end_current_quarter) RelativeDatePeriod::END_CURRENT_QUARTER; +%rename(start_prev_quarter) RelativeDatePeriod::START_PREV_QUARTER; +%rename(end_prev_quarter) RelativeDatePeriod::END_PREV_QUARTER; +%rename(start_cal_year) RelativeDatePeriod::START_CAL_YEAR; +%rename(end_cal_yea) RelativeDatePeriod::END_CAL_YEAR; +%rename(start_prev_year) RelativeDatePeriod::START_PREV_YEAR; +%rename(end_prev_year) RelativeDatePeriod::END_PREV_YEAR; +%rename(start_accounting_period) RelativeDatePeriod::START_ACCOUNTING_PERIOD; +%rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD; %typemap(typecheck, precedence=SWIG_TYPECHECK_INT64) time64 { $1 = scm_is_signed_integer($input, INT64_MAX, INT64_MIN); diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index dde592513b..7ecb2f9512 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -183,12 +183,13 @@ (test-begin "test-gnc-test-date-option") (let* ((option-db (gnc-option-db-new)) (date-opt (gnc-register-date-interval-option option-db "foo" "bar" - "baz" "Phony Option")) + "baz" "Phony Option" + (RelativeDatePeriod-today))) (a-time (gnc-dmy2time64 11 07 2019))) (test-equal (current-time) (gnc-option-value option-db "foo" "bar")) (gnc-set-option option-db "foo" "bar" a-time) - (test-equal a-time (gnc-option-value option-db "foo" "bar")) - (test-end "test-gnc-test-date-option"))) + (test-equal a-time (gnc-option-value option-db "foo" "bar"))) + (test-end "test-gnc-test-date-option")) (define (test-gnc-make-number-range-option) (test-begin "test-gnc-number-range-option") From 98ca190700cae549d355e4b5508e9e653c8a452e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 3 Dec 2019 11:04:17 -0800 Subject: [PATCH 058/298] Fix commodity in/out to use namespace & mnemonic instead of GUID. --- libgnucash/app-utils/gnc-option.cpp | 16 +++- libgnucash/app-utils/gnc-optiondb.i | 3 + .../app-utils/test/gtest-gnc-option.cpp | 96 +++++++++++++------ 3 files changed, 85 insertions(+), 30 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 9492d7d313..1ced4cdde7 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -113,7 +113,7 @@ GncOptionDateValue::get_value() const m_period == RelativeDatePeriod::END_PREV_YEAR || m_period == RelativeDatePeriod::START_PREV_QUARTER || m_period == RelativeDatePeriod::END_PREV_QUARTER; - + if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday) { //No set accounting period, use the calendar year @@ -217,13 +217,14 @@ GncOptionDateValue::in_stream(std::istream& iss) QofInstance* qof_instance_from_string(const std::string& str, GncOptionUIType type) { - auto guid{static_cast(gnc::GUID::from_string(str))}; QofIdType qof_type; + bool commodity_type{false}; switch(type) { case GncOptionUIType::CURRENCY: case GncOptionUIType::COMMODITY: qof_type = "Commodity"; + commodity_type = true; break; case GncOptionUIType::BUDGET: qof_type = "Budget"; @@ -256,6 +257,17 @@ qof_instance_from_string(const std::string& str, GncOptionUIType type) break; } auto book{gnc_get_current_book()}; + if (commodity_type) + { + auto sep{str.find(":")}; + auto name_space{str.substr(0, sep)}; + auto mnemonic{str.substr(sep + 1, -1)}; + auto table = gnc_commodity_table_get_table(book); + return QOF_INSTANCE(gnc_commodity_table_lookup(table, + name_space.c_str(), + mnemonic.c_str())); + } + auto guid{static_cast(gnc::GUID::from_string(str))}; auto col{qof_book_get_collection(book, qof_type)}; return QOF_INSTANCE(qof_collection_lookup_entity(col, &guid)); } diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 98cbc908a1..8ad19c7f40 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -184,6 +184,9 @@ using Account = struct account_s; %ignore GncOptionDateValue(GncOptionDateValue&&); %ignore GncOptionDateValue::operator=(const GncOptionDateValue&); %ignore GncOptionDateValue::operator=(GncOptionDateValue&&); +%ignore operator<<(std::ostream&, const GncOption&); +%ignore operator>>(std::istream&, GncOption&); + %rename(absolute) RelativeDatePeriod::ABSOLUTE; %rename(today) RelativeDatePeriod::TODAY; %rename(start_this_month) RelativeDatePeriod::START_THIS_MONTH; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index fb2038d9fd..2f4a9afd21 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -260,50 +260,90 @@ TEST_F(GncOptionCommodityTest, test_currency_validator) EXPECT_FALSE(option.validate(QOF_INSTANCE(m_aapl))); } +static inline std::string make_currency_str(gnc_commodity* cur) +{ + std::string cur_str{gnc_commodity_get_mnemonic(cur)}; + return cur_str; } -TEST_F(GncOptionTest, test_qofinstance_out) +static inline std::string make_commodity_str(gnc_commodity* com) { - auto table = gnc_commodity_table_new(); - qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table); - auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100); - auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true); + std::string com_str{gnc_commodity_get_namespace(com)}; + com_str += " "; + com_str += gnc_commodity_get_mnemonic(com); + return com_str; +} - std::string eur_guid{gnc::GUID{*qof_instance_get_guid(eur)}.to_string()}; +static inline std::string make_currency_SCM_str(gnc_commodity* cur) +{ + std::string cur_str{gnc_commodity_get_mnemonic(cur)}; + cur_str.insert(0, "\""); + cur_str += "\""; + return cur_str; +} + +static inline std::string make_commodity_SCM_str(gnc_commodity* com) +{ + std::string com_str{commodity_scm_intro}; + com_str += "\""; + com_str += gnc_commodity_get_namespace(com); + com_str += "\" \""; + com_str += gnc_commodity_get_mnemonic(com); + com_str += "\")"; + 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_guid, oss.str()); - gnc_commodity_destroy(eur); - qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr); - gnc_commodity_table_destroy(table); + EXPECT_EQ(eur_str, oss.str()); } -TEST_F(GncOptionTest, test_qofinstance_in) +TEST_F(GncOptionCommodityTest, test_commodity_out) { - auto table = gnc_commodity_table_new(); - qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table); - auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100); - auto usd = gnc_commodity_new(m_book, "United States Dollar", - "CURRENCY", "USD", NULL, 100); - auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.", - "NYSE", "HPE", NULL, 1); - auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true); + GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(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_guid{gnc::GUID{*qof_instance_get_guid(hpe)}.to_string()}; - std::istringstream iss{hpe_guid}; + 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_guid{gnc::GUID{*qof_instance_get_guid(usd)}.to_string()}; - std::istringstream iss{usd_guid}; + std::string usd_str{make_currency_str(m_usd)}; + std::istringstream iss{usd_str}; iss >> option; - EXPECT_EQ(QOF_INSTANCE(usd), option.get_value()); + EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value()); }); - gnc_commodity_destroy(eur); - gnc_commodity_destroy(usd); - qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr); - gnc_commodity_table_destroy(table); +} + +TEST_F(GncOptionCommodityTest, test_commodity_in) +{ + GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_aapl), + GncOptionUIType::COMMODITY}; + + std::string hpe_str{make_commodity_str(m_hpe)}; + std::istringstream iss{hpe_str}; + iss >> option; + EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); +} + } class GncUIItem From 1ea382266524566d239f5d32e01d4de9f612b159 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 3 Dec 2019 11:14:06 -0800 Subject: [PATCH 059/298] More operator <> Fixups. --- libgnucash/app-utils/gnc-option.hpp | 247 ++++++++++++------ .../app-utils/test/gtest-gnc-option.cpp | 72 ++++- 2 files changed, 229 insertions(+), 90 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index b3a750b25e..b1b881d4bd 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -176,31 +176,8 @@ private: GncOptionUIType m_ui_type; }; -/* 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 the typename = std::enable_if_t<> form so we - * have to use the non-type parameter form. - */ -template>, int> = 0> -std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt) -{ - oss << opt.get_value(); - return oss; -} - -template>, int> = 0> -std::istream& operator>>(std::istream& iss, OptionValueClass& opt) -{ - std::decay_t value; - iss >> value; - opt.set_value(value); - return iss; -} - template -class GncOptionValue : - public OptionClassifier, public OptionUIItem +class GncOptionValue : public OptionClassifier, public OptionUIItem { public: GncOptionValue(const char* section, const char* name, @@ -223,49 +200,8 @@ private: ValueType m_default_value; }; -QofInstance* qof_instance_from_string(const std::string& str, - GncOptionUIType type); -std::string qof_instance_to_string(const QofInstance* inst); - -template<> inline std::ostream& -operator<< >(std::ostream& oss, - const GncOptionValue& opt) -{ - oss << (opt.get_value() ? "#t" : "#f"); - return oss; -} - -template<> inline std::ostream& -operator<< >(std::ostream& oss, - const GncOptionValue& opt) -{ - oss << qof_instance_to_string(opt.get_value()); - return oss; -} - -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) -{ - std::string instr; - iss >> instr; - opt.set_value(qof_instance_from_string(instr, opt.get_ui_type())); - return iss; -} - template -class GncOptionValidatedValue : - public OptionClassifier, public OptionUIItem +class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem { public: GncOptionValidatedValue(const char* section, const char* name, @@ -314,25 +250,177 @@ private: ValueType m_validation_data; }; -template<> inline std::ostream& -operator<< >(std::ostream& oss, - const GncOptionValidatedValue& opt) +QofInstance* qof_instance_from_string(const std::string& str, + GncOptionUIType type); +std::string qof_instance_to_string(const QofInstance* inst); + +/* 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> && + !(std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>), int> = 0> +std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt) { - oss << qof_instance_to_string(opt.get_value()); - std::cerr << qof_instance_to_string(opt.get_value()); + 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; } -template<> inline std::istream& -operator>> >(std::istream& iss, - GncOptionValidatedValue& opt) +template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue >, int> = 0> +inline std::ostream& +operator<< (std::ostream& oss, const OptType& opt) +{ + auto value = opt.get_value(); + if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY || + type == GncOptionUIType::CURRENCY) + { + if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY) + { + oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " "; + } + oss << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)); + } + else + { + oss << qof_instance_to_string(value); + } + return oss; +} + +template> && + !(std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>), int> = 0> +std::istream& operator>>(std::istream& iss, OptionValueClass& opt) +{ + std::decay_t value; + iss >> value; + opt.set_value(value); + return iss; +} + +template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue >, int> = 0> +std::istream& +operator>> (std::istream& iss, OptType& opt) { std::string instr; - iss >> instr; + if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY || + type == GncOptionUIType::CURRENCY) + { + std::string name_space, mnemonic; + if (type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY) + { + iss >> name_space; + } + else + name_space = GNC_COMMODITY_NS_CURRENCY; + iss >> mnemonic; + instr = name_space + ":"; + instr += mnemonic; + } + else + { + 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, GncOptionValidatedValue> || std::is_same_v, GncOptionValue>, int> = 0> +inline std::ostream& +gnc_option_to_scheme (std::ostream& oss, const OptType& opt) +{ + auto value = opt.get_value(); + auto type = opt.get_ui_type(); + if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) + { + if (type == GncOptionUIType::COMMODITY) + { + oss << commodity_scm_intro; + oss << "\"" << + gnc_commodity_get_namespace(GNC_COMMODITY(value)) << "\" "; + } + + oss << "\"" << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)) << "\""; + + if (type == GncOptionUIType::COMMODITY) + { + oss << ")"; + } + } + else + { + oss << "\"" << qof_instance_to_string(value) << "\""; + } + return oss; +} + +template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue>, int> = 0> +inline std::istream& +gnc_option_from_scheme (std::istream& iss, OptType& opt) +{ + std::string instr; + auto type = opt.get_ui_type(); + if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) + { + std::string name_space, mnemonic; + if (type == GncOptionUIType::COMMODITY) + { + iss.ignore(strlen(commodity_scm_intro) + 1, '"'); + std::getline(iss, name_space, '"'); + iss.ignore(1, '"'); + } + else + name_space = GNC_COMMODITY_NS_CURRENCY; + iss.ignore(1, '"'); + std::getline(iss, mnemonic, '"'); + + if (type == GncOptionUIType::COMMODITY) + iss.ignore(2, ')'); + else + iss.ignore(1, '"'); + + instr = name_space + ":"; + instr += mnemonic; + } + else + { + iss.ignore(1, '"'); + std::getline(iss, instr, '"'); + } + opt.set_value(qof_instance_from_string(instr, opt.get_ui_type())); + return iss; +} +#endif // SWIG + /** * Used for numeric ranges and plot sizes. */ @@ -559,8 +647,15 @@ operator<< (std::ostream& oss, const GncOptionAccountValue& opt) { auto values{opt.get_value()}; + bool first = true; for (auto value : values) - oss << qof_instance_to_string(QOF_INSTANCE(value)) << " "; + { + if (first) + first = false; + else + oss << " "; + oss << qof_instance_to_string(QOF_INSTANCE(value)); + } return oss; } diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 2f4a9afd21..530caefd08 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -152,6 +152,28 @@ TEST_F(GncOptionTest, test_budget_ctor) gnc_budget_destroy(budget); } +TEST_F(GncOptionTest, test_budget_out) +{ + auto budget = gnc_budget_new(m_book); + GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(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{GncOptionValue{"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.", @@ -589,23 +611,23 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor) TEST_F(GncOptionAccountTest, test_account_list_out) { GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; - GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", - GncOptionUIType::ACCOUNT_LIST, acclist}; + GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclist}}; std::ostringstream oss; std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()}; acc_guids += " "; acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string(); - acc_guids += " "; oss << option; EXPECT_EQ(acc_guids, oss.str()); GncOptionAccountList accsel{acclist[0]}; - GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option", + GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", + "Bogus Option", GncOptionUIType::ACCOUNT_LIST, - accsel, {ACCT_TYPE_BANK}); + accsel, {ACCT_TYPE_BANK}}}; acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string(); - acc_guids += " "; oss.str(""); oss << sel_option; @@ -615,35 +637,39 @@ TEST_F(GncOptionAccountTest, test_account_list_out) TEST_F(GncOptionAccountTest, test_account_list_in) { GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; - GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", - GncOptionUIType::ACCOUNT_LIST, acclist}; + GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclist}}; std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()}; acc_guids += " "; acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string(); - acc_guids += " "; std::istringstream iss{acc_guids}; iss >> option; - EXPECT_EQ(acclist, option.get_value()); + EXPECT_EQ(acclist, option.get_value()); GncOptionAccountList accsel{acclist[0]}; - GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option", + GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", + "Bogus Option", GncOptionUIType::ACCOUNT_LIST, - accsel, {ACCT_TYPE_BANK}); + accsel, {ACCT_TYPE_BANK}}}; GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string(); acc_guids += " "; iss.str(acc_guids); iss >> sel_option; - EXPECT_EQ(accsel, sel_option.get_value()); + EXPECT_EQ(accsel, sel_option.get_value()); + iss.clear(); //Reset the failedbit from the invalid selection type. acc_guids = gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string(); EXPECT_NO_THROW({ iss.str(acc_guids); iss >> sel_option; }); - EXPECT_EQ(acclist[1], sel_option.get_value()[0]); + EXPECT_EQ(acclist[1], sel_option.get_value()[0]); +} + } class GncOptionMultichoiceTest : public ::testing::Test @@ -710,6 +736,24 @@ TEST_F(GncMultichoiceOption, test_permissible_value_stuff) 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 GncOptionDateOptionTest : public ::testing::Test { protected: From 7ccba53739f1d07a58cd605c739523fc56fefb12 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 3 Dec 2019 11:18:34 -0800 Subject: [PATCH 060/298] Add stream functions to_scheme and from_scheme to GncOption. These add or parse the textual noise needed to replicate the Scheme options' serialization technique of saving scheme forms for saving report options and then evaluating those forms to restore the option values. Required for backward saved-reports compatibility. --- libgnucash/app-utils/gnc-option.cpp | 4 + libgnucash/app-utils/gnc-option.hpp | 124 +++++++- .../app-utils/test/gtest-gnc-option.cpp | 301 +++++++++++++++++- 3 files changed, 419 insertions(+), 10 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 1ced4cdde7..78f75bedbc 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -186,11 +186,15 @@ GncOptionDateValue::in_stream(std::istream& iss) time64 time; iss >> time; set_value(time); + if (iss.get() != ')') + iss.unget(); } else if (type_str == "relative ") { std::string period_str; iss >> period_str; + if (period_str.back() == ')') + period_str.pop_back(); auto period = std::find(date_period_str.begin(), date_period_str.end(), period_str); if (period == date_period_str.end()) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index b1b881d4bd..fca5aba34e 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -117,6 +117,8 @@ enum GncOptionUIType QUERY, }; +static const char* commodity_scm_intro{"'(commodity-scm "}; + struct OptionClassifier { std::string m_section; @@ -243,6 +245,8 @@ public: throw std::invalid_argument("Validation failed, value not set."); } bool is_changed() const noexcept { return m_value != m_default_value; } + std::ostream& to_scheme(std::ostream&) const; + std::istream& from_scheme(std::istream&); private: ValueType m_value; ValueType m_default_value; @@ -280,7 +284,7 @@ operator<< >(std::ostream& oss, const GncOptionValue& opt) { oss << (opt.get_value() ? "#t" : "#f"); - return oss; + return oss; } template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue >, int> = 0> @@ -677,6 +681,49 @@ operator>> (std::istream& iss, iss.clear(); return iss; } + +template, GncOptionAccountValue>, int> = 0> +inline std::ostream& +gnc_option_to_scheme(std::ostream& oss, const OptType& opt) +{ + auto values{opt.get_value()}; + oss << "'(\""; + bool first = true; + for (auto value : values) + { + if (first) + first = false; + else + oss << " \""; + oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"'; + } + oss << ')'; + return oss; +} + +template, GncOptionAccountValue>, int> = 0> +inline std::istream& +gnc_option_from_scheme(std::istream& iss, OptType& opt) +{ + GncOptionAccountList values; + iss.ignore(3, '"'); + while (true) + { + std::string str; + std::getline(iss, str, '"'); + if (!str.empty()) + { + values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type())); + iss.ignore(2, '"'); + } + else + break; + } + opt.set_value(values); + iss.ignore(1, ')'); + return iss; +} + /** Date options * A legal date value is a pair of either and a RelativeDatePeriod, the absolute * flag and a time64, or for legacy purposes the absolute flag and a timespec. @@ -969,8 +1016,83 @@ public: }, m_option); } + std::ostream& to_scheme(std::ostream& oss) const + { + return std::visit([&oss](auto& option) ->std::ostream& { + if constexpr + (std::is_same_v, + GncOptionAccountValue>) + gnc_option_to_scheme(oss, option); + else if constexpr + (std::is_same_v, + GncOptionMultichoiceValue>) + oss << "'" << option; + else if constexpr + (std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>) + gnc_option_to_scheme(oss, option); + else if constexpr + (std::is_same_v, + GncOptionDateValue>) + oss << "'(" << option << ")"; + else if constexpr + (std::is_same_v, + std::string>) + oss << '"' << option << '"'; + else + oss << option; + return oss; + }, m_option); + } + + std::istream& from_scheme(std::istream& iss) + { + return std::visit([&iss](auto& option) ->std::istream& { + if constexpr + (std::is_same_v, + GncOptionAccountValue>) + gnc_option_from_scheme(iss, option); + else if constexpr + (std::is_same_v, + GncOptionMultichoiceValue>) + { + iss.ignore(1, '\''); + iss >> option; + } + else if constexpr + (std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>) + gnc_option_from_scheme(iss, option); + else if constexpr + (std::is_same_v, + GncOptionDateValue>) + { + iss.ignore(2, '('); + iss >> option; + //operator >> clears the trailing ')' + } + else if constexpr + (std::is_same_v, + std::string>) + { + iss.ignore(1, '"'); + std::string input; + std::getline(iss, input, '"'); + option.set_value(input); + } + else + iss >> option; + return iss; + }, m_option); + } + GncOptionVariant& _get_option() const { return m_option; } private: + inline static const std::string c_empty_string{""}; mutable GncOptionVariant m_option; }; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 530caefd08..bf1b06151d 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -84,6 +84,26 @@ TEST(GncOption, test_string_stream_in) EXPECT_EQ(pepper, option.get_value()); } +TEST(GncOption, test_string_to_scheme) +{ + GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); + std::ostringstream oss; + option.to_scheme(oss); + std::string scheme_str{"\""}; + scheme_str += option.get_value() + "\""; + EXPECT_EQ(oss.str(), scheme_str); +} + +TEST(GncOption, test_string_from_scheme) +{ + GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); + std::string pepper{"pepper"}; + std::string scheme_str{"\"pepper\""}; + std::istringstream iss{scheme_str}; + option.from_scheme(iss); + EXPECT_EQ(pepper, option.get_value()); +} + TEST(GncOption, test_int64_t_value) { GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); @@ -109,6 +129,23 @@ TEST(GncOption, test_int64_stream_in) EXPECT_EQ(INT64_C(987654321), option.get_value()); } +TEST(GncOption, test_int64_to_scheme) +{ + GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); + std::ostringstream oss; + option.to_scheme(oss); + EXPECT_STREQ(oss.str().c_str(), "123456789"); +} + +TEST(GncOption, test_int64_from_scheme) +{ + GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); + std::string number{"987654321"}; + std::istringstream iss{number}; + option.from_scheme(iss); + EXPECT_EQ(INT64_C(987654321), option.get_value()); +} + TEST(GncOption, test_bool_stream_out) { GncOption option("foo", "bar", "baz", "Phony Option", false); @@ -132,6 +169,29 @@ TEST(GncOption, test_bool_stream_in) EXPECT_FALSE(option.get_value()); } +TEST(GncOption, test_bool_to_scheme) +{ + GncOption option("foo", "bar", "baz", "Phony Option", false); + std::ostringstream oss; + oss << option; + EXPECT_STREQ(oss.str().c_str(), "#f"); + oss.str(""); + option.set_value(true); + option.to_scheme(oss); + EXPECT_STREQ(oss.str().c_str(), "#t"); +} + +TEST(GncOption, test_bool_from_scheme) +{ + GncOption option("foo", "bar", "baz", "Phony Option", false); + std::istringstream iss("#t"); + iss >> option; + EXPECT_TRUE(option.get_value()); + iss.str("#f"); + option.from_scheme(iss); + EXPECT_FALSE(option.get_value()); +} + class GncOptionTest : public ::testing::Test { protected: @@ -174,6 +234,34 @@ TEST_F(GncOptionTest, test_budget_in) EXPECT_EQ(QOF_INSTANCE(budget), option.get_value()); gnc_budget_destroy(budget); } + +TEST_F(GncOptionTest, test_budget_to_scheme) +{ + auto budget = gnc_budget_new(m_book); + GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(budget)}; + + auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()}; + std::ostringstream oss; + budget_guid.insert(0, "\""); + budget_guid += "\""; + option.to_scheme(oss); + EXPECT_EQ(budget_guid, oss.str()); + gnc_budget_destroy(budget); +} + +TEST_F(GncOptionTest, test_budget_from_scheme) +{ + auto budget = gnc_budget_new(m_book); + auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()}; + budget_guid.insert(0, "\""); + budget_guid += "\""; + std::istringstream iss{budget_guid}; + GncOption option{GncOptionValue{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}}; + option.from_scheme(iss); + 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.", @@ -366,6 +454,48 @@ TEST_F(GncOptionCommodityTest, test_commodity_in) EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); } +TEST_F(GncOptionCommodityTest, test_currency_to_scheme) +{ + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", + m_eur, true); + + std::string eur_str{make_currency_SCM_str(m_eur)}; + std::ostringstream oss; + option.to_scheme(oss); + EXPECT_EQ(eur_str, oss.str()); +} + +TEST_F(GncOptionCommodityTest, test_commodity_to_scheme) +{ + GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_hpe), + GncOptionUIType::COMMODITY}; + + std::string hpe_str{make_commodity_SCM_str(m_hpe)}; + std::ostringstream oss; + option.to_scheme(oss); + EXPECT_EQ(hpe_str, oss.str()); +} + +TEST_F(GncOptionCommodityTest, test_currency_from_scheme) +{ + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", + m_eur, true); + + std::string usd_str{make_currency_SCM_str(m_usd)}; + std::istringstream iss{usd_str}; + option.from_scheme(iss); + EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value()); +} + +TEST_F(GncOptionCommodityTest, test_commodity_from_scheme) +{ + GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_aapl), + GncOptionUIType::COMMODITY}; + + std::string hpe_str{make_commodity_SCM_str(m_hpe)}; + std::istringstream iss{hpe_str}; + option.from_scheme(iss); + EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); } class GncUIItem @@ -459,8 +589,8 @@ TEST_F(GncRangeOption, test_range_in) 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()); + EXPECT_EQ(20, m_intoption.get_value()); + EXPECT_EQ(2.0, m_doubleoption.get_value()); } using AccountPair = std::pair()[0]); } +static inline std::string +make_account_list_SCM_str(const GncOptionAccountList& acclist) +{ + std::string retval{"'("}; + bool first = true; + for (auto acc : acclist) + { + if (first) + { + first = false; + retval += '"'; + } + else + retval += " \""; + retval += gnc::GUID{*qof_instance_get_guid(acc)}.to_string(); + retval += '"'; + } + retval += ')'; + return retval; +} + +TEST_F(GncOptionAccountTest, test_account_list_to_scheme) +{ + GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; + GncOption option{GncOptionAccountValue {"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclist}}; + std::ostringstream oss; + std::string acc_guids{make_account_list_SCM_str(acclist)}; + + option.to_scheme(oss); + EXPECT_EQ(acc_guids, oss.str()); + + GncOptionAccountList accsel{acclist[0]}; + GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", + "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + accsel, {ACCT_TYPE_BANK}}}; + acc_guids = make_account_list_SCM_str(accsel); + + oss.str(""); + sel_option.to_scheme(oss); + EXPECT_EQ(acc_guids, oss.str()); +} + +TEST_F(GncOptionAccountTest, test_account_list_from_scheme) +{ + GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; + GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + acclist}}; + + std::string acc_guids{make_account_list_SCM_str(acclist)}; + std::istringstream iss{acc_guids}; + option.from_scheme(iss); + EXPECT_EQ(acclist, option.get_value()); + + GncOptionAccountList accsel{acclist[0]}; + GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", + "Bogus Option", + GncOptionUIType::ACCOUNT_LIST, + accsel, {ACCT_TYPE_BANK}}}; + GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; + acc_guids = make_account_list_SCM_str(acclistbad); + iss.str(acc_guids); + sel_option.from_scheme(iss); + EXPECT_EQ(accsel, sel_option.get_value()); + + iss.clear(); //Reset the failedbit from the invalid selection type. + acc_guids = make_account_list_SCM_str(GncOptionAccountList{acclist[1]}); + EXPECT_NO_THROW({ + iss.str(acc_guids); + sel_option.from_scheme(iss); + }); + EXPECT_EQ(acclist[1], sel_option.get_value()[0]); } class GncOptionMultichoiceTest : public ::testing::Test @@ -678,11 +883,11 @@ protected: GncOptionMultichoiceTest() : m_option{GncOptionMultichoiceValue{"foo", "bar", "baz", "Phony Option", "plugh", - { - {"plugh", "xyzzy", "thud"}, - {"waldo", "pepper", "salt"}, - {"pork", "sausage", "links"}, - {"corge", "grault", "garply"} + { + {"plugh", "xyzzy", "thud"}, + {"waldo", "pepper", "salt"}, + {"pork", "sausage", "links"}, + {"corge", "grault", "garply"} }}} {} GncOption m_option; }; @@ -754,6 +959,23 @@ TEST_F(GncMultichoiceOption, test_multichoice_in) iss >> m_option; EXPECT_EQ(iss.str(), m_option.get_value()); } + +TEST_F(GncMultichoiceOption, test_multichoice_scheme_out) +{ + std::ostringstream oss; + m_option.to_scheme(oss); + std::string scm_str{'\''}; + scm_str += m_option.get_value(); + EXPECT_EQ(scm_str, oss.str()); +} + +TEST_F(GncMultichoiceOption, test_multichoice_scheme_in) +{ + std::istringstream iss{"'pork"}; + m_option.from_scheme(iss); + EXPECT_STREQ("pork", m_option.get_value().c_str()); +} + class GncOptionDateOptionTest : public ::testing::Test { protected: @@ -1134,3 +1356,64 @@ TEST_F(GncDateOption, test_stream_in_prev_year_end) iss >> m_option; EXPECT_EQ(time1, m_option.get_value()); } + +/* We only need wiggle tests for to_scheme and from_scheme as they just wrap or +unwrap the normal stream output with "'(" and ")". +*/ + +TEST_F(GncDateOption, test_date_option_to_scheme) +{ + 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(); + timestr += ')'; + oss.str(""); + m_option.to_scheme(oss); + EXPECT_EQ(oss.str(), timestr); + + m_option.set_value(RelativeDatePeriod::TODAY); + oss.str(""); + m_option.to_scheme(oss); + EXPECT_STREQ(oss.str().c_str(), "'(relative . today)"); + + m_option.set_value(RelativeDatePeriod::START_THIS_MONTH); + oss.str(""); + m_option.to_scheme(oss); + EXPECT_STREQ(oss.str().c_str(), "'(relative . start-this-month)"); + +} + +TEST_F(GncDateOption, test_date_option_from_scheme) +{ + 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(); + timestr += ')'; + + std::istringstream iss{timestr}; + m_option.from_scheme(iss); + EXPECT_EQ(time1, m_option.get_value()); + + GDate month_start; + g_date_set_time_t(&month_start, time(nullptr)); + gnc_gdate_set_month_start(&month_start); + time1 = time64_from_gdate(&month_start, DayPart::start); + iss.clear(); + iss.str("'(relative . start-this-month)"); + m_option.from_scheme(iss); + EXPECT_EQ(time1, m_option.get_value()); + + GDate month_end; + g_date_set_time_t(&month_end, time(nullptr)); + gnc_gdate_set_month_end(&month_end); + time1 = time64_from_gdate(&month_end, DayPart::end); + iss.clear(); + iss.str("'(relative . end-this-month)"); + m_option.from_scheme(iss); + EXPECT_EQ(time1, m_option.get_value()); +} From c5294ed6b3b4cc742ef4821d0ea3ff8f9e43df11 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Dec 2019 10:03:57 -0800 Subject: [PATCH 061/298] Fix missing-declaration errors in swig-gnc-optiondb-guile.cpp. --- libgnucash/app-utils/gnc-optiondb.i | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 8ad19c7f40..a5fee701b7 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -326,8 +326,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); }; %inline %{ - SCM gnc_option_value(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) @@ -335,8 +336,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return GncOption_get_scm_value(&(db_opt->get())); } - SCM gnc_option_default_value(const GncOptionDBPtr& optiondb, - const char* section, const char* name) + 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) @@ -344,8 +346,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return GncOption_get_scm_default_value(&(db_opt->get())); } - void gnc_set_option(const GncOptionDBPtr& optiondb, const char* section, - const char* name, SCM new_value) + 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) From 826d75af16b277bcbab02e1b14e75c3f8c703d23 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Dec 2019 17:46:13 -0800 Subject: [PATCH 062/298] Use istream::getline instead of std::getline for date option type parsing. --- libgnucash/app-utils/gnc-option.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 78f75bedbc..ec67ef4497 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -179,9 +179,12 @@ GncOptionDateValue::out_stream(std::ostream& oss) const noexcept std::istream& GncOptionDateValue::in_stream(std::istream& iss) { - std::string type_str; - std::getline(iss, type_str, '.'); - if (type_str == "absolute ") + 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; @@ -189,7 +192,7 @@ GncOptionDateValue::in_stream(std::istream& iss) if (iss.get() != ')') iss.unget(); } - else if (type_str == "relative ") + else if (strcmp(type_str, "relative ") == 0) { std::string period_str; iss >> period_str; From d7a2a0ffff75251d66002564016d74a520467ac3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Dec 2019 17:47:30 -0800 Subject: [PATCH 063/298] Make a constant for std::numeric_limitsmax(). Improves readability. --- libgnucash/app-utils/gnc-option.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index fca5aba34e..a722686881 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -178,6 +178,8 @@ private: GncOptionUIType m_ui_type; }; +auto constexpr size_t_max = std::numeric_limits::max(); + template class GncOptionValue : public OptionClassifier, public OptionUIItem { @@ -496,7 +498,7 @@ public: if (value) { if (auto index = find_key(value); - index != std::numeric_limits::max()) + index != size_t_max) { m_value = index; m_default_value = index; @@ -520,13 +522,13 @@ public: bool validate(const std::string& value) const noexcept { auto index = find_key(value); - return index != std::numeric_limits::max(); + return index != size_t_max; } void set_value(const std::string& value) { auto index = find_key(value); - if (index != std::numeric_limits::max()) + if (index != size_t_max) m_value = index; else throw std::invalid_argument("Value not a valid choice."); @@ -562,7 +564,7 @@ private: if (iter != m_choices.end()) return iter - m_choices.begin(); else - return std::numeric_limits::max(); + return size_t_max; } std::size_t m_value; @@ -956,7 +958,7 @@ public: GncOptionMultichoiceValue>) return option.num_permissible_values(); else - return std::numeric_limits::max(); + return size_t_max; }, m_option); } @@ -967,7 +969,7 @@ public: GncOptionMultichoiceValue>) return option.permissible_value_index(value); else - return std::numeric_limits::max();; + return size_t_max;; }, m_option); } From 6ab5618b768cc4512acaa68f82c874a1cb3fef36 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Dec 2019 17:54:02 -0800 Subject: [PATCH 064/298] Set a 50-character limit on the saved size of option section and name. Allows use of istream::getline() to retrieve the values, simplifying delimiter detection. --- libgnucash/app-utils/gnc-option.hpp | 3 +++ libgnucash/app-utils/gnc-optiondb.cpp | 13 +++++++------ libgnucash/app-utils/gnc-optiondb.hpp | 6 +++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index a722686881..03a2dfe61f 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -119,6 +119,9 @@ enum GncOptionUIType static const char* commodity_scm_intro{"'(commodity-scm "}; +size_t constexpr classifier_size_max{50}; +size_t constexpr sort_tag_size_max{10}; + struct OptionClassifier { std::string m_section; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index c4c7fe4d4e..7c66f05c60 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -124,13 +124,13 @@ GncOptionDB::set_option_from_ui(const char* section, const char* name, std::optional> -GncOptionDB::find_section(const char* section) +GncOptionDB::find_section(const std::string& section) { auto db_section = std::find_if( m_sections.begin(), m_sections.end(), - [section](GncOptionSection sect) -> bool + [§ion](GncOptionSection sect) -> bool { - return sect.first == std::string{section}; + return section.compare(0, classifier_size_max, sect.first) == 0; }); if (db_section == m_sections.end()) return std::nullopt; @@ -138,16 +138,17 @@ GncOptionDB::find_section(const char* section) } std::optional> -GncOptionDB::find_option(const char* section, const char* name) const +GncOptionDB::find_option(const std::string& section, const std::string& name) const { auto db_section = const_cast(this)->find_section(section); if (!db_section) return std::nullopt; auto db_opt = std::find_if( db_section->get().second.begin(), db_section->get().second.end(), - [name](GncOption& option) -> bool + [&name](GncOption& option) -> bool { - return option.get_name() == std::string{name}; + return name.compare(0, classifier_size_max - 1, + option.get_name()) == 0; }); if (db_opt == db_section->get().second.end()) return std::nullopt; diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index b8c515133d..8d6187400b 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -83,13 +83,13 @@ public: // void set_selectable(const char* section, const char* name); void make_internal(const char* section, const char* name); void commit(); - std::optional> find_section(const char* section); - std::optional> find_option(const char* section, const char* name) { + std::optional> find_section(const std::string& section); + std::optional> find_option(const std::string& section, const std::string& name) { return static_cast(*this).find_option(section, name); } - std::optional> find_option(const char* section, const char* name) const; private: std::ostream& serialize_option_scheme(std::ostream& oss, + std::optional> find_option(const std::string& section, const std::string& name) const; const char* option_prolog, const char* section, const char* name) const noexcept; std::ostream& serialize_option_key_value(std::ostream& oss, From 76172af239edcfb3df3bfba94c41b3c5dbb5bd25 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Dec 2019 18:00:07 -0800 Subject: [PATCH 065/298] Implement saving and loading OptionDB items to/from scheme and key-value string representations. --- libgnucash/app-utils/gnc-optiondb.cpp | 73 ++++++++++++++++--- libgnucash/app-utils/gnc-optiondb.hpp | 25 ++++--- .../app-utils/test/gtest-gnc-optiondb.cpp | 69 ++++++++++++++++++ 3 files changed, 143 insertions(+), 24 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 7c66f05c60..239a21f068 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -22,7 +22,10 @@ \********************************************************************/ #include "gnc-optiondb.hpp" +#include +#include +auto constexpr stream_max = std::numeric_limits::max(); GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {} GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {} @@ -175,24 +178,53 @@ GncOptionDB::make_internal(const char* section, const char* name) } std::ostream& -GncOptionDB::serialize_option_scheme(std::ostream& oss, - const char* option_prolog, - const char* section, const char* name) const noexcept +GncOptionDB::save_option_scheme(std::ostream& oss, + const char* option_prolog, + const std::string& section, + const std::string& name) const noexcept { auto db_opt = find_option(section, name); + if (!db_opt || !db_opt->get().is_changed()) return oss; - oss << c_scheme_serialization_elements[0] << option_prolog; - oss << c_scheme_serialization_elements[1] << section; - oss << c_scheme_serialization_elements[1] << name; //repeats, not an error! -// oss << c_scheme_serialization_elements[2] << db_opt->get(); - oss << c_scheme_serialization_elements[3]; + oss << scheme_tags[0] << option_prolog << "\n"; + oss << scheme_tags[1] << '"' << section.substr(0, classifier_size_max) << "\"\n"; + oss << scheme_tags[1] << '"' << name.substr(0, classifier_size_max) << '"'; + oss << scheme_tags[2] << "\n" << scheme_tags[3]; + db_opt->get().to_scheme(oss); + oss << scheme_tags[4] << "\n\n"; return oss; } +std::istream& +GncOptionDB::load_option_scheme(std::istream& iss) noexcept +{ + std::string section{classifier_size_max}; + std::string name{classifier_size_max}; + iss.ignore(strlen(scheme_tags[0]) + 7, '\n'); //throw away the scheme noise; + iss >> section; // Whitespace automatically discarded. + iss >> name; // Ditto + if (section.size() > 2) + section = section.substr(1, section.size() - 2); //Trim the quotes + if (name.size() > 2 + strlen(scheme_tags[2])) + name = name.substr(1, name.size() - (2 + strlen(scheme_tags[2]))); + auto option = find_option(section.c_str(), name.c_str()); + if (!option) + { + std::cerr << "Option " << section << ":" << name << " not found." << std::endl; + iss.ignore(stream_max, '\n'); // No option, discard the line + iss.ignore(stream_max, '\n'); // And the trailing newline + return iss; + } + iss.ignore(strlen(scheme_tags[2]) +1, '\n'); + iss.ignore(strlen(scheme_tags[3])); + option->get().from_scheme(iss); + iss.ignore(strlen(scheme_tags[4]) + 2, '\n'); //discard the noise at the end. + return iss; +} std::ostream& -GncOptionDB::serialize_option_key_value(std::ostream& oss, +GncOptionDB::save_option_key_value(std::ostream& oss, const char* section, const char* name) const noexcept { @@ -200,13 +232,30 @@ GncOptionDB::serialize_option_key_value(std::ostream& oss, auto db_opt = find_option(section, name); if (!db_opt || !db_opt->get().is_changed()) return oss; - oss << section << ":" << name << "=" /* << db_opt->get() */ << ";"; + oss << section << ":" << name << "=" << db_opt->get() << ";"; return oss; } -void -GncOptionDB::commit() +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->get(); + } + return iss; } GncOptionDBPtr diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 8d6187400b..078224f6b0 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -87,30 +87,31 @@ public: std::optional> find_option(const std::string& section, const std::string& name) { return static_cast(*this).find_option(section, name); } -private: - std::ostream& serialize_option_scheme(std::ostream& oss, std::optional> find_option(const std::string& section, const std::string& name) const; - const char* option_prolog, - const char* section, const char* name) const noexcept; - std::ostream& serialize_option_key_value(std::ostream& oss, + std::ostream& save_option_scheme(std::ostream& oss, + const char* option_prolog, + const std::string& section, + const std::string& name) const noexcept; + std::istream& load_option_scheme(std::istream& iss) noexcept; + std::ostream& save_option_key_value(std::ostream& oss, const char* section, const char* name) const noexcept; - void load_option_scheme(std::istream iss); - void load_option_key_value(std::istream iss); + std::istream& load_option_key_value(std::istream& iss); +private: std::optional> m_default_section; std::vector m_sections; bool m_dirty = false; std::function m_get_ui_value; std::function m_set_ui_value; - static constexpr char const* const c_scheme_serialization_elements[] + static constexpr char const* const scheme_tags[] { "(let ((option (gnc:lookup-option ", - "\n ", - ")))\n ((lambda (o) (if o (gnc:option-set-value o", - "))) option))\n\n" + " ", + ")))", + " ((lambda (o) (if o (gnc:option-set-value o ", + "))) option))" }; - }; /** diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 1dd088dc12..9ef8884f31 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -276,3 +276,72 @@ TEST_F(GncOptionDBUITest, test_option_value_from_ui) }); EXPECT_STREQ(value, m_db->lookup_string_option("foo", "bar").c_str()); } + +class GncOptionDBIOTest : public ::testing::Test +{ +protected: + GncOptionDBIOTest() : m_db{gnc_option_db_new()} + { + 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"}); + } + + GncOptionDBPtr m_db; +}; + +TEST_F(GncOptionDBIOTest, test_option_scheme_output) +{ + std::ostringstream oss; + m_db->save_option_scheme(oss, "option", "foo", "sausage"); + EXPECT_STREQ("", oss.str().c_str()); + oss.clear(); + 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()); + oss.flush(); + m_db->save_option_scheme(oss, "option", "foo", "sausage"); + EXPECT_STREQ("(let ((option (gnc:lookup-option option\n" + " \"foo\"\n" + " \"sausage\")))\n" + " ((lambda (o) (if o (gnc:option-set-value o \"pepper\"" + "))) option))\n\n", oss.str().c_str()); +} + +TEST_F(GncOptionDBIOTest, test_option_scheme_input) +{ + const char* input{"(let ((option (gnc:lookup-option option\n" + " \"foo\"\n" + " \"sausage\")))\n" + " ((lambda (o) (if o (gnc:option-set-value o \"pepper\"" + "))) option))\n\n"}; + std::istringstream iss{input}; + EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "sausage").c_str()); + m_db->load_option_scheme(iss); + EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); +} + +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()); +} From 51a1430c24443799ac0b7f7daab66d4d5fb54804 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 14 Dec 2019 15:37:02 -0800 Subject: [PATCH 066/298] Remove stray diagnostic. --- libgnucash/app-utils/gnc-option.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 03a2dfe61f..a5adc31989 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -967,7 +967,6 @@ public: std::size_t permissible_value_index(const std::string& value) const { return std::visit([&value] (const auto& option) -> size_t { - std::cerr << typeid(option).name() << std::endl; if constexpr (std::is_same_v, GncOptionMultichoiceValue>) return option.permissible_value_index(value); From 276641ef157a189e34b559fc70014a8878a8d9d5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 14 Dec 2019 15:43:24 -0800 Subject: [PATCH 067/298] Change parse of option input to generate a parse tree. This is I hope less brittle than the previous character-counting, though it's still brittle because it relies instead on counting form-depth. --- libgnucash/app-utils/gnc-optiondb.cpp | 273 ++++++++++++++++++++++++-- libgnucash/app-utils/gnc-optiondb.hpp | 9 +- 2 files changed, 262 insertions(+), 20 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 239a21f068..d09fa2317d 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -196,30 +196,265 @@ GncOptionDB::save_option_scheme(std::ostream& oss, return oss; } -std::istream& -GncOptionDB::load_option_scheme(std::istream& iss) noexcept + +static inline bool constexpr +is_eol(char c) { - std::string section{classifier_size_max}; - std::string name{classifier_size_max}; - iss.ignore(strlen(scheme_tags[0]) + 7, '\n'); //throw away the scheme noise; - iss >> section; // Whitespace automatically discarded. - iss >> name; // Ditto - if (section.size() > 2) - section = section.substr(1, section.size() - 2); //Trim the quotes - if (name.size() > 2 + strlen(scheme_tags[2])) - name = name.substr(1, name.size() - (2 + strlen(scheme_tags[2]))); + 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); +} + +static std::string +scan_scheme_symbol_from_streambuf(std::streambuf* sbuf) +{ + std::string retval; + while(sbuf->in_avail() && !is_delim(sbuf->sgetc())) + retval += sbuf->sbumpc(); + return retval; +} + +static inline void constexpr +consume_scheme_comment(std::streambuf* sbuf) +{ + while (sbuf->in_avail() && !is_eol(sbuf->sgetc())) + sbuf->sbumpc(); +} + +static inline std::string +scan_scheme_string_from_streambuf(std::streambuf* sbuf) +{ + std::string retval{static_cast(sbuf->sbumpc())}; + while(sbuf->in_avail() && !is_double_quote(sbuf->sgetc())) + retval += sbuf->sbumpc(); + retval += sbuf->sbumpc(); // Add the closing quote. + return retval; +} + +static inline void constexpr +consume_scheme_whitespace(std::streambuf* sbuf) +{ + while (sbuf->in_avail() && is_whitespace(sbuf->sgetc())) + sbuf->sbumpc(); +} + +enum class IdentType +{ + NAME, //no introducing mark + CONST, //introduced with single quote + STRING, //delimited by double-quotes. + LIST, //introduced ' and delimited by parentheses + FORM //delimited by parentheses without ' introduction. +}; + +struct SchemeId +{ + IdentType m_type; + std::string m_name; + std::vector m_ids; +}; + +/** + * Scheme Parse Tree + * An identifier is a string and a type (name, const, string, or form). A Form + * + */ + +static void scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id); + +static void +scan_scheme_form_from_streambuf(std::streambuf* sbuf, SchemeId& id) +{ + sbuf->sbumpc(); + if (!sbuf->in_avail()) + return; + char c = sbuf->sgetc(); + while (sbuf->in_avail() && !is_end_paren(c)) + { + SchemeId next_id; + scan_scheme_id_from_streambuf(sbuf, next_id); + if (id.m_name.empty() && next_id.m_type == IdentType::NAME) + { + id.m_name = std::move(next_id.m_name); + continue; + } + id.m_ids.emplace_back(std::move(next_id)); + if (!sbuf->in_avail()) + { + std::string err{"End of streambuf before end of form "}; + err += id.m_name; + throw std::runtime_error(err); + } + c = sbuf->sgetc(); + } + sbuf->sbumpc(); +} + +static void +scan_scheme_list_from_streambuf(std::streambuf* sbuf, std::string& str) +{ + + consume_scheme_whitespace(sbuf); + if (!sbuf->in_avail()) + return; + char c = sbuf->sgetc(); + while (sbuf->in_avail() && !is_end_paren(c)) + { + str += static_cast(sbuf->sbumpc()); + if (!sbuf->in_avail()) + return; + c = sbuf->sgetc(); + } + str += static_cast(sbuf->sbumpc()); +} + +static void +scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id) +{ + consume_scheme_whitespace(sbuf); + if (!sbuf->in_avail()) + return; + auto c{sbuf->sgetc()}; + switch(c) + { + case ';': + consume_scheme_comment(sbuf); + break; + case '"': + id.m_type = IdentType::STRING; + id.m_name = scan_scheme_string_from_streambuf(sbuf); + break; + case '\'': + { + std::string value{static_cast(sbuf->sbumpc())}; + if (sbuf->sgetc() == '(') + { + id.m_type == IdentType::LIST; + scan_scheme_list_from_streambuf(sbuf, value); + if (value.back() != ')') + throw std::runtime_error("End of streambuf before end of form "); + } + else if (sbuf->sgetc() == '"') + throw std::runtime_error("Malformed scheme particle starts '\""); + else + { + id.m_type = IdentType::CONST; + value += scan_scheme_symbol_from_streambuf(sbuf); + } + id.m_name = std::move(value); + break; + } + case '(': + id.m_type = IdentType::FORM; + scan_scheme_form_from_streambuf(sbuf, id); + break; + default: + id.m_type = IdentType::NAME; + id.m_name = scan_scheme_symbol_from_streambuf(sbuf); + break; + } + return; +} + +static inline std::string +unquote_scheme_string(const std::string& str) +{ + if (str.front() == '"' && str.back() == '"') + return str.substr(1, str.size() - 2); + + return str; +} + +std::istream& +GncOptionDB::load_option_scheme(std::istream& iss) +{ + auto sbuf{iss.rdbuf()}; + SchemeId toplevel; + bool form_found = false; + while (sbuf->in_avail() && !form_found) + { + scan_scheme_id_from_streambuf(sbuf, toplevel); + if (toplevel.m_type == IdentType::FORM && + toplevel.m_name == "let" && toplevel.m_ids.size() == 2) + if (const auto& let_block{toplevel.m_ids[0]}; + let_block.m_ids.size() == 1) + if (const auto& first_form{let_block.m_ids[0]}; + first_form.m_name == "option") + form_found = true; + } + const auto& classifier = toplevel.m_ids[0].m_ids[0].m_ids[0].m_ids; + if (classifier.size() != 3) + throw std::runtime_error("Malformed option classifier."); + const auto& section = unquote_scheme_string(classifier[1].m_name); + const auto& name = unquote_scheme_string(classifier[2].m_name); auto option = find_option(section.c_str(), name.c_str()); + std::string option_str{section}; + option_str += ':'; + option_str += name; if (!option) { - std::cerr << "Option " << section << ":" << name << " not found." << std::endl; - iss.ignore(stream_max, '\n'); // No option, discard the line - iss.ignore(stream_max, '\n'); // And the trailing newline - return iss; + std::string err{"Option not found: "}; + err += option_str; + throw std::runtime_error(err); } - iss.ignore(strlen(scheme_tags[2]) +1, '\n'); - iss.ignore(strlen(scheme_tags[3])); - option->get().from_scheme(iss); - iss.ignore(strlen(scheme_tags[4]) + 2, '\n'); //discard the noise at the end. + if (!(toplevel.m_type == IdentType::FORM && toplevel.m_ids.size() == 2 && + toplevel.m_ids[1].m_type == IdentType::FORM && + toplevel.m_ids[1].m_ids.size() == 2 && + toplevel.m_ids[1].m_ids[0].m_type == IdentType::FORM && + toplevel.m_ids[1].m_ids[0].m_ids.size() == 2 && + toplevel.m_ids[1].m_ids[0].m_ids[1].m_type == IdentType::FORM && + toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids.size() == 2 && + toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_type == IdentType::FORM && + toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_ids.size() == 2)) + { + std::string err{"Option "}; + err += option_str; + throw std::runtime_error(err + " malformed value lambda form."); + } + auto value_id = toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_ids[1]; + std::istringstream value_iss{value_id.m_name}; + option->get().from_scheme(value_iss); return iss; } diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 078224f6b0..042e78c46c 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -88,11 +88,18 @@ public: return static_cast(*this).find_option(section, name); } std::optional> find_option(const std::string& section, const std::string& name) const; + std::ostream& save_to_scheme(std::ostream& oss, + const char* options_prolog) const noexcept; + std::istream& load_from_scheme(std::istream& iss) noexcept; + std::ostream& save_to_key_value(std::ostream& oss) const noexcept; + std::istream& load_from_key_value(std::istream& iss) noexcept; + void save_to_kvp() const noexcept; + void load_from_kvp() noexcept; std::ostream& save_option_scheme(std::ostream& oss, const char* option_prolog, const std::string& section, const std::string& name) const noexcept; - std::istream& load_option_scheme(std::istream& iss) noexcept; + std::istream& load_option_scheme(std::istream& iss); std::ostream& save_option_key_value(std::ostream& oss, const char* section, const char* name) const noexcept; From 009219c63d3f6a075a51fdfd28fbd5c113d42294 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 14 Dec 2019 16:44:16 -0800 Subject: [PATCH 068/298] Much less ugly and fragile. Finding the right form now relies on the form name instead of its position in the parse tree. --- libgnucash/app-utils/gnc-optiondb.cpp | 55 +++++++++++++++++---------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index d09fa2317d..becae537ac 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -406,24 +406,47 @@ unquote_scheme_string(const std::string& str) return str; } +static inline bool +string_equal_charptr(const std::string& str, const char* chars) +{ + if (!chars || str.empty() || strlen(chars) != str.size()) + return false; + return strcmp(str.c_str(), chars) == 0; +} + +static std::optional> +find_form(const SchemeId& toplevel, IdentType type, const char* name) +{ + if (toplevel.m_type == type && + string_equal_charptr(toplevel.m_name.c_str(), name)) + return std::ref(toplevel); + for (const auto& id : toplevel.m_ids) + { + if (id.m_type == type && string_equal_charptr(id.m_name.c_str(), name)) + return std::ref(id); + auto child{find_form(id, type, name)}; + if (child) + return child; + } + return std::nullopt; +} + std::istream& GncOptionDB::load_option_scheme(std::istream& iss) { auto sbuf{iss.rdbuf()}; SchemeId toplevel; + std::optional> lookup_id; bool form_found = false; - while (sbuf->in_avail() && !form_found) + while (sbuf->in_avail() && !lookup_id) { scan_scheme_id_from_streambuf(sbuf, toplevel); - if (toplevel.m_type == IdentType::FORM && - toplevel.m_name == "let" && toplevel.m_ids.size() == 2) - if (const auto& let_block{toplevel.m_ids[0]}; - let_block.m_ids.size() == 1) - if (const auto& first_form{let_block.m_ids[0]}; - first_form.m_name == "option") - form_found = true; + lookup_id = find_form(toplevel, IdentType::FORM, "gnc:lookup-option"); } - const auto& classifier = toplevel.m_ids[0].m_ids[0].m_ids[0].m_ids; + + if (!lookup_id) + throw std::runtime_error("No gnc:lookup-option found"); + const auto& classifier = lookup_id->get().m_ids; if (classifier.size() != 3) throw std::runtime_error("Malformed option classifier."); const auto& section = unquote_scheme_string(classifier[1].m_name); @@ -438,22 +461,14 @@ GncOptionDB::load_option_scheme(std::istream& iss) err += option_str; throw std::runtime_error(err); } - if (!(toplevel.m_type == IdentType::FORM && toplevel.m_ids.size() == 2 && - toplevel.m_ids[1].m_type == IdentType::FORM && - toplevel.m_ids[1].m_ids.size() == 2 && - toplevel.m_ids[1].m_ids[0].m_type == IdentType::FORM && - toplevel.m_ids[1].m_ids[0].m_ids.size() == 2 && - toplevel.m_ids[1].m_ids[0].m_ids[1].m_type == IdentType::FORM && - toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids.size() == 2 && - toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_type == IdentType::FORM && - toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_ids.size() == 2)) + auto value_id = find_form(toplevel, IdentType::FORM, "gnc:option-set-value"); + if (!(value_id && value_id->get().m_ids.size() == 2)) { std::string err{"Option "}; err += option_str; throw std::runtime_error(err + " malformed value lambda form."); } - auto value_id = toplevel.m_ids[1].m_ids[0].m_ids[1].m_ids[1].m_ids[1]; - std::istringstream value_iss{value_id.m_name}; + std::istringstream value_iss{value_id->get().m_ids[1].m_name}; option->get().from_scheme(value_iss); return iss; } From d2535fe21bf599eb58803673ca58b9e050628a3a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 19 Jan 2020 15:05:28 -0800 Subject: [PATCH 069/298] KvpValue: Use boost::typeindex::type_id instead of buildtin typeid. Under some build conditions boost::typeindex will use an internal type identifier that's different from the C++ builtin. In that case type equality tests to C++ typeid in boost::variant will fail, breaking it. Using boost::typeindex::type_id ensures that the comparisons always work. --- libgnucash/engine/kvp-value.cpp | 36 +++++++++++++++++---------------- libgnucash/engine/kvp-value.hpp | 2 +- 2 files changed, 20 insertions(+), 18 deletions(-) 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); } From ffc68664063d65c32378fbff370be4db7cf8fdb1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 20 Jan 2020 11:32:10 -0800 Subject: [PATCH 070/298] Changes to accommodate moving guile engine bindings to bindings/guile. Plus for changing targets gncmod-engine and gncmod-app-utils to gnc-engine and gnc-app-utils. --- CMakeLists.txt | 2 +- libgnucash/app-utils/gnc-optiondb.i | 5 +++++ libgnucash/app-utils/test/CMakeLists.txt | 11 +++++++---- po/POTFILES.in | 1 - 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77ec297b1c..815cd28f6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -569,7 +569,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/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index a5fee701b7..9bad4093f3 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -39,7 +39,12 @@ namespace std { %module sw_gnc_optiondb %{ +extern "C" +{ +#include #include +#include +} #include "gnc-optiondb.hpp" extern "C" SCM scm_init_sw_gnc_optiondb_module(void); %} diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index dc41f34715..628207f7ee 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -43,8 +43,8 @@ set(gtest_gnc_option_INCLUDES ${GUILE_INCLUDE_DIRS}) set(gtest_gnc_option_LIBS - gncmod-app-utils - gncmod-engine + gnc-app-utils + gnc-engine ${GLIB2_LDFLAGS} ${GUILE_LDFLAGS} gtest) @@ -94,6 +94,7 @@ if (HAVE_SRFI64) gnc_add_swig_guile_command(swig-gnc-optiondb-guile SWIG_GNC_OPTIONDB_GUILE_CPP swig-gnc-optiondb-guile.cpp ${MODULEPATH}/gnc-optiondb.i + "" ) add_library(swig-gnc-optiondb MODULE ${MODULEPATH}/gnc-option.cpp @@ -102,6 +103,7 @@ if (HAVE_SRFI64) ) set(swig_gnc_optiondb_INCLUDES ${MODULEPATH} + ${CMAKE_SOURCE_DIR}/bindings/guile ${CMAKE_SOURCE_DIR}/libgnucash/engine ${CMAKE_BINARY_DIR}/common # for config.h ${GLIB2_INCLUDE_DIRS} @@ -109,8 +111,9 @@ if (HAVE_SRFI64) ) set(swig_gnc_optiondb_LIBS - gncmod-engine - gncmod-app-utils + gnc-engine + gnc-app-utils + gnucash-guile ${GLIB2_LDFLAGS} ${GUILE_LDFLAGS} ) diff --git a/po/POTFILES.in b/po/POTFILES.in index 0f92fb3c27..fce91bb811 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -527,7 +527,6 @@ libgnucash/app-utils/gnc-exp-parser.c libgnucash/app-utils/gnc-gsettings.c libgnucash/app-utils/gnc-helpers.c libgnucash/app-utils/gnc-help-utils.c -libgnucash/app-utils/gncmod-app-utils.c libgnucash/app-utils/gnc-option.cpp libgnucash/app-utils/gnc-optiondb.cpp libgnucash/app-utils/gnc-prefs-utils.c From 5a82aac639230c20b7ee77116a9287ad4e54af96 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 20 Jan 2020 11:36:37 -0800 Subject: [PATCH 071/298] Hide constexpr constants from SWIG. Swig bindings don't need them and SWIG can't digest constexpr. --- libgnucash/app-utils/gnc-option.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index a5adc31989..a6c4b5e917 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -118,9 +118,10 @@ enum GncOptionUIType }; static const char* commodity_scm_intro{"'(commodity-scm "}; - +#ifndef SWIG size_t constexpr classifier_size_max{50}; size_t constexpr sort_tag_size_max{10}; +#endif struct OptionClassifier { @@ -181,7 +182,9 @@ private: GncOptionUIType m_ui_type; }; +#ifndef SWIG auto constexpr size_t_max = std::numeric_limits::max(); +#endif template class GncOptionValue : public OptionClassifier, public OptionUIItem From cbd0607e80e7c17ec3a0cf3bd588b054cd914167 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 20 Jan 2020 11:37:21 -0800 Subject: [PATCH 072/298] Implement load and store options from/to book options. --- libgnucash/app-utils/gnc-option.cpp | 18 +- libgnucash/app-utils/gnc-option.hpp | 1 + libgnucash/app-utils/gnc-optiondb.cpp | 173 +++++++++++++++++- libgnucash/app-utils/gnc-optiondb.hpp | 12 +- .../app-utils/test/gtest-gnc-optiondb.cpp | 158 +++++++++++++++- 5 files changed, 344 insertions(+), 18 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index ec67ef4497..f9ac5bc08b 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -222,16 +222,14 @@ GncOptionDateValue::in_stream(std::istream& iss) } QofInstance* -qof_instance_from_string(const std::string& str, GncOptionUIType type) +qof_instance_from_guid(GncGUID* guid, GncOptionUIType type) { QofIdType qof_type; - bool commodity_type{false}; switch(type) { case GncOptionUIType::CURRENCY: case GncOptionUIType::COMMODITY: qof_type = "Commodity"; - commodity_type = true; break; case GncOptionUIType::BUDGET: qof_type = "Budget"; @@ -264,8 +262,17 @@ qof_instance_from_string(const std::string& str, GncOptionUIType type) break; } auto book{gnc_get_current_book()}; - if (commodity_type) + 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) +{ + if (type == GncOptionUIType::CURRENCY || + type == GncOptionUIType::COMMODITY) { + auto book{gnc_get_current_book()}; auto sep{str.find(":")}; auto name_space{str.substr(0, sep)}; auto mnemonic{str.substr(sep + 1, -1)}; @@ -275,8 +282,7 @@ qof_instance_from_string(const std::string& str, GncOptionUIType type) mnemonic.c_str())); } auto guid{static_cast(gnc::GUID::from_string(str))}; - auto col{qof_book_get_collection(book, qof_type)}; - return QOF_INSTANCE(qof_collection_lookup_entity(col, &guid)); + return qof_instance_from_guid(&guid, type); } std::string diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index a6c4b5e917..d10e99cb1c 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -264,6 +264,7 @@ private: 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); /* These will work when m_value is a built-in class; GnuCash class and container diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index becae537ac..c87c6bf28f 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -24,6 +24,7 @@ #include "gnc-optiondb.hpp" #include #include +#include auto constexpr stream_max = std::numeric_limits::max(); GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {} @@ -445,7 +446,10 @@ GncOptionDB::load_option_scheme(std::istream& iss) } if (!lookup_id) - throw std::runtime_error("No gnc:lookup-option found"); + { + iss.setstate(std::ios_base::eofbit); + return iss; // No options + } const auto& classifier = lookup_id->get().m_ids; if (classifier.size() != 3) throw std::runtime_error("Malformed option classifier."); @@ -473,16 +477,55 @@ GncOptionDB::load_option_scheme(std::istream& iss) return iss; } +std::ostream& +GncOptionDB::save_to_scheme(std::ostream& oss, const char* options_prolog) const noexcept +{ + for (auto section : m_sections) + { + const auto& [s_name, s_vec] = section; + oss << "\n; Section: " << s_name << "\n\n"; + for (auto option : s_vec) + { + if (!option.is_changed()) + continue; + oss << scheme_tags[0] << options_prolog << "\n"; + oss << scheme_tags[1] << '"' << section.first.substr(0, classifier_size_max) << "\"\n"; + oss << scheme_tags[1] << '"' << option.get_name().substr(0, classifier_size_max) << '"'; + oss << scheme_tags[2] << "\n" << scheme_tags[3]; + option.to_scheme(oss); + oss << scheme_tags[4] << "\n\n"; + } + } + return oss; +} + +std::istream& +GncOptionDB::load_from_scheme(std::istream& iss) noexcept +{ + try { + while (iss.good()) + load_option_scheme(iss); + iss.clear(); //unset eofbit and maybe failbit + } + catch (const std::runtime_error& err) + { + std::cerr << "Load of options from Scheme failed: " << + err.what() << std::endl; + } + return iss; +} + std::ostream& GncOptionDB::save_option_key_value(std::ostream& oss, - const char* section, - const char* name) const noexcept + const std::string& section, + const std::string& name) const noexcept { auto db_opt = find_option(section, name); if (!db_opt || !db_opt->get().is_changed()) return oss; - oss << section << ":" << name << "=" << db_opt->get() << ";"; + oss << section.substr(0, classifier_size_max) << ":" << + name.substr(0, classifier_size_max) << "=" << db_opt->get() << ";"; return oss; } @@ -508,6 +551,128 @@ GncOptionDB::load_option_key_value(std::istream& iss) return iss; } +std::ostream& +GncOptionDB::save_to_key_value(std::ostream& oss) const noexcept +{ + + for (auto section : m_sections) + { + const auto& [s_name, s_vec] = section; + oss << "[Options]\n"; + for (auto option : s_vec) + { + if (option.is_changed()) + oss << section.first.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; +} + +void +GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept +{ + if (clear_options) + qof_book_options_delete(book, nullptr); + for (auto section : m_sections) + { + const auto& [s_name, s_vec] = section; + for (auto option : s_vec) + if (option.is_changed()) + { + // qof_book_set_option wants a GSList path. Let's avoid allocating and make one here. + GSList list_tail{(void*)option.get_name().c_str(), nullptr}; + GSList list_head{(void*)s_name.c_str(), &list_tail}; + auto type{option.get_ui_type()}; + if (type == GncOptionUIType::BOOLEAN) + { + auto val{option.get_value()}; + auto kvp{new KvpValue(val ? "t" : "f")}; + qof_book_set_option(book, kvp, &list_head); + } + else if (type > GncOptionUIType::DATE_FORMAT) + { + QofInstance* inst{QOF_INSTANCE(option.get_value())}; + auto guid = guid_copy(qof_instance_get_guid(inst)); + auto kvp{new KvpValue(guid)}; + qof_book_set_option(book, kvp, &list_head); + } + else if (type == GncOptionUIType::NUMBER_RANGE) + { + auto kvp{new KvpValue(option.get_value())}; + qof_book_set_option(book, kvp, &list_head); + } + else + { + auto kvp{new KvpValue{g_strdup(option.get_value().c_str())}}; + qof_book_set_option(book, kvp, &list_head); + } + } + } +} + +void +GncOptionDB::load_from_kvp(QofBook* book) noexcept +{ + for (auto section : m_sections) + { + const auto& [s_name, s_vec] = section; + for (auto option : s_vec) + { + /* qof_book_set_option wants a GSList path. Let's avoid allocating + * and make one here. */ + GSList list_tail{(void*)option.get_name().c_str(), nullptr}; + GSList list_head{(void*)s_name.c_str(), &list_tail}; + auto kvp = qof_book_get_option(book, &list_head); + if (!kvp) + continue; + switch (kvp->get_type()) + { + case KvpValue::Type::INT64: + option.set_value(kvp->get()); + break; + case KvpValue::Type::STRING: + { + auto str{kvp->get()}; + if (option.get_ui_type() == GncOptionUIType::BOOLEAN) + option.set_value(*str == 't' ? true : false); + else + option.set_value(str); + break; + } + case KvpValue::Type::GUID: + { + auto guid{kvp->get()}; + option.set_value(qof_instance_from_guid(guid, option.get_ui_type())); + break; + } + default: + continue; + break; + } + } + } +} + GncOptionDBPtr gnc_option_db_new(void) { diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 042e78c46c..95419f4fce 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -82,7 +82,7 @@ public: } // void set_selectable(const char* section, const char* name); void make_internal(const char* section, const char* name); - void commit(); + void commit() {}; std::optional> find_section(const std::string& section); std::optional> find_option(const std::string& section, const std::string& name) { return static_cast(*this).find_option(section, name); @@ -92,17 +92,17 @@ public: const char* options_prolog) const noexcept; std::istream& load_from_scheme(std::istream& iss) noexcept; std::ostream& save_to_key_value(std::ostream& oss) const noexcept; - std::istream& load_from_key_value(std::istream& iss) noexcept; - void save_to_kvp() const noexcept; - void load_from_kvp() 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_scheme(std::ostream& oss, const char* option_prolog, const std::string& section, const std::string& name) const noexcept; std::istream& load_option_scheme(std::istream& iss); std::ostream& save_option_key_value(std::ostream& oss, - const char* section, - const char* name) const noexcept; + const std::string& section, + const std::string& name) const noexcept; std::istream& load_option_key_value(std::istream& iss); private: std::optional> m_default_section; diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 9ef8884f31..a8c46a3d50 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -23,6 +23,13 @@ #include #include +#include +extern "C" +{ +#include +#include +#include +} class GncOptionDBTest : public ::testing::Test { @@ -280,8 +287,34 @@ TEST_F(GncOptionDBUITest, test_option_value_from_ui) class GncOptionDBIOTest : public ::testing::Test { protected: - GncOptionDBIOTest() : m_db{gnc_option_db_new()} + GncOptionDBIOTest() : m_book{gnc_get_current_book()}, m_root{gnc_account_create_root(m_book)}, m_db{gnc_option_db_new()} { + 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", @@ -290,8 +323,23 @@ protected: std::string{""}); gnc_register_text_option(m_db, "qux", "garply", "fred", "Phony Option", std::string{"waldo"}); + gnc_register_date_interval_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", + {aapl, 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; }; @@ -313,7 +361,7 @@ TEST_F(GncOptionDBIOTest, test_option_scheme_output) "))) option))\n\n", oss.str().c_str()); } -TEST_F(GncOptionDBIOTest, test_option_scheme_input) +TEST_F(GncOptionDBIOTest, test_string_option_scheme_input) { const char* input{"(let ((option (gnc:lookup-option option\n" " \"foo\"\n" @@ -326,6 +374,84 @@ TEST_F(GncOptionDBIOTest, test_option_scheme_input) EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); } +TEST_F(GncOptionDBIOTest, test_date_interval_option_scheme_input) +{ + const char* input{"(let ((option (gnc:lookup-option option\n" + " \"pork\"\n" + " \"garply\")))\n" + " ((lambda (o) (if o (gnc:option-set-value o " + "'(relative . end-prev-month)" + "))) option))\n\n"}; + std::istringstream iss{input}; + GDate month_end; + g_date_set_time_t(&month_end, time(nullptr)); + g_date_subtract_months(&month_end, 1); + gnc_gdate_set_month_end(&month_end); + auto time1 = time64_from_gdate(&month_end, DayPart::end); + m_db->load_option_scheme(iss); + EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value()); + +} + +TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input) +{ + auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})}; + auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))}; + auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))}; + std::string input{"(let ((option (gnc:lookup-option option\n" + " \"quux\"\n" + " \"xyzzy\")))\n" + " ((lambda (o) (if o (gnc:option-set-value o '(\""}; + input += hpe_guid + "\" \""; + input += msft_guid + "\")))) option))\n\n"; + std::istringstream iss{input}; + EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value()[0]); + m_db->load_option_scheme(iss); + EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value()[1]); + EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value().size()); + +} + +TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input) +{ + auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})}; + auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))}; + auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))}; + std::string input{";; Foo\n\n" + "(let ((option (gnc:lookup-option option\n" + " \"foo\"\n" + " \"sausage\")))\n" + " ((lambda (o) (if o (gnc:option-set-value o \"pepper\"" + "))) option))\n\n" + ";; Pork\n\n" + "(let ((option (gnc:lookup-option option\n" + " \"pork\"\n" + " \"garply\")))\n" + " ((lambda (o) (if o (gnc:option-set-value o " + "'(relative . end-prev-month)" + "))) option))\n\n" + ";; Quux\n\n" + "(let ((option (gnc:lookup-option option\n" + " \"quux\"\n" + " \"xyzzy\")))\n" + " ((lambda (o) (if o (gnc:option-set-value o '(\""}; + input += hpe_guid + "\" \""; + input += msft_guid + "\")))) option))\n\n"; + std::istringstream iss{input}; + GDate month_end; + g_date_set_time_t(&month_end, time(nullptr)); + g_date_subtract_months(&month_end, 1); + gnc_gdate_set_month_end(&month_end); + auto time1 = time64_from_gdate(&month_end, DayPart::end); + EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value()[0]); + m_db->load_from_scheme(iss); + EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); + EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value()); + EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value()[1]); + EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value().size()); + +} + TEST_F(GncOptionDBIOTest, test_option_key_value_output) { std::ostringstream oss; @@ -345,3 +471,31 @@ TEST_F(GncOptionDBIOTest, test_option_key_value_input) 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()); +} From 691cb0992e6c227bbbeb6de01b4e2c5d67986f50 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 23 Jan 2020 10:21:18 -0800 Subject: [PATCH 073/298] Follow change in loading method for engine's guile bindings. --- libgnucash/app-utils/test/test-gnc-optiondb.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 7ecb2f9512..814ca1a97b 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -28,7 +28,7 @@ (compile load eval expand) (load-extension "libswig-gnc-optiondb" "scm_init_sw_gnc_optiondb_module")) -(gnc:module-load "gnucash/engine" 0) +(use-modules (gnucash engine)) (use-modules (sw_gnc_optiondb)) (define (run-test) From cd6ccbe3318d6db8427176fa48900cac58a72492 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 23 Jan 2020 10:23:14 -0800 Subject: [PATCH 074/298] Fix account-tree double-free in scheme test when built with gcc. Oddly when built with clang it doesn't exhibit the crash. --- libgnucash/app-utils/test/test-gnc-optiondb.scm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 814ca1a97b..d1db9d7385 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -83,8 +83,7 @@ (create-account book expenses ACCT-TYPE-EXPENSE "Rent"))) (define (cleanup book root) - (xaccAccountBeginEdit root) - (xaccAccountDestroy root) +;; Destroying the book destroys the account tree too (qof-book-destroy book)) (define (test-make-account-list-option book) From 20b3ef8a892fe55fff30a3e946ceaf2f4528c008 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 23 Jan 2020 10:24:42 -0800 Subject: [PATCH 075/298] Handle some minor differences between libc++ (clang) and libstdc++ (gcc). --- libgnucash/app-utils/gnc-option.hpp | 3 +++ libgnucash/app-utils/gnc-optiondb.cpp | 8 ++++++++ libgnucash/app-utils/test/gtest-gnc-option.cpp | 15 ++++++++------- libgnucash/app-utils/test/gtest-gnc-optiondb.cpp | 10 +++++----- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index d10e99cb1c..2ceff7cbcd 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -409,7 +409,10 @@ gnc_option_from_scheme (std::istream& iss, OptType& opt) { iss.ignore(strlen(commodity_scm_intro) + 1, '"'); std::getline(iss, name_space, '"'); + // libc++ doesn't consume the end character, libstdc++ does +#ifdef _LIBCPP_VERSION iss.ignore(1, '"'); +#endif } else name_space = GNC_COMMODITY_NS_CURRENCY; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index c87c6bf28f..bcde3e5971 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -256,7 +256,11 @@ scan_scheme_symbol_from_streambuf(std::streambuf* sbuf) return retval; } +#ifdef _LIBCPP_VERSION static inline void constexpr +#else +static inline void +#endif consume_scheme_comment(std::streambuf* sbuf) { while (sbuf->in_avail() && !is_eol(sbuf->sgetc())) @@ -273,7 +277,11 @@ scan_scheme_string_from_streambuf(std::streambuf* sbuf) return retval; } +#ifdef _LIBCPP_VERSION static inline void constexpr +#else +static inline void +#endif consume_scheme_whitespace(std::streambuf* sbuf) { while (sbuf->in_avail() && is_whitespace(sbuf->sgetc())) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index bf1b06151d..99ae8897a4 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -667,9 +667,9 @@ TEST_F(GncOptionAccountTest, test_test_constructor_and_destructor) EXPECT_TRUE(m_root != NULL); EXPECT_TRUE(GNC_IS_ACCOUNT(m_root)); GncOptionAccountList list{list_of_types({ACCT_TYPE_BANK})}; - EXPECT_EQ(2, list.size()); + EXPECT_EQ(2U, list.size()); list = list_of_types({ACCT_TYPE_ASSET, ACCT_TYPE_STOCK}); - EXPECT_EQ(6, list.size()); + EXPECT_EQ(6U, list.size()); } TEST_F(GncOptionAccountTest, test_option_no_value_constructor) @@ -685,8 +685,8 @@ TEST_F(GncOptionAccountTest, test_option_value_constructor) GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, acclist}; - EXPECT_EQ(2, option.get_value().size()); - EXPECT_EQ(2, option.get_default_value().size()); + EXPECT_EQ(2U, option.get_value().size()); + EXPECT_EQ(2U, option.get_default_value().size()); EXPECT_EQ(acclist[0], option.get_value()[0]); } @@ -695,7 +695,8 @@ 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})}; GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", - GncOptionUIType::ACCOUNT_LIST, {ACCT_TYPE_BANK}}; + GncOptionUIType::ACCOUNT_LIST, + GncOptionAccountTypeList{ACCT_TYPE_BANK}}; EXPECT_TRUE(option.get_value().empty()); EXPECT_TRUE(option.get_default_value().empty()); EXPECT_EQ(true, option.validate(acclistgood)); @@ -919,13 +920,13 @@ TEST_F(GncMultichoiceOption, test_set_value) TEST_F(GncMultichoiceOption, test_num_permissible) { - EXPECT_EQ(4, m_option.num_permissible_values()); + EXPECT_EQ(4U, m_option.num_permissible_values()); } TEST_F(GncMultichoiceOption, test_permissible_value_stuff) { EXPECT_NO_THROW({ - EXPECT_EQ(3, m_option.permissible_value_index("corge")); + EXPECT_EQ(3U, m_option.permissible_value_index("corge")); EXPECT_STREQ("waldo", m_option.permissible_value(1).c_str()); EXPECT_STREQ("sausage", m_option.permissible_value_name(2).c_str()); EXPECT_STREQ("thud", diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index a8c46a3d50..2be2c3e7be 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -129,7 +129,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_option) auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; gnc_register_account_list_option(m_db, "foo", "bar", "baz", "Phony Option", acclist); - EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value().size()); + EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get().get_value().size()); EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value().at(3)); } @@ -140,7 +140,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option) gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", acclist, {ACCT_TYPE_STOCK}); - EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value().size()); + EXPECT_EQ(4U, m_db->find_option("foo", "bar")->get().get_value().size()); EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value().at(3)); } @@ -152,7 +152,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option) gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", accsel, {ACCT_TYPE_STOCK}); - EXPECT_EQ(1, m_db->find_option("foo", "bar")->get().get_value().size()); + EXPECT_EQ(1U, m_db->find_option("foo", "bar")->get().get_value().size()); EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value().at(0)); } @@ -408,7 +408,7 @@ TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input) EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value()[0]); m_db->load_option_scheme(iss); EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value()[1]); - EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value().size()); + EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value().size()); } @@ -448,7 +448,7 @@ TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input) EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value()); EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value()[1]); - EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value().size()); + EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value().size()); } From aa246d309693c29a7d08e3fe43b7a4274a3b2c26 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 27 Jan 2020 13:40:39 -0800 Subject: [PATCH 076/298] Make implementation classes and template code visible only in app-utils. So only GncOption and the GncOptionDB free-function interface are public. We don't want to expose template headers widely, it would blow up compilation times and might lead to one definition rule violations. --- libgnucash/app-utils/gnc-option-impl.cpp | 293 +++++ libgnucash/app-utils/gnc-option-impl.hpp | 794 ++++++++++++ libgnucash/app-utils/gnc-option-uitype.hpp | 55 + libgnucash/app-utils/gnc-option.cpp | 564 +++++---- libgnucash/app-utils/gnc-option.hpp | 1118 +---------------- libgnucash/app-utils/gnc-optiondb-impl.hpp | 127 ++ libgnucash/app-utils/gnc-optiondb.cpp | 71 +- libgnucash/app-utils/gnc-optiondb.hpp | 97 +- libgnucash/app-utils/gnc-optiondb.i | 15 +- libgnucash/app-utils/test/CMakeLists.txt | 2 + .../app-utils/test/gtest-gnc-option.cpp | 61 +- .../app-utils/test/gtest-gnc-optiondb.cpp | 9 +- po/POTFILES.in | 1 + 13 files changed, 1761 insertions(+), 1446 deletions(-) create mode 100644 libgnucash/app-utils/gnc-option-impl.cpp create mode 100644 libgnucash/app-utils/gnc-option-impl.hpp create mode 100644 libgnucash/app-utils/gnc-option-uitype.hpp create mode 100644 libgnucash/app-utils/gnc-optiondb-impl.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..7ab4584132 --- /dev/null +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -0,0 +1,293 @@ +/********************************************************************\ + * 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 +extern "C" +{ +#include "gnc-accounting-period.h" +#include "gnc-ui-util.h" +} + +bool +GncOptionAccountValue::validate(const GncOptionAccountList& values) const +{ + if (values.empty()) + return false; + if (get_ui_type() == GncOptionUIType::ACCOUNT_SEL && values.size() != 1) + return false; + if (m_allowed.empty()) + return true; + for(auto account : values) { + if (std::find(m_allowed.begin(), m_allowed.end(), + xaccAccountGetType(account)) == m_allowed.end()) + return false; + } + return true; +} + +static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +static void +normalize_month(struct tm& now) +{ + if (now.tm_mon < 0) + { + now.tm_mon += 12; + --now.tm_year; + } + else if (now.tm_mon > 11) + { + now.tm_mon -= 12; + ++now.tm_year; + } +} + +static void +set_day_and_time(struct tm& now, bool starting) +{ + if (starting) + { + now.tm_hour = now.tm_min = now.tm_sec = 0; + now.tm_mday = 1; + } + else + { + now.tm_min = now.tm_sec = 59; + now.tm_hour = 23; + now.tm_mday = days_in_month[now.tm_mon]; + // Check for Februrary in a leap year + if (int year = now.tm_year + 1900; now.tm_mon == 1 && + year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) + ++now.tm_mday; + } +}; + +time64 +GncOptionDateValue::get_value() const +{ + if (m_period == RelativeDatePeriod::ABSOLUTE) + return m_date; + if (m_period == RelativeDatePeriod::TODAY) + return static_cast(GncDateTime()); + if (m_period == RelativeDatePeriod::START_ACCOUNTING_PERIOD) + return gnc_accounting_period_fiscal_start(); + if (m_period == RelativeDatePeriod::END_ACCOUNTING_PERIOD) + return gnc_accounting_period_fiscal_end(); + + GncDateTime now_t; + if (m_period == RelativeDatePeriod::TODAY) + return static_cast(now_t); + struct tm now{static_cast(now_t)}; + struct tm period{static_cast(GncDateTime(gnc_accounting_period_fiscal_start()))}; + bool starting = m_period == RelativeDatePeriod::START_PREV_MONTH || + m_period == RelativeDatePeriod::START_THIS_MONTH || + m_period == RelativeDatePeriod::START_CAL_YEAR || + m_period == RelativeDatePeriod::START_PREV_YEAR || + m_period == RelativeDatePeriod::START_CURRENT_QUARTER || + m_period == RelativeDatePeriod::START_PREV_QUARTER; + + bool prev = m_period == RelativeDatePeriod::START_PREV_YEAR || + m_period == RelativeDatePeriod::END_PREV_YEAR || + m_period == RelativeDatePeriod::START_PREV_QUARTER || + m_period == RelativeDatePeriod::END_PREV_QUARTER; + + if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday) + { + //No set accounting period, use the calendar year + period.tm_mon = 0; + period.tm_mday = 0; + } + + if (m_period == RelativeDatePeriod::START_CAL_YEAR || + m_period == RelativeDatePeriod::END_CAL_YEAR || + m_period == RelativeDatePeriod::START_PREV_YEAR || + m_period == RelativeDatePeriod::END_PREV_YEAR) + { + + if (prev) + --now.tm_year; + now.tm_mon = starting ? 0 : 11; + } + else if (m_period == RelativeDatePeriod::START_PREV_QUARTER || + m_period == RelativeDatePeriod::END_PREV_QUARTER || + m_period == RelativeDatePeriod::START_CURRENT_QUARTER || + m_period == RelativeDatePeriod::END_CURRENT_QUARTER) + { + auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon : + period.tm_mon - now.tm_mon) % 3; + now.tm_mon = now.tm_mon - offset; + if (prev) + now.tm_mon -= 3; + if (!starting) + now.tm_mon += 2; + } + else if (m_period == RelativeDatePeriod::START_PREV_MONTH || + m_period == RelativeDatePeriod::END_PREV_MONTH) + --now.tm_mon; + normalize_month(now); + set_day_and_time(now, starting); + return static_cast(GncDateTime(now)); +} +static const char* date_type_str[] {"absolute", "relative"}; +static const std::array date_period_str +{ + "today", + "start-this-month", "end-this-month", + "start-prev-month", "end-prev-month", + "start-current-quarter", "end-current-quarter", + "start-prev-quarter", "end-prev-quarter", + "start-cal-year", "end-cal-year", + "start-prev-year", "end-prev-year", + "start-prev-fin-year", "end-prev-fin-year" +}; + + +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] << " . " << + date_period_str[static_cast(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 = std::find(date_period_str.begin(), date_period_str.end(), + period_str); + if (period == date_period_str.end()) + { + std::string err{"Unknown period string in date option: '"}; + err += period_str; + err += "'"; + throw std::invalid_argument(err); + } + + int64_t index = period - date_period_str.begin(); + set_value(static_cast(index)); + } + 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::CURRENCY: + case GncOptionUIType::COMMODITY: + qof_type = "Commodity"; + break; + case GncOptionUIType::BUDGET: + qof_type = "Budget"; + break; + case GncOptionUIType::OWNER: + qof_type = "gncOwner"; + 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::QUERY: + qof_type = "gncQuery"; + 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) +{ + if (type == GncOptionUIType::CURRENCY || + type == GncOptionUIType::COMMODITY) + { + auto book{gnc_get_current_book()}; + auto sep{str.find(":")}; + auto name_space{str.substr(0, sep)}; + auto mnemonic{str.substr(sep + 1, -1)}; + auto table = gnc_commodity_table_get_table(book); + return QOF_INSTANCE(gnc_commodity_table_lookup(table, + name_space.c_str(), + mnemonic.c_str())); + } + auto guid{static_cast(gnc::GUID::from_string(str))}; + return qof_instance_from_guid(&guid, type); +} + +std::string +qof_instance_to_string(const QofInstance* inst) +{ + gnc::GUID guid{*qof_instance_get_guid(inst)}; + return guid.to_string(); +} diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp new file mode 100644 index 0000000000..71e9d49245 --- /dev/null +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -0,0 +1,794 @@ +/********************************************************************\ + * 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 * + * * +\********************************************************************/ + +#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 "gnc-option-uitype.hpp" + +/* + * Unused base class to document the structure of the current Scheme option + * vector, re-expressed in C++. The comment-numbers on the right indicate which + * item in the Scheme vector each item implements. + * + * Not everything here needs to be implemented, nor will it necessarily be + * implemented the same way. For example, part of the purpose of this redesign + * is to convert from saving options as strings of Scheme code to some form of + * key-value pair in the book options, so generate_restore_form() will likely be + * supplanted with save_to_book(). + +template +class GncOptionBase +{ +public: + virtual ~GncOption = default; + virtual ValueType get_value() const = 0; //5 + virtual ValueType get_default_value() = 0; + virtual SCM get_SCM_value() = 0; + virtual SCM get_SCM_default_value() const = 0; //7 + virtual void set_value(ValueType) = 0; //6 +// generate_restore_form outputs a Scheme expression (a "form") that finds an +// option and sets it to the current value. e.g.: +//(let ((option (gnc:lookup-option options +// "Display" +// "Amount"))) +// ((lambda (option) (if option ((gnc:option-setter option) 'none))) option)) +// it uses gnc:value->string to generate the "'none" (or whatever the option's +// value would be as input to the scheme interpreter). + + virtual std::string generate_restore_form(); //8 + virtual void save_to_book(QofBook*) const noexcept; //9 + virtual void read_from_book(QofBook*); //10 + virtual std::vector get_option_strings(); //15 + virtual set_changed_callback(std::function); //14 +protected: + const std::string m_section; //0 + const std::string m_name; //1 + const std::string m_sort_tag; //2 + const std::type_info m_kvp_type; //3 + const std::string m_doc_string; //4 + std::function m_changed_callback; //Part of the make-option closure + std::functionm_option_widget_changed_callback; //16 +}; +*/ + + +static const char* commodity_scm_intro{"'(commodity-scm "}; +#ifndef SWIG +size_t constexpr classifier_size_max{50}; +size_t constexpr sort_tag_size_max{10}; +#endif + +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; +}; + +class GncOptionUIItem; + +/** + * 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 OptionUIItem +{ +public: + GncOptionUIType get_ui_type() const { return m_ui_type; } + GncOptionUIItem* const get_ui_item() const {return m_ui_item; } + void clear_ui_item() { m_ui_item = nullptr; } + void set_ui_item(GncOptionUIItem* ui_item) + { + if (m_ui_type == GncOptionUIType::INTERNAL) + { + std::string error{"INTERNAL option, setting the UI item forbidden."}; + throw std::logic_error(std::move(error)); + } + m_ui_item = ui_item; + } + void make_internal() + { + if (m_ui_item != nullptr) + { + std::string error("Option has a UI Element, can't be INTERNAL."); + throw std::logic_error(std::move(error)); + } + m_ui_type = GncOptionUIType::INTERNAL; + } +protected: + OptionUIItem(GncOptionUIType ui_type) : + m_ui_item{nullptr}, m_ui_type{ui_type} {} + OptionUIItem(const OptionUIItem&) = default; + OptionUIItem(OptionUIItem&&) = default; + ~OptionUIItem() = default; + OptionUIItem& operator=(const OptionUIItem&) = default; + OptionUIItem& operator=(OptionUIItem&&) = default; +private: + GncOptionUIItem* m_ui_item; + GncOptionUIType m_ui_type; +}; + +#ifndef SWIG +auto constexpr size_t_max = std::numeric_limits::max(); +#endif + +template +class GncOptionValue : public OptionClassifier, public OptionUIItem +{ +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}, + OptionUIItem(ui_type), + m_value{value}, m_default_value{value} {} + GncOptionValue(const GncOptionValue&) = default; + GncOptionValue(GncOptionValue&&) = default; + GncOptionValue& operator=(const GncOptionValue&) = default; + GncOptionValue& operator=(GncOptionValue&&) = default; + ValueType get_value() const { return m_value; } + ValueType get_default_value() const { return m_default_value; } + void set_value(ValueType new_value) { m_value = new_value; } + bool is_changed() const noexcept { return m_value != m_default_value; } +private: + ValueType m_value; + ValueType m_default_value; +}; + +template +class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem +{ +public: + GncOptionValidatedValue() = delete; + GncOptionValidatedValue(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, + std::functionvalidator, + GncOptionUIType ui_type = GncOptionUIType::INTERNAL + ) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem{ui_type}, + m_value{value}, m_default_value{value}, m_validator{validator} + { + if (!this->validate(value)) + throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); + } + GncOptionValidatedValue(const char* section, const char* name, + const char* key, const char* doc_string, + ValueType value, + std::functionvalidator, + ValueType val_data) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem{GncOptionUIType::INTERNAL}, m_value{value}, + m_default_value{value}, m_validator{validator}, m_validation_data{val_data} + { + if (!this->validate(value)) + throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); + } + GncOptionValidatedValue(const GncOptionValidatedValue&) = default; + GncOptionValidatedValue(GncOptionValidatedValue&&) = default; + GncOptionValidatedValue& operator=(const GncOptionValidatedValue&) = default; + GncOptionValidatedValue& operator=(GncOptionValidatedValue&&) = default; + ValueType get_value() const { return m_value; } + ValueType get_default_value() const { return m_default_value; } + bool validate(ValueType value) const { return m_validator(value); } + void set_value(ValueType value) + { + if (this->validate(value)) + m_value = value; + else + throw std::invalid_argument("Validation failed, value not set."); + } + bool is_changed() const noexcept { return m_value != m_default_value; } + std::ostream& to_scheme(std::ostream&) const; + std::istream& from_scheme(std::istream&); +private: + ValueType m_value; + ValueType m_default_value; + std::function m_validator; //11 + ValueType m_validation_data; +}; + +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); + +/* 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> && + !(std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>), int> = 0> +std::ostream& operator<<(std::ostream& oss, const OptionValueClass& 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; +} + +template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue >, int> = 0> +inline std::ostream& +operator<< (std::ostream& oss, const OptType& opt) +{ + auto value = opt.get_value(); + if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY || + type == GncOptionUIType::CURRENCY) + { + if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY) + { + oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " "; + } + oss << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)); + } + else + { + oss << qof_instance_to_string(value); + } + return oss; +} + +template> && + !(std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>), int> = 0> +std::istream& operator>>(std::istream& iss, OptionValueClass& opt) +{ + std::decay_t value; + iss >> value; + opt.set_value(value); + return iss; +} + +template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue >, int> = 0> +std::istream& +operator>> (std::istream& iss, OptType& opt) +{ + std::string instr; + if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY || + type == GncOptionUIType::CURRENCY) + { + std::string name_space, mnemonic; + if (type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY) + { + iss >> name_space; + } + else + name_space = GNC_COMMODITY_NS_CURRENCY; + iss >> mnemonic; + instr = name_space + ":"; + instr += mnemonic; + } + else + { + 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, GncOptionValidatedValue> || std::is_same_v, GncOptionValue>, int> = 0> +inline std::ostream& +gnc_option_to_scheme (std::ostream& oss, const OptType& opt) +{ + auto value = opt.get_value(); + auto type = opt.get_ui_type(); + if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) + { + if (type == GncOptionUIType::COMMODITY) + { + oss << commodity_scm_intro; + oss << "\"" << + gnc_commodity_get_namespace(GNC_COMMODITY(value)) << "\" "; + } + + oss << "\"" << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)) << "\""; + + if (type == GncOptionUIType::COMMODITY) + { + oss << ")"; + } + } + else + { + oss << "\"" << qof_instance_to_string(value) << "\""; + } + return oss; +} + +template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue>, int> = 0> +inline std::istream& +gnc_option_from_scheme (std::istream& iss, OptType& opt) +{ + std::string instr; + auto type = opt.get_ui_type(); + if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) + { + std::string name_space, mnemonic; + if (type == GncOptionUIType::COMMODITY) + { + iss.ignore(strlen(commodity_scm_intro) + 1, '"'); + std::getline(iss, name_space, '"'); + // libc++ doesn't consume the end character, libstdc++ does +#ifdef _LIBCPP_VERSION + iss.ignore(1, '"'); +#endif + } + else + name_space = GNC_COMMODITY_NS_CURRENCY; + iss.ignore(1, '"'); + std::getline(iss, mnemonic, '"'); + + if (type == GncOptionUIType::COMMODITY) + iss.ignore(2, ')'); + else + iss.ignore(1, '"'); + + instr = name_space + ":"; + instr += mnemonic; + } + else + { + iss.ignore(1, '"'); + std::getline(iss, instr, '"'); + } + opt.set_value(qof_instance_from_string(instr, opt.get_ui_type())); + return iss; +} +#endif // SWIG + +/** + * Used for numeric ranges and plot sizes. + */ + +template +class GncOptionRangeValue : + public OptionClassifier, public OptionUIItem +{ +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}, + OptionUIItem(GncOptionUIType::NUMBER_RANGE), + 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."); + } + bool is_changed() const noexcept { return m_value != m_default_value; } +private: + ValueType m_value; + ValueType m_default_value; + ValueType m_min; + ValueType m_max; + ValueType m_step; +}; + +using GncMultiChoiceOptionEntry = std::tuple; +using GncMultiChoiceOptionChoices = std::vector; + +/** 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. The + * tuple contains three strings, a key, a display + * name and a brief description for the tooltip. Both name and description + * should be localized at the point of use. + * + * + */ + +class GncOptionMultichoiceValue : + public OptionClassifier, public OptionUIItem +{ +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}, + OptionUIItem(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 = index; + m_default_value = index; + } + } + } + + GncOptionMultichoiceValue(const GncOptionMultichoiceValue&) = default; + GncOptionMultichoiceValue(GncOptionMultichoiceValue&&) = default; + GncOptionMultichoiceValue& operator=(const GncOptionMultichoiceValue&) = default; + GncOptionMultichoiceValue& operator=(GncOptionMultichoiceValue&&) = default; + + const std::string& get_value() const + { + return std::get<0>(m_choices.at(m_value)); + } + const std::string& get_default_value() const + { + return std::get<0>(m_choices.at(m_default_value)); + } + bool validate(const std::string& value) const noexcept + { + auto index = find_key(value); + return index != size_t_max; + + } + void set_value(const std::string& value) + { + auto index = find_key(value); + if (index != size_t_max) + m_value = index; + else + throw std::invalid_argument("Value not a valid choice."); + + } + std::size_t num_permissible_values() const noexcept + { + return m_choices.size(); + } + std::size_t permissible_value_index(const std::string& key) const noexcept + { + return find_key(key); + } + const std::string& permissible_value(std::size_t index) const + { + return std::get<0>(m_choices.at(index)); + } + const std::string& permissible_value_name(std::size_t index) const + { + return std::get<1>(m_choices.at(index)); + } + const std::string& permissible_value_description(std::size_t index) const + { + return std::get<2>(m_choices.at(index)); + } + bool is_changed() const noexcept { return m_value != m_default_value; } +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; + + } + std::size_t m_value; + std::size_t m_default_value; + GncMultiChoiceOptionChoices m_choices; +}; + +using GncOptionAccountList = std::vector; + +using GncOptionAccountTypeList = std::vector; + +/** Account options + * + * 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 GncOptionAccountValue : + public OptionClassifier, public OptionUIItem +{ +public: + GncOptionAccountValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), m_value{}, m_default_value{}, m_allowed{} {} + + GncOptionAccountValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + const GncOptionAccountList& value) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), + m_value{value}, + m_default_value{std::move(value)}, m_allowed{} {} + GncOptionAccountValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + GncOptionAccountTypeList&& allowed) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), + m_value{}, + m_default_value{}, m_allowed{std::move(allowed)} {} + GncOptionAccountValue(const char* section, const char* name, + const char* key, const char* doc_string, + GncOptionUIType ui_type, + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(ui_type), + m_value{}, + m_default_value{}, m_allowed{std::move(allowed)} { + if (!validate(value)) + throw std::invalid_argument("Supplied Value not in allowed set."); + m_value = value; + m_default_value = std::move(value); + } + + const GncOptionAccountList& get_value() const { return m_value; } + const GncOptionAccountList& get_default_value() const { return m_default_value; } + bool validate (const GncOptionAccountList& values) const; + void set_value (const GncOptionAccountList& values) { + if (validate(values)) + //throw! + m_value = values; + } + bool is_changed() const noexcept { return m_value != m_default_value; } +private: + GncOptionAccountList m_value; + GncOptionAccountList m_default_value; + GncOptionAccountTypeList m_allowed; +}; + +template<> inline std::ostream& +operator<< (std::ostream& oss, + const GncOptionAccountValue& opt) +{ + auto values{opt.get_value()}; + bool first = true; + for (auto value : values) + { + if (first) + first = false; + else + oss << " "; + oss << qof_instance_to_string(QOF_INSTANCE(value)); + } + return oss; +} + +template<> inline std::istream& +operator>> (std::istream& iss, + GncOptionAccountValue& opt) +{ + GncOptionAccountList values; + while (true) + { + std::string str; + std::getline(iss, str, ' '); + if (!str.empty()) + values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type())); + else + break; + } + opt.set_value(values); + iss.clear(); + return iss; +} + +template, GncOptionAccountValue>, int> = 0> +inline std::ostream& +gnc_option_to_scheme(std::ostream& oss, const OptType& opt) +{ + auto values{opt.get_value()}; + oss << "'(\""; + bool first = true; + for (auto value : values) + { + if (first) + first = false; + else + oss << " \""; + oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"'; + } + oss << ')'; + return oss; +} + +template, GncOptionAccountValue>, int> = 0> +inline std::istream& +gnc_option_from_scheme(std::istream& iss, OptType& opt) +{ + GncOptionAccountList values; + iss.ignore(3, '"'); + while (true) + { + std::string str; + std::getline(iss, str, '"'); + if (!str.empty()) + { + values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type())); + iss.ignore(2, '"'); + } + else + break; + } + opt.set_value(values); + iss.ignore(1, ')'); + return iss; +} + +/** Date options + * A legal date value is a pair of either and 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 == DateTyupe::Absolute +gnc-date-option-relative-time m_type != DateTyupe::Absolute + */ + +class GncOptionDateValue : public OptionClassifier, public OptionUIItem +{ +public: + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(GncOptionUIType::DATE), + m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, + m_date{INT64_MAX}, + m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, + m_default_date{INT64_MAX} {} + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string, + time64 time) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(GncOptionUIType::DATE), + m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time}, + m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {} + GncOptionDateValue(const char* section, const char* name, + const char* key, const char* doc_string, + const RelativeDatePeriod period) : + OptionClassifier{section, name, key, doc_string}, + OptionUIItem(GncOptionUIType::DATE), + m_period{period}, m_date{INT64_MAX}, + m_default_period{period}, m_default_date{INT64_MAX} {} + GncOptionDateValue(const GncOptionDateValue&) = default; + GncOptionDateValue(GncOptionDateValue&&) = default; + GncOptionDateValue& operator=(const GncOptionDateValue&) = default; + GncOptionDateValue& operator=(GncOptionDateValue&&) = default; + time64 get_value() const; + time64 get_default_value() const { return static_cast(GncDateTime()); } + std::ostream& out_stream(std::ostream& oss) const noexcept; + std::istream& in_stream(std::istream& iss); + void set_value(RelativeDatePeriod value) { + m_period = value; + m_date = INT64_MAX; + } + void set_value(time64 time) { + m_period = RelativeDatePeriod::ABSOLUTE; + m_date = time; + } + bool is_changed() const noexcept { return m_period != m_default_period && + m_date != m_default_date; } +private: + RelativeDatePeriod m_period; + time64 m_date; + RelativeDatePeriod m_default_period; + time64 m_default_date; +}; + +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-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp new file mode 100644 index 0000000000..48c75df1ea --- /dev/null +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -0,0 +1,55 @@ +/********************************************************************\ + * 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 * + * 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_UITYPE_HPP__ +#define GNC_OPTION_UITYPE_HPP__ + +enum GncOptionUIType +{ + INTERNAL, + BOOLEAN, + STRING, + TEXT, + CURRENCY, + COMMODITY, + MULTICHOICE, + DATE, + ACCOUNT_LIST, + ACCOUNT_SEL, + LIST, + NUMBER_RANGE, + COLOR, + FONT, + BUDGET, + PIXMAP, + RADIOBUTTON, + DATE_FORMAT, + OWNER, + CUSTOMER, + VENDOR, + EMPLOYEE, + INVOICE, + TAX_TABLE, + QUERY, +}; + +#endif // GNC_OPTION_UITYPE_H__ diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index f9ac5bc08b..4218262711 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -1,6 +1,6 @@ /********************************************************************\ * gnc-option.cpp -- Application options system * - * Copyright (C) 2019 John Ralls * + * 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 * @@ -21,273 +21,353 @@ * * \********************************************************************/ -//#include "options.h" #include "gnc-option.hpp" -#include -#include -extern "C" +#include "gnc-option-impl.hpp" +#include "gnc-option-uitype.hpp" + +template +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(GncOptionValue { + section, name, key, doc_string, value, ui_type})} { -#include "gnc-accounting-period.h" -#include "gnc-ui-util.h" +} + +template ValueType +GncOption::get_value() const +{ + return std::visit([](const auto option)->ValueType { + if constexpr (std::is_same_v, std::decay_t>) + return option.get_value(); + return ValueType {}; + }, *m_option); +} + +template ValueType +GncOption::get_default_value() const +{ + return std::visit([](const auto option)->ValueType { + if constexpr (std::is_same_v, std::decay_t>) + return option.get_default_value(); + return ValueType {}; + }, *m_option); + +} + +template void +GncOption::set_value(ValueType value) +{ + std::visit([value](auto& option) { + if constexpr + (std::is_same_v, + std::decay_t> || + (std::is_same_v, + GncOptionDateValue> && + std::is_same_v, + RelativeDatePeriod>)) + option.set_value(value); + }, *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(GncOptionUIItem* ui_elem) +{ + std::visit([ui_elem](auto& option) { + option.set_ui_item(ui_elem); + }, *m_option); +} + +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 std::visit([](const auto& option)->GncOptionUIItem* { + return option.get_ui_item(); + }, *m_option); +} + +void +GncOption::make_internal() +{ + std::visit([](auto& option) { + option.make_internal(); + }, *m_option); } bool -GncOptionAccountValue::validate(const GncOptionAccountList& values) const +GncOption::is_changed() const noexcept { - if (values.empty()) - return false; - if (get_ui_type() == GncOptionUIType::ACCOUNT_SEL && values.size() != 1) - return false; - if (m_allowed.empty()) - return true; - for(auto account : values) { - if (std::find(m_allowed.begin(), m_allowed.end(), - xaccAccountGetType(account)) == m_allowed.end()) - return false; - } - return true; + return std::visit([](const auto& option)->bool { + return option.is_changed(); + }, *m_option); } -static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -static void -normalize_month(struct tm& now) +template bool +GncOption::validate(ValueType value) const { - if (now.tm_mon < 0) - { - now.tm_mon += 12; - --now.tm_year; - } - else if (now.tm_mon > 11) - { - now.tm_mon -= 12; - ++now.tm_year; - } + return std::visit([value] (const auto& option) -> bool { + if constexpr ((std::is_same_v, + GncOptionMultichoiceValue> && + std::is_same_v, + std::string>) || + std::is_same_v, + GncOptionValidatedValue>) + return option.validate(value); + else + return false; + }, *m_option); } -static void -set_day_and_time(struct tm& now, bool starting) +std::size_t +GncOption::num_permissible_values() const { - if (starting) - { - now.tm_hour = now.tm_min = now.tm_sec = 0; - now.tm_mday = 1; - } - else - { - now.tm_min = now.tm_sec = 59; - now.tm_hour = 23; - now.tm_mday = days_in_month[now.tm_mon]; - // Check for Februrary in a leap year - if (int year = now.tm_year + 1900; now.tm_mon == 1 && - year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) - ++now.tm_mday; - } -}; - -time64 -GncOptionDateValue::get_value() const -{ - if (m_period == RelativeDatePeriod::ABSOLUTE) - return m_date; - if (m_period == RelativeDatePeriod::TODAY) - return static_cast(GncDateTime()); - if (m_period == RelativeDatePeriod::START_ACCOUNTING_PERIOD) - return gnc_accounting_period_fiscal_start(); - if (m_period == RelativeDatePeriod::END_ACCOUNTING_PERIOD) - return gnc_accounting_period_fiscal_end(); - - GncDateTime now_t; - if (m_period == RelativeDatePeriod::TODAY) - return static_cast(now_t); - struct tm now{static_cast(now_t)}; - struct tm period{static_cast(GncDateTime(gnc_accounting_period_fiscal_start()))}; - bool starting = m_period == RelativeDatePeriod::START_PREV_MONTH || - m_period == RelativeDatePeriod::START_THIS_MONTH || - m_period == RelativeDatePeriod::START_CAL_YEAR || - m_period == RelativeDatePeriod::START_PREV_YEAR || - m_period == RelativeDatePeriod::START_CURRENT_QUARTER || - m_period == RelativeDatePeriod::START_PREV_QUARTER; - - bool prev = m_period == RelativeDatePeriod::START_PREV_YEAR || - m_period == RelativeDatePeriod::END_PREV_YEAR || - m_period == RelativeDatePeriod::START_PREV_QUARTER || - m_period == RelativeDatePeriod::END_PREV_QUARTER; - - if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday) - { - //No set accounting period, use the calendar year - period.tm_mon = 0; - period.tm_mday = 0; - } - - if (m_period == RelativeDatePeriod::START_CAL_YEAR || - m_period == RelativeDatePeriod::END_CAL_YEAR || - m_period == RelativeDatePeriod::START_PREV_YEAR || - m_period == RelativeDatePeriod::END_PREV_YEAR) - { - - if (prev) - --now.tm_year; - now.tm_mon = starting ? 0 : 11; - } - else if (m_period == RelativeDatePeriod::START_PREV_QUARTER || - m_period == RelativeDatePeriod::END_PREV_QUARTER || - m_period == RelativeDatePeriod::START_CURRENT_QUARTER || - m_period == RelativeDatePeriod::END_CURRENT_QUARTER) - { - auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon : - period.tm_mon - now.tm_mon) % 3; - now.tm_mon = now.tm_mon - offset; - if (prev) - now.tm_mon -= 3; - if (!starting) - now.tm_mon += 2; - } - else if (m_period == RelativeDatePeriod::START_PREV_MONTH || - m_period == RelativeDatePeriod::END_PREV_MONTH) - --now.tm_mon; - normalize_month(now); - set_day_and_time(now, starting); - return static_cast(GncDateTime(now)); + return std::visit([] (const auto& option) -> size_t { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.num_permissible_values(); + else + return size_t_max; + }, *m_option); } -static const char* date_type_str[] {"absolute", "relative"}; -static const std::array date_period_str -{ - "today", - "start-this-month", "end-this-month", - "start-prev-month", "end-prev-month", - "start-current-quarter", "end-current-quarter", - "start-prev-quarter", "end-prev-quarter", - "start-cal-year", "end-cal-year", - "start-prev-year", "end-prev-year", - "start-prev-fin-year", "end-prev-fin-year" -}; +std::size_t +GncOption::permissible_value_index(const std::string& value) const +{ + return std::visit([&value] (const auto& option) -> size_t { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.permissible_value_index(value); + else + return size_t_max;; + }, *m_option); +} + +const std::string& +GncOption::permissible_value(std::size_t index) const +{ + return std::visit([index] (const auto& option) -> const std::string& { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.permissible_value(index); + else + return c_empty_string; + }, *m_option); +} + +const std::string& +GncOption::permissible_value_name(std::size_t index) const +{ + return std::visit([index] (const auto& option) -> const std::string& { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.permissible_value_name(index); + else + return c_empty_string; + }, *m_option); +} + +const std::string& +GncOption::permissible_value_description(std::size_t index) const +{ + return std::visit([index] (const auto& option) -> const std::string& { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return option.permissible_value_description(index); + else + return c_empty_string; + }, *m_option); +} std::ostream& -GncOptionDateValue::out_stream(std::ostream& oss) const noexcept +GncOption::out_stream(std::ostream& oss) const { - if (m_period == RelativeDatePeriod::ABSOLUTE) - oss << date_type_str[0] << " . " << m_date; - else - oss << date_type_str[1] << " . " << - date_period_str[static_cast(m_period)]; - return oss; + return std::visit([&oss](auto& option) -> std::ostream& { + oss << option; + return oss; + }, *m_option); } std::istream& -GncOptionDateValue::in_stream(std::istream& iss) +GncOption::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 = std::find(date_period_str.begin(), date_period_str.end(), - period_str); - if (period == date_period_str.end()) - { - std::string err{"Unknown period string in date option: '"}; - err += period_str; - err += "'"; - throw std::invalid_argument(err); - } - - int64_t index = period - date_period_str.begin(); - set_value(static_cast(index)); - } - else - { - std::string err{"Unknown date type string in date option: '"}; - err += type_str; - err += "'"; - throw std::invalid_argument{err}; - } - return iss; + return std::visit([&iss](auto& option) -> std::istream& { + iss >> option; + return iss; + }, *m_option); } -QofInstance* -qof_instance_from_guid(GncGUID* guid, GncOptionUIType type) +std::ostream& +GncOption::to_scheme(std::ostream& oss) const { - QofIdType qof_type; - switch(type) - { - case GncOptionUIType::CURRENCY: - case GncOptionUIType::COMMODITY: - qof_type = "Commodity"; - break; - case GncOptionUIType::BUDGET: - qof_type = "Budget"; - break; - case GncOptionUIType::OWNER: - qof_type = "gncOwner"; - 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::QUERY: - qof_type = "gncQuery"; - 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)); + return std::visit([&oss](auto& option) ->std::ostream& { + if constexpr + (std::is_same_v, + GncOptionAccountValue>) + gnc_option_to_scheme(oss, option); + else if constexpr + (std::is_same_v, + GncOptionMultichoiceValue>) + oss << "'" << option; + else if constexpr + (std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>) + gnc_option_to_scheme(oss, option); + else if constexpr + (std::is_same_v, + GncOptionDateValue>) + oss << "'(" << option << ")"; + else if constexpr + (std::is_same_v, + std::string>) + oss << '"' << option << '"'; + else + oss << option; + return oss; + }, *m_option); } -QofInstance* -qof_instance_from_string(const std::string& str, GncOptionUIType type) +std::istream& +GncOption::from_scheme(std::istream& iss) { - if (type == GncOptionUIType::CURRENCY || - type == GncOptionUIType::COMMODITY) - { - auto book{gnc_get_current_book()}; - auto sep{str.find(":")}; - auto name_space{str.substr(0, sep)}; - auto mnemonic{str.substr(sep + 1, -1)}; - auto table = gnc_commodity_table_get_table(book); - return QOF_INSTANCE(gnc_commodity_table_lookup(table, - name_space.c_str(), - mnemonic.c_str())); - } - auto guid{static_cast(gnc::GUID::from_string(str))}; - return qof_instance_from_guid(&guid, type); + return std::visit([&iss](auto& option) -> std::istream& { + if constexpr + (std::is_same_v, + GncOptionAccountValue>) + gnc_option_from_scheme(iss, option); + else if constexpr + ((std::is_same_v, + GncOptionMultichoiceValue>)) + { + iss.ignore(1, '\''); + iss >> option; + } + else if constexpr + (std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>) + gnc_option_from_scheme(iss, option); + else if constexpr + (std::is_same_v, + GncOptionDateValue>) + { + iss.ignore(2, '('); + iss >> option; + //operator >> clears the trailing ')' + } + else if constexpr + (std::is_same_v, + std::string>) + { + iss.ignore(1, '"'); + std::string input; + std::getline(iss, input, '"'); + option.set_value(input); + } + else + iss >> option; + return iss; + }, *m_option); } -std::string -qof_instance_to_string(const QofInstance* inst) -{ - gnc::GUID guid{*qof_instance_get_guid(inst)}; - return guid.to_string(); -} +/* We must instantiate all of the templates we need here because we don't expose + * the template implementation in the public header. + */ + +using GncOptionAccountList = std::vector; + +template class GncOptionValidatedValue; + +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 QofInstance*, 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 const char* GncOption::get_value() const; +template std::string GncOption::get_value() const; +template const QofInstance* GncOption::get_value() const; +template RelativeDatePeriod GncOption::get_value() const; +template GncOptionAccountList 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 RelativeDatePeriod 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(const char*); +template void GncOption::set_value(std::string); +template void GncOption::set_value(const QofInstance*); +template void GncOption::set_value(RelativeDatePeriod); + +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(RelativeDatePeriod) const; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 2ceff7cbcd..96f0db9844 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -1,6 +1,6 @@ /********************************************************************\ * gnc-option.hpp -- Application options system * - * Copyright (C) 2019 John Ralls * + * 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 * @@ -24,733 +24,92 @@ #ifndef GNC_OPTION_HPP_ #define GNC_OPTION_HPP_ -extern "C" -{ -#include -#include -#include -#include -#include -} -#include -#include #include -#include -#include -#include -#include -#include #include - -/* - * Unused base class to document the structure of the current Scheme option - * vector, re-expressed in C++. The comment-numbers on the right indicate which - * item in the Scheme vector each item implements. - * - * Not everything here needs to be implemented, nor will it necessarily be - * implemented the same way. For example, part of the purpose of this redesign - * is to convert from saving options as strings of Scheme code to some form of - * key-value pair in the book options, so generate_restore_form() will likely be - * supplanted with save_to_book(). - -template -class GncOptionBase -{ -public: - virtual ~GncOption = default; - virtual ValueType get_value() const = 0; //5 - virtual ValueType get_default_value() = 0; - virtual SCM get_SCM_value() = 0; - virtual SCM get_SCM_default_value() const = 0; //7 - virtual void set_value(ValueType) = 0; //6 -// generate_restore_form outputs a Scheme expression (a "form") that finds an -// option and sets it to the current value. e.g.: -//(let ((option (gnc:lookup-option options -// "Display" -// "Amount"))) -// ((lambda (option) (if option ((gnc:option-setter option) 'none))) option)) -// it uses gnc:value->string to generate the "'none" (or whatever the option's -// value would be as input to the scheme interpreter). - - virtual std::string generate_restore_form(); //8 - virtual void save_to_book(QofBook*) const noexcept; //9 - virtual void read_from_book(QofBook*); //10 - virtual std::vector get_option_strings(); //15 - virtual set_changed_callback(std::function); //14 -protected: - const std::string m_section; //0 - const std::string m_name; //1 - const std::string m_sort_tag; //2 - const std::type_info m_kvp_type; //3 - const std::string m_doc_string; //4 - std::function m_changed_callback; //Part of the make-option closure - std::functionm_option_widget_changed_callback; //16 -}; -*/ - -enum GncOptionUIType -{ - INTERNAL, - BOOLEAN, - STRING, - TEXT, - CURRENCY, - COMMODITY, - MULTICHOICE, - DATE, - ACCOUNT_LIST, - ACCOUNT_SEL, - LIST, - NUMBER_RANGE, - COLOR, - FONT, - BUDGET, - PIXMAP, - RADIOBUTTON, - DATE_FORMAT, - OWNER, - CUSTOMER, - VENDOR, - EMPLOYEE, - INVOICE, - TAX_TABLE, - QUERY, -}; - -static const char* commodity_scm_intro{"'(commodity-scm "}; -#ifndef SWIG -size_t constexpr classifier_size_max{50}; -size_t constexpr sort_tag_size_max{10}; -#endif - -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; -}; +#include +#include +#include "gnc-option-uitype.hpp" class GncOptionUIItem; +struct QofInstance_s; +using QofInstance = QofInstance_s; +template class GncOptionValue; +class GncOptionAccountValue; +class GncOptionMultichoiceValue; +template class GncOptionRangeValue; +template class GncOptionValidatedValue; +class GncOptionDateValue; -/** - * 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 OptionUIItem +using GncOptionVariant = std::variant, + GncOptionValue, + GncOptionValue, + GncOptionValue, + GncOptionAccountValue, + GncOptionMultichoiceValue, + GncOptionRangeValue, + GncOptionRangeValue, + GncOptionValidatedValue, + GncOptionDateValue>; + +using GncOptionVariantPtr = std::unique_ptr; + +class GncOption { public: - GncOptionUIType get_ui_type() const { return m_ui_type; } - GncOptionUIItem* const get_ui_item() const {return m_ui_item; } - void clear_ui_item() { m_ui_item = nullptr; } - void set_ui_item(GncOptionUIItem* ui_item) - { - if (m_ui_type == GncOptionUIType::INTERNAL) - { - std::string error{"INTERNAL option, setting the UI item forbidden."}; - throw std::logic_error(std::move(error)); - } - m_ui_item = ui_item; - } - void make_internal() - { - if (m_ui_item != nullptr) - { - std::string error("Option has a UI Element, can't be INTERNAL."); - throw std::logic_error(std::move(error)); - } - m_ui_type = GncOptionUIType::INTERNAL; - } -protected: - OptionUIItem(GncOptionUIType ui_type) : - m_ui_item{nullptr}, m_ui_type{ui_type} {} - OptionUIItem(const OptionUIItem&) = default; - OptionUIItem(OptionUIItem&&) = default; - ~OptionUIItem() = default; - OptionUIItem& operator=(const OptionUIItem&) = default; - OptionUIItem& operator=(OptionUIItem&&) = default; + template + GncOption(OptionType option) : + m_option{std::make_unique(option)} {} + template + 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 ValueType get_default_value() const; + template ValueType get_value() const; + + 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(GncOptionUIItem* ui_elem); + const GncOptionUIType get_ui_type() const; + GncOptionUIItem* const get_ui_item() const; + void make_internal(); + bool is_changed() const noexcept; + template bool validate(ValueType value) const; + std::size_t num_permissible_values() const; + std::size_t permissible_value_index(const std::string& value) const; + const std::string& permissible_value(std::size_t index) const; + const std::string& permissible_value_name(std::size_t index) const; + const std::string& permissible_value_description(std::size_t index) const; + std::ostream& out_stream(std::ostream& oss) const; + std::istream& in_stream(std::istream& iss); + std::ostream& to_scheme(std::ostream& oss) const; + std::istream& from_scheme(std::istream& iss); + GncOptionVariantPtr& _get_option() { return m_option; } private: - GncOptionUIItem* m_ui_item; - GncOptionUIType m_ui_type; + inline static const std::string c_empty_string{""}; + GncOptionVariantPtr m_option; }; -#ifndef SWIG -auto constexpr size_t_max = std::numeric_limits::max(); -#endif - -template -class GncOptionValue : public OptionClassifier, public OptionUIItem -{ -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}, - OptionUIItem(ui_type), - m_value{value}, m_default_value{value} {} - GncOptionValue(const GncOptionValue&) = default; - GncOptionValue(GncOptionValue&&) = default; - GncOptionValue& operator=(const GncOptionValue&) = default; - GncOptionValue& operator=(GncOptionValue&&) = default; - ValueType get_value() const { return m_value; } - ValueType get_default_value() const { return m_default_value; } - void set_value(ValueType new_value) { m_value = new_value; } - bool is_changed() const noexcept { return m_value != m_default_value; } -private: - ValueType m_value; - ValueType m_default_value; -}; - -template -class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem -{ -public: - GncOptionValidatedValue(const char* section, const char* name, - const char* key, const char* doc_string, - ValueType value, - std::functionvalidator, - GncOptionUIType ui_type = GncOptionUIType::INTERNAL - ) : - OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), - m_value{value}, m_default_value{value}, m_validator{validator} - { - if (!this->validate(value)) - throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); - } - GncOptionValidatedValue(const char* section, const char* name, - const char* key, const char* doc_string, - ValueType value, - std::functionvalidator, - ValueType val_data) : - OptionClassifier{section, name, key, doc_string}, m_value{value}, - m_default_value{value}, m_validator{validator}, m_validation_data{val_data} - { - if (!this->validate(value)) - throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); - } - GncOptionValidatedValue(const GncOptionValidatedValue&) = default; - GncOptionValidatedValue(GncOptionValidatedValue&&) = default; - GncOptionValidatedValue& operator=(const GncOptionValidatedValue&) = default; - GncOptionValidatedValue& operator=(GncOptionValidatedValue&&) = default; - ValueType get_value() const { return m_value; } - ValueType get_default_value() const { return m_default_value; } - bool validate(ValueType value) const { return m_validator(value); } - void set_value(ValueType value) - { - if (this->validate(value)) - m_value = value; - else - throw std::invalid_argument("Validation failed, value not set."); - } - bool is_changed() const noexcept { return m_value != m_default_value; } - std::ostream& to_scheme(std::ostream&) const; - std::istream& from_scheme(std::istream&); -private: - ValueType m_value; - ValueType m_default_value; - std::function m_validator; //11 - ValueType m_validation_data; -}; - -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); - -/* 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> && - !(std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>), int> = 0> -std::ostream& operator<<(std::ostream& oss, const OptionValueClass& 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; -} - -template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue >, int> = 0> inline std::ostream& -operator<< (std::ostream& oss, const OptType& opt) +operator<<(std::ostream& oss, const GncOption& opt) { - auto value = opt.get_value(); - if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY || - type == GncOptionUIType::CURRENCY) - { - if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY) - { - oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " "; - } - oss << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)); - } - else - { - oss << qof_instance_to_string(value); - } - return oss; + return opt.out_stream(oss); } -template> && - !(std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>), int> = 0> -std::istream& operator>>(std::istream& iss, OptionValueClass& opt) -{ - std::decay_t value; - iss >> value; - opt.set_value(value); - return iss; -} - -template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue >, int> = 0> -std::istream& -operator>> (std::istream& iss, OptType& opt) -{ - std::string instr; - if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY || - type == GncOptionUIType::CURRENCY) - { - std::string name_space, mnemonic; - if (type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY) - { - iss >> name_space; - } - else - name_space = GNC_COMMODITY_NS_CURRENCY; - iss >> mnemonic; - instr = name_space + ":"; - instr += mnemonic; - } - else - { - 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, GncOptionValidatedValue> || std::is_same_v, GncOptionValue>, int> = 0> -inline std::ostream& -gnc_option_to_scheme (std::ostream& oss, const OptType& opt) -{ - auto value = opt.get_value(); - auto type = opt.get_ui_type(); - if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) - { - if (type == GncOptionUIType::COMMODITY) - { - oss << commodity_scm_intro; - oss << "\"" << - gnc_commodity_get_namespace(GNC_COMMODITY(value)) << "\" "; - } - - oss << "\"" << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)) << "\""; - - if (type == GncOptionUIType::COMMODITY) - { - oss << ")"; - } - } - else - { - oss << "\"" << qof_instance_to_string(value) << "\""; - } - return oss; -} - -template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue>, int> = 0> inline std::istream& -gnc_option_from_scheme (std::istream& iss, OptType& opt) +operator>>(std::istream& iss, GncOption& opt) { - std::string instr; - auto type = opt.get_ui_type(); - if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) - { - std::string name_space, mnemonic; - if (type == GncOptionUIType::COMMODITY) - { - iss.ignore(strlen(commodity_scm_intro) + 1, '"'); - std::getline(iss, name_space, '"'); - // libc++ doesn't consume the end character, libstdc++ does -#ifdef _LIBCPP_VERSION - iss.ignore(1, '"'); -#endif - } - else - name_space = GNC_COMMODITY_NS_CURRENCY; - iss.ignore(1, '"'); - std::getline(iss, mnemonic, '"'); - - if (type == GncOptionUIType::COMMODITY) - iss.ignore(2, ')'); - else - iss.ignore(1, '"'); - - instr = name_space + ":"; - instr += mnemonic; - } - else - { - iss.ignore(1, '"'); - std::getline(iss, instr, '"'); - } - opt.set_value(qof_instance_from_string(instr, opt.get_ui_type())); - return iss; + return opt.in_stream(iss); } -#endif // SWIG - /** - * Used for numeric ranges and plot sizes. - */ - -template -class GncOptionRangeValue : - public OptionClassifier, public OptionUIItem -{ -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}, - OptionUIItem(GncOptionUIType::NUMBER_RANGE), - 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."); - } - bool is_changed() const noexcept { return m_value != m_default_value; } -private: - ValueType m_value; - ValueType m_default_value; - ValueType m_min; - ValueType m_max; - ValueType m_step; -}; - -/** 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. The - * tuple contains three strings, a key, a display - * name and a brief description for the tooltip. Both name and description - * should be localized at the point of use. - * - * - */ -using GncMultiChoiceOptionEntry = std::tuple; -using GncMultiChoiceOptionChoices = std::vector; - -class GncOptionMultichoiceValue : - public OptionClassifier, public OptionUIItem -{ -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}, - OptionUIItem(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 = index; - m_default_value = index; - } - } - } - - GncOptionMultichoiceValue(const GncOptionMultichoiceValue&) = default; - GncOptionMultichoiceValue(GncOptionMultichoiceValue&&) = default; - GncOptionMultichoiceValue& operator=(const GncOptionMultichoiceValue&) = default; - GncOptionMultichoiceValue& operator=(GncOptionMultichoiceValue&&) = default; - - const std::string& get_value() const - { - return std::get<0>(m_choices.at(m_value)); - } - const std::string& get_default_value() const - { - return std::get<0>(m_choices.at(m_default_value)); - } - bool validate(const std::string& value) const noexcept - { - auto index = find_key(value); - return index != size_t_max; - - } - void set_value(const std::string& value) - { - auto index = find_key(value); - if (index != size_t_max) - m_value = index; - else - throw std::invalid_argument("Value not a valid choice."); - - } - std::size_t num_permissible_values() const noexcept - { - return m_choices.size(); - } - std::size_t permissible_value_index(const std::string& key) const noexcept - { - return find_key(key); - } - const std::string& permissible_value(std::size_t index) const - { - return std::get<0>(m_choices.at(index)); - } - const std::string& permissible_value_name(std::size_t index) const - { - return std::get<1>(m_choices.at(index)); - } - const std::string& permissible_value_description(std::size_t index) const - { - return std::get<2>(m_choices.at(index)); - } - bool is_changed() const noexcept { return m_value != m_default_value; } -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; - - } - std::size_t m_value; - std::size_t m_default_value; - GncMultiChoiceOptionChoices m_choices; -}; - -/** Account options - * - * 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. - * - - */ - -using GncOptionAccountList = std::vector; -using GncOptionAccountTypeList = std::vector; - -class GncOptionAccountValue : - public OptionClassifier, public OptionUIItem -{ -public: - GncOptionAccountValue(const char* section, const char* name, - const char* key, const char* doc_string, - GncOptionUIType ui_type) : - OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), m_value{}, m_default_value{}, m_allowed{} {} - - GncOptionAccountValue(const char* section, const char* name, - const char* key, const char* doc_string, - GncOptionUIType ui_type, - const GncOptionAccountList& value) : - OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), - m_value{value}, - m_default_value{std::move(value)}, m_allowed{} {} - GncOptionAccountValue(const char* section, const char* name, - const char* key, const char* doc_string, - GncOptionUIType ui_type, - GncOptionAccountTypeList&& allowed) : - OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), - m_value{}, - m_default_value{}, m_allowed{std::move(allowed)} {} - GncOptionAccountValue(const char* section, const char* name, - const char* key, const char* doc_string, - GncOptionUIType ui_type, - const GncOptionAccountList& value, - GncOptionAccountTypeList&& allowed) : - OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), - m_value{}, - m_default_value{}, m_allowed{std::move(allowed)} { - if (!validate(value)) - throw std::invalid_argument("Supplied Value not in allowed set."); - m_value = value; - m_default_value = std::move(value); - } - - const GncOptionAccountList& get_value() const { return m_value; } - const GncOptionAccountList& get_default_value() const { return m_default_value; } - bool validate (const GncOptionAccountList& values) const; - void set_value (const GncOptionAccountList& values) { - if (validate(values)) - //throw! - m_value = values; - } - bool is_changed() const noexcept { return m_value != m_default_value; } -private: - GncOptionAccountList m_value; - GncOptionAccountList m_default_value; - GncOptionAccountTypeList m_allowed; -}; - -template<> inline std::ostream& -operator<< (std::ostream& oss, - const GncOptionAccountValue& opt) -{ - auto values{opt.get_value()}; - bool first = true; - for (auto value : values) - { - if (first) - first = false; - else - oss << " "; - oss << qof_instance_to_string(QOF_INSTANCE(value)); - } - return oss; -} - -template<> inline std::istream& -operator>> (std::istream& iss, - GncOptionAccountValue& opt) -{ - GncOptionAccountList values; - while (true) - { - std::string str; - std::getline(iss, str, ' '); - if (!str.empty()) - values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type())); - else - break; - } - opt.set_value(values); - iss.clear(); - return iss; -} - -template, GncOptionAccountValue>, int> = 0> -inline std::ostream& -gnc_option_to_scheme(std::ostream& oss, const OptType& opt) -{ - auto values{opt.get_value()}; - oss << "'(\""; - bool first = true; - for (auto value : values) - { - if (first) - first = false; - else - oss << " \""; - oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"'; - } - oss << ')'; - return oss; -} - -template, GncOptionAccountValue>, int> = 0> -inline std::istream& -gnc_option_from_scheme(std::istream& iss, OptType& opt) -{ - GncOptionAccountList values; - iss.ignore(3, '"'); - while (true) - { - std::string str; - std::getline(iss, str, '"'); - if (!str.empty()) - { - values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type())); - iss.ignore(2, '"'); - } - else - break; - } - opt.set_value(values); - iss.ignore(1, ')'); - return iss; -} - -/** Date options - * A legal date value is a pair of either and a RelativeDatePeriod, the absolute - * flag and a time64, or for legacy purposes the absolute flag and a timespec. + * 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. */ -/* -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 == DateTyupe::Absolute -gnc-date-option-relative-time m_type != DateTyupe::Absolute - */ - enum class RelativeDatePeriod : int { ABSOLUTE = -1, @@ -771,352 +130,5 @@ enum class RelativeDatePeriod : int END_ACCOUNTING_PERIOD }; -class GncOptionDateValue : public OptionClassifier, public OptionUIItem -{ -public: - GncOptionDateValue(const char* section, const char* name, - const char* key, const char* doc_string) : - OptionClassifier{section, name, key, doc_string}, - OptionUIItem(GncOptionUIType::DATE), - m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, - m_date{INT64_MAX}, - m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, - m_default_date{INT64_MAX} {} - GncOptionDateValue(const char* section, const char* name, - const char* key, const char* doc_string, - time64 time) : - OptionClassifier{section, name, key, doc_string}, - OptionUIItem(GncOptionUIType::DATE), - m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time}, - m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {} - GncOptionDateValue(const char* section, const char* name, - const char* key, const char* doc_string, - const RelativeDatePeriod period) : - OptionClassifier{section, name, key, doc_string}, - OptionUIItem(GncOptionUIType::DATE), - m_period{period}, m_date{INT64_MAX}, - m_default_period{period}, m_default_date{INT64_MAX} {} - GncOptionDateValue(const GncOptionDateValue&) = default; - GncOptionDateValue(GncOptionDateValue&&) = default; - GncOptionDateValue& operator=(const GncOptionDateValue&) = default; - GncOptionDateValue& operator=(GncOptionDateValue&&) = default; - time64 get_value() const; - time64 get_default_value() const { return static_cast(GncDateTime()); } - std::ostream& out_stream(std::ostream& oss) const noexcept; - std::istream& in_stream(std::istream& iss); - void set_value(RelativeDatePeriod value) { - m_period = value; - m_date = INT64_MAX; - } - void set_value(time64 time) { - m_period = RelativeDatePeriod::ABSOLUTE; - m_date = time; - } - bool is_changed() const noexcept { return m_period != m_default_period && - m_date != m_default_date; } -private: - RelativeDatePeriod m_period; - time64 m_date; - RelativeDatePeriod m_default_period; - time64 m_default_date; -}; - -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); -} - -using GncOptionVariant = std::variant, - GncOptionValue, - GncOptionValue, - GncOptionValue, - GncOptionAccountValue, - GncOptionMultichoiceValue, - GncOptionRangeValue, - GncOptionRangeValue, - GncOptionValidatedValue, - GncOptionDateValue>; - -class GncOption -{ -public: - template - GncOption(OptionType option) : m_option{option} {} - - template - GncOption(const char* section, const char* name, - const char* key, const char* doc_string, - ValueType value, - GncOptionUIType ui_type = GncOptionUIType::INTERNAL) : - m_option{GncOptionValue { - section, name, key, doc_string, value, ui_type}} {} - - template ValueType get_value() const - { - return std::visit([](const auto& option)->ValueType { - if constexpr (std::is_same_v, std::decay_t>) - return option.get_value(); - return ValueType {}; - }, m_option); - } - - template ValueType get_default_value() const - { - return std::visit([](const auto& option)->ValueType { - if constexpr (std::is_same_v, std::decay_t>) - return option.get_default_value(); - return ValueType {}; - }, m_option); - - } - - template void set_value(ValueType value) - { - std::visit([value](auto& option) { - if constexpr - (std::is_same_v, - std::decay_t> || - (std::is_same_v, - GncOptionDateValue> && - std::is_same_v, - RelativeDatePeriod>)) - option.set_value(value); - }, m_option); - } - const std::string& get_section() const - { - return std::visit([](const auto& option)->const std::string& { - return option.m_section; - }, m_option); - } - const std::string& get_name() const - { - return std::visit([](const auto& option)->const std::string& { - return option.m_name; - }, m_option); - } - const std::string& get_key() const - { - return std::visit([](const auto& option)->const std::string& { - return option.m_sort_tag; - }, m_option); - } - const std::string& get_docstring() const - { - return std::visit([](const auto& option)->const std::string& { - return option.m_doc_string; - }, m_option); - } - void set_ui_item(GncOptionUIItem* ui_elem) - { - std::visit([ui_elem](auto& option) { - option.set_ui_item(ui_elem); - }, m_option); - } - const GncOptionUIType get_ui_type() const - { - return std::visit([](const auto& option)->GncOptionUIType { - return option.get_ui_type(); - }, m_option); - } - GncOptionUIItem* const get_ui_item() const - { - return std::visit([](const auto& option)->GncOptionUIItem* { - return option.get_ui_item(); - }, m_option); - } - void make_internal() - { - std::visit([](auto& option) { - option.make_internal(); - }, m_option); - } - bool is_changed() - { - return std::visit([](const auto& option)->bool { - return option.is_changed(); - }, m_option); - } - - template - bool validate(ValueType value) const { - return std::visit([value] (const auto& option) -> bool { - if constexpr ((std::is_same_v, - GncOptionMultichoiceValue> && - std::is_same_v, - std::string>) || - std::is_same_v, - GncOptionValidatedValue>) - return option.validate(value); - else - return false; - }, m_option); - } - - std::size_t num_permissible_values() const { - return std::visit([] (const auto& option) -> size_t { - if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) - return option.num_permissible_values(); - else - return size_t_max; - }, m_option); - } - - std::size_t permissible_value_index(const std::string& value) const { - return std::visit([&value] (const auto& option) -> size_t { - if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) - return option.permissible_value_index(value); - else - return size_t_max;; - }, m_option); - } - - const std::string& permissible_value(std::size_t index) const { - return std::visit([index] (const auto& option) -> const std::string& { - if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) - return option.permissible_value(index); - else - return c_empty_string; - }, m_option); - } - - const std::string& permissible_value_name(std::size_t index) const { - return std::visit([index] (const auto& option) -> const std::string& { - if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) - return option.permissible_value_name(index); - else - return c_empty_string; - }, m_option); - } - - const std::string& permissible_value_description(std::size_t index) const { - return std::visit([index] (const auto& option) -> const std::string& { - if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) - return option.permissible_value_description(index); - else - return c_empty_string; - }, m_option); - } - - std::ostream& out_stream(std::ostream& oss) const - { - return std::visit([&oss](auto& option) -> std::ostream& { - oss << option; - return oss; - }, m_option); - } - std::istream& in_stream(std::istream& iss) - { - return std::visit([&iss](auto& option) -> std::istream& { - iss >> option; - return iss; - }, m_option); - } - - std::ostream& to_scheme(std::ostream& oss) const - { - return std::visit([&oss](auto& option) ->std::ostream& { - if constexpr - (std::is_same_v, - GncOptionAccountValue>) - gnc_option_to_scheme(oss, option); - else if constexpr - (std::is_same_v, - GncOptionMultichoiceValue>) - oss << "'" << option; - else if constexpr - (std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>) - gnc_option_to_scheme(oss, option); - else if constexpr - (std::is_same_v, - GncOptionDateValue>) - oss << "'(" << option << ")"; - else if constexpr - (std::is_same_v, - std::string>) - oss << '"' << option << '"'; - else - oss << option; - return oss; - }, m_option); - } - - std::istream& from_scheme(std::istream& iss) - { - return std::visit([&iss](auto& option) ->std::istream& { - if constexpr - (std::is_same_v, - GncOptionAccountValue>) - gnc_option_from_scheme(iss, option); - else if constexpr - (std::is_same_v, - GncOptionMultichoiceValue>) - { - iss.ignore(1, '\''); - iss >> option; - } - else if constexpr - (std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>) - gnc_option_from_scheme(iss, option); - else if constexpr - (std::is_same_v, - GncOptionDateValue>) - { - iss.ignore(2, '('); - iss >> option; - //operator >> clears the trailing ')' - } - else if constexpr - (std::is_same_v, - std::string>) - { - iss.ignore(1, '"'); - std::string input; - std::getline(iss, input, '"'); - option.set_value(input); - } - else - iss >> option; - return iss; - }, m_option); - } - - GncOptionVariant& _get_option() const { return m_option; } -private: - inline static const std::string c_empty_string{""}; - mutable GncOptionVariant m_option; -}; - -inline std::ostream& -operator<<(std::ostream& oss, const GncOption& opt) -{ - return opt.out_stream(oss); -} - -inline std::istream& -operator>>(std::istream& iss, GncOption& opt) -{ - return opt.in_stream(iss); -} #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..6a9c5470af --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -0,0 +1,127 @@ +/********************************************************************\ + * 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 * + * * +\********************************************************************/ + +#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 +} + +using GncOptionVec = std::vector; +using GncOptionSection = std::pair; +class GncOptionDB +{ +public: + GncOptionDB(); + GncOptionDB(QofBook* book); + ~GncOptionDB() = default; + + void save_to_book(QofBook* book, bool do_clear) const; + int 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 unregister_option(const char* section, const char* name); + void set_default_section(const char* section); + const GncOptionSection* const get_default_section() const noexcept; + void set_ui_item(const char* section, const char* name, GncOptionUIItem* ui_item); + GncOptionUIItem* const get_ui_item(const char* section, const char* name); + GncOptionUIType get_ui_type(const char* section, const char* name); + void set_ui_from_option(const char* section, const char* name, + std::function func); + void set_option_from_ui(const char* section, const char* name, + std::function func); + 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->get().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() {}; + std::optional> find_section(const std::string& section); + std::optional> find_option(const std::string& section, const std::string& name) { + return static_cast(*this).find_option(section, name); + } + std::optional> find_option(const std::string& section, const std::string& name) const; + std::ostream& save_to_scheme(std::ostream& oss, + const char* options_prolog) const noexcept; + std::istream& load_from_scheme(std::istream& iss) noexcept; + 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_scheme(std::ostream& oss, + const char* option_prolog, + const std::string& section, + const std::string& name) const noexcept; + std::istream& load_option_scheme(std::istream& iss); + 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); +private: + std::optional> m_default_section; + std::vector m_sections; + bool m_dirty = false; + + std::function m_get_ui_value; + std::function m_set_ui_value; + static constexpr char const* const scheme_tags[] + { + "(let ((option (gnc:lookup-option ", + " ", + ")))", + " ((lambda (o) (if o (gnc:option-set-value o ", + "))) option))" + }; +}; + + +#endif // GNC_OPTIONDB_P_HPP_ diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index bcde3e5971..9bdb5de8e3 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -21,10 +21,12 @@ * * \********************************************************************/ -#include "gnc-optiondb.hpp" +#include #include #include #include +#include "gnc-optiondb.hpp" +#include "gnc-optiondb-impl.hpp" auto constexpr stream_max = std::numeric_limits::max(); GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {} @@ -43,7 +45,8 @@ GncOptionDB::register_option(const char* section, GncOption&& option) if (db_section) { - db_section->get().second.emplace_back(std::move(option)); + auto& sec_vec = db_section->get().second; + sec_vec.emplace_back(std::move(option)); return; } @@ -59,10 +62,11 @@ GncOptionDB::unregister_option(const char* section, const char* name) auto db_section = find_section(section); if (db_section) { - db_section->get().second.erase( + auto& sec_vec = db_section->get().second; + sec_vec.erase( std::remove_if( - db_section->get().second.begin(), db_section->get().second.end(), - [name](const GncOption& option) -> bool + sec_vec.begin(), sec_vec.end(), + [name](const auto& option) -> bool { return option.get_name() == std::string{name}; })); @@ -132,7 +136,7 @@ GncOptionDB::find_section(const std::string& section) { auto db_section = std::find_if( m_sections.begin(), m_sections.end(), - [§ion](GncOptionSection sect) -> bool + [§ion](auto& sect) -> bool { return section.compare(0, classifier_size_max, sect.first) == 0; }); @@ -147,14 +151,15 @@ GncOptionDB::find_option(const std::string& section, const std::string& name) co auto db_section = const_cast(this)->find_section(section); if (!db_section) return std::nullopt; + auto& sec_vec = db_section->get().second; auto db_opt = std::find_if( - db_section->get().second.begin(), db_section->get().second.end(), + sec_vec.begin(), sec_vec.end(), [&name](GncOption& option) -> bool { return name.compare(0, classifier_size_max - 1, option.get_name()) == 0; }); - if (db_opt == db_section->get().second.end()) + if (db_opt == sec_vec.end()) return std::nullopt; return *db_opt; } @@ -173,6 +178,7 @@ GncOptionDB::lookup_string_option(const char* section, const char* name) void GncOptionDB::make_internal(const char* section, const char* name) { + auto db_opt = find_option(section, name); if (db_opt) db_opt->get().make_internal(); @@ -306,8 +312,7 @@ struct SchemeId /** * Scheme Parse Tree - * An identifier is a string and a type (name, const, string, or form). A Form - * + * An identifier is a string and a type (name, const, string, or form). */ static void scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id); @@ -488,11 +493,11 @@ GncOptionDB::load_option_scheme(std::istream& iss) std::ostream& GncOptionDB::save_to_scheme(std::ostream& oss, const char* options_prolog) const noexcept { - for (auto section : m_sections) + for (auto& section : m_sections) { const auto& [s_name, s_vec] = section; oss << "\n; Section: " << s_name << "\n\n"; - for (auto option : s_vec) + for (auto& option : s_vec) { if (!option.is_changed()) continue; @@ -563,11 +568,11 @@ std::ostream& GncOptionDB::save_to_key_value(std::ostream& oss) const noexcept { - for (auto section : m_sections) + for (auto& section : m_sections) { const auto& [s_name, s_vec] = section; oss << "[Options]\n"; - for (auto option : s_vec) + for (auto& option : s_vec) { if (option.is_changed()) oss << section.first.substr(0, classifier_size_max) << @@ -601,10 +606,10 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept { if (clear_options) qof_book_options_delete(book, nullptr); - for (auto section : m_sections) + for (auto& section : m_sections) { const auto& [s_name, s_vec] = section; - for (auto option : s_vec) + for (auto& option : s_vec) if (option.is_changed()) { // qof_book_set_option wants a GSList path. Let's avoid allocating and make one here. @@ -619,7 +624,7 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept } else if (type > GncOptionUIType::DATE_FORMAT) { - QofInstance* inst{QOF_INSTANCE(option.get_value())}; + const QofInstance* inst{QOF_INSTANCE(option.get_value())}; auto guid = guid_copy(qof_instance_get_guid(inst)); auto kvp{new KvpValue(guid)}; qof_book_set_option(book, kvp, &list_head); @@ -641,13 +646,14 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept void GncOptionDB::load_from_kvp(QofBook* book) noexcept { - for (auto section : m_sections) + for (auto& section : m_sections) { - const auto& [s_name, s_vec] = section; - for (auto option : s_vec) + auto& [s_name, s_vec] = section; + for (auto& option : s_vec) { /* qof_book_set_option wants a GSList path. Let's avoid allocating - * and make one here. */ + * and make one here. + */ GSList list_tail{(void*)option.get_name().c_str(), nullptr}; GSList list_head{(void*)s_name.c_str(), &list_tail}; auto kvp = qof_book_get_option(book, &list_head); @@ -670,7 +676,7 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept case KvpValue::Type::GUID: { auto guid{kvp->get()}; - option.set_value(qof_instance_from_guid(guid, option.get_ui_type())); + option.set_value((const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type())); break; } default: @@ -723,7 +729,7 @@ gnc_register_budget_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, GncBudget *value) { - GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOption option{section, name, key, doc_string, (const QofInstance*)value, GncOptionUIType::BUDGET}; db->register_option(section, std::move(option)); } @@ -743,7 +749,7 @@ gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { - GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOption option{section, name, key, doc_string, (const QofInstance*)value, GncOptionUIType::COMMODITY}; db->register_option(section, std::move(option)); } @@ -880,7 +886,8 @@ gnc_register_list_option(const GncOptionDBPtr& db, const char* section, } /* Only balance-forecast.scm, hello-world.scm, and net-charts.scm - * use decimals and fractional steps and they can be worked around. */ + * use decimals and fractional steps and they can be worked around. + */ void gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, @@ -908,7 +915,7 @@ gnc_register_query_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, QofQuery* value) { - GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOption option{section, name, key, doc_string, (const QofInstance*)value, GncOptionUIType::INTERNAL}; db->register_option(section, std::move(option)); } @@ -928,7 +935,7 @@ gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, GncInvoice* value) { - GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOption option{section, name, key, doc_string, (const QofInstance*)value, GncOptionUIType::INVOICE}; db->register_option(section, std::move(option)); } @@ -938,7 +945,7 @@ gnc_register_owner_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, GncOwner* value) { - GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOption option{section, name, key, doc_string, (const QofInstance*)value, GncOptionUIType::OWNER}; db->register_option(section, std::move(option)); } @@ -948,7 +955,7 @@ gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, GncTaxTable* value) { - GncOption option{section, name, key, doc_string, QOF_INSTANCE(value), + GncOption option{section, name, key, doc_string, (const QofInstance*)value, GncOptionUIType::TAX_TABLE}; db->register_option(section, std::move(option)); } @@ -989,9 +996,9 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { - GncOption option{GncOptionValidatedValue{ - section, name, key, doc_string, QOF_INSTANCE(value), - [](QofInstance* new_value) -> bool + GncOption option{GncOptionValidatedValue{ + section, name, key, doc_string, (const QofInstance*)value, + [](const QofInstance* new_value) -> bool { return GNC_IS_COMMODITY (new_value) && gnc_commodity_is_currency(GNC_COMMODITY(new_value)); diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 95419f4fce..f29e81774f 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -24,102 +24,33 @@ #ifndef GNC_OPTIONDB_HPP_ #define GNC_OPTIONDB_HPP_ -#include "gnc-option.hpp" +#include #include #include #include #include extern "C" { +#include +#include +#include +#include #include #include #include } +#include "gnc-option.hpp" +#include + class GncOptionDB; +using GncOptionAccountList = std::vector; -using GncOptionVec = std::vector; -using GncOptionSection = std::pair; -class GncOptionDB -{ -public: - GncOptionDB(); - GncOptionDB(QofBook* book); - ~GncOptionDB() = default; - - void save_to_book(QofBook* book, bool do_clear) const; - int 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 unregister_option(const char* section, const char* name); - void set_default_section(const char* section); - const GncOptionSection* const get_default_section() const noexcept; - void set_ui_item(const char* section, const char* name, GncOptionUIItem* ui_item); - GncOptionUIItem* const get_ui_item(const char* section, const char* name); - GncOptionUIType get_ui_type(const char* section, const char* name); - void set_ui_from_option(const char* section, const char* name, - std::function func); - void set_option_from_ui(const char* section, const char* name, - std::function func); - 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->get().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() {}; - std::optional> find_section(const std::string& section); - std::optional> find_option(const std::string& section, const std::string& name) { - return static_cast(*this).find_option(section, name); - } - std::optional> find_option(const std::string& section, const std::string& name) const; - std::ostream& save_to_scheme(std::ostream& oss, - const char* options_prolog) const noexcept; - std::istream& load_from_scheme(std::istream& iss) noexcept; - 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_scheme(std::ostream& oss, - const char* option_prolog, - const std::string& section, - const std::string& name) const noexcept; - std::istream& load_option_scheme(std::istream& iss); - 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); -private: - std::optional> m_default_section; - std::vector m_sections; - bool m_dirty = false; - - std::function m_get_ui_value; - std::function m_set_ui_value; - static constexpr char const* const scheme_tags[] - { - "(let ((option (gnc:lookup-option ", - " ", - ")))", - " ((lambda (o) (if o (gnc:option-set-value o ", - "))) option))" - }; -}; +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 diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 9bad4093f3..08f0251951 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -46,6 +46,7 @@ extern "C" #include } #include "gnc-optiondb.hpp" +#include "gnc-optiondb-impl.hpp" extern "C" SCM scm_init_sw_gnc_optiondb_module(void); %} @@ -299,28 +300,30 @@ using Account = struct account_s; wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %include "gnc-option.hpp" +%include "gnc-option-impl.hpp" %include "gnc-optiondb.hpp" +%include "gnc-optiondb-impl.hpp" %extend GncOption { - SCM get_scm_value() const + SCM get_scm_value() { return std::visit([](const auto& option)->SCM { auto value{option.get_value()}; return scm_from_value(static_cast(value)); - }, $self->_get_option()); + }, *($self->_get_option())); } - SCM get_scm_default_value() const + SCM get_scm_default_value() { return std::visit([](const auto& option)->SCM { auto value{option.get_default_value()}; return scm_from_value(static_cast(value)); - }, $self->_get_option()); + }, *($self->_get_option())); } void set_value_from_scm(SCM new_value) { std::visit([new_value](auto& option) { option.set_value(scm_to_value>(new_value)); - }, $self->_get_option()); + }, *($self->_get_option())); } }; @@ -331,6 +334,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); }; %inline %{ + using GncOptionDBPtr = std::unique_ptr; + static SCM gnc_option_value(const GncOptionDBPtr& optiondb, const char* section, const char* name) diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index 628207f7ee..deb1da2e9d 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -31,6 +31,7 @@ add_app_utils_test(test-sx test-sx.cpp) set(gtest_gnc_option_SOURCES ${MODULEPATH}/gnc-option.cpp + ${MODULEPATH}/gnc-option-impl.cpp ${MODULEPATH}/gnc-optiondb.cpp gtest-gnc-option.cpp gtest-gnc-optiondb.cpp) @@ -98,6 +99,7 @@ if (HAVE_SRFI64) ) add_library(swig-gnc-optiondb MODULE ${MODULEPATH}/gnc-option.cpp + ${MODULEPATH}/gnc-option-impl.cpp ${MODULEPATH}/gnc-optiondb.cpp ${SWIG_GNC_OPTIONDB_GUILE_CPP} ) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 99ae8897a4..08aa941b65 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -23,9 +23,15 @@ #include #include +#include #include extern "C" { +#include +#include +#include +#include +#include #include #include #include @@ -207,7 +213,7 @@ TEST_F(GncOptionTest, test_budget_ctor) auto budget = gnc_budget_new(m_book); EXPECT_NO_THROW({ GncOption option("foo", "bar", "baz", "Phony Option", - QOF_INSTANCE(budget)); + (const QofInstance*)budget); }); gnc_budget_destroy(budget); } @@ -215,7 +221,7 @@ TEST_F(GncOptionTest, test_budget_ctor) TEST_F(GncOptionTest, test_budget_out) { auto budget = gnc_budget_new(m_book); - GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(budget)}; + GncOption option{"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; @@ -229,16 +235,16 @@ 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{GncOptionValue{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}}; + GncOption option{GncOptionValue{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}}; iss >> option; - EXPECT_EQ(QOF_INSTANCE(budget), option.get_value()); + EXPECT_EQ(QOF_INSTANCE(budget), option.get_value()); gnc_budget_destroy(budget); } TEST_F(GncOptionTest, test_budget_to_scheme) { auto budget = gnc_budget_new(m_book); - GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(budget)}; + GncOption option{"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; @@ -256,9 +262,9 @@ TEST_F(GncOptionTest, test_budget_from_scheme) budget_guid.insert(0, "\""); budget_guid += "\""; std::istringstream iss{budget_guid}; - GncOption option{GncOptionValue{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}}; + GncOption option{GncOptionValue{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}}; option.from_scheme(iss); - EXPECT_EQ(QOF_INSTANCE(budget), option.get_value()); + EXPECT_EQ(QOF_INSTANCE(budget), option.get_value()); gnc_budget_destroy(budget); } @@ -268,7 +274,7 @@ TEST_F(GncOptionTest, test_commodity_ctor) "NYSE", "HPE", NULL, 1); EXPECT_NO_THROW({ GncOption option("foo", "bar", "baz", "Phony Option", - QOF_INSTANCE(hpe)); + (const QofInstance*)hpe); }); gnc_commodity_destroy(hpe); } @@ -320,9 +326,9 @@ 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{GncOptionValidatedValue{ - section, name, key, doc_string, QOF_INSTANCE(value), - [](QofInstance* new_value) -> bool + GncOption option{GncOptionValidatedValue{ + section, name, key, doc_string, (const QofInstance*)value, + [](const QofInstance* new_value) -> bool { return GNC_IS_COMMODITY (new_value) && gnc_commodity_is_currency(GNC_COMMODITY(new_value)); @@ -349,25 +355,26 @@ TEST_F(GncOptionCommodityTest, test_currency_ctor) TEST_F(GncOptionCommodityTest, test_currency_setter) { - auto option = make_currency_option("foo", "bar", "baz", "Phony Option", m_eur, true); + auto option = make_currency_option("foo", "bar", "baz", "Phony Option", + m_eur, true); EXPECT_NO_THROW({ - option.set_value(QOF_INSTANCE(m_usd)); + option.set_value((const QofInstance*)m_usd); }); EXPECT_PRED2(gnc_commodity_equal, m_usd, - GNC_COMMODITY(option.get_value())); + GNC_COMMODITY(option.get_value())); EXPECT_THROW({ - option.set_value(QOF_INSTANCE(m_hpe)); + option.set_value((const QofInstance*)m_hpe); }, std::invalid_argument); EXPECT_PRED2(gnc_commodity_equal, m_usd, - GNC_COMMODITY(option.get_value())); + 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(QOF_INSTANCE(m_usd))); - EXPECT_FALSE(option.validate(QOF_INSTANCE(m_aapl))); + m_eur, true); + EXPECT_TRUE(option.validate((const QofInstance*)m_usd)); + EXPECT_FALSE(option.validate((const QofInstance*)m_aapl)); } static inline std::string make_currency_str(gnc_commodity* cur) @@ -416,7 +423,7 @@ TEST_F(GncOptionCommodityTest, test_currency_out) TEST_F(GncOptionCommodityTest, test_commodity_out) { - GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_hpe), + GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe, GncOptionUIType::COMMODITY}; std::string hpe_str{make_commodity_str(m_hpe)}; std::ostringstream oss; @@ -439,19 +446,19 @@ TEST_F(GncOptionCommodityTest, test_currency_in) std::string usd_str{make_currency_str(m_usd)}; std::istringstream iss{usd_str}; iss >> option; - EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value()); + EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value()); }); } TEST_F(GncOptionCommodityTest, test_commodity_in) { - GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_aapl), + GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl, GncOptionUIType::COMMODITY}; std::string hpe_str{make_commodity_str(m_hpe)}; std::istringstream iss{hpe_str}; iss >> option; - EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); + EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); } TEST_F(GncOptionCommodityTest, test_currency_to_scheme) @@ -467,7 +474,7 @@ TEST_F(GncOptionCommodityTest, test_currency_to_scheme) TEST_F(GncOptionCommodityTest, test_commodity_to_scheme) { - GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_hpe), + GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe, GncOptionUIType::COMMODITY}; std::string hpe_str{make_commodity_SCM_str(m_hpe)}; @@ -484,18 +491,18 @@ TEST_F(GncOptionCommodityTest, test_currency_from_scheme) std::string usd_str{make_currency_SCM_str(m_usd)}; std::istringstream iss{usd_str}; option.from_scheme(iss); - EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value()); + EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value()); } TEST_F(GncOptionCommodityTest, test_commodity_from_scheme) { - GncOption option{"foo", "bar", "baz", "Phony Option", QOF_INSTANCE(m_aapl), + GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl, GncOptionUIType::COMMODITY}; std::string hpe_str{make_commodity_SCM_str(m_hpe)}; std::istringstream iss{hpe_str}; option.from_scheme(iss); - EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); + EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); } class GncUIItem diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 2be2c3e7be..5d3db80210 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -23,6 +23,7 @@ #include #include +#include #include extern "C" { @@ -140,7 +141,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option) 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().get_value().size()); + EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value().size()); EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value().at(3)); } @@ -152,7 +153,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option) 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().get_value().size()); + EXPECT_EQ(1, m_db->find_option("foo", "bar")->get().get_value().size()); EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value().at(0)); } @@ -408,7 +409,7 @@ TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input) EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value()[0]); m_db->load_option_scheme(iss); EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value()[1]); - EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value().size()); + EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value().size()); } @@ -448,7 +449,7 @@ TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input) EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value()); EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value()[1]); - EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value().size()); + EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value().size()); } diff --git a/po/POTFILES.in b/po/POTFILES.in index fce91bb811..39773851c6 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -529,6 +529,7 @@ libgnucash/app-utils/gnc-helpers.c libgnucash/app-utils/gnc-help-utils.c libgnucash/app-utils/gnc-option.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 From 81c5ac66897fe7cdc5c8e078911b2bd4333df157 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 8 Feb 2020 15:54:34 -0800 Subject: [PATCH 077/298] Remove the incomplete book-currency code. Not everything from the 6 Book-Currency commits is removed: Switching the Num and split-action fields and restricting edits of transactions older than n days were included and those changes are left in place. Some other partly-implemented features were also part of these commits and were removed: Options for setting a default capital gains account and currency, completion of the LIFO cap-gains policy, and creation of a list of cap-gains policies. If any of these are to be revived they should each be done in a separate feature branch and submitted via Github pull request for a code review before merging; a design discussion on gnucash-devel before restarting work is also advisable. --- bindings/engine.i | 4 - gnucash/gnome-utils/dialog-options.c | 213 +-------------- gnucash/gnome-utils/dialog-utils.c | 45 ---- gnucash/gnome-utils/gnc-main-window.c | 28 -- gnucash/gnome/assistant-hierarchy.c | 23 +- libgnucash/app-utils/app-utils.scm | 40 +++ libgnucash/app-utils/gnc-ui-util.c | 119 --------- libgnucash/app-utils/gnc-ui-util.h | 34 --- libgnucash/app-utils/option-util.c | 194 +------------- libgnucash/app-utils/option-util.h | 93 ------- libgnucash/app-utils/options.scm | 264 ------------------- libgnucash/app-utils/test/CMakeLists.txt | 2 +- libgnucash/app-utils/test/test-app-utils.c | 1 - libgnucash/app-utils/test/test-gnc-ui-util.c | 262 ------------------ libgnucash/engine/engine-helpers.c | 16 -- libgnucash/engine/engine-helpers.h | 5 - libgnucash/engine/gnc-features.c | 1 - libgnucash/engine/gnc-features.h | 1 - libgnucash/engine/policy-p.h | 3 - libgnucash/engine/policy.c | 83 ------ libgnucash/engine/policy.h | 34 +-- libgnucash/engine/qofbook.cpp | 120 --------- libgnucash/engine/qofbook.h | 21 -- libgnucash/engine/qofbookslots.h | 9 - libgnucash/engine/test/test-qofbook.c | 106 -------- 25 files changed, 60 insertions(+), 1661 deletions(-) delete mode 100644 libgnucash/app-utils/test/test-gnc-ui-util.c diff --git a/bindings/engine.i b/bindings/engine.i index e7af4047a9..c7c6866993 100644 --- a/bindings/engine.i +++ b/bindings/engine.i @@ -396,10 +396,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/gnucash/gnome-utils/dialog-options.c b/gnucash/gnome-utils/dialog-options.c index e3f9d27e6c..ec0a9fda23 100644 --- a/gnucash/gnome-utils/dialog-options.c +++ b/gnucash/gnome-utils/dialog-options.c @@ -66,7 +66,6 @@ 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" @@ -82,8 +81,6 @@ static QofLogModule log_module = GNC_MOD_GUI; /* 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; @@ -127,43 +124,12 @@ enum page_tree 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 * @@ -520,6 +486,7 @@ gnc_option_radiobutton_cb (GtkWidget *w, gpointer data) gnc_option_changed_widget_cb (widget, option); } +<<<<<<< HEAD static gboolean gnc_gain_loss_account_view_filter (Account *account, gpointer data) { @@ -975,6 +942,8 @@ gnc_option_currency_accounting_book_cb (GtkWidget *widget, gpointer data) gtk_widget_set_sensitive (book_currency_data->book_currency_vbox, TRUE); gnc_option_radiobutton_cb (widget, (gpointer) book_currency_data->option); } +======= +>>>>>>> f87d57145 (Remove the incomplete book-currency code.) static GtkWidget * gnc_option_create_date_widget (GNCOption *option) @@ -1186,6 +1155,7 @@ gnc_option_create_radiobutton_widget (char *name, GNCOption *option) return frame; } +<<<<<<< HEAD static GtkWidget * gnc_option_create_currency_accounting_widget (char *name, GNCOption *option) { @@ -1358,6 +1328,8 @@ gnc_option_create_currency_accounting_widget (char *name, GNCOption *option) return frame; } +======= +>>>>>>> f87d57145 (Remove the incomplete book-currency code.) static void gnc_option_account_cb (GtkTreeSelection *selection, gpointer data) { @@ -2161,27 +2133,6 @@ component_close_handler (gpointer data) 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 @@ -2294,21 +2245,11 @@ gnc_options_dialog_new_modal (gboolean modal, gchar *title, 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); + NULL, 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); @@ -3106,28 +3047,6 @@ gnc_option_set_ui_widget_budget (GNCOption *option, GtkGrid *page_box, 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 * ************************* @@ -3617,113 +3536,6 @@ gnc_option_set_ui_value_plot_size (GNCOption *option, gboolean use_default, 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 * ************************* @@ -4045,6 +3857,7 @@ gnc_option_get_ui_value_plot_size (GNCOption *option, GtkWidget *widget) return scm_cons (type, val); } +<<<<<<< HEAD static SCM gnc_option_get_ui_value_currency_accounting (GNCOption *option, GtkWidget *widget) @@ -4148,6 +3961,8 @@ gnc_option_get_ui_value_currency_accounting (GNCOption *option, return (scm_cons (gnc_option_permissible_value (option, index), value)); } +======= +>>>>>>> f87d57145 (Remove the incomplete book-currency code.) /************************************/ /* INITIALIZATION */ /************************************/ @@ -4228,12 +4043,6 @@ static void gnc_options_initialize_options (void) "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; diff --git a/gnucash/gnome-utils/dialog-utils.c b/gnucash/gnome-utils/dialog-utils.c index eddb02c3ce..622386db41 100644 --- a/gnucash/gnome-utils/dialog-utils.c +++ b/gnucash/gnome-utils/dialog-utils.c @@ -820,51 +820,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-main-window.c b/gnucash/gnome-utils/gnc-main-window.c index f9ceea9852..ddea6b2f23 100644 --- a/gnucash/gnome-utils/gnc-main-window.c +++ b/gnucash/gnome-utils/gnc-main-window.c @@ -4169,12 +4169,9 @@ 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; @@ -4199,7 +4196,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; @@ -4211,11 +4207,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; @@ -4264,25 +4255,6 @@ 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) diff --git a/gnucash/gnome/assistant-hierarchy.c b/gnucash/gnome/assistant-hierarchy.c index 6d39f37ad3..aa8c59a0e7 100644 --- a/gnucash/gnome/assistant-hierarchy.c +++ b/gnucash/gnome/assistant-hierarchy.c @@ -1473,24 +1473,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); } } diff --git a/libgnucash/app-utils/app-utils.scm b/libgnucash/app-utils/app-utils.scm index b9f218a367..94bcfa55df 100644 --- a/libgnucash/app-utils/app-utils.scm +++ b/libgnucash/app-utils/app-utils.scm @@ -33,6 +33,46 @@ (gnucash app-utils options) (gnucash app-utils c-interface)) +(export gnc:make-internal-option) +(export gnc:make-query-option) +(export gnc:make-color-option) +(export gnc:make-dateformat-option) +(export gnc:dateformat-get-format) + +(export gnc:color->html) +(export gnc:color-option->html) +(export gnc:color-option->hex-string) +(export gnc:new-options) + +(export gnc:register-option) +(export gnc:unregister-option) +(export gnc:options-register-callback) +(export gnc:options-register-c-callback) +(export gnc:options-unregister-callback-id) +(export gnc:options-for-each) +(export gnc:options-for-each-general) +(export gnc:lookup-option) +(export gnc:generate-restore-forms) +(export gnc:options-fancy-date) +(export gnc:options-scm->kvp) +(export gnc:options-kvp->scm) +(export gnc:options-clear-changes) +(export gnc:options-touch) +(export gnc:options-run-callbacks) +(export gnc:options-set-default-section) +(export gnc:options-get-default-section) +(export gnc:options-copy-values) +(export gnc:send-options) + +(define (gnc:option-get-value book category key) + ;;Access an option directly + (qof-book-get-option book + (if (list? key) + (append (list category) key) + (list category key)))) +(export gnc:option-get-value) + +;; gw-engine-spec.scm (re-export HOOK-SAVE-OPTIONS) (export gnc:get-debit-string) diff --git a/libgnucash/app-utils/gnc-ui-util.c b/libgnucash/app-utils/gnc-ui-util.c index aea6bbba3a..6deba2fa4e 100644 --- a/libgnucash/app-utils/gnc-ui-util.c +++ b/libgnucash/app-utils/gnc-ui-util.c @@ -438,122 +438,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) { @@ -1168,9 +1052,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 b9bb385c60..7a56a0a0b8 100644 --- a/libgnucash/app-utils/gnc-ui-util.h +++ b/libgnucash/app-utils/gnc-ui-util.h @@ -97,40 +97,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 index 01c475204e..04ff31ea0d 100644 --- a/libgnucash/app-utils/option-util.c +++ b/libgnucash/app-utils/option-util.c @@ -106,15 +106,6 @@ struct _Getters 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; }; @@ -565,24 +556,6 @@ initialize_getters(void) 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; } @@ -728,7 +701,7 @@ gnc_option_default_getter (GNCOption *option) * Args: option - the GNCOption * * Returns: SCM handle to function * \********************************************************************/ -static SCM +SCM gnc_option_value_validator(GNCOption *option) { initialize_getters (); @@ -749,7 +722,7 @@ gnc_option_value_validator(GNCOption *option) * Returns: SCM handle to function * * If no such function exists, returns SCM_UNDEFINED. * \********************************************************************/ -static SCM +SCM gnc_option_widget_changed_proc_getter(GNCOption *option) { SCM cb; @@ -2133,169 +2106,6 @@ gnc_plot_size_option_value_get_value (SCM option_value) 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) { diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h index c059e18962..23742f011e 100644 --- a/libgnucash/app-utils/option-util.h +++ b/libgnucash/app-utils/option-util.h @@ -201,89 +201,6 @@ guint32 gnc_option_db_lookup_color_option_argb (GNCOptionDB *odb, const char *name, guint32 default_value); -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_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); -char * gnc_option_permissible_value_description(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); -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); - -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); - -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); - -gdouble gnc_option_db_lookup_number_option(GNCOptionDB *odb, - const char *section, - const char *name, - gdouble default_value); - gboolean gnc_option_db_set_option(GNCOptionDB *odb, const char *section, const char *name, @@ -313,16 +230,6 @@ 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, diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 0b64b1f818..2188c4eaa0 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -141,11 +141,6 @@ (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 (gnc:option-get-value book category key) (define acc (if (pair? key) cons list)) @@ -1529,262 +1524,6 @@ the option '~a'.")) (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)) @@ -1968,11 +1707,8 @@ the option '~a'.")) (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))))))))) diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index deb1da2e9d..0bbf98d3ce 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -12,7 +12,7 @@ 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) +set(test_app_utils_SOURCES test-app-utils.c test-option-util.cpp) macro(add_app_utils_test _TARGET _SOURCE_FILES) gnc_add_test(${_TARGET} "${_SOURCE_FILES}" APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS) diff --git a/libgnucash/app-utils/test/test-app-utils.c b/libgnucash/app-utils/test/test-app-utils.c index 17289e77fc..ae63a01cf2 100644 --- a/libgnucash/app-utils/test/test-app-utils.c +++ b/libgnucash/app-utils/test/test-app-utils.c @@ -35,7 +35,6 @@ guile_main (void *closure, int argc, char **argv) scm_c_use_module("gnucash app-utils"); test_suite_option_util (); - test_suite_gnc_ui_util (); retval = g_test_run (); exit (retval); 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/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-features.c b/libgnucash/engine/gnc-features.c index 10ea1009cd..7521c55e0f 100644 --- a/libgnucash/engine/gnc-features.c +++ b/libgnucash/engine/gnc-features.c @@ -44,7 +44,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 030c02531e..366de78caf 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/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 c768296ea1..53873e8e94 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) diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h index f48ed94002..b4401382b1 100644 --- a/libgnucash/engine/qofbook.h +++ b/libgnucash/engine/qofbook.h @@ -267,27 +267,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); diff --git a/libgnucash/engine/qofbookslots.h b/libgnucash/engine/qofbookslots.h index 10e1d6450b..4ec7b0f301 100644 --- a/libgnucash/engine/qofbookslots.h +++ b/libgnucash/engine/qofbookslots.h @@ -64,10 +64,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 +76,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 ); From cbf7d70ecdcf60615c3748aa30df7df6496929f8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 13 Feb 2020 10:36:54 -0800 Subject: [PATCH 078/298] Integrate gnc-option into app-utils. Includes converting swig-app-utils-guile to c++. --- libgnucash/app-utils/CMakeLists.txt | 20 +++-- libgnucash/app-utils/app-utils.i | 16 +++- libgnucash/app-utils/gnc-optiondb.i | 77 ++++++++++--------- libgnucash/app-utils/test/CMakeLists.txt | 45 +---------- .../app-utils/test/test-gnc-optiondb.scm | 8 +- 5 files changed, 75 insertions(+), 91 deletions(-) diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt index 7a0df5d351..0f05e930a8 100644 --- a/libgnucash/app-utils/CMakeLists.txt +++ b/libgnucash/app-utils/CMakeLists.txt @@ -11,6 +11,8 @@ set (app_utils_noinst_HEADERS calculation/finproto.h calculation/fin_spl_protos.h calculation/fin_static_proto.h + gnc-option-impl.hpp + gnc-optiondb-impl.hpp ) set (app_utils_HEADERS @@ -27,8 +29,10 @@ set (app_utils_HEADERS gnc-gsettings.h gnc-help-utils.h gnc-helpers.h + gnc-option.hpp + 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 @@ -36,10 +40,13 @@ set (app_utils_HEADERS ) # 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 + SWIG_APP_UTILS_GUILE_CPP swig-app-utils-guile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/app-utils.i + ${CMAKE_CURRENT_SOURCE_DIR}/gnc-optiondb.i "" ) +unset(SWIG_ARGS) # Command to generate the swig-app-utils-python.c wrapper file gnc_add_swig_python_command (swig-app-utils-python @@ -62,6 +69,9 @@ set (app_utils_SOURCES gnc-exp-parser.c gnc-gsettings.c gnc-helpers.c + gnc-option.cpp + gnc-option-impl.cpp + gnc-optiondb.cpp gnc-prefs-utils.c gnc-sx-instance-model.c gnc-state.c @@ -103,7 +113,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 diff --git a/libgnucash/app-utils/app-utils.i b/libgnucash/app-utils/app-utils.i index 28730bc614..274bc696f8 100644 --- a/libgnucash/app-utils/app-utils.i +++ b/libgnucash/app-utils/app-utils.i @@ -21,6 +21,10 @@ %module sw_app_utils %{ /* Includes the header in the wrapper code */ +#ifdef __cplusplus +extern "C" +{ +#endif #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) @@ -76,7 +88,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); } diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 08f0251951..4aab780759 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -3,6 +3,7 @@ * * 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; @@ -37,17 +38,11 @@ namespace std { %enddef -%module sw_gnc_optiondb + //%module sw_gnc_optiondb %{ -extern "C" -{ -#include -#include -#include -} #include "gnc-optiondb.hpp" #include "gnc-optiondb-impl.hpp" -extern "C" SCM scm_init_sw_gnc_optiondb_module(void); +SCM scm_init_sw_gnc_optiondb_module(void); %} %include @@ -118,27 +113,6 @@ scm_from_value(const QofInstance* value) return scm_guid; } -/* Account is actually a typedef for struct account_s and SWIG insists on using - * the struct name (i.e. account_s) in C++ and the alias (i.e. Account) in - * C. Oddly the compiler's type resolution also fails to consider them the same - * so we have to use the struct name here to get the template to resolve - * correctly. - */ -using GncOptionAccount_sList = std::vector; - -template <>inline SCM -scm_from_value(GncOptionAccount_sList value) -{ - SCM s_list = SCM_EOL; - for (auto acct : value) - { - SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0)); - s_list = scm_append(scm_list_2(s_list, elem)); - } - - return s_list; -} - template inline ValueType scm_to_value(SCM new_value) { @@ -166,19 +140,34 @@ scm_to_value(SCM new_value) return scm_to_int64(new_value); } +template <>inline SCM +scm_from_value(GncOptionAccountList value) +{ + SCM s_list = SCM_EOL; + for (auto acct : value) + { + SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0)); + s_list = scm_append(scm_list_2(s_list, elem)); + } + + return s_list; +} + +QofBook* gnc_option_test_book_new(); +void gnc_option_test_book_destroy(QofBook*); + QofBook* -qof_book_new() +gnc_option_test_book_new() { return static_cast(g_object_new(QOF_TYPE_BOOK, nullptr)); } void -qof_book_destroy(QofBook* book) +gnc_option_test_book_destroy(QofBook* book) { g_object_unref(book); } -using Account = struct account_s; %} %ignore OptionClassifier; @@ -228,6 +217,19 @@ using Account = struct account_s; $1 = &choices; } + +%typemap(in) GncOptionAccountList +{ + auto len = scm_to_size_t(scm_length($input)); + 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); + $1.push_back(acct); + } +} + %typemap(in) GncOptionAccountTypeList& (GncOptionAccountTypeList types) { auto len = scm_to_size_t(scm_length($input)); @@ -259,7 +261,7 @@ using Account = struct account_s; { SCM s_account = scm_list_ref($input, scm_from_size_t(i)); Account* acct = (Account*)SWIG_MustGetPtr(s_account, - SWIGTYPE_p_account_s, 1, 0); + SWIGTYPE_p_Account, 1, 0); $1.push_back(acct); } } @@ -271,7 +273,7 @@ using Account = struct account_s; { SCM s_account = scm_list_ref($input, scm_from_size_t(i)); Account* acct = (Account*)SWIG_MustGetPtr(s_account, - SWIGTYPE_p_account_s, 1, 0); + SWIGTYPE_p_Account, 1, 0); acclist.push_back(acct); } $1 = &acclist; @@ -282,7 +284,7 @@ using Account = struct account_s; $result = SCM_EOL; for (auto acct : $1) { - SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0)); + SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0)); $result = scm_append(scm_list_2($result, elem)); } } @@ -292,7 +294,7 @@ using Account = struct account_s; $result = SCM_EOL; for (auto acct : *$1) { - SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_account_s, 0)); + SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0)); $result = scm_append(scm_list_2($result, elem)); } } @@ -326,7 +328,6 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); }, *($self->_get_option())); } }; - %extend GncOptionDB { %template(set_option_string) set_option; %template(set_option_int) set_option; @@ -369,3 +370,5 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); GncOption_set_value_from_scm(&(db_opt->get()), new_value); } %} + +#endif //SWIGGUILE diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index 0bbf98d3ce..eb6b27c0e3 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -30,9 +30,6 @@ 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 - ${MODULEPATH}/gnc-option.cpp - ${MODULEPATH}/gnc-option-impl.cpp - ${MODULEPATH}/gnc-optiondb.cpp gtest-gnc-option.cpp gtest-gnc-optiondb.cpp) @@ -91,49 +88,10 @@ if (HAVE_SRFI64) FALSE ) - set(SWIG_ARGS "-c++" "-procdoc" "sw-gnc-option-doc" "-procdocformat" "plain") - gnc_add_swig_guile_command(swig-gnc-optiondb-guile - SWIG_GNC_OPTIONDB_GUILE_CPP swig-gnc-optiondb-guile.cpp - ${MODULEPATH}/gnc-optiondb.i - "" - ) - add_library(swig-gnc-optiondb MODULE - ${MODULEPATH}/gnc-option.cpp - ${MODULEPATH}/gnc-option-impl.cpp - ${MODULEPATH}/gnc-optiondb.cpp - ${SWIG_GNC_OPTIONDB_GUILE_CPP} - ) - set(swig_gnc_optiondb_INCLUDES - ${MODULEPATH} - ${CMAKE_SOURCE_DIR}/bindings/guile - ${CMAKE_SOURCE_DIR}/libgnucash/engine - ${CMAKE_BINARY_DIR}/common # for config.h - ${GLIB2_INCLUDE_DIRS} - ${GUILE_INCLUDE_DIRS} - ) - - set(swig_gnc_optiondb_LIBS - gnc-engine - gnc-app-utils - gnucash-guile - ${GLIB2_LDFLAGS} - ${GUILE_LDFLAGS} - ) - - target_link_libraries(swig-gnc-optiondb ${swig_gnc_optiondb_LIBS}) - target_include_directories(swig-gnc-optiondb - PRIVATE ${swig_gnc_optiondb_INCLUDES}) - - install(TARGETS swig-gnc-optiondb - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - ) - gnc_add_scheme_test_targets(scm-test-gnc-optiondb "test-gnc-optiondb.scm" "tests" - "swig-gnc-optiondb;scm-srfi64-extras" + "swig-apputils-guile-cpp;scm-srfi64-extras" FALSE ) gnc_add_scheme_tests("test-gnc-optiondb.scm") @@ -173,4 +131,5 @@ set_dist_list(test_app_utils_DIST ${test_app_utils_scheme_SOURCES} ${test_app_utils_SOURCES} ${test_autoclear_SOURCES} + ${gtest_gnc_option_SOURCES} ) diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index d1db9d7385..966c5669fc 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -26,10 +26,10 @@ (use-modules (gnucash gnc-module)) (eval-when (compile load eval expand) - (load-extension "libswig-gnc-optiondb" "scm_init_sw_gnc_optiondb_module")) + (load-extension "_sw_app_utils" "scm_init_sw_app_utils_module")) (use-modules (gnucash engine)) -(use-modules (sw_gnc_optiondb)) +(use-modules (sw_app_utils)) (define (run-test) (test-runner-factory gnc:test-runner) @@ -84,7 +84,7 @@ (define (cleanup book root) ;; Destroying the book destroys the account tree too - (qof-book-destroy book)) + (gnc-option-test-book-destroy book)) (define (test-make-account-list-option book) (test-group "test-make-account-list-option" @@ -122,7 +122,7 @@ (let ((acct (gnc-option-value optiondb "salt" "pork"))) (test-equal (list (cadr acctlist)) acct))))) - (let* ((book (qof-book-new)) + (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) From 1bea809cec542cd81f21c94a837b75ca5d22776a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 14 Feb 2020 16:17:41 -0800 Subject: [PATCH 079/298] Remove the UI interface from GncOptionDB. UI operations occur only in dialog-option so there's no need to expose them from GncOptionDB. --- libgnucash/app-utils/gnc-optiondb-impl.hpp | 7 -- libgnucash/app-utils/gnc-optiondb.cpp | 44 ---------- .../app-utils/test/gtest-gnc-optiondb.cpp | 80 ------------------- 3 files changed, 131 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index 6a9c5470af..074b36247c 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -56,13 +56,6 @@ public: void unregister_option(const char* section, const char* name); void set_default_section(const char* section); const GncOptionSection* const get_default_section() const noexcept; - void set_ui_item(const char* section, const char* name, GncOptionUIItem* ui_item); - GncOptionUIItem* const get_ui_item(const char* section, const char* name); - GncOptionUIType get_ui_type(const char* section, const char* name); - void set_ui_from_option(const char* section, const char* name, - std::function func); - void set_option_from_ui(const char* section, const char* name, - std::function func); std::string lookup_string_option(const char* section, const char* name); template diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 9bdb5de8e3..476ee6ede4 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -87,50 +87,6 @@ GncOptionDB::get_default_section() const noexcept return nullptr; } -void -GncOptionDB::set_ui_item(const char* section, const char* name, - GncOptionUIItem* ui_item) -{ - auto option = find_option(section, name); - if (!option) return; - option->get().set_ui_item(ui_item); -} - -GncOptionUIItem* const -GncOptionDB::get_ui_item(const char* section, const char* name) -{ - auto option = find_option(section, name); - if (!option) return nullptr; - return option->get().get_ui_item(); -} - -GncOptionUIType -GncOptionDB::get_ui_type(const char* section, const char* name) -{ - auto option = find_option(section, name); - if (!option) return GncOptionUIType::INTERNAL; - return option->get().get_ui_type(); -} - -void -GncOptionDB::set_ui_from_option(const char* section, const char* name, - std::function func) -{ - auto option = find_option(section, name); - if (!option) return; - func(option->get()); -} - -void -GncOptionDB::set_option_from_ui(const char* section, const char* name, - std::function func) -{ - auto option = find_option(section, name); - if (!option) return; - func(option->get()); -} - - std::optional> GncOptionDB::find_section(const std::string& section) { diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 5d3db80210..1c7ee7de42 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -205,86 +205,6 @@ TEST_F(GncOptionDBTest, test_register_date_interval_option) ASSERT_TRUE(m_db->set_option("foo", "bar", time)); EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value()); } - -class GncUIType -{ -public: - void set_value(const std::string& value) { m_value = value; } - const std::string& get_value() const { return m_value; } -private: - std::string m_value; -}; - -class GncOptionUIItem -{ -public: - GncOptionUIItem(GncUIType* widget) : m_widget{widget} {} - GncUIType* m_widget; -}; - -class GncOptionUITest : public ::testing::Test -{ -protected: - GncOptionUITest() : - m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}, - GncOptionUIType::STRING} {} - - GncOption m_option; -}; - -class GncOptionDBUITest : public ::testing::Test -{ -protected: - GncOptionDBUITest() : m_db{gnc_option_db_new()} - { - 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"}); - } - - GncOptionDBPtr m_db; -}; - -TEST_F(GncOptionDBUITest, test_set_ui_item) -{ - GncUIType entry; - GncOptionUIItem ui_item(&entry); - m_db->set_ui_item("foo", "bar", &ui_item); - EXPECT_EQ(&entry, m_db->get_ui_item("foo", "bar")->m_widget); -} - -TEST_F(GncOptionDBUITest, test_ui_value_from_option) -{ - GncUIType entry; - GncOptionUIItem ui_item(&entry); - const char* value{"waldo"}; - m_db->set_ui_item("foo", "bar", &ui_item); - m_db->set_ui_from_option("foo", "bar", [](GncOption& option){ - auto new_ui_item = option.get_ui_item(); - new_ui_item->m_widget->set_value(option.get_value()); - }); - EXPECT_STREQ(value, entry.get_value().c_str()); -} - -TEST_F(GncOptionDBUITest, test_option_value_from_ui) -{ - GncUIType entry; - GncOptionUIItem ui_item(&entry); - const char* value{"pepper"}; - m_db->set_ui_item("foo", "bar", &ui_item); - entry.set_value(value); - m_db->set_option_from_ui("foo", "bar", [](GncOption& option){ - auto new_ui_item = option.get_ui_item()->m_widget; - option.set_value(new_ui_item->get_value()); - }); - EXPECT_STREQ(value, m_db->lookup_string_option("foo", "bar").c_str()); -} - class GncOptionDBIOTest : public ::testing::Test { protected: From 99c2c5e4395a92dff6da76b6cdac2b218f8e2e4b Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 11 Feb 2020 13:35:27 -0800 Subject: [PATCH 080/298] GncOptionUIItem from the GncOptionVariant classes to GncOption. Separating the UI from the data model. Note that the GncOptionVariant classes still have a GncOptionUIType member to ensure that a GncOptionUIItem of the right type is attached. --- libgnucash/app-utils/gnc-option-impl.hpp | 111 ++++++------------ libgnucash/app-utils/gnc-option-ui.hpp | 93 +++++++++++++++ libgnucash/app-utils/gnc-option.cpp | 54 +++++++-- libgnucash/app-utils/gnc-option.hpp | 10 +- libgnucash/app-utils/gnc-optiondb.cpp | 1 + libgnucash/app-utils/gnc-optiondb.hpp | 1 + libgnucash/app-utils/gnc-optiondb.i | 4 + .../app-utils/test/gtest-gnc-option.cpp | 50 ++++++-- .../app-utils/test/gtest-gnc-optiondb.cpp | 2 + 9 files changed, 225 insertions(+), 101 deletions(-) create mode 100644 libgnucash/app-utils/gnc-option-ui.hpp diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 71e9d49245..adc47a40fa 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -1,5 +1,5 @@ /********************************************************************\ - * gnc-option-impl.hpp -- Application options system * + * gnc-option-impl.hpp -- Application options system * * Copyright (C) 2019 John Ralls * * * * This program is free software; you can redistribute it and/or * @@ -107,62 +107,12 @@ struct OptionClassifier std::string m_doc_string; }; -class GncOptionUIItem; - -/** - * 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 OptionUIItem -{ -public: - GncOptionUIType get_ui_type() const { return m_ui_type; } - GncOptionUIItem* const get_ui_item() const {return m_ui_item; } - void clear_ui_item() { m_ui_item = nullptr; } - void set_ui_item(GncOptionUIItem* ui_item) - { - if (m_ui_type == GncOptionUIType::INTERNAL) - { - std::string error{"INTERNAL option, setting the UI item forbidden."}; - throw std::logic_error(std::move(error)); - } - m_ui_item = ui_item; - } - void make_internal() - { - if (m_ui_item != nullptr) - { - std::string error("Option has a UI Element, can't be INTERNAL."); - throw std::logic_error(std::move(error)); - } - m_ui_type = GncOptionUIType::INTERNAL; - } -protected: - OptionUIItem(GncOptionUIType ui_type) : - m_ui_item{nullptr}, m_ui_type{ui_type} {} - OptionUIItem(const OptionUIItem&) = default; - OptionUIItem(OptionUIItem&&) = default; - ~OptionUIItem() = default; - OptionUIItem& operator=(const OptionUIItem&) = default; - OptionUIItem& operator=(OptionUIItem&&) = default; -private: - GncOptionUIItem* m_ui_item; - GncOptionUIType m_ui_type; -}; - #ifndef SWIG auto constexpr size_t_max = std::numeric_limits::max(); #endif template -class GncOptionValue : public OptionClassifier, public OptionUIItem +class GncOptionValue : public OptionClassifier { public: GncOptionValue(const char* section, const char* name, @@ -170,8 +120,7 @@ public: ValueType value, GncOptionUIType ui_type = GncOptionUIType::INTERNAL) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), - m_value{value}, m_default_value{value} {} + m_ui_type(ui_type), m_value{value}, m_default_value{value} {} GncOptionValue(const GncOptionValue&) = default; GncOptionValue(GncOptionValue&&) = default; GncOptionValue& operator=(const GncOptionValue&) = default; @@ -180,13 +129,16 @@ public: ValueType get_default_value() const { return m_default_value; } void set_value(ValueType new_value) { m_value = new_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; } private: + GncOptionUIType m_ui_type; ValueType m_value; ValueType m_default_value; }; template -class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem +class GncOptionValidatedValue : public OptionClassifier { public: GncOptionValidatedValue() = delete; @@ -197,8 +149,8 @@ public: GncOptionUIType ui_type = GncOptionUIType::INTERNAL ) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem{ui_type}, - m_value{value}, m_default_value{value}, m_validator{validator} + m_ui_type{ui_type}, m_value{value}, m_default_value{value}, + m_validator{validator} { if (!this->validate(value)) throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); @@ -209,7 +161,7 @@ public: std::functionvalidator, ValueType val_data) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem{GncOptionUIType::INTERNAL}, m_value{value}, + m_ui_type{GncOptionUIType::INTERNAL}, m_value{value}, m_default_value{value}, m_validator{validator}, m_validation_data{val_data} { if (!this->validate(value)) @@ -232,7 +184,10 @@ public: bool is_changed() const noexcept { return m_value != m_default_value; } std::ostream& to_scheme(std::ostream&) const; std::istream& from_scheme(std::istream&); + GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } + void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } private: + GncOptionUIType m_ui_type; ValueType m_value; ValueType m_default_value; std::function m_validator; //11 @@ -419,8 +374,7 @@ gnc_option_from_scheme (std::istream& iss, OptType& opt) */ template -class GncOptionRangeValue : - public OptionClassifier, public OptionUIItem +class GncOptionRangeValue : public OptionClassifier { public: GncOptionRangeValue(const char* section, const char* name, @@ -428,7 +382,6 @@ public: ValueType value, ValueType min, ValueType max, ValueType step) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(GncOptionUIType::NUMBER_RANGE), 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} {} @@ -448,7 +401,10 @@ public: throw std::invalid_argument("Validation failed, value not set."); } 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; } private: + GncOptionUIType m_ui_type = GncOptionUIType::NUMBER_RANGE; ValueType m_value; ValueType m_default_value; ValueType m_min; @@ -471,8 +427,7 @@ using GncMultiChoiceOptionChoices = std::vector; * */ -class GncOptionMultichoiceValue : - public OptionClassifier, public OptionUIItem +class GncOptionMultichoiceValue : public OptionClassifier { public: GncOptionMultichoiceValue(const char* section, const char* name, @@ -481,7 +436,7 @@ public: GncMultiChoiceOptionChoices&& choices, GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), + m_ui_type{ui_type}, m_value{}, m_default_value{}, m_choices{std::move(choices)} { if (value) { @@ -543,6 +498,8 @@ public: return std::get<2>(m_choices.at(index)); } 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; } private: std::size_t find_key (const std::string& key) const noexcept { @@ -555,6 +512,7 @@ private: return size_t_max; } + GncOptionUIType m_ui_type; std::size_t m_value; std::size_t m_default_value; GncMultiChoiceOptionChoices m_choices; @@ -581,31 +539,28 @@ using GncOptionAccountTypeList = std::vector; */ -class GncOptionAccountValue : - public OptionClassifier, public OptionUIItem +class GncOptionAccountValue : public OptionClassifier { public: GncOptionAccountValue(const char* section, const char* name, const char* key, const char* doc_string, GncOptionUIType ui_type) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), m_value{}, m_default_value{}, m_allowed{} {} + m_ui_type{ui_type}, m_value{}, m_default_value{}, m_allowed{} {} GncOptionAccountValue(const char* section, const char* name, const char* key, const char* doc_string, GncOptionUIType ui_type, const GncOptionAccountList& value) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), - m_value{value}, + m_ui_type{ui_type}, m_value{value}, m_default_value{std::move(value)}, m_allowed{} {} GncOptionAccountValue(const char* section, const char* name, const char* key, const char* doc_string, GncOptionUIType ui_type, GncOptionAccountTypeList&& allowed) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), - m_value{}, + m_ui_type{ui_type}, m_value{}, m_default_value{}, m_allowed{std::move(allowed)} {} GncOptionAccountValue(const char* section, const char* name, const char* key, const char* doc_string, @@ -613,8 +568,7 @@ public: const GncOptionAccountList& value, GncOptionAccountTypeList&& allowed) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(ui_type), - m_value{}, + m_ui_type{ui_type}, m_value{}, m_default_value{}, m_allowed{std::move(allowed)} { if (!validate(value)) throw std::invalid_argument("Supplied Value not in allowed set."); @@ -631,7 +585,10 @@ public: m_value = values; } 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; } private: + GncOptionUIType m_ui_type; GncOptionAccountList m_value; GncOptionAccountList m_default_value; GncOptionAccountTypeList m_allowed; @@ -727,13 +684,12 @@ gnc-date-option-absolute-time m_type == DateTyupe::Absolute gnc-date-option-relative-time m_type != DateTyupe::Absolute */ -class GncOptionDateValue : public OptionClassifier, public OptionUIItem +class GncOptionDateValue : public OptionClassifier { public: GncOptionDateValue(const char* section, const char* name, const char* key, const char* doc_string) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(GncOptionUIType::DATE), m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, m_date{INT64_MAX}, m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, @@ -742,14 +698,12 @@ public: const char* key, const char* doc_string, time64 time) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(GncOptionUIType::DATE), m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time}, m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {} GncOptionDateValue(const char* section, const char* name, const char* key, const char* doc_string, const RelativeDatePeriod period) : OptionClassifier{section, name, key, doc_string}, - OptionUIItem(GncOptionUIType::DATE), m_period{period}, m_date{INT64_MAX}, m_default_period{period}, m_default_date{INT64_MAX} {} GncOptionDateValue(const GncOptionDateValue&) = default; @@ -770,7 +724,10 @@ public: } 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; } private: + GncOptionUIType m_ui_type = GncOptionUIType::DATE; RelativeDatePeriod m_period; time64 m_date; RelativeDatePeriod m_default_period; diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp new file mode 100644 index 0000000000..ef2a8a4a5b --- /dev/null +++ b/libgnucash/app-utils/gnc-option-ui.hpp @@ -0,0 +1,93 @@ +/********************************************************************\ + * 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 "gnc-option-uitype.hpp" +template +class GncUIItem +{ +public: + GncUIItem(UIType* widget) : m_widget{widget} {} + UIType* m_widget; +}; + +class GncUIType; +using OptionUIItem = GncUIItem; +using OptionSyncFunc = std::function; +/** + * 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(OptionUIItem&& ui_item, GncOptionUIType type, + OptionSyncFunc to_ui, OptionSyncFunc from_ui) : + m_ui_item{std::move(ui_item)}, m_ui_type{type}, + m_set_ui_item_from_option{to_ui}, m_set_option_from_ui_item{from_ui} {} + GncOptionUIItem(GncOptionUIType ui_type) : + m_ui_item{nullptr}, m_ui_type{ui_type} {} + GncOptionUIItem(const GncOptionUIItem&) = default; + GncOptionUIItem(GncOptionUIItem&&) = default; + ~GncOptionUIItem() = default; + GncOptionUIItem& operator=(const GncOptionUIItem&) = default; + GncOptionUIItem& operator=(GncOptionUIItem&&) = default; + GncOptionUIType get_ui_type() const { return m_ui_type; } + const OptionUIItem& get_ui_item() const {return m_ui_item; } + void clear_ui_item() { m_ui_item = nullptr; } + void set_ui_item(OptionUIItem&& ui_item) + { + if (m_ui_type == GncOptionUIType::INTERNAL) + { + std::string error{"INTERNAL option, setting the UI item forbidden."}; + throw std::logic_error(std::move(error)); + } + m_ui_item = std::move(ui_item); + } + void set_ui_item_from_option(GncOption& option) + { + m_set_ui_item_from_option(m_ui_item, option); + } + void set_option_from_ui_item(GncOption& option) + { + m_set_option_from_ui_item(m_ui_item, option); + } +private: + OptionUIItem m_ui_item; + GncOptionUIType m_ui_type; + OptionSyncFunc m_set_ui_item_from_option; + OptionSyncFunc m_set_option_from_ui_item; +}; + +using GncOptionUIItemPtr = std::unique_ptr; + +#endif //GNC_OPTION_UI_HPP__ diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 4218262711..4bedeb4a2a 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -24,6 +24,14 @@ #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 GncOption::GncOption(const char* section, const char* name, @@ -103,11 +111,21 @@ GncOption::get_docstring() const } void -GncOption::set_ui_item(GncOptionUIItem* ui_elem) +GncOption::set_ui_item(GncOptionUIItemPtr&& ui_item) { - std::visit([ui_elem](auto& option) { - option.set_ui_item(ui_elem); - }, *m_option); + + 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); } const GncOptionUIType @@ -118,17 +136,37 @@ GncOption::get_ui_type() const }, *m_option); } -GncOptionUIItem* const +const GncOptionUIItem* GncOption::get_ui_item() const { - return std::visit([](const auto& option)->GncOptionUIItem* { - return option.get_ui_item(); - }, *m_option); + 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); diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 96f0db9844..8a990e3ba4 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -31,6 +31,7 @@ #include "gnc-option-uitype.hpp" class GncOptionUIItem; +using GncOptionUIItemPtr = std::unique_ptr; struct QofInstance_s; using QofInstance = QofInstance_s; template class GncOptionValue; @@ -72,9 +73,11 @@ public: const std::string& get_name() const; const std::string& get_key() const; const std::string& get_docstring() const; - void set_ui_item(GncOptionUIItem* ui_elem); + void set_ui_item(GncOptionUIItemPtr&& ui_elem); const GncOptionUIType get_ui_type() const; - GncOptionUIItem* const get_ui_item() const; + const GncOptionUIItem* get_ui_item() const; + void set_ui_item_from_option(); + void set_option_from_ui_item(); void make_internal(); bool is_changed() const noexcept; template bool validate(ValueType value) const; @@ -87,10 +90,11 @@ public: std::istream& in_stream(std::istream& iss); std::ostream& to_scheme(std::ostream& oss) const; std::istream& from_scheme(std::istream& iss); - GncOptionVariantPtr& _get_option() { return m_option; } + GncOptionVariant* const _get_option() { return m_option.get(); } private: inline static const std::string c_empty_string{""}; GncOptionVariantPtr m_option; + GncOptionUIItemPtr m_ui_item{nullptr}; }; inline std::ostream& diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 476ee6ede4..82735a6102 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -27,6 +27,7 @@ #include #include "gnc-optiondb.hpp" #include "gnc-optiondb-impl.hpp" +#include "gnc-option-ui.hpp" auto constexpr stream_max = std::numeric_limits::max(); GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {} diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index f29e81774f..eebf933f88 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -206,4 +206,5 @@ void gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, RelativeDatePeriod period); + #endif //GNC_OPTIONDB_HPP_ diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 4aab780759..818dd876d5 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -181,6 +181,7 @@ gnc_option_test_book_destroy(QofBook* book) %ignore GncOptionDateValue::operator=(GncOptionDateValue&&); %ignore operator<<(std::ostream&, const GncOption&); %ignore operator>>(std::istream&, GncOption&); +%ignore GncOption::_get_option(); %rename(absolute) RelativeDatePeriod::ABSOLUTE; %rename(today) RelativeDatePeriod::TODAY; @@ -305,6 +306,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %include "gnc-option-impl.hpp" %include "gnc-optiondb.hpp" %include "gnc-optiondb-impl.hpp" +%inline %{ +#include "gnc-option-ui.hpp" +%} %extend GncOption { SCM get_scm_value() diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 08aa941b65..03c4f4dba8 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include extern "C" { @@ -505,7 +506,7 @@ TEST_F(GncOptionCommodityTest, test_commodity_from_scheme) EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); } -class GncUIItem +class GncUIType { public: void set_value(const std::string& value) { m_value = value; } @@ -514,20 +515,29 @@ private: std::string m_value; }; -class GncOptionUIItem -{ -public: - GncOptionUIItem(GncUIItem* widget) : m_widget{widget} {} - GncUIItem* m_widget; -}; +using OptionUIItem = GncUIItem; class GncOptionUITest : public ::testing::Test { protected: GncOptionUITest() : + m_widget{}, m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}, - GncOptionUIType::STRING} {} - + GncOptionUIType::STRING} + { + auto to_ui = [](OptionUIItem& ui, GncOption& opt) { + ui.m_widget->set_value(opt.get_value()); + }; + auto from_ui = [](OptionUIItem& ui, GncOption& opt) { + opt.set_value(ui.m_widget->get_value()); + }; + auto ui_item{std::make_unique( + OptionUIItem{&m_widget}, + GncOptionUIType::STRING, + to_ui, from_ui)}; + m_option.set_ui_item(std::move(ui_item)); + } + GncUIType m_widget; GncOption m_option; }; @@ -540,10 +550,24 @@ TEST_F(GncOptionUI, test_option_ui_type) TEST_F(GncOptionUI, test_set_option_ui_item) { - GncUIItem ui_item; - GncOptionUIItem option_ui_item{&ui_item}; - m_option.set_ui_item(&option_ui_item); - EXPECT_EQ(&ui_item, m_option.get_ui_item()->m_widget); + EXPECT_EQ(&m_widget, m_option.get_ui_item()->get_ui_item().m_widget); +} + +TEST_F(GncOptionUI, test_ui_value_from_option) +{ + const char* value{"waldo"}; + + m_option.set_value(value); + m_option.set_ui_item_from_option(); + EXPECT_STREQ(value, m_widget.get_value().c_str()); +} + +TEST_F(GncOptionUI, test_option_value_from_ui) +{ + const char* value{"pepper"}; + m_widget.set_value(value); + m_option.set_option_from_ui_item(); + EXPECT_STREQ(value, m_option.get_value().c_str()); } class GncOptionRangeTest : public ::testing::Test diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 1c7ee7de42..e0199c8c9b 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include extern "C" { @@ -205,6 +206,7 @@ TEST_F(GncOptionDBTest, test_register_date_interval_option) ASSERT_TRUE(m_db->set_option("foo", "bar", time)); EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value()); } + class GncOptionDBIOTest : public ::testing::Test { protected: From 6c7f976a65dcb952087b1d8f3af62cf38bc053d9 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 16 Feb 2020 15:30:26 -0800 Subject: [PATCH 081/298] Make second arg to gnc_glist_to_scm_list const char* So that C++ won't complain when passed a static string. --- bindings/guile/glib-guile.c | 2 +- bindings/guile/glib-guile.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/guile/glib-guile.c b/bindings/guile/glib-guile.c index 6a36178fd4..cce037272a 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); From 102f36c3be237967842cb6df073fa5a7ffa98b61 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 18 Feb 2020 14:23:59 -0800 Subject: [PATCH 082/298] Provide more than one Date UI type to match options available in dialog-option.c Also differentiates begin-period and end-period controls. --- libgnucash/app-utils/gnc-option-impl.hpp | 23 +++++++++++-------- libgnucash/app-utils/gnc-option-uitype.hpp | 6 ++++- libgnucash/app-utils/gnc-optiondb.cpp | 5 +++- .../app-utils/test/gtest-gnc-option.cpp | 3 ++- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index adc47a40fa..700efaa343 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -680,31 +680,34 @@ gnc_option_from_scheme(std::istream& iss, OptType& opt) 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 == DateTyupe::Absolute -gnc-date-option-relative-time m_type != DateTyupe::Absolute +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) : + const char* key, const char* doc_string, + GncOptionUIType ui_type) : OptionClassifier{section, name, key, doc_string}, - m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, + m_ui_type{ui_type}, m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, m_date{INT64_MAX}, m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, m_default_date{INT64_MAX} {} GncOptionDateValue(const char* section, const char* name, const char* key, const char* doc_string, - time64 time) : + GncOptionUIType ui_type, time64 time) : OptionClassifier{section, name, key, doc_string}, - m_period{RelativeDatePeriod::ABSOLUTE}, m_date{time}, - m_default_period{RelativeDatePeriod::ABSOLUTE}, m_default_date{time} {} + m_ui_type{ui_type}, m_period{RelativeDatePeriod::ABSOLUTE}, + m_date{time}, m_default_period{RelativeDatePeriod::ABSOLUTE}, + m_default_date{time} {} GncOptionDateValue(const char* section, const char* name, const char* key, const char* doc_string, + GncOptionUIType ui_type, const RelativeDatePeriod period) : OptionClassifier{section, name, key, doc_string}, - m_period{period}, m_date{INT64_MAX}, + m_ui_type{ui_type}, m_period{period}, m_date{INT64_MAX}, m_default_period{period}, m_default_date{INT64_MAX} {} GncOptionDateValue(const GncOptionDateValue&) = default; GncOptionDateValue(GncOptionDateValue&&) = default; @@ -712,6 +715,8 @@ public: GncOptionDateValue& operator=(GncOptionDateValue&&) = default; time64 get_value() const; time64 get_default_value() const { return static_cast(GncDateTime()); } + RelativeDatePeriod get_period() const noexcept { return m_period; } + RelativeDatePeriod get_default_period() const noexcept { return m_default_period; } std::ostream& out_stream(std::ostream& oss) const noexcept; std::istream& in_stream(std::istream& iss); void set_value(RelativeDatePeriod value) { @@ -727,7 +732,7 @@ public: GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } private: - GncOptionUIType m_ui_type = GncOptionUIType::DATE; + GncOptionUIType m_ui_type; RelativeDatePeriod m_period; time64 m_date; RelativeDatePeriod m_default_period; diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp index 48c75df1ea..9814d709f0 100644 --- a/libgnucash/app-utils/gnc-option-uitype.hpp +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -32,7 +32,11 @@ enum GncOptionUIType CURRENCY, COMMODITY, MULTICHOICE, - DATE, + DATE_ABSOLUTE, + DATE_RELATIVE_BEGIN, + DATE_BOTH_BEGIN, + DATE_RELATIVE_END, + DATE_BOTH_END, ACCOUNT_LIST, ACCOUNT_SEL, LIST, diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 82735a6102..0b78e7be3f 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -971,6 +971,9 @@ gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section, const char* doc_string, RelativeDatePeriod period) { - GncOption option{GncOptionDateValue(section, name, key, doc_string, period)}; + auto ui_type = static_cast(period) % 2 ? + GncOptionUIType::DATE_BOTH_END : GncOptionUIType::DATE_BOTH_BEGIN; + GncOption option{GncOptionDateValue(section, name, key, doc_string, + ui_type, period)}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 03c4f4dba8..992294af11 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -1012,7 +1012,8 @@ class GncOptionDateOptionTest : public ::testing::Test { protected: GncOptionDateOptionTest() : - m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option"}} {} + m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option", + GncOptionUIType::DATE_BOTH_END}} {} GncOption m_option; }; From dc876d4041729af8fdbae28c2e97bfe808f7b48d Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 18 Feb 2020 14:27:41 -0800 Subject: [PATCH 083/298] Provide an accessor to GncOptionAccountValue::m_allowed To enable its use by gnc_account_sel_filter --- libgnucash/app-utils/CMakeLists.txt | 1 + libgnucash/app-utils/gnc-option-impl.cpp | 18 ++++++++++++++++++ libgnucash/app-utils/gnc-option-impl.hpp | 1 + libgnucash/app-utils/gnc-option.cpp | 12 ++++++++++++ libgnucash/app-utils/gnc-option.hpp | 6 ++++++ 5 files changed, 38 insertions(+) diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt index 0f05e930a8..e384adb2c0 100644 --- a/libgnucash/app-utils/CMakeLists.txt +++ b/libgnucash/app-utils/CMakeLists.txt @@ -92,6 +92,7 @@ set(app_utils_ALL_LIBRARIES gnc-engine gnc-locale-tax gnucash-guile + ${GLIB_LDFLAGS} ${GIO_LDFLAGS} ${LIBXML2_LDFLAGS} ${LIBXSLT_LDFLAGS} diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 7ab4584132..d561ba6655 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -48,6 +48,24 @@ GncOptionAccountValue::validate(const GncOptionAccountList& values) const return true; } +/** + * 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* +GncOptionAccountValue::account_type_list() const noexcept +{ + if (m_allowed.empty()) + return nullptr; + GList* retval; + for (auto type : m_allowed) + retval = g_list_prepend(retval, GINT_TO_POINTER(type)); + return g_list_reverse(retval); +} + static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static void diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 700efaa343..5d221011a5 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -584,6 +584,7 @@ public: //throw! m_value = values; } + GList* account_type_list() const noexcept; 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; } diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 4bedeb4a2a..a833fdab33 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -256,6 +256,18 @@ GncOption::permissible_value_description(std::size_t index) const }, *m_option); } +GList* +GncOption::account_type_list() const noexcept +{ + return std::visit([] (const auto& option) -> GList* { + if constexpr (std::is_same_v, + GncOptionAccountValue>) + return option.account_type_list(); + else + return nullptr; + }, *m_option); +} + std::ostream& GncOption::out_stream(std::ostream& oss) const { diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 8a990e3ba4..548690dbb2 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -24,6 +24,11 @@ #ifndef GNC_OPTION_HPP_ #define GNC_OPTION_HPP_ +extern "C" +{ +#include +} + #include #include #include @@ -86,6 +91,7 @@ public: const std::string& permissible_value(std::size_t index) const; const std::string& permissible_value_name(std::size_t index) const; const std::string& permissible_value_description(std::size_t index) const; + GList* account_type_list() const noexcept; std::ostream& out_stream(std::ostream& oss) const; std::istream& in_stream(std::istream& iss); std::ostream& to_scheme(std::ostream& oss) const; From 93a3716c00f77420f495de1ffd3099e80ddfc1d5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 18 Feb 2020 14:29:29 -0800 Subject: [PATCH 084/298] Enable retrieval of a GncOptionDateValue's relative period. So that it can be displayed by dialog-option. --- libgnucash/app-utils/gnc-option.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index a833fdab33..14fb90e397 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -48,6 +48,11 @@ GncOption::get_value() const return std::visit([](const auto option)->ValueType { if constexpr (std::is_same_v, std::decay_t>) return option.get_value(); + if constexpr (std::is_same_v, + GncOptionDateValue> && + std::is_same_v, + RelativeDatePeriod>) + return option.get_period(); return ValueType {}; }, *m_option); } From d5f6a2539b13c6c9a5984d96575ac52de89cb1a7 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 20 Feb 2020 12:09:21 -0800 Subject: [PATCH 085/298] Replace GncOption::_get_option() with a friend function swig_get_option. Implemented in gnc-optiondb.i it more clearly indicates the intended use and restricts the access. Unfortunately further limiting the friend declaration with #ifdef SWIG prevented the declaration from working, raising the error "m_option is a private member of GncOption". --- libgnucash/app-utils/gnc-option.hpp | 5 ++++- libgnucash/app-utils/gnc-optiondb.i | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 548690dbb2..be8e93101f 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -96,7 +96,10 @@ public: std::istream& in_stream(std::istream& iss); std::ostream& to_scheme(std::ostream& oss) const; std::istream& from_scheme(std::istream& iss); - GncOptionVariant* const _get_option() { return m_option.get(); } + + + friend GncOptionVariant& swig_get_option(GncOption*); + private: inline static const std::string c_empty_string{""}; GncOptionVariantPtr m_option; diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 818dd876d5..1f2c70c85a 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -302,34 +302,43 @@ gnc_option_test_book_destroy(QofBook* book) wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); +%ignore swig_get_option(GncOption&); +%inline %{ +#include "gnc-option.hpp" +#include "gnc-option-ui.hpp" + + GncOptionVariant& swig_get_option(GncOption* option) + { + return *option->m_option; + } +%} + %include "gnc-option.hpp" %include "gnc-option-impl.hpp" %include "gnc-optiondb.hpp" %include "gnc-optiondb-impl.hpp" -%inline %{ -#include "gnc-option-ui.hpp" -%} %extend GncOption { + SCM get_scm_value() { return std::visit([](const auto& option)->SCM { auto value{option.get_value()}; return scm_from_value(static_cast(value)); - }, *($self->_get_option())); + }, swig_get_option($self)); } SCM get_scm_default_value() { return std::visit([](const auto& option)->SCM { auto value{option.get_default_value()}; return scm_from_value(static_cast(value)); - }, *($self->_get_option())); + }, swig_get_option($self)); } void set_value_from_scm(SCM new_value) { std::visit([new_value](auto& option) { option.set_value(scm_to_value>(new_value)); - }, *($self->_get_option())); + }, swig_get_option($self)); } }; %extend GncOptionDB { From 3b78b6e894623109c0f075b1deaceeaa931da495 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Mar 2020 17:55:15 -0800 Subject: [PATCH 086/298] Change the GncOptionMultichoiceValue permissible value return types to const char*. From std::string. The consumer is a GtkWidget so we might as well do the conversion inside the class and this will simplify adding these functions to GncOptionDateValue in the next commit. --- libgnucash/app-utils/gnc-option-impl.cpp | 182 +++++++++-------------- libgnucash/app-utils/gnc-option-impl.hpp | 14 +- libgnucash/app-utils/gnc-option.cpp | 20 +-- libgnucash/app-utils/gnc-option.hpp | 8 +- 4 files changed, 89 insertions(+), 135 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index d561ba6655..e927f5dae4 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -25,6 +25,7 @@ #include "gnc-option-impl.hpp" #include #include + extern "C" { #include "gnc-accounting-period.h" @@ -66,122 +67,77 @@ GncOptionAccountValue::account_type_list() const noexcept return g_list_reverse(retval); } -static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -static void -normalize_month(struct tm& now) -{ - if (now.tm_mon < 0) - { - now.tm_mon += 12; - --now.tm_year; - } - else if (now.tm_mon > 11) - { - now.tm_mon -= 12; - ++now.tm_year; - } +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; } -static void -set_day_and_time(struct tm& now, bool starting) -{ - if (starting) - { - now.tm_hour = now.tm_min = now.tm_sec = 0; - now.tm_mday = 1; - } - else - { - now.tm_min = now.tm_sec = 59; - now.tm_hour = 23; - now.tm_mday = days_in_month[now.tm_mon]; - // Check for Februrary in a leap year - if (int year = now.tm_year + 1900; now.tm_mon == 1 && - year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) - ++now.tm_mday; - } -}; - time64 -GncOptionDateValue::get_value() const +GncOptionDateValue::get_value() const noexcept { if (m_period == RelativeDatePeriod::ABSOLUTE) return m_date; - if (m_period == RelativeDatePeriod::TODAY) - return static_cast(GncDateTime()); - if (m_period == RelativeDatePeriod::START_ACCOUNTING_PERIOD) - return gnc_accounting_period_fiscal_start(); - if (m_period == RelativeDatePeriod::END_ACCOUNTING_PERIOD) - return gnc_accounting_period_fiscal_end(); - - GncDateTime now_t; - if (m_period == RelativeDatePeriod::TODAY) - return static_cast(now_t); - struct tm now{static_cast(now_t)}; - struct tm period{static_cast(GncDateTime(gnc_accounting_period_fiscal_start()))}; - bool starting = m_period == RelativeDatePeriod::START_PREV_MONTH || - m_period == RelativeDatePeriod::START_THIS_MONTH || - m_period == RelativeDatePeriod::START_CAL_YEAR || - m_period == RelativeDatePeriod::START_PREV_YEAR || - m_period == RelativeDatePeriod::START_CURRENT_QUARTER || - m_period == RelativeDatePeriod::START_PREV_QUARTER; - - bool prev = m_period == RelativeDatePeriod::START_PREV_YEAR || - m_period == RelativeDatePeriod::END_PREV_YEAR || - m_period == RelativeDatePeriod::START_PREV_QUARTER || - m_period == RelativeDatePeriod::END_PREV_QUARTER; - - if (period.tm_mon == now.tm_mon && period.tm_mday == now.tm_mday) - { - //No set accounting period, use the calendar year - period.tm_mon = 0; - period.tm_mday = 0; - } - - if (m_period == RelativeDatePeriod::START_CAL_YEAR || - m_period == RelativeDatePeriod::END_CAL_YEAR || - m_period == RelativeDatePeriod::START_PREV_YEAR || - m_period == RelativeDatePeriod::END_PREV_YEAR) - { - - if (prev) - --now.tm_year; - now.tm_mon = starting ? 0 : 11; - } - else if (m_period == RelativeDatePeriod::START_PREV_QUARTER || - m_period == RelativeDatePeriod::END_PREV_QUARTER || - m_period == RelativeDatePeriod::START_CURRENT_QUARTER || - m_period == RelativeDatePeriod::END_CURRENT_QUARTER) - { - auto offset = (now.tm_mon > period.tm_mon ? now.tm_mon - period.tm_mon : - period.tm_mon - now.tm_mon) % 3; - now.tm_mon = now.tm_mon - offset; - if (prev) - now.tm_mon -= 3; - if (!starting) - now.tm_mon += 2; - } - else if (m_period == RelativeDatePeriod::START_PREV_MONTH || - m_period == RelativeDatePeriod::END_PREV_MONTH) - --now.tm_mon; - normalize_month(now); - set_day_and_time(now, starting); - return static_cast(GncDateTime(now)); + return gnc_relative_date_to_time64(m_period); } -static const char* date_type_str[] {"absolute", "relative"}; -static const std::array date_period_str -{ - "today", - "start-this-month", "end-this-month", - "start-prev-month", "end-prev-month", - "start-current-quarter", "end-current-quarter", - "start-prev-quarter", "end-prev-quarter", - "start-cal-year", "end-cal-year", - "start-prev-year", "end-prev-year", - "start-prev-fin-year", "end-prev-fin-year" -}; +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. + */ +int8_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(); +} + +int8_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 @@ -190,7 +146,7 @@ GncOptionDateValue::out_stream(std::ostream& oss) const noexcept oss << date_type_str[0] << " . " << m_date; else oss << date_type_str[1] << " . " << - date_period_str[static_cast(m_period)]; + gnc_relative_date_storage_string(m_period); return oss; } @@ -216,9 +172,8 @@ GncOptionDateValue::in_stream(std::istream& iss) iss >> period_str; if (period_str.back() == ')') period_str.pop_back(); - auto period = std::find(date_period_str.begin(), date_period_str.end(), - period_str); - if (period == date_period_str.end()) + 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; @@ -226,8 +181,7 @@ GncOptionDateValue::in_stream(std::istream& iss) throw std::invalid_argument(err); } - int64_t index = period - date_period_str.begin(); - set_value(static_cast(index)); + set_value(period); } else { diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 5d221011a5..d8160860db 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -481,21 +481,21 @@ public: { return m_choices.size(); } - std::size_t permissible_value_index(const std::string& key) const noexcept + std::size_t permissible_value_index(const char* key) const noexcept { return find_key(key); } - const std::string& permissible_value(std::size_t index) const + const char* permissible_value(std::size_t index) const { - return std::get<0>(m_choices.at(index)); + return std::get<0>(m_choices.at(index)).c_str(); } - const std::string& permissible_value_name(std::size_t index) const + const char* permissible_value_name(std::size_t index) const { - return std::get<1>(m_choices.at(index)); + return std::get<1>(m_choices.at(index)).c_str(); } - const std::string& permissible_value_description(std::size_t index) const + const char* permissible_value_description(std::size_t index) const { - return std::get<2>(m_choices.at(index)); + return std::get<2>(m_choices.at(index)).c_str(); } bool is_changed() const noexcept { return m_value != m_default_value; } GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 14fb90e397..3212580661 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -214,7 +214,7 @@ GncOption::num_permissible_values() const } std::size_t -GncOption::permissible_value_index(const std::string& value) const +GncOption::permissible_value_index(const char* value) const { return std::visit([&value] (const auto& option) -> size_t { if constexpr (std::is_same_v, @@ -225,39 +225,39 @@ GncOption::permissible_value_index(const std::string& value) const }, *m_option); } -const std::string& +const char* GncOption::permissible_value(std::size_t index) const { - return std::visit([index] (const auto& option) -> const std::string& { + return std::visit([index] (const auto& option) -> const char* { if constexpr (std::is_same_v, GncOptionMultichoiceValue>) return option.permissible_value(index); else - return c_empty_string; + return ""; }, *m_option); } -const std::string& +const char* GncOption::permissible_value_name(std::size_t index) const { - return std::visit([index] (const auto& option) -> const std::string& { + return std::visit([index] (const auto& option) -> const char* { if constexpr (std::is_same_v, GncOptionMultichoiceValue>) return option.permissible_value_name(index); else - return c_empty_string; + return ""; }, *m_option); } -const std::string& +const char* GncOption::permissible_value_description(std::size_t index) const { - return std::visit([index] (const auto& option) -> const std::string& { + return std::visit([index] (const auto& option) -> const char* { if constexpr (std::is_same_v, GncOptionMultichoiceValue>) return option.permissible_value_description(index); else - return c_empty_string; + return ""; }, *m_option); } diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index be8e93101f..4f226ae5f3 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -87,10 +87,10 @@ public: bool is_changed() const noexcept; template bool validate(ValueType value) const; std::size_t num_permissible_values() const; - std::size_t permissible_value_index(const std::string& value) const; - const std::string& permissible_value(std::size_t index) const; - const std::string& permissible_value_name(std::size_t index) const; - const std::string& permissible_value_description(std::size_t index) const; + std::size_t permissible_value_index(const char* value) const; + const char* permissible_value(std::size_t index) const; + const char* permissible_value_name(std::size_t index) const; + const char* permissible_value_description(std::size_t index) const; GList* account_type_list() const noexcept; std::ostream& out_stream(std::ostream& oss) const; std::istream& in_stream(std::istream& iss); From 6c8e0e23f7a1ae44edb173243db70a96c99acd81 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Mar 2020 18:06:46 -0800 Subject: [PATCH 087/298] Redesign GncOptionDateValue. After getting a better understanding of how it interacts with dialog-options and finding some additional scheme API needing implementation (exposed only in hello-world.scm, but possibly used in user-written reports). Creates a new file-pair, gnc-options-date.[ch]pp to separate the creation of the static structs with the type info and strings for the various RelativeDatePeriod values from the already too large gnc-option-impl. Although the result is a rather C-ish design the alternative with classes or template specializations would be more complicated and offer no benefit. Implements the permissible_values functions of GncOptionMultichoiceValue for GncDateValue and provides for absolute, relative, and both UI values for the three widget configs afforded by dialog-options. --- libgnucash/app-utils/CMakeLists.txt | 2 + libgnucash/app-utils/gnc-option-date.cpp | 553 ++++++++++++++++++ libgnucash/app-utils/gnc-option-date.hpp | 88 +++ libgnucash/app-utils/gnc-option-impl.hpp | 89 ++- libgnucash/app-utils/gnc-option-uitype.hpp | 6 +- libgnucash/app-utils/gnc-option.cpp | 46 +- libgnucash/app-utils/gnc-option.hpp | 26 +- libgnucash/app-utils/gnc-optiondb.cpp | 90 ++- libgnucash/app-utils/gnc-optiondb.hpp | 38 +- libgnucash/app-utils/gnc-optiondb.i | 30 + .../app-utils/test/gtest-gnc-option.cpp | 462 ++++++++------- .../app-utils/test/gtest-gnc-optiondb.cpp | 83 ++- .../app-utils/test/test-gnc-optiondb.scm | 24 +- 13 files changed, 1257 insertions(+), 280 deletions(-) create mode 100644 libgnucash/app-utils/gnc-option-date.cpp create mode 100644 libgnucash/app-utils/gnc-option-date.hpp diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt index e384adb2c0..20cd3c6d93 100644 --- a/libgnucash/app-utils/CMakeLists.txt +++ b/libgnucash/app-utils/CMakeLists.txt @@ -11,6 +11,7 @@ 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 ) @@ -69,6 +70,7 @@ set (app_utils_SOURCES gnc-exp-parser.c gnc-gsettings.c gnc-helpers.c + gnc-option-date.cpp gnc-option.cpp gnc-option-impl.cpp gnc-optiondb.cpp diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp new file mode 100644 index 0000000000..e7f1132265 --- /dev/null +++ b/libgnucash/app-utils/gnc-option-date.cpp @@ -0,0 +1,553 @@ +/********************************************************************\ + * 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 + +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}; + +static void +normalize_tm(struct tm& now) +{ + auto tmp_mon = now.tm_mon + (now.tm_mon < 0 ? 12 : + now.tm_mon > 11 ? -12 : 0); + if (now.tm_mday < 1) + { + --now.tm_mon; + now.tm_mday += days_in_month[tmp_mon - 1]; + } + else if (now.tm_mday > days_in_month[tmp_mon]) + { + ++now.tm_mon; + now.tm_mday -= days_in_month[tmp_mon]; + } + if (now.tm_mon < 0) + { + now.tm_mon += 12; + --now.tm_year; + } + else if (now.tm_mon > 11) + { + now.tm_mon -= 12; + ++now.tm_year; + } +} + +static void +set_day_and_time(struct tm& now, RelativeDateType type) +{ + if (type == RelativeDateType::START) + { + now.tm_hour = now.tm_min = now.tm_sec = 0; + now.tm_mday = 1; + } + else if (type == RelativeDateType::END) + { + now.tm_min = now.tm_sec = 59; + now.tm_hour = 23; + now.tm_mday = days_in_month[now.tm_mon]; + // Check for Februrary in a leap year + if (int year = now.tm_year + 1900; now.tm_mon == 1 && + year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) + ++now.tm_mday; + } + // 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; + now.tm_mday = days_in_month[now.tm_mon]; + break; + case RelativeDateOffset::SIX: + if (reldate_is_prev(period)) + now.tm_mon -= 6; + else if (reldate_is_next(period)) + now.tm_mon += 6; + now.tm_mday = days_in_month[now.tm_mon]; + 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; + now.tm_mday = days_in_month[now.tm_mon]; + break; + case RelativeDateOffset::MONTH: + if (reldate_is_prev(period)) + --now.tm_mon; + else if (reldate_is_next(period)) + ++now.tm_mon; + now.tm_mday = days_in_month[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_tm(now); + set_day_and_time(now, checked_reldate(period).m_type); + return static_cast(GncDateTime(now)); +} diff --git a/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp new file mode 100644 index 0000000000..954e0a1cc8 --- /dev/null +++ b/libgnucash/app-utils/gnc-option-date.hpp @@ -0,0 +1,88 @@ +/********************************************************************\ + * 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 * + * * +\********************************************************************/ + +#ifndef GNC_OPTION_DATE_HPP_ +#define GNC_OPTION_DATE_HPP_ + +extern "C" +{ +#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, +}; + +using RelativeDatePeriodVec = std::vector; + +bool gnc_relative_date_is_single(RelativeDatePeriod); +bool gnc_relative_date_is_starting(RelativeDatePeriod); +bool gnc_relative_date_is_ending(RelativeDatePeriod); +const char* gnc_relative_date_storage_string(RelativeDatePeriod); +const char* gnc_relative_date_display_string(RelativeDatePeriod); +const char* gnc_relative_date_description(RelativeDatePeriod); +RelativeDatePeriod gnc_relative_date_from_storage_string(const char*); +time64 gnc_relative_date_to_time64(RelativeDatePeriod); + + +#endif //GNC_OPTION_DATE_HPP_ diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index d8160860db..6977208c17 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -692,52 +692,97 @@ public: const char* key, const char* doc_string, GncOptionUIType ui_type) : OptionClassifier{section, name, key, doc_string}, - m_ui_type{ui_type}, m_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, - m_date{INT64_MAX}, - m_default_period{RelativeDatePeriod::END_ACCOUNTING_PERIOD}, - m_default_date{INT64_MAX} {} + 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_period{RelativeDatePeriod::ABSOLUTE}, - m_date{time}, m_default_period{RelativeDatePeriod::ABSOLUTE}, - m_default_date{time} {} + 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, - const RelativeDatePeriod period) : + RelativeDatePeriod period) : OptionClassifier{section, name, key, doc_string}, - m_ui_type{ui_type}, m_period{period}, m_date{INT64_MAX}, - m_default_period{period}, m_default_date{INT64_MAX} {} - GncOptionDateValue(const GncOptionDateValue&) = default; - GncOptionDateValue(GncOptionDateValue&&) = default; - GncOptionDateValue& operator=(const GncOptionDateValue&) = default; - GncOptionDateValue& operator=(GncOptionDateValue&&) = default; - time64 get_value() const; - time64 get_default_value() const { return static_cast(GncDateTime()); } + 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; } + int8_t get_period_index() const noexcept; + int8_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) { - m_period = value; - m_date = INT64_MAX; + if (validate(value)) + { + m_period = value; + m_date = INT64_MAX; + } } void set_value(time64 time) { - m_period = RelativeDatePeriod::ABSOLUTE; - m_date = time; + if (validate(time)) + { + m_period = RelativeDatePeriod::ABSOLUTE; + m_date = time; + } + } + void set_value(size_t index) noexcept; + 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)); + } + const char* permissible_value_description(std::size_t index) const + { + return gnc_relative_date_description(m_period_set.at(index)); } 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; } private: GncOptionUIType m_ui_type; - RelativeDatePeriod m_period; time64 m_date; - RelativeDatePeriod m_default_period; time64 m_default_date; + RelativeDatePeriod m_period; + RelativeDatePeriod m_default_period; + RelativeDatePeriodVec m_period_set; }; template<> inline std::ostream& diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp index 9814d709f0..b33a4f95e8 100644 --- a/libgnucash/app-utils/gnc-option-uitype.hpp +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -33,10 +33,8 @@ enum GncOptionUIType COMMODITY, MULTICHOICE, DATE_ABSOLUTE, - DATE_RELATIVE_BEGIN, - DATE_BOTH_BEGIN, - DATE_RELATIVE_END, - DATE_BOTH_END, + DATE_RELATIVE, + DATE_BOTH, ACCOUNT_LIST, ACCOUNT_SEL, LIST, diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 3212580661..7e63937651 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -49,10 +49,15 @@ GncOption::get_value() const if constexpr (std::is_same_v, std::decay_t>) return option.get_value(); if constexpr (std::is_same_v, - GncOptionDateValue> && - std::is_same_v, + GncOptionDateValue> && + std::is_same_v, RelativeDatePeriod>) return option.get_period(); + if constexpr (std::is_same_v, + GncOptionDateValue> && + std::is_same_v, + size_t>) + return option.get_period_index(); return ValueType {}; }, *m_option); } @@ -63,6 +68,16 @@ GncOption::get_default_value() const return std::visit([](const auto option)->ValueType { if constexpr (std::is_same_v, std::decay_t>) return option.get_default_value(); + if constexpr (std::is_same_v, + GncOptionDateValue> && + std::is_same_v, + RelativeDatePeriod>) + return option.get_default_period(); + if constexpr (std::is_same_v, + GncOptionDateValue> && + std::is_same_v, + size_t>) + return option.get_default_period_index(); return ValueType {}; }, *m_option); @@ -77,8 +92,9 @@ GncOption::set_value(ValueType value) std::decay_t> || (std::is_same_v, GncOptionDateValue> && - std::is_same_v, - RelativeDatePeriod>)) + (std::is_same_v, + RelativeDatePeriod> || + std::is_same_v, size_t>))) option.set_value(value); }, *m_option); } @@ -206,7 +222,9 @@ GncOption::num_permissible_values() const { return std::visit([] (const auto& option) -> size_t { if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) + GncOptionMultichoiceValue> || + std::is_same_v, + GncOptionDateValue>) return option.num_permissible_values(); else return size_t_max; @@ -218,7 +236,9 @@ GncOption::permissible_value_index(const char* value) const { return std::visit([&value] (const auto& option) -> size_t { if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) + GncOptionMultichoiceValue> || + std::is_same_v, + GncOptionDateValue>) return option.permissible_value_index(value); else return size_t_max;; @@ -230,7 +250,9 @@ GncOption::permissible_value(std::size_t index) const { return std::visit([index] (const auto& option) -> const char* { if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) + GncOptionMultichoiceValue> || + std::is_same_v, + GncOptionDateValue>) return option.permissible_value(index); else return ""; @@ -242,7 +264,9 @@ 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>) + GncOptionMultichoiceValue> || + std::is_same_v, + GncOptionDateValue>) return option.permissible_value_name(index); else return ""; @@ -254,7 +278,9 @@ GncOption::permissible_value_description(std::size_t index) const { return std::visit([index] (const auto& option) -> const char* { if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) + GncOptionMultichoiceValue> || + std::is_same_v, + GncOptionDateValue>) return option.permissible_value_description(index); else return ""; @@ -394,6 +420,7 @@ 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; @@ -417,6 +444,7 @@ 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(RelativeDatePeriod); +template void GncOption::set_value(size_t); template bool GncOption::validate(bool) const; template bool GncOption::validate(int) const; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 4f226ae5f3..6e8cbd3846 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -34,6 +34,7 @@ extern "C" #include #include #include "gnc-option-uitype.hpp" +#include "gnc-option-date.hpp" class GncOptionUIItem; using GncOptionUIItemPtr = std::unique_ptr; @@ -117,31 +118,6 @@ operator>>(std::istream& iss, GncOption& opt) { return opt.in_stream(iss); } -/** - * 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, - START_THIS_MONTH, - END_THIS_MONTH, - START_PREV_MONTH, - END_PREV_MONTH, - START_CURRENT_QUARTER, - END_CURRENT_QUARTER, - START_PREV_QUARTER, - END_PREV_QUARTER, - START_CAL_YEAR, - END_CAL_YEAR, - START_PREV_YEAR, - END_PREV_YEAR, - START_ACCOUNTING_PERIOD, - END_ACCOUNTING_PERIOD -}; #endif //GNC_OPTION_HPP_ diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 0b78e7be3f..b7f7fcb5ee 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -966,14 +966,92 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_date_interval_option(const GncOptionDBPtr& db, const char* section, - const char* name, const char* key, - const char* doc_string, - RelativeDatePeriod period) +gnc_register_date_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, time64 time, + RelativeDateUI ui) { - auto ui_type = static_cast(period) % 2 ? - GncOptionUIType::DATE_BOTH_END : GncOptionUIType::DATE_BOTH_BEGIN; + 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(const GncOptionDBPtr& 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(const GncOptionDBPtr& 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(const GncOptionDBPtr& 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(const GncOptionDBPtr& 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)); +} diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index eebf933f88..26d5a3c892 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -202,9 +202,39 @@ void gnc_register_dateformat_option(const GncOptionDBPtr& db, const char* key, const char* doc_string, std::string value); -void gnc_register_date_interval_option(const GncOptionDBPtr& db, - const char* section, const char* name, - const char* key, const char* doc_string, - RelativeDatePeriod period); +enum RelativeDateUI : uint8_t +{ + ABSOLUTE, + RELATIVE, + BOTH +}; + +void gnc_register_date_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + RelativeDatePeriod period = + RelativeDatePeriod::TODAY, + RelativeDateUI ui = RelativeDateUI::BOTH); + +void gnc_register_date_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, time64 time, + RelativeDateUI ui = RelativeDateUI::BOTH); + +void gnc_register_date_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, + RelativeDatePeriodVec& period_set, + bool both = true); + +void gnc_register_start_date_option(const GncOptionDBPtr& db, + const char* section, + const char* name, const char* key, + const char* doc_string, bool both = true); + +void gnc_register_end_date_option(const GncOptionDBPtr& db, const char* section, + const char* name, const char* key, + const char* doc_string, bool both = true); + #endif //GNC_OPTIONDB_HPP_ diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 1f2c70c85a..823f51c312 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -42,6 +42,8 @@ namespace std { %{ #include "gnc-optiondb.hpp" #include "gnc-optiondb-impl.hpp" +#include "gnc-option-date.hpp" + SCM scm_init_sw_gnc_optiondb_module(void); %} @@ -179,12 +181,23 @@ gnc_option_test_book_destroy(QofBook* book) %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::istream&, GncOption&); %ignore GncOption::_get_option(); %rename(absolute) RelativeDatePeriod::ABSOLUTE; %rename(today) RelativeDatePeriod::TODAY; +%rename(one_week_ago) RelativeDatePeriod::ONE_WEEK_AGO; +%rename(one_week_ahead) RelativeDatePeriod::ONE_WEEK_AHEAD; +%rename(one_month_ago) RelativeDatePeriod::ONE_MONTH_AGO; +%rename(one_month_ahead) RelativeDatePeriod::ONE_MONTH_AHEAD; +%rename(three_months_ago) RelativeDatePeriod::THREE_MONTHS_AGO; +%rename(three_months_ahead) RelativeDatePeriod::THREE_MONTHS_AHEAD; +%rename(six_months_ago) RelativeDatePeriod::SIX_MONTHS_AGO; +%rename(six_months_ahead) RelativeDatePeriod::SIX_MONTHS_AHEAD; +%rename(one_year_ago) RelativeDatePeriod::ONE_YEAR_AGO; +%rename(one_year_ahead) RelativeDatePeriod::ONE_YEAR_AHEAD; %rename(start_this_month) RelativeDatePeriod::START_THIS_MONTH; %rename(end_this_month) RelativeDatePeriod::END_THIS_MONTH; %rename(start_prev_month) RelativeDatePeriod::START_PREV_MONTH; @@ -200,10 +213,26 @@ gnc_option_test_book_destroy(QofBook* book) %rename(start_accounting_period) RelativeDatePeriod::START_ACCOUNTING_PERIOD; %rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD; +%rename(gnc_register_date_option_set) + gnc_register_date_option(const 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) RelativeDatePeriodVec& (RelativeDatePeriodVec period_set) +{ + auto len = scm_to_size_t(scm_length($input)); + 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((RelativeDatePeriod)scm_to_int(s_reldateperiod)); + } + $1 = &period_set; +} + %typemap(in) GncMultiChoiceOptionChoices&& (GncMultiChoiceOptionChoices choices) { auto len = scm_to_size_t(scm_length($input)); @@ -313,6 +342,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } %} +%include "gnc-option-date.hpp" %include "gnc-option.hpp" %include "gnc-option-impl.hpp" %include "gnc-optiondb.hpp" diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 992294af11..0e939ae50b 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -509,35 +509,52 @@ TEST_F(GncOptionCommodityTest, test_commodity_from_scheme) class GncUIType { public: - void set_value(const std::string& value) { m_value = value; } - const std::string& get_value() { return m_value; } + 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: - std::string m_value; + mutable std::string m_value; }; -using OptionUIItem = GncUIItem; + +class OptionUIItem : public GncOptionUIItem +{ + GncUIType m_widget; + bool m_dirty = false; +public: + ~OptionUIItem() = default; + GncOptionUIType get_ui_type() const noexcept override { return GncOptionUIType::STRING; } + void set_dirty(bool status) noexcept override { m_dirty = status; } + bool get_dirty() const noexcept override { return m_dirty; } + 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_widget{}, m_option{"foo", "bar", "baz", "Phony Option", std::string{"waldo"}, GncOptionUIType::STRING} { - auto to_ui = [](OptionUIItem& ui, GncOption& opt) { - ui.m_widget->set_value(opt.get_value()); - }; - auto from_ui = [](OptionUIItem& ui, GncOption& opt) { - opt.set_value(ui.m_widget->get_value()); - }; - auto ui_item{std::make_unique( - OptionUIItem{&m_widget}, - GncOptionUIType::STRING, - to_ui, from_ui)}; + auto ui_item{std::make_unique()}; m_option.set_ui_item(std::move(ui_item)); } - GncUIType m_widget; GncOption m_option; }; @@ -548,24 +565,23 @@ TEST_F(GncOptionUI, test_option_ui_type) EXPECT_EQ(GncOptionUIType::STRING, m_option.get_ui_type()); } -TEST_F(GncOptionUI, test_set_option_ui_item) -{ - EXPECT_EQ(&m_widget, m_option.get_ui_item()->get_ui_item().m_widget); -} - TEST_F(GncOptionUI, test_ui_value_from_option) { const char* value{"waldo"}; m_option.set_value(value); m_option.set_ui_item_from_option(); - EXPECT_STREQ(value, m_widget.get_value().c_str()); + 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"}; - m_widget.set_value(value); + 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()); } @@ -958,10 +974,10 @@ 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).c_str()); - EXPECT_STREQ("sausage", m_option.permissible_value_name(2).c_str()); + EXPECT_STREQ("waldo", m_option.permissible_value(1)); + EXPECT_STREQ("sausage", m_option.permissible_value_name(2)); EXPECT_STREQ("thud", - m_option.permissible_value_description(0).c_str()); + m_option.permissible_value_description(0)); }); EXPECT_THROW({ auto result = m_option.permissible_value(7); }, std::out_of_range); @@ -1008,18 +1024,6 @@ TEST_F(GncMultichoiceOption, test_multichoice_scheme_in) EXPECT_STREQ("pork", m_option.get_value().c_str()); } -class GncOptionDateOptionTest : public ::testing::Test -{ -protected: - GncOptionDateOptionTest() : - m_option{GncOptionDateValue{"foo", "bar", "a", "Phony Date Option", - GncOptionUIType::DATE_BOTH_END}} {} - - GncOption m_option; -}; - -using GncDateOption = GncOptionDateOptionTest; - static time64 time64_from_gdate(const GDate* g_date, DayPart when) { @@ -1029,6 +1033,171 @@ time64_from_gdate(const GDate* g_date, DayPart 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"))}; @@ -1036,124 +1205,25 @@ TEST_F(GncDateOption, test_set_and_get_absolute) EXPECT_EQ(time1, m_option.get_value()); } -TEST_F(GncDateOption, test_set_and_get_month_start) +TEST_F(GncDateOptionList, test_set_and_get_relative) { - 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)}; + 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()); -} - -TEST_F(GncDateOption, test_set_and_get_month_end) -{ - GDate month_end; - g_date_set_time_t(&month_end, time(nullptr)); - gnc_gdate_set_month_end(&month_end); - time64 time1{time64_from_gdate(&month_end, DayPart::end)}; + EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, m_option.get_value()); + auto 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(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_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)}; - m_option.set_value(RelativeDatePeriod::START_PREV_MONTH); - EXPECT_EQ(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_prev_month_end) -{ - GDate prev_month_end; - g_date_set_time_t(&prev_month_end, time(nullptr)); - gnc_gdate_set_prev_month_end(&prev_month_end); - time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)}; - m_option.set_value(RelativeDatePeriod::END_PREV_MONTH); - EXPECT_EQ(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_quarter_start) -{ - GDate quarter_start; - g_date_set_time_t(&quarter_start, time(nullptr)); - gnc_gdate_set_quarter_start(&quarter_start); - time64 time1{time64_from_gdate(&quarter_start, DayPart::start)}; - m_option.set_value(RelativeDatePeriod::START_CURRENT_QUARTER); - EXPECT_EQ(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_quarter_end) -{ - GDate quarter_end; - g_date_set_time_t(&quarter_end, time(nullptr)); - gnc_gdate_set_quarter_end(&quarter_end); - time64 time1{time64_from_gdate(&quarter_end, DayPart::end)}; - m_option.set_value(RelativeDatePeriod::END_CURRENT_QUARTER); - EXPECT_EQ(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_prev_quarter_start) -{ - GDate prev_quarter_start; - g_date_set_time_t(&prev_quarter_start, time(nullptr)); - gnc_gdate_set_prev_quarter_start(&prev_quarter_start); - time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)}; - m_option.set_value(RelativeDatePeriod::START_PREV_QUARTER); - EXPECT_EQ(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_prev_quarter_end) -{ - GDate prev_quarter_end; - g_date_set_time_t(&prev_quarter_end, time(nullptr)); - gnc_gdate_set_prev_quarter_end(&prev_quarter_end); - time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)}; - m_option.set_value(RelativeDatePeriod::END_PREV_QUARTER); - EXPECT_EQ(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_year_start) -{ - GDate year_start; - g_date_set_time_t(&year_start, time(nullptr)); - gnc_gdate_set_year_start(&year_start); - time64 time1{time64_from_gdate(&year_start, DayPart::start)}; - m_option.set_value(RelativeDatePeriod::START_CAL_YEAR); - EXPECT_EQ(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_year_end) -{ - GDate year_end; - g_date_set_time_t(&year_end, time(nullptr)); - gnc_gdate_set_year_end(&year_end); - time64 time1{time64_from_gdate(&year_end, DayPart::end)}; - m_option.set_value(RelativeDatePeriod::END_CAL_YEAR); - EXPECT_EQ(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_prev_year_start) -{ - 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)}; - m_option.set_value(RelativeDatePeriod::START_PREV_YEAR); - EXPECT_EQ(time1, m_option.get_value()); -} - -TEST_F(GncDateOption, test_set_and_get_prev_year_end) -{ - GDate prev_year_end; - g_date_set_time_t(&prev_year_end, time(nullptr)); - gnc_gdate_set_prev_year_end(&prev_year_end); - time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)}; - m_option.set_value(RelativeDatePeriod::END_PREV_YEAR); - EXPECT_EQ(time1, m_option.get_value()); + 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(5, m_option.get_value()); } TEST_F(GncDateOption, test_stream_out) @@ -1271,10 +1341,10 @@ TEST_F(GncDateOption, test_stream_in_month_start) TEST_F(GncDateOption, test_stream_in_month_end) { - GDate month_end; - g_date_set_time_t(&month_end, time(nullptr)); - gnc_gdate_set_month_end(&month_end); - time64 time1{time64_from_gdate(&month_end, DayPart::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()); @@ -1293,10 +1363,10 @@ TEST_F(GncDateOption, test_stream_in_prev_month_start) TEST_F(GncDateOption, test_stream_in_prev_month_end) { - GDate prev_month_end; - g_date_set_time_t(&prev_month_end, time(nullptr)); - gnc_gdate_set_prev_month_end(&prev_month_end); - time64 time1{time64_from_gdate(&prev_month_end, DayPart::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()); @@ -1304,10 +1374,10 @@ TEST_F(GncDateOption, test_stream_in_prev_month_end) TEST_F(GncDateOption, test_stream_in_quarter_start) { - GDate quarter_start; - g_date_set_time_t(&quarter_start, time(nullptr)); - gnc_gdate_set_quarter_start(&quarter_start); - time64 time1{time64_from_gdate(&quarter_start, DayPart::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()); @@ -1315,10 +1385,10 @@ TEST_F(GncDateOption, test_stream_in_quarter_start) TEST_F(GncDateOption, test_stream_in_quarter_end) { - GDate quarter_end; - g_date_set_time_t(&quarter_end, time(nullptr)); - gnc_gdate_set_quarter_end(&quarter_end); - time64 time1{time64_from_gdate(&quarter_end, DayPart::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()); @@ -1326,10 +1396,10 @@ TEST_F(GncDateOption, test_stream_in_quarter_end) TEST_F(GncDateOption, test_stream_in_prev_quarter_start) { - GDate prev_quarter_start; - g_date_set_time_t(&prev_quarter_start, time(nullptr)); - gnc_gdate_set_prev_quarter_start(&prev_quarter_start); - time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::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()); @@ -1337,10 +1407,10 @@ TEST_F(GncDateOption, test_stream_in_prev_quarter_start) TEST_F(GncDateOption, test_stream_in_prev_quarter_end) { - GDate prev_quarter_end; - g_date_set_time_t(&prev_quarter_end, time(nullptr)); - gnc_gdate_set_prev_quarter_end(&prev_quarter_end); - time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::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()); @@ -1348,10 +1418,10 @@ TEST_F(GncDateOption, test_stream_in_prev_quarter_end) TEST_F(GncDateOption, test_stream_in_year_start) { - GDate year_start; - g_date_set_time_t(&year_start, time(nullptr)); - gnc_gdate_set_year_start(&year_start); - time64 time1{time64_from_gdate(&year_start, DayPart::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()); @@ -1359,10 +1429,10 @@ TEST_F(GncDateOption, test_stream_in_year_start) TEST_F(GncDateOption, test_stream_in_year_end) { - GDate year_end; - g_date_set_time_t(&year_end, time(nullptr)); - gnc_gdate_set_year_end(&year_end); - time64 time1{time64_from_gdate(&year_end, DayPart::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()); @@ -1370,10 +1440,10 @@ TEST_F(GncDateOption, test_stream_in_year_end) TEST_F(GncDateOption, test_stream_in_prev_year_start) { - 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)}; + 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()); @@ -1381,10 +1451,10 @@ TEST_F(GncDateOption, test_stream_in_prev_year_start) TEST_F(GncDateOption, test_stream_in_prev_year_end) { - GDate prev_year_end; - g_date_set_time_t(&prev_year_end, time(nullptr)); - gnc_gdate_set_prev_year_end(&prev_year_end); - time64 time1{time64_from_gdate(&prev_year_end, DayPart::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()); @@ -1441,10 +1511,10 @@ TEST_F(GncDateOption, test_date_option_from_scheme) m_option.from_scheme(iss); EXPECT_EQ(time1, m_option.get_value()); - GDate month_end; - g_date_set_time_t(&month_end, time(nullptr)); - gnc_gdate_set_month_end(&month_end); - time1 = time64_from_gdate(&month_end, DayPart::end); + GDate date; + g_date_set_time_t(&date, time(nullptr)); + gnc_gdate_set_month_end(&date); + time1 = time64_from_gdate(&date, DayPart::end); iss.clear(); iss.str("'(relative . end-this-month)"); m_option.from_scheme(iss); diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index e0199c8c9b..9935773425 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -190,21 +190,82 @@ 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); + GncDateTime time1{date, when}; + return static_cast(time1); } -TEST_F(GncOptionDBTest, test_register_date_interval_option) +TEST_F(GncOptionDBTest, test_register_relative_date_option) { - gnc_register_date_interval_option(m_db, "foo", "bar", "baz", "Phony Option", - RelativeDatePeriod::START_ACCOUNTING_PERIOD); + 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 time{time64_from_gdate(&prev_year_start, DayPart::start)}; - ASSERT_TRUE(m_db->set_option("foo", "bar", time)); - EXPECT_EQ(time, m_db->find_option("foo", "bar")->get().get_value()); + 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().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().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().get_value()); + ASSERT_TRUE(m_db->set_option("foo", "bar", time1)); + EXPECT_EQ(time1, + m_db->find_option("foo", "bar")->get().get_value()); + EXPECT_EQ(RelativeDatePeriod::ABSOLUTE, + m_db->find_option("foo", "bar")->get().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().get_value()); + + auto index(std::find(begin_dates.begin(), begin_dates.end(), + RelativeDatePeriod::START_THIS_MONTH) - begin_dates.begin()); + /* If this fails check that the begin_dates vector above matches the one in + * gnc-optiondb.cpp. + */ + EXPECT_EQ(index, + m_db->find_option("foo", "bar")->get().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().get_value()); + m_db->set_option("foo", "bar", static_cast(5)); + EXPECT_EQ(5, m_db->find_option("foo", "bar")->get().get_value()); + } class GncOptionDBIOTest : public ::testing::Test @@ -246,9 +307,9 @@ protected: std::string{""}); gnc_register_text_option(m_db, "qux", "garply", "fred", "Phony Option", std::string{"waldo"}); - gnc_register_date_interval_option(m_db, "pork", "garply", "first", - "Phony Date Option", - RelativeDatePeriod::START_CURRENT_QUARTER); + 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", {aapl, hpe}); diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 966c5669fc..89f683c81f 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -39,6 +39,7 @@ (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-end "test-gnc-optiondb-scheme")) @@ -181,15 +182,32 @@ (define (test-gnc-make-date-option) (test-begin "test-gnc-test-date-option") (let* ((option-db (gnc-option-db-new)) - (date-opt (gnc-register-date-interval-option option-db "foo" "bar" - "baz" "Phony Option" - (RelativeDatePeriod-today))) + (date-opt (gnc-register-date-option option-db "foo" "bar" + "baz" "Phony Option" + (RelativeDatePeriod-today))) (a-time (gnc-dmy2time64 11 07 2019))) (test-equal (current-time) (gnc-option-value option-db "foo" "bar")) (gnc-set-option option-db "foo" "bar" a-time) (test-equal 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 (gnc-option-db-new)) + (date-opt (gnc-register-date-option-set + option-db "foo" "bar" "baz" "Phony Option" + (list (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)) #t))) + (test-equal (gnc-accounting-period-fiscal-start) + (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 (gnc-option-db-new)) From 00aa0f603deb3af1a8936006f1a8c310474b6fda Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Mar 2020 18:07:35 -0800 Subject: [PATCH 088/298] Add a PLOTSIZE option UI type. I'd missed this one earlier. --- libgnucash/app-utils/gnc-option-uitype.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp index b33a4f95e8..ac3889f4ac 100644 --- a/libgnucash/app-utils/gnc-option-uitype.hpp +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -41,6 +41,7 @@ enum GncOptionUIType NUMBER_RANGE, COLOR, FONT, + PLOT_SIZE, BUDGET, PIXMAP, RADIOBUTTON, From fce33799afcf2d1bb1633d77e8513dc757ce9e1b Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Mar 2020 20:00:48 -0800 Subject: [PATCH 089/298] Change GncOptionUIItem to be a pure virtual class instead of a templated one. Simplifies the design because derived classes can have whatever save UI class they need and implement the void(void) set_ui_item_from option and set_option_from_ui_item with whatever functions are appropriate for the UI class. No need for callbacks or std::function members. --- libgnucash/app-utils/gnc-option-ui.hpp | 60 ++++++-------------------- 1 file changed, 12 insertions(+), 48 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp index ef2a8a4a5b..847813b484 100644 --- a/libgnucash/app-utils/gnc-option-ui.hpp +++ b/libgnucash/app-utils/gnc-option-ui.hpp @@ -25,17 +25,10 @@ #define GNC_OPTION_UI_HPP_ #include "gnc-option-uitype.hpp" -template -class GncUIItem -{ -public: - GncUIItem(UIType* widget) : m_widget{widget} {} - UIType* m_widget; -}; -class GncUIType; -using OptionUIItem = GncUIItem; -using OptionSyncFunc = std::function; +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 @@ -47,47 +40,18 @@ using OptionSyncFunc = std::function; * 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(OptionUIItem&& ui_item, GncOptionUIType type, - OptionSyncFunc to_ui, OptionSyncFunc from_ui) : - m_ui_item{std::move(ui_item)}, m_ui_type{type}, - m_set_ui_item_from_option{to_ui}, m_set_option_from_ui_item{from_ui} {} - GncOptionUIItem(GncOptionUIType ui_type) : - m_ui_item{nullptr}, m_ui_type{ui_type} {} - GncOptionUIItem(const GncOptionUIItem&) = default; - GncOptionUIItem(GncOptionUIItem&&) = default; - ~GncOptionUIItem() = default; - GncOptionUIItem& operator=(const GncOptionUIItem&) = default; - GncOptionUIItem& operator=(GncOptionUIItem&&) = default; - GncOptionUIType get_ui_type() const { return m_ui_type; } - const OptionUIItem& get_ui_item() const {return m_ui_item; } - void clear_ui_item() { m_ui_item = nullptr; } - void set_ui_item(OptionUIItem&& ui_item) - { - if (m_ui_type == GncOptionUIType::INTERNAL) - { - std::string error{"INTERNAL option, setting the UI item forbidden."}; - throw std::logic_error(std::move(error)); - } - m_ui_item = std::move(ui_item); - } - void set_ui_item_from_option(GncOption& option) - { - m_set_ui_item_from_option(m_ui_item, option); - } - void set_option_from_ui_item(GncOption& option) - { - m_set_option_from_ui_item(m_ui_item, option); - } -private: - OptionUIItem m_ui_item; - GncOptionUIType m_ui_type; - OptionSyncFunc m_set_ui_item_from_option; - OptionSyncFunc m_set_option_from_ui_item; + GncOptionUIItem() = default; + virtual ~GncOptionUIItem() = default; + virtual GncOptionUIType get_ui_type() const noexcept = 0; + virtual void set_dirty(bool status) noexcept = 0; + virtual bool get_dirty() 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; }; -using GncOptionUIItemPtr = std::unique_ptr; - #endif //GNC_OPTION_UI_HPP__ From 3200bd4966ce5f8a3a8d78f36f5834178b1288b5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 10 Mar 2020 14:48:06 -0700 Subject: [PATCH 090/298] Add a multiselect boolean member to GncOptionAccountValue. To support a variation available in dialog-options. Even though it's not used in any GnuCash code nor documented anywhere it might be in some custom reports. --- libgnucash/app-utils/gnc-option-impl.cpp | 3 +- libgnucash/app-utils/gnc-option-impl.hpp | 32 ++++++++++--------- libgnucash/app-utils/gnc-option.cpp | 12 +++++++ libgnucash/app-utils/gnc-option.hpp | 1 + .../app-utils/test/gtest-gnc-option.cpp | 27 ++++++++++------ 5 files changed, 50 insertions(+), 25 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index e927f5dae4..382b8fb480 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -37,7 +37,8 @@ GncOptionAccountValue::validate(const GncOptionAccountList& values) const { if (values.empty()) return false; - if (get_ui_type() == GncOptionUIType::ACCOUNT_SEL && values.size() != 1) + if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) && + values.size() != 1) return false; if (m_allowed.empty()) return true; diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 6977208c17..b7bae3c07a 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -544,32 +544,32 @@ class GncOptionAccountValue : public OptionClassifier public: GncOptionAccountValue(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{}, m_default_value{}, m_allowed{} {} + 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} {} GncOptionAccountValue(const char* section, const char* name, const char* key, const char* doc_string, GncOptionUIType ui_type, - const GncOptionAccountList& value) : - OptionClassifier{section, name, key, doc_string}, - m_ui_type{ui_type}, m_value{value}, - m_default_value{std::move(value)}, m_allowed{} {} + 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} {} GncOptionAccountValue(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{}, - m_default_value{}, m_allowed{std::move(allowed)} {} + 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} {} GncOptionAccountValue(const char* section, const char* name, const char* key, const char* doc_string, GncOptionUIType ui_type, const GncOptionAccountList& value, - GncOptionAccountTypeList&& allowed) : - OptionClassifier{section, name, key, doc_string}, - m_ui_type{ui_type}, m_value{}, - m_default_value{}, m_allowed{std::move(allowed)} { + 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; @@ -588,11 +588,13 @@ public: 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_multiselect() const noexcept { return m_multiselect; } private: GncOptionUIType m_ui_type; GncOptionAccountList m_value; GncOptionAccountList m_default_value; GncOptionAccountTypeList m_allowed; + bool m_multiselect; }; template<> inline std::ostream& diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 7e63937651..5abc02622f 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -201,6 +201,18 @@ GncOption::is_changed() const noexcept }, *m_option); } +bool +GncOption::is_multiselect() const noexcept +{ + return std::visit([](const auto& option)->bool { + if constexpr (std::is_same_v, + GncOptionAccountValue>) + return option.is_multiselect(); + else + return false; + }, *m_option); +} + template bool GncOption::validate(ValueType value) const { diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 6e8cbd3846..8a1b126ba2 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -86,6 +86,7 @@ public: void set_option_from_ui_item(); void make_internal(); bool is_changed() const noexcept; + bool is_multiselect() const noexcept; template bool validate(ValueType value) const; std::size_t num_permissible_values() const; std::size_t permissible_value_index(const char* value) const; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 0e939ae50b..6be3328ae8 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -757,29 +757,34 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor) EXPECT_THROW({ GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, - acclistbad, {ACCT_TYPE_BANK}); + acclistbad, + GncOptionAccountTypeList{ACCT_TYPE_BANK}); }, std::invalid_argument); EXPECT_THROW({ GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_SEL, - acclistgood, {ACCT_TYPE_BANK}); + acclistgood, + GncOptionAccountTypeList{ACCT_TYPE_BANK}); }, std::invalid_argument); EXPECT_NO_THROW({ GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, - acclistgood, {ACCT_TYPE_BANK}); + acclistgood, + GncOptionAccountTypeList{ACCT_TYPE_BANK}); }); EXPECT_NO_THROW({ GncOptionAccountList accsel{acclistgood[0]}; GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, - accsel, {ACCT_TYPE_BANK}); + accsel, + GncOptionAccountTypeList{ACCT_TYPE_BANK}); }); GncOptionAccountValue option {"foo", "bar", "baz", "Bogus Option", - GncOptionUIType::ACCOUNT_LIST, acclistgood, {ACCT_TYPE_BANK}}; + 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)); @@ -804,7 +809,8 @@ TEST_F(GncOptionAccountTest, test_account_list_out) GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, - accsel, {ACCT_TYPE_BANK}}}; + accsel, + GncOptionAccountTypeList{ACCT_TYPE_BANK}}}; acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string(); oss.str(""); @@ -830,7 +836,8 @@ TEST_F(GncOptionAccountTest, test_account_list_in) GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, - accsel, {ACCT_TYPE_BANK}}}; + accsel, + GncOptionAccountTypeList{ACCT_TYPE_BANK}}}; GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string(); acc_guids += " "; @@ -885,7 +892,8 @@ TEST_F(GncOptionAccountTest, test_account_list_to_scheme) GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, - accsel, {ACCT_TYPE_BANK}}}; + accsel, + GncOptionAccountTypeList{ACCT_TYPE_BANK}}}; acc_guids = make_account_list_SCM_str(accsel); oss.str(""); @@ -909,7 +917,8 @@ TEST_F(GncOptionAccountTest, test_account_list_from_scheme) GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, - accsel, {ACCT_TYPE_BANK}}}; + accsel, + GncOptionAccountTypeList{ACCT_TYPE_BANK}}}; GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; acc_guids = make_account_list_SCM_str(acclistbad); iss.str(acc_guids); From ac0e7063c79bbc86ab01dbf57d955db131b74deb Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 10 Mar 2020 14:49:49 -0700 Subject: [PATCH 091/298] Move the GncOptionUIType and dirty members to GncOptionUIUtem. Even though it makes the class not pure virtual any more it is necessary behavior common to all possible subclasses. --- libgnucash/app-utils/gnc-option-ui.hpp | 11 +++++++---- libgnucash/app-utils/test/gtest-gnc-option.cpp | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp index 847813b484..87ca289a8b 100644 --- a/libgnucash/app-utils/gnc-option-ui.hpp +++ b/libgnucash/app-utils/gnc-option-ui.hpp @@ -44,14 +44,17 @@ using GncOptionUIItemPtr = std::unique_ptr; class GncOptionUIItem { public: - GncOptionUIItem() = default; + GncOptionUIItem(GncOptionUIType type) : m_type{type} {} virtual ~GncOptionUIItem() = default; - virtual GncOptionUIType get_ui_type() const noexcept = 0; - virtual void set_dirty(bool status) noexcept = 0; - virtual bool get_dirty() const noexcept = 0; + 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 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/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 6be3328ae8..a1144a4e0d 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -522,8 +522,8 @@ class OptionUIItem : public GncOptionUIItem GncUIType m_widget; bool m_dirty = false; public: + OptionUIItem() : GncOptionUIItem{GncOptionUIType::STRING} {} ~OptionUIItem() = default; - GncOptionUIType get_ui_type() const noexcept override { return GncOptionUIType::STRING; } void set_dirty(bool status) noexcept override { m_dirty = status; } bool get_dirty() const noexcept override { return m_dirty; } void clear_ui_item() override { m_widget.clear(); } From 16da3208fcfa2d393d9f92e6d9c1f08e64c102bd Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 12 Mar 2020 17:29:12 -0700 Subject: [PATCH 092/298] Make GncOptionDateValue::get_period_index return value size_t. Instead of int8_t to match the built-in vector index type. --- libgnucash/app-utils/gnc-option-impl.cpp | 4 ++-- libgnucash/app-utils/gnc-option-impl.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 382b8fb480..9ac10c0122 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -97,7 +97,7 @@ GncOptionDateValue::get_default_value() const noexcept /* Use asserts for pre- and post-conditions to deliberately crash if they're not * met as the program design should prevent that from happening. */ -int8_t +size_t GncOptionDateValue::get_period_index() const noexcept { assert (m_period != RelativeDatePeriod::ABSOLUTE); @@ -107,7 +107,7 @@ GncOptionDateValue::get_period_index() const noexcept return item - m_period_set.begin(); } -int8_t +size_t GncOptionDateValue::get_default_period_index() const noexcept { assert(m_period != RelativeDatePeriod::ABSOLUTE); diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index b7bae3c07a..9716fdb262 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -731,8 +731,8 @@ public: time64 get_default_value() const noexcept; RelativeDatePeriod get_period() const noexcept { return m_period; } RelativeDatePeriod get_default_period() const noexcept { return m_default_period; } - int8_t get_period_index() const noexcept; - int8_t get_default_period_index() const noexcept; + 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); From a995343a8b9b65d1e517225572c2756018764ee3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 12 Mar 2020 17:30:52 -0700 Subject: [PATCH 093/298] Provide for multiple selections in GncOptionMultichoiceValue To support the GncOptionUIType::LIST. This UI type is unused in GnuCash code but might be used in user custom reports. --- libgnucash/app-utils/gnc-option-impl.cpp | 3 + libgnucash/app-utils/gnc-option-impl.hpp | 232 ++++++++++++++++-- libgnucash/app-utils/gnc-option.cpp | 63 +++-- .../app-utils/test/gtest-gnc-option.cpp | 89 +++++++ 4 files changed, 346 insertions(+), 41 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 9ac10c0122..38b1f8e048 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -32,6 +32,9 @@ extern "C" #include "gnc-ui-util.h" } +const std::string GncOptionMultichoiceValue::c_empty_string{""}; +const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"}; + bool GncOptionAccountValue::validate(const GncOptionAccountList& values) const { diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 9716fdb262..69951dbb13 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -412,13 +412,14 @@ private: ValueType m_step; }; -using GncMultiChoiceOptionEntry = std::tuple; -using GncMultiChoiceOptionChoices = std::vector; +using GncMultichoiceOptionIndexVec = std::vector; +using GncMultichoiceOptionChoices = std::vector; -/** MultiChoice options have a vector of valid options - * (GncMultiChoiceOptionChoices) and validate the selection as being one of +/** 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. The * tuple contains three strings, a key, a display * name and a brief description for the tooltip. Both name and description @@ -433,22 +434,48 @@ public: GncOptionMultichoiceValue(const char* section, const char* name, const char* key, const char* doc_string, const char* value, - GncMultiChoiceOptionChoices&& choices, + 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) + m_value{}, m_default_value{}, m_choices{std::move(choices)} + { + if (value) + { + if (auto index = find_key(value); + index != size_t_max) { - if (auto index = find_key(value); - index != size_t_max) - { - m_value = index; - m_default_value = index; - } + 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; @@ -456,27 +483,84 @@ public: const std::string& get_value() const { - return std::get<0>(m_choices.at(m_value)); + 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 { - return std::get<0>(m_choices.at(m_default_value)); + 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; } - bool validate(const std::string& value) const noexcept + + 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 = index; + { + 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_multiple(const GncMultichoiceOptionIndexVec& indexes) + { + if (validate(indexes)) + m_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(); @@ -513,11 +597,119 @@ private: } GncOptionUIType m_ui_type; - std::size_t m_value; - std::size_t m_default_value; - GncMultiChoiceOptionChoices m_choices; + 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; +} + +template, GncOptionMultichoiceValue>, int> = 0> +inline std::ostream& +gnc_option_to_scheme(std::ostream& oss, const OptType& opt) +{ + auto indexes{opt.get_multiple()}; + if (indexes.size() > 1) + oss << "'("; + bool first = true; + for (auto index : indexes) + { + if (first) + first = false; + else + oss << " "; + oss << "'" << opt.permissible_value(index); + } + if (indexes.size() > 1) + oss << ')'; + return oss; +} + +template, GncOptionMultichoiceValue>, int> = 0> +inline std::istream& +gnc_option_from_scheme(std::istream& iss, OptType& opt) +{ + iss.ignore(3, '\''); + auto c{iss.peek()}; + if (static_cast(c) == '(') + { + GncMultichoiceOptionIndexVec values; + iss.ignore(3, '\''); + while (true) + { + std::string str; + std::getline(iss, str, ' '); + if (!str.empty()) + { + if (str.back() == ')') + { + str.pop_back(); + break; + } + values.push_back(opt.permissible_value_index(str.c_str())); + iss.ignore(2, '\''); + } + else + break; + } + opt.set_multiple(values); + } + else + { + std::string str; + std::getline(iss, str, ' '); + opt.set_value(str); + } + return iss; +} + using GncOptionAccountList = std::vector; using GncOptionAccountTypeList = std::vector; diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 5abc02622f..b4b8e33f7b 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -58,6 +58,18 @@ GncOption::get_value() const std::is_same_v, size_t>) return option.get_period_index(); + if constexpr + (std::is_same_v, + GncOptionMultichoiceValue> && + std::is_same_v, + size_t>) + return option.get_index(); + if constexpr + (std::is_same_v, + GncOptionMultichoiceValue> && + std::is_same_v, + GncMultichoiceOptionIndexVec>) + return option.get_multiple(); return ValueType {}; }, *m_option); } @@ -78,6 +90,12 @@ GncOption::get_default_value() const std::is_same_v, size_t>) return option.get_default_period_index(); + if constexpr + (std::is_same_v, + GncOptionMultichoiceValue> && + std::is_same_v, + GncMultichoiceOptionIndexVec>) + return option.get_default_multiple(); return ValueType {}; }, *m_option); @@ -96,6 +114,12 @@ GncOption::set_value(ValueType value) RelativeDatePeriod> || std::is_same_v, size_t>))) option.set_value(value); + if constexpr + (std::is_same_v, + GncOptionMultichoiceValue> && + std::is_same_v, + GncMultichoiceOptionIndexVec>) + option.set_multiple(value); }, *m_option); } @@ -221,6 +245,10 @@ GncOption::validate(ValueType value) const GncOptionMultichoiceValue> && std::is_same_v, std::string>) || + (std::is_same_v, + GncOptionMultichoiceValue> && + std::is_same_v, + GncMultichoiceOptionIndexVec>) || std::is_same_v, GncOptionValidatedValue>) return option.validate(value); @@ -334,19 +362,15 @@ GncOption::to_scheme(std::ostream& oss) const { return std::visit([&oss](auto& option) ->std::ostream& { if constexpr - (std::is_same_v, - GncOptionAccountValue>) - gnc_option_to_scheme(oss, option); - else if constexpr - (std::is_same_v, - GncOptionMultichoiceValue>) - oss << "'" << option; - else if constexpr - (std::is_same_v, + ((std::is_same_v, + GncOptionAccountValue>) || + (std::is_same_v, + GncOptionMultichoiceValue>) || + std::is_same_v, GncOptionValue> || std::is_same_v, GncOptionValidatedValue>) - gnc_option_to_scheme(oss, option); + gnc_option_to_scheme(oss, option); else if constexpr (std::is_same_v, GncOptionDateValue>) @@ -366,18 +390,11 @@ GncOption::from_scheme(std::istream& iss) { return std::visit([&iss](auto& option) -> std::istream& { if constexpr - (std::is_same_v, - GncOptionAccountValue>) - gnc_option_from_scheme(iss, option); - else if constexpr ((std::is_same_v, - GncOptionMultichoiceValue>)) - { - iss.ignore(1, '\''); - iss >> option; - } - else if constexpr - (std::is_same_v, + GncOptionAccountValue>) || + (std::is_same_v, + GncOptionMultichoiceValue>) || + std::is_same_v, GncOptionValue> || std::is_same_v, GncOptionValidatedValue>) @@ -438,6 +455,7 @@ template std::string GncOption::get_value() const; template const QofInstance* GncOption::get_value() const; template RelativeDatePeriod GncOption::get_value() const; template GncOptionAccountList GncOption::get_value() const; +template GncMultichoiceOptionIndexVec GncOption::get_value() const; template bool GncOption::get_default_value() const; template int GncOption::get_default_value() const; @@ -447,6 +465,7 @@ 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 RelativeDatePeriod GncOption::get_default_value() const; +template GncMultichoiceOptionIndexVec GncOption::get_default_value() const; template void GncOption::set_value(bool); template void GncOption::set_value(int); @@ -457,6 +476,7 @@ template void GncOption::set_value(std::string); template void GncOption::set_value(const QofInstance*); template void GncOption::set_value(RelativeDatePeriod); template void GncOption::set_value(size_t); +template void GncOption::set_value(GncMultichoiceOptionIndexVec); template bool GncOption::validate(bool) const; template bool GncOption::validate(int) const; @@ -466,3 +486,4 @@ 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(RelativeDatePeriod) const; +template bool GncOption::validate(GncMultichoiceOptionIndexVec) const; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index a1144a4e0d..d36b19b752 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -1033,6 +1033,95 @@ TEST_F(GncMultichoiceOption, test_multichoice_scheme_in) EXPECT_STREQ("pork", m_option.get_value().c_str()); } +class GncOptionListTest : public ::testing::Test +{ +protected: + GncOptionListTest() : + m_option{GncOptionMultichoiceValue{"foo", "bar", "baz", + "Phony Option", + GncMultichoiceOptionIndexVec{0, 2}, + { + {"plugh", "xyzzy", "thud"}, + {"waldo", "pepper", "salt"}, + {"pork", "sausage", "links"}, + {"corge", "grault", "garply"} + }}} {} + 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()); +} + +TEST_F(GncListOption, test_list_scheme_out) +{ + std::ostringstream oss; + m_option.to_scheme(oss); + std::string value{"'('"}; + auto vec{m_option.get_value()}; + value += m_option.permissible_value(vec[0]); + value += " '"; + value += m_option.permissible_value(vec[1]); + value += ")"; + EXPECT_EQ(value, oss.str()); +} + +TEST_F(GncListOption, test_list_scheme_in) +{ + std::istringstream iss{"'('pork 'waldo)"}; + m_option.from_scheme(iss); + EXPECT_STREQ("pork", m_option.get_value().c_str()); +} + static time64 time64_from_gdate(const GDate* g_date, DayPart when) { From 5fd53c94defb6d08bdf5069a2e561006d7e313a5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 15 Mar 2020 13:39:34 -0700 Subject: [PATCH 094/298] Provide GncOption::GetLimits for setting a NUMBER_RANGE spin button. --- libgnucash/app-utils/gnc-option-impl.hpp | 6 ++++++ libgnucash/app-utils/gnc-option.cpp | 13 +++++++++++++ libgnucash/app-utils/gnc-option.hpp | 2 ++ libgnucash/app-utils/test/gtest-gnc-option.cpp | 14 ++++++++++++++ 4 files changed, 35 insertions(+) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 69951dbb13..e26e29dcf6 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -400,6 +400,12 @@ public: 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; + } 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; } diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index b4b8e33f7b..9f9d9c28ce 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -123,6 +123,17 @@ GncOption::set_value(ValueType 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 + (std::is_same_v, + GncOptionRangeValue>) + option.get_limits(max, min, step); + }, *m_option); +} + const std::string& GncOption::get_section() const { @@ -478,6 +489,8 @@ template void GncOption::set_value(RelativeDatePeriod); template void GncOption::set_value(size_t); template void GncOption::set_value(GncMultichoiceOptionIndexVec); +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; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 8a1b126ba2..befa221ea6 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -87,6 +87,8 @@ public: void make_internal(); bool is_changed() const noexcept; bool is_multiselect() const noexcept; + template void get_limits(ValueType&, ValueType&, + ValueType&) const noexcept; template bool validate(ValueType value) const; std::size_t num_permissible_values() const; std::size_t permissible_value_index(const char* value) const; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index d36b19b752..790303b7ff 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -622,6 +622,20 @@ TEST_F(GncRangeOption, test_setter) 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; From 0106176436569bcf46d51b90cf4ad6b9fcd45096 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 16 Mar 2020 10:27:59 -0700 Subject: [PATCH 095/298] Provide GncOption::is_alternate() for GncOptionRangeValue<>. Needed for GncOptionUIType::PLOT_SIZE to indicate whether the option value represents pixels or percent. --- libgnucash/app-utils/gnc-option-impl.hpp | 102 +++++++++++++++++++---- libgnucash/app-utils/gnc-option.cpp | 68 +++++++++++++++ libgnucash/app-utils/gnc-option.hpp | 2 + 3 files changed, 157 insertions(+), 15 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index e26e29dcf6..de985b437c 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -107,6 +107,7 @@ struct OptionClassifier std::string m_doc_string; }; + #ifndef SWIG auto constexpr size_t_max = std::numeric_limits::max(); #endif @@ -207,14 +208,18 @@ std::string qof_instance_to_string(const QofInstance* inst); * to parse the templates to figure out the symbols. */ #ifndef SWIG -template> && - !(std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>), int> = 0> -std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt) + std::decay_t> && + ! (std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue> || + std::is_same_v, + GncOptionRangeValue> || + std::is_same_v, + GncOptionRangeValue>), int> = 0> +std::ostream& operator<<(std::ostream& oss, const OptType& opt) { oss << opt.get_value(); return oss; @@ -228,7 +233,11 @@ operator<< >(std::ostream& oss, return oss; } -template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue >, int> = 0> +template, + GncOptionValidatedValue> || + std::is_same_v, + GncOptionValue >, int> = 0> inline std::ostream& operator<< (std::ostream& oss, const OptType& opt) { @@ -249,13 +258,18 @@ operator<< (std::ostream& oss, const OptType& opt) return oss; } -template> && - !(std::is_same_v, +template> && + !(std::is_same_v, GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>), int> = 0> -std::istream& operator>>(std::istream& iss, OptionValueClass& opt) + std::is_same_v, + GncOptionValidatedValue> || + std::is_same_v, + GncOptionRangeValue> || + std::is_same_v, + GncOptionRangeValue>), int> = 0> +std::istream& operator>>(std::istream& iss, OptType& opt) { std::decay_t value; iss >> value; @@ -263,7 +277,12 @@ std::istream& operator>>(std::istream& iss, OptionValueClass& opt) return iss; } -template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue >, int> = 0> +template, + GncOptionValidatedValue> || + std::is_same_v, + GncOptionValue >, + int> = 0> std::istream& operator>> (std::istream& iss, OptType& opt) { @@ -409,6 +428,11 @@ public: 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; + } private: GncOptionUIType m_ui_type = GncOptionUIType::NUMBER_RANGE; ValueType m_value; @@ -416,8 +440,56 @@ private: ValueType m_min; ValueType m_max; ValueType m_step; + bool m_alternate = false; }; +template, + GncOptionRangeValue> || + std::is_same_v, + GncOptionRangeValue>, + 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, + GncOptionRangeValue> || + std::is_same_v, + GncOptionRangeValue>, + 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; diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 9f9d9c28ce..42c069742d 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -350,6 +350,31 @@ GncOption::account_type_list() const noexcept }, *m_option); } +bool +GncOption::is_alternate() const noexcept +{ + return std::visit([](auto& option) -> bool { + if constexpr(std::is_same_v, + GncOptionRangeValue> || + std::is_same_v, + GncOptionRangeValue>) + return option.is_alternate(); + return false; + }, *m_option); +} + +void +GncOption::set_alternate(bool alt) noexcept +{ + std::visit([alt](auto& option) { + if constexpr(std::is_same_v, + GncOptionRangeValue> || + std::is_same_v, + GncOptionRangeValue>) + option.set_alternate(alt); + }, *m_option); +} + std::ostream& GncOption::out_stream(std::ostream& oss) const { @@ -390,6 +415,19 @@ GncOption::to_scheme(std::ostream& oss) const (std::is_same_v, std::string>) oss << '"' << option << '"'; + else if constexpr + (std::is_same_v, + GncOptionRangeValue> || + std::is_same_v, + GncOptionRangeValue>) + { + if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE) + oss << "'(" << (option.is_alternate() ? + "\"percent\" . " : "\"pixels\" . "); + oss << option.get_value(); + if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE) + oss << ")"; + } else oss << option; return oss; @@ -427,6 +465,36 @@ GncOption::from_scheme(std::istream& iss) std::getline(iss, input, '"'); option.set_value(input); } + else if constexpr + (std::is_same_v, + GncOptionRangeValue> || + std::is_same_v, + GncOptionRangeValue>) + { + if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE) + { + iss.ignore(3, '"'); + std::string alt; + iss >> alt; + option.set_alternate( + strncmp(alt.c_str(), "pixels", + strlen("pixels")) == 0); + iss.ignore(4, ' '); + } + if constexpr(std::is_same_v, + GncOptionRangeValue>) + { + int val; + iss >> val; + option.set_value(val); + } + else + { + double val; + iss >> val; + option.set_value(val); + } + } else iss >> option; return iss; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index befa221ea6..2d901d3037 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -96,6 +96,8 @@ public: const char* permissible_value_name(std::size_t index) const; const char* permissible_value_description(std::size_t index) const; GList* account_type_list() const noexcept; + bool is_alternate() const noexcept; + void set_alternate(bool) noexcept; std::ostream& out_stream(std::ostream& oss) const; std::istream& in_stream(std::istream& iss); std::ostream& to_scheme(std::ostream& oss) const; From c63db36a60bb30db6636bc42b07f5c769690f6d6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 17 Mar 2020 12:19:26 -0700 Subject: [PATCH 096/298] Rename string_equal_charptr to operator==(const std::string&, const char*) Much prettier and more idiomatic as well as being a lot less typing. --- libgnucash/app-utils/gnc-optiondb.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index b7f7fcb5ee..1ab1509f19 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -33,6 +33,11 @@ auto constexpr stream_max = std::numeric_limits::max(); GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {} GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {} +static bool +operator==(const std::string& str, const char* cstr) +{ + return strcmp(str.c_str(), cstr) == 0; +} void GncOptionDB::save_to_book(QofBook* book, bool do_clear) const @@ -377,23 +382,14 @@ unquote_scheme_string(const std::string& str) return str; } -static inline bool -string_equal_charptr(const std::string& str, const char* chars) -{ - if (!chars || str.empty() || strlen(chars) != str.size()) - return false; - return strcmp(str.c_str(), chars) == 0; -} - static std::optional> find_form(const SchemeId& toplevel, IdentType type, const char* name) { - if (toplevel.m_type == type && - string_equal_charptr(toplevel.m_name.c_str(), name)) + if (toplevel.m_type == type && toplevel.m_name == name) return std::ref(toplevel); for (const auto& id : toplevel.m_ids) { - if (id.m_type == type && string_equal_charptr(id.m_name.c_str(), name)) + if (id.m_type == type && id.m_name == name) return std::ref(id); auto child{find_form(id, type, name)}; if (child) From 3b4785e744a88ab062c6b13b3accd0cc766d9488 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 17 Mar 2020 12:24:20 -0700 Subject: [PATCH 097/298] Convert GncOptionSection from a std::pair to a class. Provides find_option(const char*) and foreach_option(func) for easy iteration. find_option and find_section now return plain const ptrs instead of std::optionals. Much less cumbersome though the compiler won't nag if you forget the nullptr check. --- libgnucash/app-utils/gnc-optiondb-impl.hpp | 57 ++- libgnucash/app-utils/gnc-optiondb.cpp | 339 +++++++++--------- libgnucash/app-utils/gnc-optiondb.i | 9 +- .../app-utils/test/gtest-gnc-optiondb.cpp | 48 +-- 4 files changed, 252 insertions(+), 201 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index 074b36247c..372f558d92 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -41,7 +41,25 @@ extern "C" } using GncOptionVec = std::vector; -using GncOptionSection = std::pair; + +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); + 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; + class GncOptionDB { public: @@ -49,15 +67,27 @@ public: 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(); } void save_to_book(QofBook* book, bool do_clear) const; - int 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 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); + std::string lookup_string_option(const char* section, const char* name); template bool set_option(const char* section, const char* name, ValueType value) { @@ -66,7 +96,7 @@ public: auto option{find_option(section, name)}; if (!option) return false; - option->get().set_value(value); + option->set_value(value); return true; } catch(const std::invalid_argument& err) @@ -78,11 +108,16 @@ public: // void set_selectable(const char* section, const char* name); void make_internal(const char* section, const char* name); void commit() {}; - std::optional> find_section(const std::string& section); - std::optional> find_option(const std::string& section, const std::string& name) { - return static_cast(*this).find_option(section, name); + GncOptionSection* find_section(const std::string& sectname) + { + return const_cast(static_cast(*this).find_section(sectname)); } - std::optional> find_option(const std::string& section, const std::string& name) const; + 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_scheme(std::ostream& oss, const char* options_prolog) const noexcept; std::istream& load_from_scheme(std::istream& iss) noexcept; @@ -100,8 +135,8 @@ public: const std::string& name) const noexcept; std::istream& load_option_key_value(std::istream& iss); private: - std::optional> m_default_section; - std::vector m_sections; + GncOptionSection* m_default_section; + std::vector m_sections; bool m_dirty = false; std::function m_get_ui_value; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 1ab1509f19..36367aace4 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -30,100 +30,107 @@ #include "gnc-option-ui.hpp" auto constexpr stream_max = std::numeric_limits::max(); -GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {} -GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {} 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::add_option(GncOption&& option) +{ + m_options.emplace_back(std::move(option)); +} + +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; + }); + return (option == m_options.end() ? nullptr : &*option); +} + +GncOptionDB::GncOptionDB() : m_default_section{} {} + +GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {} + void GncOptionDB::save_to_book(QofBook* book, bool do_clear) const { } void -GncOptionDB::register_option(const char* section, GncOption&& option) +GncOptionDB::register_option(const char* sectname, GncOption&& option) { - auto db_section = find_section(section); + auto section = find_section(sectname); - if (db_section) + if (section) { - auto& sec_vec = db_section->get().second; - sec_vec.emplace_back(std::move(option)); + section->add_option(std::move(option)); return; } - m_sections.emplace_back(std::make_pair(std::string{section}, - GncOptionVec{})); - auto new_section = std::prev(m_sections.end()); - new_section->second.emplace_back(std::move(option)); + m_sections.emplace_back(std::make_shared(sectname)); + m_sections.back()->add_option(std::move(option)); } void -GncOptionDB::unregister_option(const char* section, const char* name) +GncOptionDB::unregister_option(const char* sectname, const char* name) { - auto db_section = find_section(section); - if (db_section) - { - auto& sec_vec = db_section->get().second; - sec_vec.erase( - std::remove_if( - sec_vec.begin(), sec_vec.end(), - [name](const auto& option) -> bool - { - return option.get_name() == std::string{name}; - })); - } + auto section = find_section(sectname); + if (section) + section->remove_option(name); } void -GncOptionDB::set_default_section(const char* section) +GncOptionDB::set_default_section(const char* sectname) { - m_default_section = find_section(section); + m_default_section = find_section(sectname); } const GncOptionSection* const GncOptionDB::get_default_section() const noexcept { - if (m_default_section) - return &(m_default_section.value().get()); - return nullptr; + return m_default_section; } -std::optional> -GncOptionDB::find_section(const std::string& 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.compare(0, classifier_size_max, sect.first) == 0; - }); - if (db_section == m_sections.end()) - return std::nullopt; - return *db_section; + 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(); } -std::optional> -GncOptionDB::find_option(const std::string& section, const std::string& name) const +const GncOption* +GncOptionDB::find_option(const std::string& section, const char* name) const { auto db_section = const_cast(this)->find_section(section); if (!db_section) - return std::nullopt; - auto& sec_vec = db_section->get().second; - auto db_opt = std::find_if( - sec_vec.begin(), sec_vec.end(), - [&name](GncOption& option) -> bool - { - return name.compare(0, classifier_size_max - 1, - option.get_name()) == 0; - }); - if (db_opt == sec_vec.end()) - return std::nullopt; - return *db_opt; + return nullptr; + return db_section->find_option(name); } std::string @@ -134,7 +141,7 @@ GncOptionDB::lookup_string_option(const char* section, const char* name) auto db_opt = find_option(section, name); if (!db_opt) return empty_string; - return db_opt->get().get_value(); + return db_opt->get_value(); } void @@ -143,7 +150,7 @@ GncOptionDB::make_internal(const char* section, const char* name) auto db_opt = find_option(section, name); if (db_opt) - db_opt->get().make_internal(); + db_opt->make_internal(); } std::ostream& @@ -152,15 +159,15 @@ GncOptionDB::save_option_scheme(std::ostream& oss, const std::string& section, const std::string& name) const noexcept { - auto db_opt = find_option(section, name); + auto db_opt = find_option(section, name.c_str()); - if (!db_opt || !db_opt->get().is_changed()) + if (!db_opt || !db_opt->is_changed()) return oss; oss << scheme_tags[0] << option_prolog << "\n"; oss << scheme_tags[1] << '"' << section.substr(0, classifier_size_max) << "\"\n"; oss << scheme_tags[1] << '"' << name.substr(0, classifier_size_max) << '"'; oss << scheme_tags[2] << "\n" << scheme_tags[3]; - db_opt->get().to_scheme(oss); + db_opt->to_scheme(oss); oss << scheme_tags[4] << "\n\n"; return oss; @@ -421,7 +428,7 @@ GncOptionDB::load_option_scheme(std::istream& iss) throw std::runtime_error("Malformed option classifier."); const auto& section = unquote_scheme_string(classifier[1].m_name); const auto& name = unquote_scheme_string(classifier[2].m_name); - auto option = find_option(section.c_str(), name.c_str()); + auto option = find_option(section, name.c_str()); std::string option_str{section}; option_str += ':'; option_str += name; @@ -439,29 +446,30 @@ GncOptionDB::load_option_scheme(std::istream& iss) throw std::runtime_error(err + " malformed value lambda form."); } std::istringstream value_iss{value_id->get().m_ids[1].m_name}; - option->get().from_scheme(value_iss); + option->from_scheme(value_iss); return iss; } std::ostream& GncOptionDB::save_to_scheme(std::ostream& oss, const char* options_prolog) const noexcept { - for (auto& section : m_sections) - { - const auto& [s_name, s_vec] = section; - oss << "\n; Section: " << s_name << "\n\n"; - for (auto& option : s_vec) + foreach_section( + [&oss, options_prolog](const GncOptionSectionPtr& section) { - if (!option.is_changed()) - continue; - oss << scheme_tags[0] << options_prolog << "\n"; - oss << scheme_tags[1] << '"' << section.first.substr(0, classifier_size_max) << "\"\n"; - oss << scheme_tags[1] << '"' << option.get_name().substr(0, classifier_size_max) << '"'; - oss << scheme_tags[2] << "\n" << scheme_tags[3]; - option.to_scheme(oss); - oss << scheme_tags[4] << "\n\n"; - } - } + oss << "\n; Section: " << section->get_name() << "\n\n"; + section->foreach_option( + [&oss, options_prolog, §ion](auto& option) + { + if (!option.is_changed()) + return; + oss << scheme_tags[0] << options_prolog << "\n"; + oss << scheme_tags[1] << '"' << section->get_name().substr(0, classifier_size_max) << "\"\n"; + oss << scheme_tags[1] << '"' << option.get_name().substr(0, classifier_size_max) << '"'; + oss << scheme_tags[2] << "\n" << scheme_tags[3]; + option.to_scheme(oss); + oss << scheme_tags[4] << "\n\n"; + }); + }); return oss; } @@ -487,11 +495,11 @@ GncOptionDB::save_option_key_value(std::ostream& oss, const std::string& name) const noexcept { - auto db_opt = find_option(section, name); - if (!db_opt || !db_opt->get().is_changed()) + 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->get() << ";"; + name.substr(0, classifier_size_max) << "=" << *db_opt << ";"; return oss; } @@ -512,7 +520,7 @@ GncOptionDB::load_option_key_value(std::istream& iss) std::string value; std::getline(iss, value, ';'); std::istringstream item_iss{value}; - item_iss >> option->get(); + item_iss >> *option; } return iss; } @@ -521,18 +529,19 @@ std::ostream& GncOptionDB::save_to_key_value(std::ostream& oss) const noexcept { - for (auto& section : m_sections) - { - const auto& [s_name, s_vec] = section; - oss << "[Options]\n"; - for (auto& option : s_vec) + foreach_section( + [&oss](const GncOptionSectionPtr& section) { - if (option.is_changed()) - oss << section.first.substr(0, classifier_size_max) << - ':' << option.get_name().substr(0, classifier_size_max) << - '=' << option << '\n'; - } - } + 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; } @@ -559,85 +568,89 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept { if (clear_options) qof_book_options_delete(book, nullptr); - for (auto& section : m_sections) - { - const auto& [s_name, s_vec] = section; - for (auto& option : s_vec) - if (option.is_changed()) - { - // qof_book_set_option wants a GSList path. Let's avoid allocating and make one here. - GSList list_tail{(void*)option.get_name().c_str(), nullptr}; - GSList list_head{(void*)s_name.c_str(), &list_tail}; - auto type{option.get_ui_type()}; - if (type == GncOptionUIType::BOOLEAN) - { - auto val{option.get_value()}; - auto kvp{new KvpValue(val ? "t" : "f")}; - qof_book_set_option(book, kvp, &list_head); - } - else if (type > GncOptionUIType::DATE_FORMAT) - { - const QofInstance* inst{QOF_INSTANCE(option.get_value())}; - auto guid = guid_copy(qof_instance_get_guid(inst)); - auto kvp{new KvpValue(guid)}; - qof_book_set_option(book, kvp, &list_head); - } - else if (type == GncOptionUIType::NUMBER_RANGE) - { - auto kvp{new KvpValue(option.get_value())}; - qof_book_set_option(book, kvp, &list_head); - } - else - { - auto kvp{new KvpValue{g_strdup(option.get_value().c_str())}}; - qof_book_set_option(book, kvp, &list_head); - } - } - } + const_cast(this)->foreach_section( + [book](GncOptionSectionPtr& section) + { + section->foreach_option( + [book, §ion](auto& option) { + if (option.is_changed()) + { + // qof_book_set_option wants a GSList path. Let's avoid + // allocating and make one here. + GSList list_tail{(void*)option.get_name().c_str(), nullptr}; + GSList list_head{(void*)section->get_name().c_str(), &list_tail}; + auto type{option.get_ui_type()}; + if (type == GncOptionUIType::BOOLEAN) + { + auto val{option.template get_value()}; + auto kvp{new KvpValue(val ? "t" : "f")}; + qof_book_set_option(book, kvp, &list_head); + } + else if (type > GncOptionUIType::DATE_FORMAT) + { + const QofInstance* inst{QOF_INSTANCE(option.template get_value())}; + auto guid = guid_copy(qof_instance_get_guid(inst)); + auto kvp{new KvpValue(guid)}; + qof_book_set_option(book, kvp, &list_head); + } + else if (type == GncOptionUIType::NUMBER_RANGE) + { + auto kvp{new KvpValue(option.template get_value())}; + qof_book_set_option(book, kvp, &list_head); + } + else + { + auto kvp{new KvpValue{g_strdup(option.template get_value().c_str())}}; + qof_book_set_option(book, kvp, &list_head); + } + } + }); + }); } void GncOptionDB::load_from_kvp(QofBook* book) noexcept { - for (auto& section : m_sections) - { - auto& [s_name, s_vec] = section; - for (auto& option : s_vec) + foreach_section( + [book](GncOptionSectionPtr& section) { - /* qof_book_set_option wants a GSList path. Let's avoid allocating - * and make one here. - */ - GSList list_tail{(void*)option.get_name().c_str(), nullptr}; - GSList list_head{(void*)s_name.c_str(), &list_tail}; - auto kvp = qof_book_get_option(book, &list_head); - if (!kvp) - continue; - switch (kvp->get_type()) - { - case KvpValue::Type::INT64: - option.set_value(kvp->get()); - break; - case KvpValue::Type::STRING: + section->foreach_option( + [book, §ion](auto& option) { - auto str{kvp->get()}; - if (option.get_ui_type() == GncOptionUIType::BOOLEAN) - option.set_value(*str == 't' ? true : false); - else - option.set_value(str); - break; - } - case KvpValue::Type::GUID: - { - auto guid{kvp->get()}; - option.set_value((const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type())); - break; - } - default: - continue; - break; - } - } - } + /* qof_book_set_option wants a GSList path. Let's avoid allocating + * and make one here. + */ + GSList list_tail{(void*)option.get_name().c_str(), nullptr}; + GSList list_head{(void*)section->get_name().c_str(), &list_tail}; + auto kvp = qof_book_get_option(book, &list_head); + if (!kvp) + return; + switch (kvp->get_type()) + { + case KvpValue::Type::INT64: + option.set_value(kvp->get()); + break; + case KvpValue::Type::STRING: + { + auto str{kvp->get()}; + if (option.get_ui_type() == GncOptionUIType::BOOLEAN) + option.set_value(*str == 't' ? true : false); + else + option.set_value(str); + break; + } + case KvpValue::Type::GUID: + { + auto guid{kvp->get()}; + option.set_value((const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type())); + break; + } + default: + return; + break; + } + }); + }); } GncOptionDBPtr diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 823f51c312..c850f52415 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -342,6 +342,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } %} +%ignore gnc_option_to_scheme; +%ignore gnc_option_from_scheme; + %include "gnc-option-date.hpp" %include "gnc-option.hpp" %include "gnc-option-impl.hpp" @@ -387,7 +390,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); auto db_opt = optiondb->find_option(section, name); if (!db_opt) return SCM_BOOL_F; - return GncOption_get_scm_value(&(db_opt->get())); + return GncOption_get_scm_value(db_opt); } static SCM @@ -397,7 +400,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); auto db_opt = optiondb->find_option(section, name); if (!db_opt) return SCM_BOOL_F; - return GncOption_get_scm_default_value(&(db_opt->get())); + return GncOption_get_scm_default_value(db_opt); } static void @@ -410,7 +413,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); // PWARN("Attempt to write non-existent option %s/%s", section, name); return; } - GncOption_set_value_from_scm(&(db_opt->get()), new_value); + GncOption_set_value_from_scm(db_opt, new_value); } %} diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 9935773425..70754bea78 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -131,8 +131,8 @@ TEST_F(GncOptionDBTest, test_register_account_list_option) auto acclist{gnc_account_list_from_types(book.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().get_value().size()); - EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value().at(3)); + 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(GncOptionDBTest, test_register_account_list_limited_option) @@ -142,8 +142,8 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option) gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", acclist, {ACCT_TYPE_STOCK}); - EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value().size()); - EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value().at(3)); + EXPECT_EQ(4, 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(GncOptionDBTest, test_register_account_sel_limited_option) @@ -154,8 +154,8 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option) gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", accsel, {ACCT_TYPE_STOCK}); - EXPECT_EQ(1, m_db->find_option("foo", "bar")->get().get_value().size()); - EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value().at(0)); + EXPECT_EQ(1, 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(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct) @@ -204,7 +204,7 @@ TEST_F(GncOptionDBTest, test_register_relative_date_option) 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().get_value()); + EXPECT_EQ(time1, m_db->find_option("foo", "bar")->get_value()); } TEST_F(GncOptionDBTest, test_register_absolute_date_option) @@ -217,7 +217,7 @@ TEST_F(GncOptionDBTest, test_register_absolute_date_option) 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().get_value()); + m_db->find_option("foo", "bar")->get_value()); } /* Copied from gnc-optiondb.cpp for the purpose of finding the index of the @@ -243,15 +243,15 @@ TEST_F(GncOptionDBTest, test_register_start_date_option) 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().get_value()); + 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().get_value()); + m_db->find_option("foo", "bar")->get_value()); EXPECT_EQ(RelativeDatePeriod::ABSOLUTE, - m_db->find_option("foo", "bar")->get().get_value()); + 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().get_value()); + m_db->find_option("foo", "bar")->get_value()); auto index(std::find(begin_dates.begin(), begin_dates.end(), RelativeDatePeriod::START_THIS_MONTH) - begin_dates.begin()); @@ -259,12 +259,12 @@ TEST_F(GncOptionDBTest, test_register_start_date_option) * gnc-optiondb.cpp. */ EXPECT_EQ(index, - m_db->find_option("foo", "bar")->get().get_value()); + 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().get_value()); + m_db->find_option("foo", "bar")->get_value()); m_db->set_option("foo", "bar", static_cast(5)); - EXPECT_EQ(5, m_db->find_option("foo", "bar")->get().get_value()); + EXPECT_EQ(5, m_db->find_option("foo", "bar")->get_value()); } @@ -335,7 +335,7 @@ TEST_F(GncOptionDBIOTest, test_option_scheme_output) oss.clear(); 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()); + EXPECT_TRUE(m_db->find_option("foo", "sausage")->is_changed()); oss.flush(); m_db->save_option_scheme(oss, "option", "foo", "sausage"); EXPECT_STREQ("(let ((option (gnc:lookup-option option\n" @@ -373,7 +373,7 @@ TEST_F(GncOptionDBIOTest, test_date_interval_option_scheme_input) gnc_gdate_set_month_end(&month_end); auto time1 = time64_from_gdate(&month_end, DayPart::end); m_db->load_option_scheme(iss); - EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value()); + EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value()); } @@ -389,10 +389,10 @@ TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input) input += hpe_guid + "\" \""; input += msft_guid + "\")))) option))\n\n"; std::istringstream iss{input}; - EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value()[0]); + EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value()[0]); m_db->load_option_scheme(iss); - EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value()[1]); - EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value().size()); + EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value()[1]); + EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get_value().size()); } @@ -427,12 +427,12 @@ TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input) g_date_subtract_months(&month_end, 1); gnc_gdate_set_month_end(&month_end); auto time1 = time64_from_gdate(&month_end, DayPart::end); - EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get().get_value()[0]); + EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value()[0]); m_db->load_from_scheme(iss); EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); - EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get().get_value()); - EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value()[1]); - EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value().size()); + EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value()); + EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value()[1]); + EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get_value().size()); } From 67508ea039d3cffd4403c2c0857729bed229f44d Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 15 Dec 2020 10:26:26 -0800 Subject: [PATCH 098/298] Reimplement dialog-options for C++. --- gnucash/gnome-utils/CMakeLists.txt | 4 +- gnucash/gnome-utils/dialog-options.cpp | 2887 ++++++++++++++++++++++++ gnucash/gnome-utils/dialog-options.h | 73 +- gnucash/gnome-utils/gnc-gnome-utils.c | 2 + gnucash/gnome/business-options-gnome.c | 68 +- gnucash/gnucash.cpp | 3 + libgnucash/app-utils/gnc-option.cpp | 4 +- libgnucash/engine/qofbook.h | 5 + po/POTFILES.in | 2 + 9 files changed, 2957 insertions(+), 91 deletions(-) create mode 100644 gnucash/gnome-utils/dialog-options.cpp diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt index 35efced09e..76fe198fcf 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 @@ -59,7 +59,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 diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp new file mode 100644 index 0000000000..9b04423b7f --- /dev/null +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -0,0 +1,2887 @@ +/********************************************************************\ + * dialog-options.cpp -- 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 * +\********************************************************************/ + +extern "C" +{ +#include +} + +#include // To include as C++ overriding later indirect includes +#include "dialog-options.h" +extern "C" +{ +#include +#include +#include +#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-combott.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 + +#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; + +template inline const QofInstance* +qof_instance_cast(Instance inst) +{ + static_assert(std::is_pointer_v, "Pointers Only!"); + return reinterpret_cast(inst); +} + +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" + +struct gnc_option_win +{ + GtkWidget * window; + GtkWidget * notebook; + GtkWidget * page_list_view; + GtkWidget * page_list; + + bool 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 */ + bool destroyed; +}; + +enum page_tree +{ + PAGE_INDEX = 0, + PAGE_NAME, + NUM_COLUMNS +}; + +class GncOptionGtkUIItem : public GncOptionUIItem +{ +public: + GncOptionGtkUIItem(GtkWidget* widget, GncOptionUIType type) : + GncOptionUIItem(type), + m_widget{static_cast(g_object_ref(widget))} {} + GncOptionGtkUIItem(const GncOptionGtkUIItem& item) : + GncOptionUIItem{item.get_ui_type()}, + m_widget{static_cast(g_object_ref(item.get_widget()))} {} + GncOptionGtkUIItem(GncOptionGtkUIItem&&) = default; + virtual ~GncOptionGtkUIItem() override + { + if (m_widget) + g_object_unref(m_widget); + } + void clear_ui_item() override + { + if (m_widget) + g_object_unref(m_widget); + m_widget = nullptr; + } + void 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)); + } + virtual GtkWidget* const get_widget() const { return m_widget; } + +private: + GtkWidget* m_widget; +}; + +static GNCOptionWinCallback global_help_cb = NULL; +gpointer global_help_cb_data = NULL; + +static void dialog_reset_cb(GtkWidget * w, gpointer data); +void dialog_list_select_cb (GtkTreeSelection *selection, + gpointer data); +static void component_close_handler (gpointer data); + +static void +section_reset_widgets(GncOptionSection* section) +{ +} + +GtkWidget* const +gnc_option_get_gtk_widget (const GncOption& option) +{ + 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) +{ + 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); + } + g_list_free (children); + } + } + g_list_free (children); + } + } + g_list_free (children); + } +} + +void +gnc_options_dialog_changed (GNCOptionWin *win) +{ + if (!win) return; + + dialog_changed_internal (win->window, TRUE); +} + +static void +widget_changed_cb(GtkWidget *widget, GncOption* option) +{ + const_cast(option->get_ui_item())->set_dirty(true); +} + +void +option_changed_cb(GtkWidget *dummy, GncOption* option) +{ + option->set_ui_item_from_option(); +} + + +/********************************************************************\ + * set_selectable * + * 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 +set_selectable (GncOption& option, bool selectable) +{ + auto widget = gnc_option_get_gtk_widget(option); + if (widget) + gtk_widget_set_sensitive (widget, selectable); +} + +template GtkWidget* +create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**, + bool*) +{ + return nullptr; +} + +static GtkWidget* option_widget_factory(GncOption& option, GtkGrid* page, + GtkLabel* name, char* description, + GtkWidget** enclosing, bool* packed); +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 = option_widget_factory(option, page_box, name_label, + documentation, &enclosing, &packed); + /* attach the name label to the first column of the grid and + align to the end */ + 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(GNCOptionWin* propertybox, GtkBox* page_content_box, + const char* name) +{ + auto page_count = gtk_notebook_page_num(GTK_NOTEBOOK(propertybox->notebook), + GTK_WIDGET(page_content_box)); + + if (propertybox->page_list_view) + { + /* Build the matching list item for selecting from large page sets */ + auto view = GTK_TREE_VIEW(propertybox->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(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); + + } + return page_count; +} + +static int +gnc_options_dialog_append_page(GNCOptionWin * propertybox, + GncOptionSectionPtr& section) +{ + auto name = section->get_name().c_str(); + if (!name) + 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), propertybox); + 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(propertybox->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(propertybox, page_content_box, name); +} + +/********************************************************************\ + * 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) +{ + gint default_page = -1; + + gint page; + + g_return_if_fail (propertybox != NULL); + g_return_if_fail (odb != NULL); + + propertybox->option_db = odb; + + auto num_sections = odb->num_sections(); + auto default_section = odb->get_default_section(); + + PINFO("Default Section name is %s", default_section->get_name().c_str()); + + odb->foreach_section( + [propertybox, default_section, &default_page] + (GncOptionSectionPtr& section) { + auto page = gnc_options_dialog_append_page(propertybox, section); + if (section.get() == default_section) + default_page = page; + }); + + /* call each option widget changed callbacks once at this point, now that + * all options widgets exist. + * + * Note that this may be superfluous as each create_option_widget + * specialization calls option.set_ui_item_from_option after creating the UI + * item. + */ + odb->foreach_section( + [](GncOptionSectionPtr& section) { + section->foreach_option( + [](GncOption& option) { + option.set_ui_item_from_option(); + }); + }); + + 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); + } + 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)); + 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 bool +gnc_options_dialog_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + GNCOptionWin *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) +{ + GNCOptionWin *win = static_cast(data); + gpointer val; + + 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( + [](GncOption& option) { + option.set_ui_item_from_option(); + const_cast(option.get_ui_item())->set_dirty(true); + }); + + dialog_changed_internal (win->window, TRUE); +} + +void +dialog_list_select_cb (GtkTreeSelection *selection, + gpointer data) +{ + GNCOptionWin * 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->notebook), index); +} + +static void +component_close_handler (gpointer data) +{ + GNCOptionWin *win = static_cast(data); + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window)); + gnc_options_dialog_cancel_button_cb (NULL, win); +} + +/* 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")); + + /* 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 (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); + + 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); + + component_id = gnc_register_gui_component (retval->component_class, + NULL, component_close_handler, + retval); + gnc_gui_component_set_session (component_id, gnc_get_current_session()); + + 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; +} + +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 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(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 (); + + auto ui_item{std::make_unique(GncGtkBooleanUIItem{widget})}; + + g_signal_connect(G_OBJECT(widget), "toggled", + G_CALLBACK(widget_changed_cb), &option); + + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_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(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)}; + + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(widget_changed_cb), &option); + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_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(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); + gtk_container_add (GTK_CONTAINER (scroll), widget); + + auto ui_item{std::make_unique(widget)}; + auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); + g_signal_connect(G_OBJECT(text_buffer), "changed", + G_CALLBACK(widget_changed_cb), &option); + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_option(); + + 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 currency = + GNC_COMMODITY(option.get_value()); + gnc_currency_edit_set_currency(widget, currency); + } + 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)}; + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(widget_changed_cb), &option); + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_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 commodity = + GNC_COMMODITY(option.get_value()); + gnc_general_select_set_selected(widget, commodity); + } + 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)}; + + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(widget_changed_cb), &option); + 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(widget_changed_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); + + /* GtkComboBox still does not support per-item tooltips, so have + created a basic one called Combott implemented in gnc-combott. + Have highlighted changes in this file with comments for when + the feature of per-item tooltips is implemented in gtk, + see https://bugs.gnucash.org/show_bug.cgi?id=303717 */ + + auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + /* Add values to the list store, entry and tooltip */ + for (auto i = 0; i < num_values; i++) + { + GtkTreeIter iter; + auto itemstring = option.permissible_value_name(i); + auto description = option.permissible_value_description(i); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, + (itemstring && *itemstring) ? _(itemstring) : "", 1, + (description && *description) ? _(description) : "", -1); + } + /* Create the new Combo with tooltip and add the store */ + auto widget = GTK_WIDGET(gnc_combott_new()); + g_object_set( G_OBJECT( widget ), "model", GTK_TREE_MODEL(store), NULL ); + g_object_unref(store); + + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(widget_changed_cb), &option); + + 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{GNC_COMBOTT(get_widget())}; + gnc_combott_set_active(widget, option.get_value()); + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + auto widget{GNC_COMBOTT(get_widget())}; + option.set_value(gnc_combott_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)}; + +//GncCombott doesn't emit a changed signal + option.set_ui_item(std::move(ui_item)); + option.set_ui_item_from_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 +}; + + +using GncDateEntryPtr = std::unique_ptr; + +class AbsoluteDateEntry : public GncDateEntry +{ +public: + AbsoluteDateEntry(GncOption& option); + ~AbsoluteDateEntry() { g_object_unref(G_OBJECT(m_entry)); } + 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); } +private: + GNCDateEdit* m_entry; +}; + +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; + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(option_changed_cb), &option); +} + +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() { g_object_unref(G_OBJECT(m_entry)); }; + 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; } +private: + GtkWidget* m_entry; +}; + + +RelativeDateEntry::RelativeDateEntry(GncOption& option) : + m_entry{GTK_WIDGET(gnc_combott_new())} + +{ + + /* GtkComboBox still does not support per-item tooltips, so have + created a basic one called Combott implemented in gnc-combott. + Have highlighted changes in this file with comments for when + the feature of per-item tooltips is implemented in gtk, + see https://bugs.gnucash.org/show_bug.cgi?id=303717 */ + + auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + /* Add values to the list store, entry and tooltip */ + auto num = option.num_permissible_values(); + for (auto 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, + option.permissible_value_description(index), -1); + } + + /* Create the new Combo with tooltip and add the store */ + g_object_set( G_OBJECT(m_entry), "model", GTK_TREE_MODEL(store), nullptr); + g_object_unref(store); + + g_signal_connect(G_OBJECT(m_entry), "changed", + G_CALLBACK(widget_changed_cb), &option); +} + +void +RelativeDateEntry::set_entry_from_option(GncOption& option) +{ + gnc_combott_set_active(GNC_COMBOTT(m_entry), option.get_value()); +} + +void +RelativeDateEntry::set_option_from_entry(GncOption& option) +{ + option.set_value(gnc_combott_get_active(GNC_COMBOTT(m_entry))); +} + +using AbsoluteDateEntryPtr = std::unique_ptr; +using RelativeDateEntryPtr = std::unique_ptr; + +class BothDateEntry : public GncDateEntry +{ +public: + BothDateEntry(GncOption& option); + ~BothDateEntry(); //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; +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; +}; + +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); + g_signal_connect(G_OBJECT(m_abs_button), "toggled", + G_CALLBACK(date_set_absolute_cb), &option); + 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); + +} + +BothDateEntry::~BothDateEntry() +{ + g_object_unref(G_OBJECT(m_abs_button)); + g_object_unref(G_OBJECT(m_rel_button)); +} + +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) +{ + if (use_absolute) + { + gtk_widget_set_sensitive(m_abs_entry->get_entry(), TRUE); + gtk_widget_set_sensitive(m_rel_entry->get_entry(), FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_abs_button), TRUE); + } + else + { + gtk_widget_set_sensitive(m_rel_entry->get_entry(), TRUE); + gtk_widget_set_sensitive(m_abs_entry->get_entry(), FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_rel_button), TRUE); + } + m_use_absolute = use_absolute; +} + +void +BothDateEntry::set_entry_from_option(GncOption& option) +{ + if (m_use_absolute) + m_abs_entry->set_entry_from_option(option); + else + m_rel_entry->set_entry_from_option(option); +} + +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); +} + + +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); + option_changed_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); + option_changed_cb(widget, option); + } +} + +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", type); + return nullptr; + break; + } + + option.set_ui_item_from_option(); + auto widget{gnc_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); + + 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(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); + widget_changed_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(gnc_option_get_gtk_widget (*option)); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); + gtk_tree_selection_unselect_all(selection); + widget_changed_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(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, 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(gnc_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); + widget_changed_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(); + for (auto account : accounts) + g_list_prepend(acc_list, static_cast(const_cast(account))); + 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(g_list_length(acc_list)); + for (auto node = acc_list; node; node = g_list_next(node)) + acc_vec.push_back(static_cast(node->data)); + g_list_free(acc_list); + option.set_value(acc_vec); + } +}; + +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); + 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(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); + + option.set_ui_item(std::make_unique(tree)); + option.set_ui_item_from_option(); + + return frame; +} + +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 = gnc_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(widget_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())}; + gnc_account_sel_set_account(widget, + GNC_ACCOUNT(option.get_value()), + 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(widget_changed_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(gnc_option_get_gtk_widget (*option)); + widget_changed_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(gnc_option_get_gtk_widget(*option)); + selection = gtk_tree_view_get_selection(view); + gtk_tree_selection_select_all(selection); + widget_changed_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(gnc_option_get_gtk_widget (*option)); + selection = gtk_tree_view_get_selection(view); + gtk_tree_selection_unselect_all(selection); + widget_changed_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::MULTICHOICE} {} + 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) +{ + GtkListStore *store; + GtkTreeView *view; + GtkTreeIter iter; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + + GtkWidget *button; + GtkWidget *frame; + GtkWidget *hbox; + GtkWidget *bbox; + + 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); + + auto num_values = option.num_permissible_values(); + for (auto i = 0; i < num_values; i++) + { + 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); + } + + 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(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(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(); + + return frame; +} + +template<> GtkWidget * +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) +{ + GtkWidget *value; + GtkWidget *eventbox; + gint grid_row = GPOINTER_TO_INT(g_object_get_data + (G_OBJECT(page_box), "options-grid-row")); + + *enclosing = create_list_widget(option, NULL); + value = gnc_option_get_gtk_widget (option); + + align_label (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); + + 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(widget_changed_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; + if (gdk_rgba_parse(&color, + option.get_value().c_str())) + { + auto color_button = GTK_COLOR_CHOOSER(get_widget()); + gtk_color_chooser_set_rgba(color_button, &color); + } + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + GdkRGBA color; + auto color_button = GTK_COLOR_CHOOSER(get_widget()); + gtk_color_chooser_set_rgba(color_button, &color); + auto rgba_str = gdk_rgba_to_string(&color); + option.set_value(rgba_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(widget_changed_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(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(widget_changed_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 = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(get_widget())); + DEBUG("filename %s", string ? string : "(null)"); + option.set_value(string); + g_free(string); + } + void set_option_from_ui_item(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); + } + } +}; + +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); + g_signal_connect(G_OBJECT (widget), "selection-changed", + G_CALLBACK(widget_changed_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); + + option.set_ui_item(std::make_unique(widget)); + option.set_ui_item_from_option(); + + 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); + 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)); + widget_changed_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_INT (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); + + /* Iterate over the options and create a radio button for each one */ + for (auto i = 0; i < num_values; i++) + { + auto label = option.permissible_value_name(i); + auto tip = option.permissible_value_description(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)); + gtk_widget_set_tooltip_text(widget, tip && *tip ? _(tip) : ""); + 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); + + option.set_ui_item(std::make_unique(widget)); + option.set_ui_item_from_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::STRING} {} + 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(widget_changed_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 = gnc_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); + option_changed_cb(widget, option); +} + +static void +gnc_rd_option_p_set_cb(GtkWidget *widget, GncOption* option) +{ + option->set_alternate(false); + option_changed_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); + + gtk_container_add(GTK_CONTAINER(*enclosing), hbox); + auto value_px = create_range_spinner(option); + + g_signal_connect(G_OBJECT(value_px), "changed", + G_CALLBACK(widget_changed_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(widget_changed_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), 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(); + + 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 budget{GNC_BUDGET(option.get_value())}; + auto tree_model{gtk_combo_box_get_model(widget)}; + if (gnc_tree_model_budget_get_iter_for_budget(tree_model, &iter, + budget)) + 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(widget_changed_cb), &option); + + gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); + gtk_widget_show_all(*enclosing); + return widget; +} + +static GtkWidget* +option_widget_factory(GncOption& option, GtkGrid* page, GtkLabel* name, + char* description, GtkWidget** enclosing, bool* packed) +{ + switch(option.get_ui_type()) + { + case INTERNAL: + return nullptr; + case BOOLEAN: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case STRING: + return create_option_widget(option, page, name, description, + enclosing, packed); + case TEXT: + return create_option_widget(option, page, name, description, + enclosing, packed); + case CURRENCY: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case COMMODITY: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case MULTICHOICE: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case DATE_ABSOLUTE: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case DATE_RELATIVE: + return create_option_widget(option, page, name, + description, + enclosing, packed); + case DATE_BOTH: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case ACCOUNT_LIST: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case ACCOUNT_SEL: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case LIST: + return create_option_widget(option, page, name, description, + enclosing, packed); + case NUMBER_RANGE: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case COLOR: + return create_option_widget(option, page, name, description, + enclosing, packed); + case FONT: + return create_option_widget(option, page, name, description, + enclosing, packed); + case PLOT_SIZE: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case BUDGET: + return create_option_widget(option, page, name, description, + enclosing, packed); + case PIXMAP: + return create_option_widget(option, page, name, description, + enclosing, packed); + case RADIOBUTTON: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case DATE_FORMAT: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case OWNER: + return create_option_widget(option, page, name, description, + enclosing, packed); + case CUSTOMER: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case VENDOR: + return create_option_widget(option, page, name, description, + enclosing, packed); + case EMPLOYEE: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case INVOICE: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case TAX_TABLE: + return create_option_widget(option, page, name, + description, enclosing, + packed); + case QUERY: + return create_option_widget(option, page, name, description, + enclosing, packed); + } +} + +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{gnc_option_get_gtk_widget(option)}; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (num_source_button), + num_split_action); + } +} + + +static void +gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat) +{ + gnc_gnome_help (GTK_WINDOW (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); +} + +/* Dummy function impls. The following functions are declared in + * dialog_options.h and used by clients but they're made obsolete by the new + * options system. They're here to satisfy the linker. + */ +GtkWidget* const +gnc_option_get_gtk_widget (GncOption *option) +{ + return nullptr; +} + +void +gnc_options_ui_initialize (void) +{ + return; +} diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h index 3000717348..b3613de210 100644 --- a/gnucash/gnome-utils/dialog-options.h +++ b/gnucash/gnome-utils/dialog-options.h @@ -24,12 +24,23 @@ #define OPTIONS_DIALOG_H #include -#include "option-util.h" #include +#ifdef __cplusplus +class GncOption; +class GncOptionDB; +using GNCOption = GncOption; +using GNCOptionDB = GncOptionDB; +extern "C" +{ +#else +#include "option-util.h" +typedef GNCOption GncOption; +typedef GNCOptionDB GncOptionDB; +#endif /** 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); +GtkWidget* const gnc_option_get_gtk_widget (GncOption *option); typedef struct gnc_option_win GNCOptionWin; @@ -39,7 +50,6 @@ 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); @@ -49,8 +59,8 @@ 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_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, @@ -67,50 +77,21 @@ void gnc_options_dialog_set_global_help_cb (GNCOptionWinCallback thunk, 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); +/** 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); +#ifdef __cplusplus +} +#endif #endif /* OPTIONS_DIALOG_H */ diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c index a771f3525b..58e4272a9b 100644 --- a/gnucash/gnome-utils/gnc-gnome-utils.c +++ b/gnucash/gnome-utils/gnc-gnome-utils.c @@ -99,6 +99,7 @@ gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat) gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_BOOK_OPTIONS); } +#if 0 // Reimplemented in dialog-options.cpp void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win) { @@ -129,6 +130,7 @@ gnc_options_dialog_set_new_book_option_values (GNCOptionDB *odb) num_source_is_split_action); } } +#endif static void gnc_style_sheet_options_help_cb (GNCOptionWin *win, gpointer dat) diff --git a/gnucash/gnome/business-options-gnome.c b/gnucash/gnome/business-options-gnome.c index afee518cae..a9bc680dd1 100644 --- a/gnucash/gnome/business-options-gnome.c +++ b/gnucash/gnome/business-options-gnome.c @@ -42,9 +42,10 @@ #include "dialog-invoice.h" #define FUNC_NAME G_STRFUNC - +/* To be consolidated into dialog-options.cpp later. */ +#if 0 static GtkWidget * -create_owner_widget (GNCOption *option, GncOwnerType type, GtkWidget *hbox) +create_owner_widget (GncOption *option, GncOwnerType type, GtkWidget *hbox) { GtkWidget *widget; GncOwner owner; @@ -91,7 +92,7 @@ make_name_label (char *name) static GncOwnerType -get_owner_type_from_option (GNCOption *option) +get_owner_type_from_option (GncOption *option) { SCM odata = gnc_option_get_option_data (option); @@ -102,7 +103,7 @@ get_owner_type_from_option (GNCOption *option) /* Function to set the UI widget based upon the option */ static GtkWidget * -owner_set_widget (GNCOption *option, GtkGrid *page_box, +owner_set_widget (GncOption *option, GtkGrid *page_box, GtkLabel *name_label, char *documentation, /* Return values */ GtkWidget **enclosing, gboolean *packed) @@ -123,7 +124,7 @@ owner_set_widget (GNCOption *option, GtkGrid *page_box, /* Function to set the UI Value for a particular option */ static gboolean -owner_set_value (GNCOption *option, gboolean use_default, +owner_set_value (GncOption *option, gboolean use_default, GtkWidget *widget, SCM value) { GncOwner owner_def; @@ -150,7 +151,7 @@ owner_set_value (GNCOption *option, gboolean use_default, /* Function to get the UI Value for a particular option */ static SCM -owner_get_value (GNCOption *option, GtkWidget *widget) +owner_get_value (GncOption *option, GtkWidget *widget) { static GncOwner owner; /* XXX: might cause trouble? */ GncOwnerType type; @@ -169,7 +170,7 @@ owner_get_value (GNCOption *option, GtkWidget *widget) /* Function to set the UI widget based upon the option */ static GtkWidget * -customer_set_widget (GNCOption *option, GtkGrid *page_box, +customer_set_widget (GncOption *option, GtkGrid *page_box, GtkLabel *name_label, char *documentation, /* Return values */ GtkWidget **enclosing, gboolean *packed) @@ -189,7 +190,7 @@ customer_set_widget (GNCOption *option, GtkGrid *page_box, /* Function to set the UI Value for a particular option */ static gboolean -customer_set_value (GNCOption *option, gboolean use_default, +customer_set_value (GncOption *option, gboolean use_default, GtkWidget *widget, SCM value) { GncOwner owner; @@ -209,7 +210,7 @@ customer_set_value (GNCOption *option, gboolean use_default, /* Function to get the UI Value for a particular option */ static SCM -customer_get_value (GNCOption *option, GtkWidget *widget) +customer_get_value (GncOption *option, GtkWidget *widget) { GncOwner owner; @@ -225,7 +226,7 @@ customer_get_value (GNCOption *option, GtkWidget *widget) /* Function to set the UI widget based upon the option */ static GtkWidget * -vendor_set_widget (GNCOption *option, GtkGrid *page_box, +vendor_set_widget (GncOption *option, GtkGrid *page_box, GtkLabel *name_label, char *documentation, /* Return values */ GtkWidget **enclosing, gboolean *packed) @@ -245,7 +246,7 @@ vendor_set_widget (GNCOption *option, GtkGrid *page_box, /* Function to set the UI Value for a particular option */ static gboolean -vendor_set_value (GNCOption *option, gboolean use_default, +vendor_set_value (GncOption *option, gboolean use_default, GtkWidget *widget, SCM value) { GncOwner owner; @@ -265,7 +266,7 @@ vendor_set_value (GNCOption *option, gboolean use_default, /* Function to get the UI Value for a particular option */ static SCM -vendor_get_value (GNCOption *option, GtkWidget *widget) +vendor_get_value (GncOption *option, GtkWidget *widget) { GncOwner owner; @@ -280,7 +281,7 @@ vendor_get_value (GNCOption *option, GtkWidget *widget) /* Function to set the UI widget based upon the option */ static GtkWidget * -employee_set_widget (GNCOption *option, GtkGrid *page_box, +employee_set_widget (GncOption *option, GtkGrid *page_box, GtkLabel *name_label, char *documentation, /* Return values */ GtkWidget **enclosing, gboolean *packed) @@ -300,7 +301,7 @@ employee_set_widget (GNCOption *option, GtkGrid *page_box, /* Function to set the UI Value for a particular option */ static gboolean -employee_set_value (GNCOption *option, gboolean use_default, +employee_set_value (GncOption *option, gboolean use_default, GtkWidget *widget, SCM value) { GncOwner owner; @@ -320,7 +321,7 @@ employee_set_value (GNCOption *option, gboolean use_default, /* Function to get the UI Value for a particular option */ static SCM -employee_get_value (GNCOption *option, GtkWidget *widget) +employee_get_value (GncOption *option, GtkWidget *widget) { GncOwner owner; @@ -335,7 +336,7 @@ employee_get_value (GNCOption *option, GtkWidget *widget) static GtkWidget * -create_invoice_widget (GNCOption *option, GtkWidget *hbox) +create_invoice_widget (GncOption *option, GtkWidget *hbox) { GtkWidget *widget; @@ -352,7 +353,7 @@ create_invoice_widget (GNCOption *option, GtkWidget *hbox) /* Function to set the UI widget based upon the option */ static GtkWidget * -invoice_set_widget (GNCOption *option, GtkGrid *page_box, +invoice_set_widget (GncOption *option, GtkGrid *page_box, GtkLabel *name_label, char *documentation, /* Return values */ GtkWidget **enclosing, gboolean *packed) @@ -372,7 +373,7 @@ invoice_set_widget (GNCOption *option, GtkGrid *page_box, /* Function to set the UI Value for a particular option */ static gboolean -invoice_set_value (GNCOption *option, gboolean use_default, +invoice_set_value (GncOption *option, gboolean use_default, GtkWidget *widget, SCM value) { GncInvoice *invoice; @@ -390,7 +391,7 @@ invoice_set_value (GNCOption *option, gboolean use_default, /* Function to get the UI Value for a particular option */ static SCM -invoice_get_value (GNCOption *option, GtkWidget *widget) +invoice_get_value (GncOption *option, GtkWidget *widget) { GncInvoice *invoice; @@ -404,7 +405,7 @@ invoice_get_value (GNCOption *option, GtkWidget *widget) static GtkWidget * -create_taxtable_widget (GNCOption *option, GtkWidget *hbox) +create_taxtable_widget (GncOption *option, GtkWidget *hbox) { GtkWidget *widget; GtkBuilder *builder; @@ -428,7 +429,7 @@ create_taxtable_widget (GNCOption *option, GtkWidget *hbox) /* Function to set the UI widget based upon the option */ static GtkWidget * -taxtable_set_widget (GNCOption *option, GtkGrid *page_box, +taxtable_set_widget (GncOption *option, GtkGrid *page_box, GtkLabel *name_label, char *documentation, /* Return values */ GtkWidget **enclosing, gboolean *packed) @@ -448,7 +449,7 @@ taxtable_set_widget (GNCOption *option, GtkGrid *page_box, /* Function to set the UI Value for a particular option */ static gboolean -taxtable_set_value (GNCOption *option, gboolean use_default, +taxtable_set_value (GncOption *option, gboolean use_default, GtkWidget *widget, SCM value) { GncTaxTable *taxtable; @@ -466,7 +467,7 @@ taxtable_set_value (GNCOption *option, gboolean use_default, /* Function to get the UI Value for a particular option */ static SCM -taxtable_get_value (GNCOption *option, GtkWidget *widget) +taxtable_get_value (GncOption *option, GtkWidget *widget) { GncTaxTable *taxtable; @@ -474,28 +475,11 @@ taxtable_get_value (GNCOption *option, GtkWidget *widget) return SWIG_NewPointerObj(taxtable, SWIG_TypeQuery("_p__gncTaxTable"), 0); } - +#endif 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])); +/* Create the above option types. */ } diff --git a/gnucash/gnucash.cpp b/gnucash/gnucash.cpp index d7a76466a0..0592960ee8 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 diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 42c069742d..50b087a4a4 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -505,7 +505,6 @@ GncOption::from_scheme(std::istream& iss) * the template implementation in the public header. */ -using GncOptionAccountList = std::vector; template class GncOptionValidatedValue; @@ -544,17 +543,20 @@ 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 RelativeDatePeriod GncOption::get_default_value() const; +template GncOptionAccountList GncOption::get_default_value() const; template GncMultichoiceOptionIndexVec 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(RelativeDatePeriod); template void GncOption::set_value(size_t); +template void GncOption::set_value(GncOptionAccountList); template void GncOption::set_value(GncMultichoiceOptionIndexVec); template void GncOption::get_limits(double&, double&, double&) const noexcept; diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h index b4401382b1..7c0d330355 100644 --- a/libgnucash/engine/qofbook.h +++ b/libgnucash/engine/qofbook.h @@ -74,7 +74,12 @@ typedef struct KvpValueImpl KvpValue; typedef void (*QofBookDirtyCB) (QofBook *, gboolean dirty, gpointer user_data); +#ifdef __cplusplus +class GncOptionDB; +using GNCOptionDB = GncOptionDB; +#else typedef struct gnc_option_db GNCOptionDB; +#endif typedef void (*GNCOptionSave) (GNCOptionDB*, QofBook*, gboolean); typedef void (*GNCOptionLoad) (GNCOptionDB*, QofBook*); diff --git a/po/POTFILES.in b/po/POTFILES.in index 39773851c6..84ae44d040 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -136,6 +136,7 @@ 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 @@ -528,6 +529,7 @@ libgnucash/app-utils/gnc-gsettings.c 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 From eb6e31f8036614e87e998e2483f0c90ac8c3d5b8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 24 Mar 2020 11:06:57 -0700 Subject: [PATCH 099/298] dialog-options: Change signature of gnc_option_get_gtk_widget() So that it can be called from C. --- gnucash/gnome-utils/dialog-options.cpp | 29 ++++++++++++---------- libgnucash/app-utils/gnc-option.cpp | 2 +- libgnucash/app-utils/gnc-option.hpp | 2 +- libgnucash/app-utils/gnc-optiondb-impl.hpp | 1 + libgnucash/app-utils/gnc-optiondb.cpp | 8 +++++- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 9b04423b7f..d76c59cfe2 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -189,9 +189,10 @@ section_reset_widgets(GncOptionSection* section) } GtkWidget* const -gnc_option_get_gtk_widget (const GncOption& option) +gnc_option_get_gtk_widget (const GncOption* option) { - auto ui_item{dynamic_cast(option.get_ui_item())}; + if (!option) return nullptr; + auto ui_item{dynamic_cast(option->get_ui_item())}; if (ui_item) return ui_item->get_widget(); @@ -250,12 +251,14 @@ gnc_options_dialog_changed (GNCOptionWin *win) static void widget_changed_cb(GtkWidget *widget, GncOption* option) { + if (!option) return; const_cast(option->get_ui_item())->set_dirty(true); } void option_changed_cb(GtkWidget *dummy, GncOption* option) { + if (!option) return; option->set_ui_item_from_option(); } @@ -274,7 +277,7 @@ option_changed_cb(GtkWidget *dummy, GncOption* option) static void set_selectable (GncOption& option, bool selectable) { - auto widget = gnc_option_get_gtk_widget(option); + auto widget = gnc_option_get_gtk_widget(&option); if (widget) gtk_widget_set_sensitive (widget, selectable); } @@ -1490,7 +1493,7 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box, } option.set_ui_item_from_option(); - auto widget{gnc_option_get_gtk_widget(option)}; + auto widget{gnc_option_get_gtk_widget(&option)}; if (type == GncOptionUIType::DATE_RELATIVE) { *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); @@ -1570,7 +1573,7 @@ account_select_all_cb(GtkWidget *widget, gpointer data) GncTreeViewAccount *tree_view; GtkTreeSelection *selection; - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (*option)); + 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); @@ -1584,7 +1587,7 @@ account_clear_all_cb(GtkWidget *widget, gpointer data) GncTreeViewAccount *tree_view; GtkTreeSelection *selection; - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (*option)); + 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); widget_changed_cb(widget, option); @@ -1597,7 +1600,7 @@ account_select_children_cb(GtkWidget *widget, gpointer data) GncTreeViewAccount *tree_view; GList *acct_list = NULL, *acct_iter = NULL; - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (*option)); + 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) @@ -1815,7 +1818,7 @@ create_option_widget(GncOption& option, gtk_grid_attach (GTK_GRID(page_box), *enclosing, 1, grid_row, 1, 1); *packed = TRUE; - auto widget = gnc_option_get_gtk_widget(option); + auto widget = gnc_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(widget_changed_cb), &option); @@ -1877,7 +1880,7 @@ create_option_widget (GncOption& option, static void list_changed_cb(GtkTreeSelection *selection, GncOption* option) { - GtkTreeView *view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (*option)); + GtkTreeView *view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option)); widget_changed_cb(GTK_WIDGET(view), option); } @@ -1888,7 +1891,7 @@ list_select_all_cb(GtkWidget *widget, gpointer data) GtkTreeView *view; GtkTreeSelection *selection; - view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(*option)); + view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option)); selection = gtk_tree_view_get_selection(view); gtk_tree_selection_select_all(selection); widget_changed_cb(GTK_WIDGET(view), option); @@ -1901,7 +1904,7 @@ list_clear_all_cb(GtkWidget *widget, gpointer data) GtkTreeView *view; GtkTreeSelection *selection; - view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (*option)); + view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option)); selection = gtk_tree_view_get_selection(view); gtk_tree_selection_unselect_all(selection); widget_changed_cb(GTK_WIDGET(view), option); @@ -2045,7 +2048,7 @@ create_option_widget (GncOption& option, (G_OBJECT(page_box), "options-grid-row")); *enclosing = create_list_widget(option, NULL); - value = gnc_option_get_gtk_widget (option); + auto value = gnc_option_get_gtk_widget(&option); align_label (name_label); @@ -2524,7 +2527,7 @@ gnc_plot_size_option_set_select_method(GncOption& option, bool set_buttons) GtkWidget *px_widget, *p_widget; GtkWidget *widget; - widget = gnc_option_get_gtk_widget (option); + widget = gnc_option_get_gtk_widget(&option); widget_list = gtk_container_get_children(GTK_CONTAINER(widget)); // px_button item 0 diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 50b087a4a4..c50f0a04f4 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -192,7 +192,7 @@ GncOption::get_ui_type() const }, *m_option); } -const GncOptionUIItem* +GncOptionUIItem* const GncOption::get_ui_item() const { return m_ui_item.get(); diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 2d901d3037..72ba3fedd9 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -81,7 +81,7 @@ public: const std::string& get_docstring() const; void set_ui_item(GncOptionUIItemPtr&& ui_elem); const GncOptionUIType get_ui_type() const; - const GncOptionUIItem* get_ui_item() const; + GncOptionUIItem* const get_ui_item() const; void set_ui_item_from_option(); void set_option_from_ui_item(); void make_internal(); diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index 372f558d92..0a87756bb7 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -51,6 +51,7 @@ public: ~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); diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 36367aace4..fd2b953290 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -43,6 +43,12 @@ 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) { @@ -615,7 +621,7 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept [book](GncOptionSectionPtr& section) { section->foreach_option( - [book, §ion](auto& option) + [book, §ion](GncOption& option) { /* qof_book_set_option wants a GSList path. Let's avoid allocating * and make one here. From f9e136dbace3c44eeb444c9b574734008ac4295c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 26 Mar 2020 16:09:02 -0700 Subject: [PATCH 100/298] Get the spelling of GncMultichoiceOptionEntry consistent. --- libgnucash/app-utils/gnc-optiondb.cpp | 4 ++-- libgnucash/app-utils/gnc-optiondb.hpp | 8 ++++---- libgnucash/app-utils/gnc-optiondb.i | 2 +- libgnucash/app-utils/test/gtest-gnc-optiondb.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index fd2b953290..c149e083dc 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -839,7 +839,7 @@ void gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, - GncMultiChoiceOptionChoices&& choices) + GncMultichoiceOptionChoices&& choices) { GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string, std::get<0>(choices.at(0)).c_str(), std::move(choices)}}; @@ -850,7 +850,7 @@ void gnc_register_list_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, const char* value, - GncMultiChoiceOptionChoices&& list) + GncMultichoiceOptionChoices&& list) { GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string, value, std::move(list), GncOptionUIType::LIST}}; diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 26d5a3c892..399316222d 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -47,10 +47,10 @@ class GncOptionDB; using GncOptionAccountList = std::vector; using GncOptionAccountTypeList = std::vector; -using GncMultiChoiceOptionEntry = std::tuple; -using GncMultiChoiceOptionChoices = std::vector; +using GncMultichoiceOptionChoices = std::vector; /** * Extract a list of accounts in the book having one of the GNCAccountTypes in @@ -141,12 +141,12 @@ void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db, void gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, - GncMultiChoiceOptionChoices&& value); + GncMultichoiceOptionChoices&& value); void gnc_register_list_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, const char* value, - GncMultiChoiceOptionChoices&& list); + GncMultichoiceOptionChoices&& list); void gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section, const char* name, diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index c850f52415..db2a19b4d5 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -233,7 +233,7 @@ gnc_option_test_book_destroy(QofBook* book) $1 = &period_set; } -%typemap(in) GncMultiChoiceOptionChoices&& (GncMultiChoiceOptionChoices choices) +%typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices) { auto len = scm_to_size_t(scm_length($input)); for (std::size_t i = 0; i < len; ++i) diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 70754bea78..a34612c2e4 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -174,7 +174,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct) TEST_F(GncOptionDBTest, test_register_multichoice_option) { - GncMultiChoiceOptionChoices choices{ + GncMultichoiceOptionChoices choices{ { "plugh", "xyzzy", "thud"}, { "waldo", "pepper", "salt"}, { "pork", "sausage", "links"}, From 6feb92d4e8f88b03408787e4f5b2a43fd9003ed8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 26 Mar 2020 16:56:07 -0700 Subject: [PATCH 101/298] Provide scheme function new-gnc-optiondb. Wraps creating a GncOptionDBPtr, freeing up gnc_option_db_new() to return a GncOptionDB* for use in C code. Convert gnc_option_db_new() calls in gtest-gnc-optiondb to call std::make_unique() as well. --- libgnucash/app-utils/gnc-optiondb.i | 7 +++++++ .../app-utils/test/gtest-gnc-optiondb.cpp | 6 ++++-- .../app-utils/test/test-gnc-optiondb.scm | 18 +++++++++--------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index db2a19b4d5..61b235feba 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -415,6 +415,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } GncOption_set_value_from_scm(db_opt, new_value); } + + GncOptionDBPtr + new_gnc_optiondb() + { + auto db_ptr{std::make_unique()}; + return db_ptr; + } %} #endif //SWIGGUILE diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index a34612c2e4..963626b461 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -36,7 +36,7 @@ extern "C" class GncOptionDBTest : public ::testing::Test { protected: - GncOptionDBTest() : m_db{gnc_option_db_new()} {} + GncOptionDBTest() : m_db{std::make_unique()} {} GncOptionDBPtr m_db; }; @@ -271,7 +271,9 @@ TEST_F(GncOptionDBTest, test_register_start_date_option) class GncOptionDBIOTest : public ::testing::Test { protected: - GncOptionDBIOTest() : m_book{gnc_get_current_book()}, m_root{gnc_account_create_root(m_book)}, m_db{gnc_option_db_new()} + 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* { diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 89f683c81f..5a407f31b3 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -45,7 +45,7 @@ (define (test-gnc-make-text-option) (test-begin "test-gnc-test-string-option") - (let* ((option-db (gnc-option-db-new)) + (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")) @@ -89,7 +89,7 @@ (define (test-make-account-list-option book) (test-group "test-make-account-list-option" - (let ((optiondb (gnc-option-db-new)) + (let ((optiondb (new-gnc-optiondb)) (acctlist (gnc-account-list-from-types book (list ACCT-TYPE-STOCK)))) (gnc-register-account-list-option optiondb "foo" "bar" "baz" @@ -100,7 +100,7 @@ (define (test-make-account-list-limited-option book) (test-group "test-make-account-list-option" - (let ((optiondb (gnc-option-db-new)) + (let ((optiondb (new-gnc-optiondb)) (acctlist (gnc-account-list-from-types book (list ACCT-TYPE-STOCK)))) (gnc-register-account-list-limited-option optiondb "foo" "bar" "baz" @@ -115,7 +115,7 @@ (define (test-make-account-sel-limited-option book) (test-group "test-make-account-list-option" - (let ((optiondb (gnc-option-db-new)) + (let ((optiondb (new-gnc-optiondb)) (acctlist (gnc-account-list-from-types book (list ACCT-TYPE-STOCK)))) (gnc-register-account-sel-limited-option optiondb "salt" "pork" "baz" @@ -148,7 +148,7 @@ (assq-ref (assq-ref keylist key) info)) (test-begin "test-gnc-test-multichoice-option") - (let* ((option-db (gnc-option-db-new)) + (let* ((option-db (new-gnc-optiondb)) (multilist (list (list "plugh" (cons 'text "xyzzy") (cons 'tip "thud")) (list "waldo" (cons 'text "pepper") (cons 'tip "salt")) @@ -166,7 +166,7 @@ (define (test-gnc-make-list-option) (test-begin "test-gnc-test-list-option") - (let* ((option-db (gnc-option-db-new)) + (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"))) @@ -181,7 +181,7 @@ (define (test-gnc-make-date-option) (test-begin "test-gnc-test-date-option") - (let* ((option-db (gnc-option-db-new)) + (let* ((option-db (new-gnc-optiondb)) (date-opt (gnc-register-date-option option-db "foo" "bar" "baz" "Phony Option" (RelativeDatePeriod-today))) @@ -193,7 +193,7 @@ (define (test-gnc-make-date-set-option) (test-begin "test-gnc-test-date-set-option") - (let* ((option-db (gnc-option-db-new)) + (let* ((option-db (new-gnc-optiondb)) (date-opt (gnc-register-date-option-set option-db "foo" "bar" "baz" "Phony Option" (list (RelativeDatePeriod-today) @@ -210,7 +210,7 @@ (define (test-gnc-make-number-range-option) (test-begin "test-gnc-number-range-option") - (let* ((option-db (gnc-option-db-new)) + (let* ((option-db (new-gnc-optiondb)) (number-opt (gnc-register-number-range-option option-db "foo" "bar" "baz" "Phony Option" 15 5 30 1))) From 4451f58bd6e8c2f6fb7556164d004b1a4b999ecf Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 26 Mar 2020 17:09:51 -0700 Subject: [PATCH 102/298] Rename RelativeDatePeriods missed earlier. --- libgnucash/app-utils/gnc-optiondb.i | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 61b235feba..48f4492339 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -202,14 +202,20 @@ gnc_option_test_book_destroy(QofBook* book) %rename(end_this_month) RelativeDatePeriod::END_THIS_MONTH; %rename(start_prev_month) RelativeDatePeriod::START_PREV_MONTH; %rename(end_prev_month) RelativeDatePeriod::END_PREV_MONTH; +%rename(start_next_month) RelativeDatePeriod::START_NEXT_MONTH; +%rename(end_next_month) RelativeDatePeriod::END_NEXT_MONTH; %rename(start_current_quarter) RelativeDatePeriod::START_CURRENT_QUARTER; %rename(end_current_quarter) RelativeDatePeriod::END_CURRENT_QUARTER; %rename(start_prev_quarter) RelativeDatePeriod::START_PREV_QUARTER; %rename(end_prev_quarter) RelativeDatePeriod::END_PREV_QUARTER; +%rename(start_next_quarter) RelativeDatePeriod::START_NEXT_QUARTER; +%rename(end_next_quarter) RelativeDatePeriod::END_NEXT_QUARTER; %rename(start_cal_year) RelativeDatePeriod::START_CAL_YEAR; -%rename(end_cal_yea) RelativeDatePeriod::END_CAL_YEAR; +%rename(end_cal_year) RelativeDatePeriod::END_CAL_YEAR; %rename(start_prev_year) RelativeDatePeriod::START_PREV_YEAR; %rename(end_prev_year) RelativeDatePeriod::END_PREV_YEAR; +%rename(start_next_year) RelativeDatePeriod::START_NEXT_YEAR; +%rename(end_next_year) RelativeDatePeriod::END_NEXT_YEAR; %rename(start_accounting_period) RelativeDatePeriod::START_ACCOUNTING_PERIOD; %rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD; From 25b717d47a59e10e7930e36832426e75b0f4cbd8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 26 Mar 2020 17:43:52 -0700 Subject: [PATCH 103/298] Add a rudimentary C API for GncOptionDB. So that most of the gnome-util and gnome consumers don't need to be converted to C++. Hide the corresponding functions in options-utils to stop the compiler whining. Note that this commit breaks the build but is necessary at least temporarily to compartmentalize the changes. --- gnucash/gnome-utils/dialog-options.cpp | 75 ++++++++-------- gnucash/gnome-utils/dialog-options.h | 15 +++- gnucash/gnome-utils/gnc-main-window.c | 22 +++-- gnucash/gnome-utils/gnc-main-window.h | 13 --- gnucash/gnome/assistant-hierarchy.c | 2 + libgnucash/app-utils/CMakeLists.txt | 1 + libgnucash/app-utils/app-utils.i | 17 ++-- libgnucash/app-utils/app-utils.scm | 41 --------- libgnucash/app-utils/business-prefs.scm | 2 +- libgnucash/app-utils/gnc-optiondb.cpp | 86 ++++++++++++++++-- libgnucash/app-utils/gnc-optiondb.h | 112 ++++++++++++++++++++++++ libgnucash/app-utils/gnc-optiondb.hpp | 9 -- libgnucash/app-utils/option-util.c | 12 ++- libgnucash/app-utils/option-util.h | 16 ++-- 14 files changed, 279 insertions(+), 144 deletions(-) create mode 100644 libgnucash/app-utils/gnc-optiondb.h diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index d76c59cfe2..f5efcaf8a8 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -24,11 +24,11 @@ extern "C" { -#include +#include // Need this to include Account.h } #include // To include as C++ overriding later indirect includes -#include "dialog-options.h" +#include extern "C" { #include @@ -62,6 +62,8 @@ extern "C" #include "misc-gnome-utils.h" } +#include "dialog-options.h" +#include #include #define GNC_PREF_CLOCK_24H "clock-24h" @@ -204,8 +206,7 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive) { while (widget && !GTK_IS_WINDOW(widget)) widget = gtk_widget_get_parent(widget); - if (widget == NULL) - return; + if (!widget) 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 */ @@ -263,17 +264,16 @@ option_changed_cb(GtkWidget *dummy, GncOption* option) } -/********************************************************************\ +/* * set_selectable * - * 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 * -\********************************************************************/ + * Change the selectable state of the widget that represents a + * GUI option. + * + * 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. + */ static void set_selectable (GncOption& option, bool selectable) { @@ -282,6 +282,7 @@ set_selectable (GncOption& option, bool selectable) gtk_widget_set_sensitive (widget, selectable); } +// This do-nothing template is specialized for each GncOptionUIType. template GtkWidget* create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**, bool*) @@ -489,9 +490,8 @@ gnc_options_dialog_append_page(GNCOptionWin * propertybox, * 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 * + * @param propertybox - gnome property box to use * + * @param odb - option database to use * \********************************************************************/ void gnc_options_dialog_build_contents (GNCOptionWin *propertybox, @@ -506,10 +506,9 @@ gnc_options_dialog_build_contents (GNCOptionWin *propertybox, * 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 * + * @param propertybox - gnome property box to use * + * @param odb - option database to use * + * @param show_dialog - should dialog be made visible or not * \********************************************************************/ void gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox, @@ -723,8 +722,7 @@ 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 @@ -1000,7 +998,7 @@ public: void set_option_from_ui_item(GncOption& option) noexcept override { auto widget{GTK_TEXT_VIEW(get_widget())}; - option.set_value(xxxgtk_textview_get_text(widget)); + option.set_value(std::string{xxxgtk_textview_get_text(widget)}); } }; @@ -1625,7 +1623,7 @@ show_hidden_toggled_cb(GtkWidget *widget, GncOption* option) option->get_ui_type() != GncOptionUIType::ACCOUNT_SEL) return; - auto tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option)); + auto tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_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)); @@ -2042,10 +2040,8 @@ create_option_widget (GncOption& option, GtkWidget **enclosing, bool *packed) { - GtkWidget *value; - GtkWidget *eventbox; - gint grid_row = GPOINTER_TO_INT(g_object_get_data - (G_OBJECT(page_box), "options-grid-row")); + 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 = gnc_option_get_gtk_widget(&option); @@ -2054,7 +2050,7 @@ create_option_widget (GncOption& option, /* Pack option widget into an extra eventbox because otherwise the "documentation" tooltip is not displayed. */ - eventbox = gtk_event_box_new(); + 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); @@ -2166,7 +2162,7 @@ public: auto color_button = GTK_COLOR_CHOOSER(get_widget()); gtk_color_chooser_set_rgba(color_button, &color); auto rgba_str = gdk_rgba_to_string(&color); - option.set_value(rgba_str); + option.set_value(std::string{rgba_str}); g_free(rgba_str); } }; @@ -2287,13 +2283,6 @@ public: GncGtkPixmapUIItem(GtkWidget* widget) : GncOptionGtkUIItem{widget, GncOptionUIType::PIXMAP} {} void set_ui_item_from_option(GncOption& option) noexcept override - { - auto string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(get_widget())); - DEBUG("filename %s", string ? string : "(null)"); - option.set_value(string); - g_free(string); - } - void set_option_from_ui_item(GncOption& option) noexcept override { auto string{option.get_value()}; if (!string.empty()) @@ -2309,6 +2298,13 @@ public: 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)"); + option.set_value(std::string{string}); + g_free(string); + } }; template<> GtkWidget * @@ -2359,11 +2355,10 @@ static void radiobutton_set_cb(GtkWidget *w, gpointer data) { GncOption* option = static_cast(data); - GtkWidget *widget; gpointer _current, _new_value; gint current, new_value; - widget = gnc_option_get_gtk_widget (option); + auto widget = gnc_option_get_gtk_widget(option); _current = g_object_get_data(G_OBJECT(widget), "gnc_radiobutton_index"); current = GPOINTER_TO_INT (_current); diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h index b3613de210..9bac92ac0f 100644 --- a/gnucash/gnome-utils/dialog-options.h +++ b/gnucash/gnome-utils/dialog-options.h @@ -24,7 +24,6 @@ #define OPTIONS_DIALOG_H #include -#include #ifdef __cplusplus class GncOption; class GncOptionDB; @@ -37,10 +36,18 @@ extern "C" typedef GNCOption GncOption; typedef GNCOptionDB GncOptionDB; #endif +#include +#include -/** A simple wrapper that casts the gpointer result of - * gnc_option_get_widget() already into a GtkWidget*. */ -GtkWidget* const gnc_option_get_gtk_widget (GncOption *option); + +/** + * Retrieve the GtkWidget* used for packing the option control. + * + * This is not ncessarily the widget that has the input or handles signals. + * @param option The option + * @return a GtkWidget* const + */ +GtkWidget* const gnc_option_get_gtk_widget (const GncOption* option); typedef struct gnc_option_win GNCOptionWin; diff --git a/gnucash/gnome-utils/gnc-main-window.c b/gnucash/gnome-utils/gnc-main-window.c index ddea6b2f23..a8bda02f06 100644 --- a/gnucash/gnome-utils/gnc-main-window.c +++ b/gnucash/gnome-utils/gnc-main-window.c @@ -70,7 +70,6 @@ #include "gnc-warnings.h" #include "gnc-window.h" #include "gnc-prefs.h" -#include "option-util.h" // +JSLED //#include "gnc-html.h" #include "gnc-autosave.h" @@ -115,6 +114,19 @@ 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. */ @@ -4164,7 +4176,7 @@ 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 = @@ -4218,7 +4230,7 @@ static void gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin, gpointer user_data) { - GNCOptionDB * options = user_data; + GncOptionDB * options = user_data; if (!options) return; @@ -4230,7 +4242,7 @@ static void gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin, gpointer user_data) { - GNCOptionDB * options = user_data; + GncOptionDB * options = user_data; gnc_options_dialog_destroy(optionwin); gnc_option_db_destroy(options); @@ -4275,7 +4287,7 @@ GtkWidget * gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent) { QofBook *book = gnc_get_current_book (); - GNCOptionDB *options; + GncOptionDB *options; GNCOptionWin *optionwin; options = gnc_option_db_new_for_type (QOF_ID_BOOK); diff --git a/gnucash/gnome-utils/gnc-main-window.h b/gnucash/gnome-utils/gnc-main-window.h index 35b70d5880..fe11c42ecf 100644 --- a/gnucash/gnome-utils/gnc-main-window.h +++ b/gnucash/gnome-utils/gnc-main-window.h @@ -443,19 +443,6 @@ void gnc_main_window_show_all_windows(void); GtkWidget *gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow *parent); -/** - * 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. - **/ -gboolean gnc_book_options_dialog_apply_helper(GNCOptionDB * options); - #endif /* __GNC_MAIN_WINDOW_H */ /** @} */ diff --git a/gnucash/gnome/assistant-hierarchy.c b/gnucash/gnome/assistant-hierarchy.c index aa8c59a0e7..5d07d85dec 100644 --- a/gnucash/gnome/assistant-hierarchy.c +++ b/gnucash/gnome/assistant-hierarchy.c @@ -126,6 +126,8 @@ typedef struct } hierarchy_data; +extern gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options); + void on_prepare (GtkAssistant *assistant, GtkWidget *page, hierarchy_data *data); void on_choose_account_categories_prepare (hierarchy_data *data); diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt index 20cd3c6d93..142976109e 100644 --- a/libgnucash/app-utils/CMakeLists.txt +++ b/libgnucash/app-utils/CMakeLists.txt @@ -31,6 +31,7 @@ set (app_utils_HEADERS gnc-help-utils.h gnc-helpers.h gnc-option.hpp + gnc-optiondb.h gnc-optiondb.hpp gnc-prefs-utils.h gnc-state.h diff --git a/libgnucash/app-utils/app-utils.i b/libgnucash/app-utils/app-utils.i index 274bc696f8..2fc313df6a 100644 --- a/libgnucash/app-utils/app-utils.i +++ b/libgnucash/app-utils/app-utils.i @@ -26,7 +26,6 @@ extern "C" { #endif #include -#include #include #include #include @@ -65,9 +64,10 @@ PyObject* SWIG_init (void); %import "base-typemaps.i" + /* OBSOLETE typedef void (*GNCOptionChangeCallback) (gpointer user_data); typedef int GNCOptionDBHandle; - + */ void gnc_prefs_init(); QofBook * gnc_get_current_book (void); @@ -76,11 +76,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 * { @@ -103,10 +98,9 @@ gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table * table); gnc_commodity * gnc_default_currency (void); gnc_commodity * gnc_default_report_currency (void); +/* Obsolete: Options are C++ now, no need for convoluted callbacks. 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); @@ -133,8 +127,9 @@ gnc_numeric gnc_convert_from_euro(const gnc_commodity * currency, time64 gnc_accounting_period_fiscal_start(void); time64 gnc_accounting_period_fiscal_end(void); +/* OBSOLETE Options are C++, no SCM generators. 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 94bcfa55df..d0ed9635fc 100644 --- a/libgnucash/app-utils/app-utils.scm +++ b/libgnucash/app-utils/app-utils.scm @@ -29,49 +29,8 @@ (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)) - -(export gnc:make-internal-option) -(export gnc:make-query-option) -(export gnc:make-color-option) -(export gnc:make-dateformat-option) -(export gnc:dateformat-get-format) - -(export gnc:color->html) -(export gnc:color-option->html) -(export gnc:color-option->hex-string) -(export gnc:new-options) - -(export gnc:register-option) -(export gnc:unregister-option) -(export gnc:options-register-callback) -(export gnc:options-register-c-callback) -(export gnc:options-unregister-callback-id) -(export gnc:options-for-each) -(export gnc:options-for-each-general) -(export gnc:lookup-option) -(export gnc:generate-restore-forms) -(export gnc:options-fancy-date) -(export gnc:options-scm->kvp) -(export gnc:options-kvp->scm) -(export gnc:options-clear-changes) -(export gnc:options-touch) -(export gnc:options-run-callbacks) -(export gnc:options-set-default-section) -(export gnc:options-get-default-section) -(export gnc:options-copy-values) -(export gnc:send-options) - -(define (gnc:option-get-value book category key) - ;;Access an option directly - (qof-book-get-option book - (if (list? key) - (append (list category) key) - (list category key)))) -(export gnc:option-get-value) - ;; gw-engine-spec.scm (re-export HOOK-SAVE-OPTIONS) diff --git a/libgnucash/app-utils/business-prefs.scm b/libgnucash/app-utils/business-prefs.scm index 9e50ae3c12..84fc701fd1 100644 --- a/libgnucash/app-utils/business-prefs.scm +++ b/libgnucash/app-utils/business-prefs.scm @@ -196,4 +196,4 @@ counter-types)) -(gnc-register-kvp-option-generator QOF-ID-BOOK-SCM book-options-generator) +;;(gnc-register-kvp-option-generator QOF-ID-BOOK-SCM book-options-generator) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index c149e083dc..11a47f879a 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -25,11 +25,14 @@ #include #include #include +#include "gnc-optiondb.h" #include "gnc-optiondb.hpp" #include "gnc-optiondb-impl.hpp" #include "gnc-option-ui.hpp" -auto constexpr stream_max = std::numeric_limits::max(); +constexpr const char* log_module{G_LOG_DOMAIN}; + +constexpr auto stream_max = std::numeric_limits::max(); static bool operator==(const std::string& str, const char* cstr) @@ -659,12 +662,6 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept }); } -GncOptionDBPtr -gnc_option_db_new(void) -{ - return GncOptionDBPtr{new GncOptionDB}; -} - void gnc_register_string_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, @@ -1070,3 +1067,78 @@ gnc_register_end_date_option(const GncOptionDBPtr& db, const char* section, ui_type, end_dates)}; db->register_option(section, std::move(option)); } + +GncOptionDB* +gnc_option_db_new(void) +{ + return new GncOptionDB; +} + +GncOptionDB* +gnc_option_db_new_for_type(QofIdType type) +{ + if (strcmp(type, QOF_ID_BOOK)) + return nullptr; + auto db = new GncOptionDB; + gnc_option_db_book_options(db); + return db; +} + +void +gnc_option_db_destroy(GncOptionDB* odb) +{ + delete 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()); + } }); + }); + 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_book(book, static_cast(clear_options)); +} + +void +gnc_option_db_book_options(GncOptionDB*) +{ +} diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h new file mode 100644 index 0000000000..3b86dbfea8 --- /dev/null +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -0,0 +1,112 @@ +/********************************************************************\ + * 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 * + * * +\********************************************************************/ + +#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 +#ifdef __cplusplus +class GncOptionDB; +extern "C" +{ +#else +typedef struct GncOptionDB GncOptionDB; +#endif +#include +#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); + +/** + * Convenence function duplicating an option-util function. We need this temporarily to make gnc-main-window and assistant-hierarchy happy. + * @param type The QofType + * @return a new GncOptionDB*. Transfers ownership. + */ +GncOptionDB* gnc_option_db_new_for_type(QofIdType type); + +/** + * 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*); + + + +#ifdef __cplusplus +} +#endif +#endif //GNC_OPTIONDB_H_ diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 399316222d..9268153153 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -73,15 +73,6 @@ gnc_account_list_from_types(QofBook *book, using GncOptionDBPtr = std::unique_ptr; -/** - * Create an empty option database. - * - * It would be nice to use a std::shared_ptr here but Swig doesn't implement - * that for Guile. - * @return A newly allocated GncOptionDB. Use delete to destroy it. - */ -GncOptionDBPtr gnc_option_db_new(void); - void gnc_register_string_option(const GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); diff --git a/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c index 04ff31ea0d..45228b697f 100644 --- a/libgnucash/app-utils/option-util.c +++ b/libgnucash/app-utils/option-util.c @@ -191,6 +191,7 @@ gnc_option_db_init (GNCOptionDB *odb) scm_call_2 (func, scm_from_int (odb->handle), odb->guile_options); } +#if 0 /********************************************************************\ * gnc_option_db_new * * allocate a new option database and initialize its values * @@ -228,7 +229,7 @@ gnc_option_db_new (SCM guile_options) return odb; } - +#endif /* 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. */ @@ -262,7 +263,7 @@ gnc_make_kvp_options (QofIdType id_type) } return options; } - +#if 0 GNCOptionDB * gnc_option_db_new_for_type (QofIdType id_type) { @@ -382,7 +383,7 @@ gnc_option_db_destroy (GNCOptionDB *odb) g_free(odb); } - +#endif void gnc_option_db_set_ui_callbacks (GNCOptionDB *odb, GNCOptionGetUIValue get_ui_value, @@ -1260,7 +1261,7 @@ compare_option_tags (gconstpointer a, gconstpointer b) return result; } - +#if 0 /********************************************************************\ * gnc_option_db_clean * * resets the dirty flag of the option database * @@ -1273,6 +1274,7 @@ gnc_option_db_clean (GNCOptionDB *odb) odb->options_dirty = FALSE; } +#endif /********************************************************************\ * _gnc_option_db_register_option * @@ -1592,6 +1594,7 @@ gnc_option_db_get_changed (GNCOptionDB *odb) return FALSE; } +#if 0 /********************************************************************\ * gnc_option_db_commit * * commits the options which have changed, and which are valid * @@ -1640,6 +1643,7 @@ gnc_option_db_commit (GNCOptionDB *odb) return commit_errors; } +#endif /********************************************************************\ * gnc_option_db_section_reset_widgets * diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h index 23742f011e..4c1676e35e 100644 --- a/libgnucash/app-utils/option-util.h +++ b/libgnucash/app-utils/option-util.h @@ -35,8 +35,7 @@ typedef struct gnc_option GNCOption; typedef struct gnc_option_section GNCOptionSection; -/* typedef struct gnc_option_db GNCOptionDB is in qof-book.h */ - +typedef struct gnc_option_db GNCOptionDB; typedef int GNCOptionDBHandle; typedef SCM (*GNCOptionGetUIValue) (GNCOption *option); @@ -64,18 +63,17 @@ 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); +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); +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, From 01c0fe2364bd620268c25785deab29330930271f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 13:10:19 -0700 Subject: [PATCH 104/298] Include what we use. --- .../{business-options-gnome.c => business-options-gnome.cpp} | 0 libgnucash/app-utils/gnc-option-ui.hpp | 2 ++ 2 files changed, 2 insertions(+) rename gnucash/gnome/{business-options-gnome.c => business-options-gnome.cpp} (100%) diff --git a/gnucash/gnome/business-options-gnome.c b/gnucash/gnome/business-options-gnome.cpp similarity index 100% rename from gnucash/gnome/business-options-gnome.c rename to gnucash/gnome/business-options-gnome.cpp diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp index 87ca289a8b..dbd44e8b9a 100644 --- a/libgnucash/app-utils/gnc-option-ui.hpp +++ b/libgnucash/app-utils/gnc-option-ui.hpp @@ -24,8 +24,10 @@ #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; From 9111f118e098211a59f1a379d230c557b8e0cc26 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 13:31:34 -0700 Subject: [PATCH 105/298] Business option create_option_widget specializations. Required creating a registration class for create_option_widget overload functions because without it the dispatch function complained of missing symbols at link time. --- gnucash/gnome-utils/CMakeLists.txt | 1 + gnucash/gnome-utils/dialog-options.cpp | 331 ++++++------ gnucash/gnome-utils/dialog-options.h | 8 +- gnucash/gnome-utils/dialog-options.hpp | 77 +++ gnucash/gnome/CMakeLists.txt | 2 +- gnucash/gnome/business-options-gnome.cpp | 553 ++++++--------------- libgnucash/app-utils/gnc-option-uitype.hpp | 1 + po/POTFILES.in | 2 +- 8 files changed, 369 insertions(+), 606 deletions(-) create mode 100644 gnucash/gnome-utils/dialog-options.hpp diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt index 76fe198fcf..960651b529 100644 --- a/gnucash/gnome-utils/CMakeLists.txt +++ b/gnucash/gnome-utils/CMakeLists.txt @@ -113,6 +113,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 diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index f5efcaf8a8..78a67fae4e 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1,8 +1,9 @@ /********************************************************************\ - * dialog-options.cpp -- GNOME option handling * + * 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 * @@ -62,7 +63,10 @@ extern "C" #include "misc-gnome-utils.h" } -#include "dialog-options.h" +#include +#include + +#include "dialog-options.hpp" #include #include @@ -83,13 +87,6 @@ extern "C" /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_GUI; -template inline const QofInstance* -qof_instance_cast(Instance inst) -{ - static_assert(std::is_pointer_v, "Pointers Only!"); - return reinterpret_cast(inst); -} - static constexpr const char* DIALOG_OPTIONS_CM_CLASS{"dialog-options"}; static constexpr const char* GNC_PREFS_GROUP{"dialogs.options"}; @@ -137,45 +134,64 @@ enum page_tree NUM_COLUMNS }; -class GncOptionGtkUIItem : public GncOptionUIItem +//Init the class static. +std::vector GncOptionUIFactory::s_registry{static_cast(GncOptionUIType::MAX_VALUE)}; + +void +GncOptionUIFactory::set_func(GncOptionUIType type, WidgetCreateFunc func) { -public: - GncOptionGtkUIItem(GtkWidget* widget, GncOptionUIType type) : - GncOptionUIItem(type), - m_widget{static_cast(g_object_ref(widget))} {} - GncOptionGtkUIItem(const GncOptionGtkUIItem& item) : + s_registry[static_cast(type)] = func; +} + +GtkWidget* +GncOptionUIFactory::create(GncOption& option, GtkGrid* page, GtkLabel* name, + char* description, GtkWidget** enclosing, bool* packed) +{ + auto type{option.get_ui_type()}; + auto func{s_registry[static_cast(type)]}; + if (func) + return func(option, page, name, description, enclosing, packed); + 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&&) = default; - virtual ~GncOptionGtkUIItem() override - { - if (m_widget) - g_object_unref(m_widget); - } - void clear_ui_item() override - { - if (m_widget) - g_object_unref(m_widget); - m_widget = nullptr; - } - void 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); +GncOptionGtkUIItem::~GncOptionGtkUIItem() +{ + if (m_widget) + g_object_unref(m_widget); +} - m_widget = static_cast(g_object_ref(widget)); +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)); } - virtual GtkWidget* const get_widget() const { return m_widget; } -private: - GtkWidget* m_widget; -}; + if (m_widget) + g_object_unref(m_widget); + + m_widget = static_cast(g_object_ref(widget)); +} + static GNCOptionWinCallback global_help_cb = NULL; gpointer global_help_cb_data = NULL; @@ -249,18 +265,19 @@ gnc_options_dialog_changed (GNCOptionWin *win) dialog_changed_internal (win->window, TRUE); } -static void -widget_changed_cb(GtkWidget *widget, GncOption* option) +void +gnc_option_changed_widget_cb(GtkWidget *widget, GncOption* option) { if (!option) return; const_cast(option->get_ui_item())->set_dirty(true); } void -option_changed_cb(GtkWidget *dummy, GncOption* option) +gnc_option_changed_option_cb(GtkWidget *dummy, GncOption* option) { if (!option) return; - option->set_ui_item_from_option(); + auto widget{gnc_option_get_gtk_widget(option)}; + gnc_option_changed_widget_cb(widget, option); } @@ -290,9 +307,6 @@ create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**, return nullptr; } -static GtkWidget* option_widget_factory(GncOption& option, GtkGrid* page, - GtkLabel* name, char* description, - GtkWidget** enclosing, bool* packed); static void gnc_option_set_ui_widget(GncOption& option, GtkGrid *page_box, gint grid_row) { @@ -326,8 +340,9 @@ gnc_option_set_ui_widget(GncOption& option, GtkGrid *page_box, gint grid_row) documentation = nullptr; name_label = GTK_LABEL(gtk_label_new (name)); - auto widget = option_widget_factory(option, page_box, name_label, - documentation, &enclosing, &packed); + 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 */ gtk_grid_attach (GTK_GRID(page_box), GTK_WIDGET(name_label), @@ -930,7 +945,7 @@ create_option_widget (GncOption& option, auto ui_item{std::make_unique(GncGtkBooleanUIItem{widget})}; g_signal_connect(G_OBJECT(widget), "toggled", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); option.set_ui_item(std::move(ui_item)); option.set_ui_item_from_option(); @@ -976,7 +991,7 @@ create_option_widget (GncOption& option, auto ui_item{std::make_unique(widget)}; g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); option.set_ui_item(std::move(ui_item)); option.set_ui_item_from_option(); @@ -1032,7 +1047,7 @@ create_option_widget (GncOption& option, GtkGrid *page_bo auto ui_item{std::make_unique(widget)}; auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); g_signal_connect(G_OBJECT(text_buffer), "changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); option.set_ui_item(std::move(ui_item)); option.set_ui_item_from_option(); @@ -1072,7 +1087,7 @@ create_option_widget (GncOption& option, GtkGrid *pag auto widget = gnc_currency_edit_new(); auto ui_item{std::make_unique(widget)}; g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); option.set_ui_item(std::move(ui_item)); option.set_ui_item_from_option(); @@ -1117,7 +1132,7 @@ create_option_widget (GncOption& option, GtkGrid *pa auto ui_item{std::make_unique(widget)}; g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); option.set_ui_item(std::move(ui_item)); option.set_ui_item_from_option(); @@ -1126,7 +1141,7 @@ create_option_widget (GncOption& option, GtkGrid *pa documentation); g_signal_connect(G_OBJECT(GNC_GENERAL_SELECT(widget)->entry), "changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); gtk_widget_show_all(*enclosing); @@ -1164,7 +1179,7 @@ create_multichoice_widget(GncOption& option) g_object_unref(store); g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); return widget; } @@ -1243,7 +1258,7 @@ AbsoluteDateEntry::AbsoluteDateEntry(GncOption& option) : { auto entry = GNC_DATE_EDIT(m_entry)->date_entry; g_signal_connect(G_OBJECT(entry), "changed", - G_CALLBACK(option_changed_cb), &option); + G_CALLBACK(gnc_option_changed_option_cb), &option); } void @@ -1300,7 +1315,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) : g_object_unref(store); g_signal_connect(G_OBJECT(m_entry), "changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); } void @@ -1448,7 +1463,7 @@ date_set_absolute_cb(GtkWidget *widget, gpointer data1) if (auto date_ui = dynamic_cast(ui_item)) { const_cast(date_ui)->get_entry()->toggle_relative(true); - option_changed_cb(widget, option); + gnc_option_changed_option_cb(widget, option); } } @@ -1460,7 +1475,7 @@ date_set_relative_cb(GtkWidget *widget, gpointer data1) if (auto date_ui = dynamic_cast(ui_item)) { const_cast(date_ui)->get_entry()->toggle_relative(false); - option_changed_cb(widget, option); + gnc_option_changed_option_cb(widget, option); } } @@ -1575,7 +1590,7 @@ account_select_all_cb(GtkWidget *widget, gpointer data) 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); - widget_changed_cb(widget, option); + gnc_option_changed_widget_cb(widget, option); } static void @@ -1588,7 +1603,7 @@ account_clear_all_cb(GtkWidget *widget, gpointer data) 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); - widget_changed_cb(widget, option); + gnc_option_changed_widget_cb(widget, option); } static void @@ -1628,7 +1643,7 @@ show_hidden_toggled_cb(GtkWidget *widget, GncOption* 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); - widget_changed_cb(widget, option); + gnc_option_changed_widget_cb(widget, option); } class GncGtkAccountListUIItem : public GncOptionGtkUIItem @@ -1819,7 +1834,7 @@ create_option_widget(GncOption& option, auto widget = gnc_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(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); // gtk_clist_set_row_height(GTK_CLIST(value), 0); // gtk_widget_set_size_request(value, -1, GTK_CLIST(value)->row_height * 10); @@ -1861,7 +1876,7 @@ create_option_widget (GncOption& option, acct_type_list, NULL); g_list_free(acct_type_list); g_signal_connect(widget, "account_sel_changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); // gnc_account_sel doesn't emit a changed signal @@ -1879,7 +1894,7 @@ static void list_changed_cb(GtkTreeSelection *selection, GncOption* option) { GtkTreeView *view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option)); - widget_changed_cb(GTK_WIDGET(view), option); + gnc_option_changed_widget_cb(GTK_WIDGET(view), option); } static void @@ -1892,7 +1907,7 @@ list_select_all_cb(GtkWidget *widget, gpointer data) view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option)); selection = gtk_tree_view_get_selection(view); gtk_tree_selection_select_all(selection); - widget_changed_cb(GTK_WIDGET(view), option); + gnc_option_changed_widget_cb(GTK_WIDGET(view), option); } static void @@ -1905,7 +1920,7 @@ list_clear_all_cb(GtkWidget *widget, gpointer data) view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option)); selection = gtk_tree_view_get_selection(view); gtk_tree_selection_unselect_all(selection); - widget_changed_cb(GTK_WIDGET(view), option); + gnc_option_changed_widget_cb(GTK_WIDGET(view), option); } static void @@ -2133,7 +2148,7 @@ create_option_widget (GncOption& option, option.set_ui_item_from_option(); g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); gtk_box_pack_start(GTK_BOX(*enclosing), GTK_WIDGET(widget), FALSE, FALSE, 0); @@ -2185,7 +2200,7 @@ create_option_widget (GncOption& option, GtkGrid *page_b option.set_ui_item_from_option(); g_signal_connect(G_OBJECT(widget), "color-set", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); gtk_widget_show_all(*enclosing); @@ -2229,7 +2244,7 @@ create_option_widget (GncOption& option, GtkGrid *page_bo option.set_ui_item_from_option(); g_signal_connect(G_OBJECT(widget), "font-set", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); gtk_widget_show_all(*enclosing); @@ -2331,7 +2346,7 @@ create_option_widget (GncOption& option, "preview-widget", gtk_image_new(), (char *)NULL); g_signal_connect(G_OBJECT (widget), "selection-changed", - G_CALLBACK(widget_changed_cb), &option); + 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", @@ -2371,7 +2386,7 @@ radiobutton_set_cb(GtkWidget *w, gpointer data) g_object_set_data (G_OBJECT(widget), "gnc_radiobutton_index", GINT_TO_POINTER(new_value)); - widget_changed_cb(widget, option); + gnc_option_changed_widget_cb(widget, option); } class GncGtkRadioButtonUIItem : public GncOptionGtkUIItem @@ -2510,7 +2525,7 @@ create_option_widget (GncOption& option, option.set_ui_item_from_option(); g_signal_connect(G_OBJECT(*enclosing), "format_changed", - G_CALLBACK(widget_changed_cb), &option); + G_CALLBACK(gnc_option_changed_widget_cb), &option); gtk_widget_show_all(*enclosing); return *enclosing; } @@ -2547,14 +2562,14 @@ static void gnc_rd_option_px_set_cb(GtkWidget *widget, GncOption* option) { option->set_alternate(true); - option_changed_cb(widget, option); + gnc_option_changed_option_cb(widget, option); } static void gnc_rd_option_p_set_cb(GtkWidget *widget, GncOption* option) { option->set_alternate(false); - option_changed_cb(widget, option); + gnc_option_changed_option_cb(widget, option); } class GncGtkPlotSizeUIItem : public GncOptionGtkUIItem @@ -2625,7 +2640,7 @@ create_option_widget (GncOption& option, auto value_px = create_range_spinner(option); g_signal_connect(G_OBJECT(value_px), "changed", - G_CALLBACK(widget_changed_cb), &option); + 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); @@ -2635,7 +2650,7 @@ create_option_widget (GncOption& option, gtk_widget_set_sensitive(value_percent, FALSE); g_signal_connect(G_OBJECT(value_percent), "changed", - G_CALLBACK(widget_changed_cb), &option); + 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); @@ -2721,120 +2736,58 @@ create_option_widget (GncOption& option, GtkGrid *page_ /* Maybe connect destroy handler for tree model here? */ g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(widget_changed_cb), &option); + 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* -option_widget_factory(GncOption& option, GtkGrid* page, GtkLabel* name, - char* description, GtkWidget** enclosing, bool* packed) +void +gnc_options_ui_initialize(void) { - switch(option.get_ui_type()) - { - case INTERNAL: - return nullptr; - case BOOLEAN: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case STRING: - return create_option_widget(option, page, name, description, - enclosing, packed); - case TEXT: - return create_option_widget(option, page, name, description, - enclosing, packed); - case CURRENCY: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case COMMODITY: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case MULTICHOICE: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case DATE_ABSOLUTE: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case DATE_RELATIVE: - return create_option_widget(option, page, name, - description, - enclosing, packed); - case DATE_BOTH: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case ACCOUNT_LIST: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case ACCOUNT_SEL: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case LIST: - return create_option_widget(option, page, name, description, - enclosing, packed); - case NUMBER_RANGE: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case COLOR: - return create_option_widget(option, page, name, description, - enclosing, packed); - case FONT: - return create_option_widget(option, page, name, description, - enclosing, packed); - case PLOT_SIZE: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case BUDGET: - return create_option_widget(option, page, name, description, - enclosing, packed); - case PIXMAP: - return create_option_widget(option, page, name, description, - enclosing, packed); - case RADIOBUTTON: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case DATE_FORMAT: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case OWNER: - return create_option_widget(option, page, name, description, - enclosing, packed); - case CUSTOMER: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case VENDOR: - return create_option_widget(option, page, name, description, - enclosing, packed); - case EMPLOYEE: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case INVOICE: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case TAX_TABLE: - return create_option_widget(option, page, name, - description, enclosing, - packed); - case QUERY: - return create_option_widget(option, page, name, description, - enclosing, packed); - } + 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 @@ -2877,9 +2830,3 @@ gnc_option_get_gtk_widget (GncOption *option) { return nullptr; } - -void -gnc_options_ui_initialize (void) -{ - return; -} diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h index 9bac92ac0f..82d0fdd681 100644 --- a/gnucash/gnome-utils/dialog-options.h +++ b/gnucash/gnome-utils/dialog-options.h @@ -97,7 +97,13 @@ 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); +void gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb); + +/** + * Initialize the option UI elements. + */ +void gnc_options_ui_initialize(void); + #ifdef __cplusplus } #endif diff --git a/gnucash/gnome-utils/dialog-options.hpp b/gnucash/gnome-utils/dialog-options.hpp new file mode 100644 index 0000000000..7daef23517 --- /dev/null +++ b/gnucash/gnome-utils/dialog-options.hpp @@ -0,0 +1,77 @@ +/********************************************************************\ + * dialog-options.hpp -- 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 * +\********************************************************************/ + +#ifndef GNC_DIALOG_OPTIONS_HPP_ +#define GNC_DIALOG_OPTIONS_HPP_ + +#include + +#include +#include +#include "dialog-options.h" + +typedef GtkWidget* (*WidgetCreateFunc)(GncOption&, GtkGrid*, GtkLabel*, char*, + GtkWidget**, bool*); +class GncOptionUIFactory +{ +public: + static void set_func(GncOptionUIType type, WidgetCreateFunc func); + static GtkWidget* create(GncOption&, GtkGrid*, GtkLabel*, char*, + GtkWidget**, bool*); +private: + static std::vector s_registry; +}; + +class GncOptionGtkUIItem : public GncOptionUIItem +{ +public: + GncOptionGtkUIItem(GtkWidget* widget, GncOptionUIType type); + GncOptionGtkUIItem(const GncOptionGtkUIItem& item); + GncOptionGtkUIItem(GncOptionGtkUIItem&&) = default; + virtual ~GncOptionGtkUIItem() override; + 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*); + +template inline const QofInstance* +qof_instance_cast(Instance inst) +{ + static_assert(std::is_pointer_v, "Pointers Only!"); + return reinterpret_cast(inst); +} + +#endif // GNC_DIALOG_OPTIONS_HPP_ diff --git a/gnucash/gnome/CMakeLists.txt b/gnucash/gnome/CMakeLists.txt index 64096ff884..6cea3d0c85 100644 --- a/gnucash/gnome/CMakeLists.txt +++ b/gnucash/gnome/CMakeLists.txt @@ -72,7 +72,7 @@ set (gnc_gnome_SOURCES assistant-hierarchy.c assistant-loan.cpp assistant-stock-split.c - business-options-gnome.c + business-options-gnome.cpp business-urls.c business-gnome-utils.c dialog-doclink.c diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp index a9bc680dd1..2e26b992a3 100644 --- a/gnucash/gnome/business-options-gnome.cpp +++ b/gnucash/gnome/business-options-gnome.cpp @@ -22,464 +22,195 @@ * Boston, MA 02110-1301, USA gnu@gnu.org */ +#include + +extern "C" +{ #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" +} +#include +#include +#include + +#include +#include #define FUNC_NAME G_STRFUNC -/* To be consolidated into dialog-options.cpp later. */ -#if 0 -static GtkWidget * -create_owner_widget (GncOption *option, GncOwnerType type, GtkWidget *hbox) -{ - GtkWidget *widget; - GncOwner owner; - switch (type) + +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 " << 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 { - 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; + 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); } - - 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) + void set_option_from_ui_item(GncOption& option) noexcept override { - owner_def.type = get_owner_type_from_option (option); - owner_def.owner.undefined = NULL; - owner = &owner_def; + 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)); } +}; - 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) +template<> GtkWidget* +create_option_widget(GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, + /* Return values */ + GtkWidget **enclosing, + bool *packed) { - 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); + 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); - 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); + 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_option_cb), option); - + G_CALLBACK (gnc_option_changed_widget_cb), &option); + gtk_widget_show_all(*enclosing); 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) +class GncGtkInvoiceUIItem : public GncOptionGtkUIItem { - GtkWidget *value; +public: + GncGtkInvoiceUIItem(GtkWidget* widget) : + GncOptionGtkUIItem(widget, GncOptionUIType::INVOICE) {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + gnc_general_search_set_selected(GNC_GENERAL_SEARCH(get_widget()), + GNC_INVOICE(option.get_value())); + } + 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)}; - 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)); + 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; } -/* 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) +class GncGtkTaxTableUIItem : public GncOptionGtkUIItem { - GtkWidget *value; +public: + GncGtkTaxTableUIItem(GtkWidget* widget) : + GncOptionGtkUIItem(widget, GncOptionUIType::TAX_TABLE) {} + void set_ui_item_from_option(GncOption& option) noexcept override + { + } + void set_option_from_ui_item(GncOption& option) noexcept override + { + } +}; +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))}; + g_object_unref(builder); + 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_signal_connect (G_OBJECT (widget), "changed", + G_CALLBACK (gnc_option_changed_widget_cb), &option); - value = create_taxtable_widget (option, *enclosing); - - gnc_option_set_ui_value (option, FALSE); - - gtk_widget_show_all (*enclosing); - return value; + gtk_widget_show_all(*enclosing); + return widget; } -/* 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); -} - -#endif - - void -gnc_business_options_gnome_initialize (void) +gnc_business_options_gnome_initialize(void) { -/* Create the above option types. */ + 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/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp index ac3889f4ac..b053503117 100644 --- a/libgnucash/app-utils/gnc-option-uitype.hpp +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -53,6 +53,7 @@ enum GncOptionUIType INVOICE, TAX_TABLE, QUERY, + MAX_VALUE, //Nake sure this one is always last }; #endif // GNC_OPTION_UITYPE_H__ diff --git a/po/POTFILES.in b/po/POTFILES.in index 84ae44d040..5e1439b19f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -52,7 +52,7 @@ gnucash/gnome/assistant-hierarchy.c gnucash/gnome/assistant-loan.cpp gnucash/gnome/assistant-stock-split.c 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 From 8fe338f315253cf4c82ed5d1553e7628e97604d4 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 13:48:20 -0700 Subject: [PATCH 106/298] Temporarily disable building dialog-report-column-view. It will require some major surgery to get working, and disabling it allows building GnuCash so that the Book Properties dialog can be runtime-tested. --- gnucash/gnome/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnucash/gnome/CMakeLists.txt b/gnucash/gnome/CMakeLists.txt index 6cea3d0c85..00e28d5c08 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.h dialog-report-style-sheet.h dialog-sx-editor.h dialog-sx-editor2.h @@ -98,7 +98,7 @@ set (gnc_gnome_SOURCES dialog-price-edit-db.c dialog-print-check.c dialog-progress.c - dialog-report-column-view.c +# dialog-report-column-view.c dialog-report-style-sheet.c dialog-sx-editor.c dialog-sx-editor2.c From 99103ffd104618b40018a7725d7b7c22d66325ef Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:16:22 -0700 Subject: [PATCH 107/298] Change gnc-register-option functions to take GncOptionDB*. Instead of std::unique_ptr because there's no way to get a unique_ptr through a C call. --- libgnucash/app-utils/gnc-optiondb.cpp | 60 +++++++++++++------------- libgnucash/app-utils/gnc-optiondb.hpp | 61 +++++++++++++-------------- 2 files changed, 60 insertions(+), 61 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 11a47f879a..0dcaa917bf 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -663,7 +663,7 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept } void -gnc_register_string_option(const GncOptionDBPtr& db, const char* section, +gnc_register_string_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { @@ -673,7 +673,7 @@ gnc_register_string_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const char* name, +gnc_register_text_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { @@ -684,7 +684,7 @@ gnc_register_text_option(const GncOptionDBPtr& db, const char* section, const ch } void -gnc_register_font_option(const GncOptionDBPtr& db, const char* section, +gnc_register_font_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { @@ -694,7 +694,7 @@ gnc_register_font_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_budget_option(const GncOptionDBPtr& db, const char* section, +gnc_register_budget_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncBudget *value) { @@ -704,7 +704,7 @@ gnc_register_budget_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_color_option(const GncOptionDBPtr& db, const char* section, +gnc_register_color_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { @@ -714,7 +714,7 @@ gnc_register_color_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section, +gnc_register_commodity_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { @@ -724,7 +724,7 @@ gnc_register_commodity_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_simple_boolean_option(const GncOptionDBPtr& db, +gnc_register_simple_boolean_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, bool value) @@ -735,7 +735,7 @@ gnc_register_simple_boolean_option(const GncOptionDBPtr& db, } void -gnc_register_complex_boolean_option(const GncOptionDBPtr& db, +gnc_register_complex_boolean_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, bool value) @@ -746,7 +746,7 @@ gnc_register_complex_boolean_option(const GncOptionDBPtr& db, } void -gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section, +gnc_register_pixmap_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { @@ -756,7 +756,7 @@ gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_account_list_option(const GncOptionDBPtr& db, const char* section, +gnc_register_account_list_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, const GncOptionAccountList& value) @@ -767,7 +767,7 @@ gnc_register_account_list_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_account_list_limited_option(const GncOptionDBPtr& db, +gnc_register_account_list_limited_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, @@ -814,7 +814,7 @@ gnc_account_list_from_types(QofBook *book, void -gnc_register_account_sel_limited_option(const GncOptionDBPtr& db, +gnc_register_account_sel_limited_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, const GncOptionAccountList& value, @@ -833,7 +833,7 @@ gnc_register_account_sel_limited_option(const GncOptionDBPtr& db, } void -gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section, +gnc_register_multichoice_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncMultichoiceOptionChoices&& choices) @@ -844,7 +844,7 @@ gnc_register_multichoice_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_list_option(const GncOptionDBPtr& db, const char* section, +gnc_register_list_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, const char* value, GncMultichoiceOptionChoices&& list) @@ -858,7 +858,7 @@ gnc_register_list_option(const GncOptionDBPtr& db, const char* section, * use decimals and fractional steps and they can be worked around. */ void -gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section, +gnc_register_number_range_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, int value, int min, int max, int step) @@ -869,7 +869,7 @@ gnc_register_number_range_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_number_plot_size_option(const GncOptionDBPtr& db, +gnc_register_number_plot_size_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, int value) @@ -880,7 +880,7 @@ gnc_register_number_plot_size_option(const GncOptionDBPtr& db, } void -gnc_register_query_option(const GncOptionDBPtr& db, const char* section, +gnc_register_query_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, QofQuery* value) { @@ -890,7 +890,7 @@ gnc_register_query_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_internal_option(const GncOptionDBPtr& db, const char* section, +gnc_register_internal_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { @@ -900,7 +900,7 @@ gnc_register_internal_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section, +gnc_register_owner_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncInvoice* value) { @@ -910,7 +910,7 @@ gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_owner_option(const GncOptionDBPtr& db, const char* section, +gnc_register_invoice_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncOwner* value) { @@ -920,7 +920,7 @@ gnc_register_owner_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section, +gnc_register_taxtable_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncTaxTable* value) { @@ -930,7 +930,7 @@ gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_counter_option(const GncOptionDBPtr& db, const char* section, +gnc_register_counter_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, int value) { @@ -940,7 +940,7 @@ gnc_register_counter_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_counter_format_option(const GncOptionDBPtr& db, +gnc_register_counter_format_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) @@ -951,7 +951,7 @@ gnc_register_counter_format_option(const GncOptionDBPtr& db, } void -gnc_register_dateformat_option(const GncOptionDBPtr& db, const char* section, +gnc_register_dateformat_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value) { @@ -961,7 +961,7 @@ gnc_register_dateformat_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, +gnc_register_currency_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { @@ -978,7 +978,7 @@ gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_date_option(const GncOptionDBPtr& db, const char* section, +gnc_register_date_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, time64 time, RelativeDateUI ui) @@ -992,7 +992,7 @@ gnc_register_date_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_date_option(const GncOptionDBPtr& db, const char* section, +gnc_register_date_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, RelativeDatePeriod period, RelativeDateUI ui) @@ -1006,7 +1006,7 @@ gnc_register_date_option(const GncOptionDBPtr& db, const char* section, } void -gnc_register_date_option(const GncOptionDBPtr& db, +gnc_register_date_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, RelativeDatePeriodVec& period_set, @@ -1033,7 +1033,7 @@ static const RelativeDatePeriodVec begin_dates }; void -gnc_register_start_date_option(const GncOptionDBPtr& db, const char* section, +gnc_register_start_date_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, bool both) { @@ -1057,7 +1057,7 @@ static const RelativeDatePeriodVec end_dates }; void -gnc_register_end_date_option(const GncOptionDBPtr& db, const char* section, +gnc_register_end_date_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, bool both) { diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 9268153153..b373aa7635 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -72,123 +72,122 @@ gnc_account_list_from_types(QofBook *book, const GncOptionAccountTypeList& types); -using GncOptionDBPtr = std::unique_ptr; -void gnc_register_string_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_string_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); -void gnc_register_text_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_text_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); -void gnc_register_font_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_font_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); -void gnc_register_budget_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_budget_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncBudget* value); -void gnc_register_commodity_option(const GncOptionDBPtr& db, +void gnc_register_commodity_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity* value); -void gnc_register_simple_boolean_option(const GncOptionDBPtr& db, +void gnc_register_simple_boolean_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, bool value); -void gnc_register_complex_boolean_option(const GncOptionDBPtr& db, +void gnc_register_complex_boolean_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, bool value); -void gnc_register_pixmap_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_pixmap_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); -void gnc_register_account_list_limited_option(const GncOptionDBPtr& db, +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); -void gnc_register_account_list_option(const GncOptionDBPtr& db, +void gnc_register_account_list_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, const GncOptionAccountList& value); -void gnc_register_account_sel_limited_option(const GncOptionDBPtr& db, +void gnc_register_account_sel_limited_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, const GncOptionAccountList& value, GncOptionAccountTypeList&& allowed); -void gnc_register_multichoice_option(const GncOptionDBPtr& db, +void gnc_register_multichoice_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncMultichoiceOptionChoices&& value); -void gnc_register_list_option(const GncOptionDBPtr& db, const char* section, +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); -void gnc_register_number_range_option(const GncOptionDBPtr& db, +void gnc_register_number_range_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, int value, int min, int max, int step); -void gnc_register_number_plot_size_option(const GncOptionDBPtr& db, +void gnc_register_number_plot_size_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, int value); -void gnc_register_query_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_query_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, QofQuery* value); -void gnc_register_color_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_color_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); -void gnc_register_internal_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_internal_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); -void gnc_register_currency_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_currency_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity* value); -void gnc_register_invoice_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_invoice_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncInvoice* value); -void gnc_register_owner_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_owner_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncOwner* value); -void gnc_register_taxtable_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_taxtable_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncTaxTable* value); -void gnc_register_counter_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_counter_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, int value); -void gnc_register_counter_format_option(const GncOptionDBPtr& db, +void gnc_register_counter_format_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); -void gnc_register_dateformat_option(const GncOptionDBPtr& db, +void gnc_register_dateformat_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, std::string value); @@ -200,30 +199,30 @@ enum RelativeDateUI : uint8_t BOTH }; -void gnc_register_date_option(const GncOptionDBPtr& db, const char* section, +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); -void gnc_register_date_option(const GncOptionDBPtr& db, const char* section, +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); -void gnc_register_date_option(const GncOptionDBPtr& db, const char* section, +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); -void gnc_register_start_date_option(const GncOptionDBPtr& db, +void gnc_register_start_date_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, bool both = true); -void gnc_register_end_date_option(const GncOptionDBPtr& db, const char* section, +void gnc_register_end_date_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, bool both = true); From 1eef796f098bd20c143040e55ee5b0332181d192 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:23:55 -0700 Subject: [PATCH 108/298] Add some skeleton functions to make the option-using code in gnucash/gnome-utils and gnucash/gnome happy. --- libgnucash/app-utils/gnc-optiondb.cpp | 18 ++++++++ libgnucash/app-utils/gnc-optiondb.h | 63 +++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 0dcaa917bf..def04f72ab 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1142,3 +1142,21 @@ void gnc_option_db_book_options(GncOptionDB*) { } + +const QofInstance* +gnc_option_db_lookup_qofinstance_value(GncOptionDB*, const char*, const char*) +{ + return nullptr; +} + +GList* +gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*) +{ + return nullptr; +} + +void +gnc_option_db_set_glist_value(GncOptionDB*, const char*, const char*, GList*) +{ +} + diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h index 3b86dbfea8..f4b3d4246c 100644 --- a/libgnucash/app-utils/gnc-optiondb.h +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -104,7 +104,70 @@ void gnc_option_db_save(GncOptionDB* odb, QofBook* book, */ 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*); + +/** + * Retrieve the GList* 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 GList* of the value or nullptr if the option isn't found + * or if its value isn't a string. + */ +GList* gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*); + +/** + * Set the GList* 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_glist_value(GncOptionDB*, const char*, + const char*, GList*); #ifdef __cplusplus } From 41e59df71fb74ec157b7cc3af767f17cf1a4e7bb Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:24:43 -0700 Subject: [PATCH 109/298] Remove unused and un-needed function. --- libgnucash/app-utils/gnc-optiondb-impl.hpp | 1 - libgnucash/app-utils/gnc-optiondb.cpp | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index 0a87756bb7..058f308be3 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -82,7 +82,6 @@ public: func(section); } size_t num_sections() const noexcept { return m_sections.size(); } - void save_to_book(QofBook* book, bool do_clear) const; bool get_changed() const noexcept { return m_dirty; } void register_option(const char* section, GncOption&& option); void unregister_option(const char* section, const char* name); diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index def04f72ab..0aea402c7f 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -82,11 +82,6 @@ GncOptionDB::GncOptionDB() : m_default_section{} {} GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {} -void -GncOptionDB::save_to_book(QofBook* book, bool do_clear) const -{ -} - void GncOptionDB::register_option(const char* sectname, GncOption&& option) { @@ -1135,7 +1130,8 @@ void gnc_option_db_save(GncOptionDB* odb, QofBook* book, gboolean clear_options) { - odb->save_to_book(book, static_cast(clear_options)); + odb->save_to_kvp(book, static_cast(clear_options)); +} } void From 28438e312632aa7b490b33d59da1ddbb14718ac6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:26:49 -0700 Subject: [PATCH 110/298] Rewire gnome and gnome-utils to use GncOptionDB. --- gnucash/gnome/assistant-hierarchy.c | 7 ++-- gnucash/gnome/dialog-report-style-sheet.c | 14 +++---- gnucash/gnome/gnc-plugin-page-report.c | 49 ++++++++++++----------- gnucash/gnome/window-report.c | 26 ++++++------ gnucash/gnome/window-report.h | 3 +- libgnucash/app-utils/gnc-optiondb.cpp | 5 +-- libgnucash/engine/qofbook.cpp | 6 +-- libgnucash/engine/qofbook.h | 35 ++++++++-------- 8 files changed, 74 insertions(+), 71 deletions(-) diff --git a/gnucash/gnome/assistant-hierarchy.c b/gnucash/gnome/assistant-hierarchy.c index 5d07d85dec..4682477ec6 100644 --- a/gnucash/gnome/assistant-hierarchy.c +++ b/gnucash/gnome/assistant-hierarchy.c @@ -119,7 +119,7 @@ typedef struct gboolean use_defaults; gboolean new_book; /* presumably only used for new book creation but we check*/ - GNCOptionDB *options; + GncOptionDB *options; GNCOptionWin *optionwin; GncHierarchyAssistantFinishedCallback when_completed; @@ -1503,7 +1503,7 @@ static void book_options_dialog_close_cb(GNCOptionWin * optionwin, gpointer user_data) { - GNCOptionDB * options = user_data; + GncOptionDB * options = user_data; gnc_options_dialog_destroy(optionwin); gnc_option_db_destroy(options); @@ -1516,7 +1516,8 @@ assistant_insert_book_options_page (hierarchy_data *data) 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); diff --git a/gnucash/gnome/dialog-report-style-sheet.c b/gnucash/gnome/dialog-report-style-sheet.c index 1df072a859..a9c5d84151 100644 --- a/gnucash/gnome/dialog-report-style-sheet.c +++ b/gnucash/gnome/dialog-report-style-sheet.c @@ -55,10 +55,10 @@ struct _stylesheetdialog typedef struct ss_info { - GNCOptionWin * odialog; - GNCOptionDB * odb; - SCM stylesheet; - GtkTreeRowReference * row_ref; + GNCOptionWin * odialog; + GncOptionDB * odb; + SCM stylesheet; + GtkTreeRowReference *row_ref; } ss_info; enum @@ -169,9 +169,9 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss, 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 = gnc_options_dialog_new(title, parent); + ssinfo->odb = (GncOptionDB *)scm_to_pointer(scm_options); ssinfo->stylesheet = sheet_info; ssinfo->row_ref = row_ref; g_free (title); diff --git a/gnucash/gnome/gnc-plugin-page-report.c b/gnucash/gnome/gnc-plugin-page-report.c index 3c5f4104c6..bd1e5f40bc 100644 --- a/gnucash/gnome/gnc-plugin-page-report.c +++ b/gnucash/gnome/gnc-plugin-page-report.c @@ -105,14 +105,14 @@ typedef struct GncPluginPageReportPrivate /// The report which this Page is satisfying SCM cur_report; /// The Option DB for this report. - GNCOptionDB *cur_odb; + GncOptionDB *cur_odb; SCM option_change_cb_id; /* 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; + GncOptionDB * initial_odb; SCM name_change_cb_id; /* keep a list of edited reports so that we can destroy them when @@ -648,18 +648,23 @@ 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 = + (GncOptionDB *)scm_to_pointer(scm_call_1(get_options, 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"); +*/ } if ((priv->cur_report != SCM_BOOL_F) && (priv->cur_odb != NULL)) { +/* 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; } @@ -669,12 +674,14 @@ 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 = (GncOptionDB *)scm_to_pointer(scm_call_1(get_options, + 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); - +*/ if (gnc_html_history_forward_p(gnc_html_get_history(priv->html))) { gnc_plugin_page_report_set_fwd_button(report, TRUE); @@ -730,8 +737,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 @@ -1033,8 +1041,8 @@ gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *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); + 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)) @@ -1044,7 +1052,7 @@ gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name) } /* Store the new name for the report. */ - gnc_option_db_set_string_option(priv->cur_odb, "General", + gnc_option_db_set_string_value(priv->cur_odb, "General", "Report name", name); /* Have to manually call the option change hook. */ @@ -1110,9 +1118,7 @@ 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; } @@ -1787,14 +1793,10 @@ gnc_plugin_page_report_options_cb( GtkAction *action, GncPluginPageReport *repor static GncInvoice* lookup_invoice(GncPluginPageReportPrivate *priv) { - SCM opt_val = gnc_option_db_lookup_option(priv->cur_odb, "General", - "Invoice Number", NULL); - if (opt_val == SCM_UNDEFINED) - return NULL; - -#define FUNC_NAME G_STRFUNC - return SWIG_MustGetPtr(opt_val, SWIG_TypeQuery("_p__gncInvoice"), 1, 0); -#undef FUNC_NAME + 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" @@ -1849,8 +1851,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 diff --git a/gnucash/gnome/window-report.c b/gnucash/gnome/window-report.c index 31df07d5b2..25fb3f6aa0 100644 --- a/gnucash/gnome/window-report.c +++ b/gnucash/gnome/window-report.c @@ -63,8 +63,7 @@ reportWindow(int report_id, GtkWindow *parent) struct report_default_params_data { GNCOptionWin * win; - GNCOptionDB * db; - SCM scm_options; + GncOptionDB * odb; SCM cur_report; }; @@ -78,7 +77,7 @@ gnc_options_dialog_apply_cb(GNCOptionWin * propertybox, GList *results = NULL, *iter; if (!win) return; - results = gnc_option_db_commit (win->db); + results = gnc_option_db_commit (win->odb); for (iter = results; iter; iter = iter->next) { GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW (win->win), @@ -124,8 +123,7 @@ gnc_options_dialog_close_cb(GNCOptionWin * propertybox, 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); + gnc_option_db_destroy(win->odb); g_free(win); } @@ -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"); @@ -166,9 +164,8 @@ gnc_report_window_default_params_editor(SCM options, SCM report, 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); @@ -188,11 +185,10 @@ gnc_report_window_default_params_editor(SCM options, SCM report, 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); + gnc_options_dialog_build_contents(prm->win, prm->odb); + gnc_option_db_clean(prm->odb); gnc_options_dialog_set_apply_cb(prm->win, gnc_options_dialog_apply_cb, @@ -215,6 +211,7 @@ gnc_report_edit_options(SCM report, GtkWindow *parent) SCM get_report_type = scm_c_eval_string("gnc:report-type"); SCM ptr; SCM options; + GncOptionDB* odb; GtkWidget *options_widget = NULL; /* If the options editor widget already exists we simply raise it */ @@ -229,16 +226,19 @@ gnc_report_edit_options(SCM report, GtkWindow *parent) _("There are no options for this report.")); return FALSE; } + odb = (GncOptionDB*)scm_to_pointer(options); /* Multi-column type reports need a special options dialog */ ptr = scm_call_1(get_report_type, report); if (scm_is_string(ptr)) { gchar *rpt_type = gnc_scm_to_utf8_string (ptr); +#if 0 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); +#endif + options_widget = gnc_report_window_default_params_editor (odb, report, parent); g_free (rpt_type); } diff --git a/gnucash/gnome/window-report.h b/gnucash/gnome/window-report.h index e51bbc0a1d..17ff0195c2 100644 --- a/gnucash/gnome/window-report.h +++ b/gnucash/gnome/window-report.h @@ -27,13 +27,14 @@ //#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 diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 0aea402c7f..f0018994e3 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -897,7 +897,7 @@ gnc_register_internal_option(GncOptionDB* db, const char* section, void gnc_register_owner_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, GncInvoice* value) + const char* doc_string, GncOwner* value) { GncOption option{section, name, key, doc_string, (const QofInstance*)value, GncOptionUIType::INVOICE}; @@ -907,7 +907,7 @@ gnc_register_owner_option(GncOptionDB* db, const char* section, void gnc_register_invoice_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, GncOwner* value) + const char* doc_string, GncInvoice* value) { GncOption option{section, name, key, doc_string, (const QofInstance*)value, GncOptionUIType::OWNER}; @@ -1132,7 +1132,6 @@ gnc_option_db_save(GncOptionDB* odb, QofBook* book, { odb->save_to_kvp(book, static_cast(clear_options)); } -} void gnc_option_db_book_options(GncOptionDB*) diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp index 53873e8e94..01e32ea6c0 100644 --- a/libgnucash/engine/qofbook.cpp +++ b/libgnucash/engine/qofbook.cpp @@ -1137,14 +1137,14 @@ qof_book_set_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 7c0d330355..5932ecab15 100644 --- a/libgnucash/engine/qofbook.h +++ b/libgnucash/engine/qofbook.h @@ -42,6 +42,12 @@ #ifdef __cplusplus #include //To preempt it being included extern "C" in a later header. +class GncOptionDB; +#else +#include +typedef GNCOptionDB GncOptionDB; +#endif +#ifdef __cplusplus extern "C" { #endif @@ -74,15 +80,8 @@ typedef struct KvpValueImpl KvpValue; typedef void (*QofBookDirtyCB) (QofBook *, gboolean dirty, gpointer user_data); -#ifdef __cplusplus -class GncOptionDB; -using GNCOptionDB = GncOptionDB; -#else -typedef struct gnc_option_db GNCOptionDB; -#endif - -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 @@ -375,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 From ae79fd016df7ee1902c6b8015a382e31b7ce802a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:32:16 -0700 Subject: [PATCH 111/298] More skeletons --- libgnucash/app-utils/gnc-optiondb.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index f0018994e3..44326a27c9 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1132,9 +1132,15 @@ gnc_option_db_save(GncOptionDB* odb, QofBook* book, { odb->save_to_kvp(book, static_cast(clear_options)); } +const char* +gnc_option_db_lookup_string_value(GncOptionDB*, const char*, const char*) +{ + return nullptr; +} void -gnc_option_db_book_options(GncOptionDB*) +gnc_option_db_set_string_value(GncOptionDB*, const char*, + const char*, const char*) { } From e78c01269949feeefea911663a3fa98cf29cfb5d Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:33:06 -0700 Subject: [PATCH 112/298] Fill in the book options. --- libgnucash/app-utils/gnc-optiondb.cpp | 151 ++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 44326a27c9..f854ea6189 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "gnc-optiondb.h" #include "gnc-optiondb.hpp" #include "gnc-optiondb-impl.hpp" @@ -1132,6 +1133,156 @@ gnc_option_db_save(GncOptionDB* odb, QofBook* book, { 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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*, const char*, const char*) { From 3514725a97f0737b63a84701d40d8f9c5e9e7135 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:34:45 -0700 Subject: [PATCH 113/298] Make gnc_register_number_range_option a template on ValueType. GtkSpinButton works with doubles, but we want to preserve the ability to use other types. It really should have enable_if. --- libgnucash/app-utils/gnc-optiondb.cpp | 27 +++++++++++++++++++-------- libgnucash/app-utils/gnc-optiondb.hpp | 6 ++++-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index f854ea6189..2ee330443d 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -853,14 +853,15 @@ gnc_register_list_option(GncOptionDB* db, const char* section, /* Only balance-forecast.scm, hello-world.scm, and net-charts.scm * use decimals and fractional steps and they can be worked around. */ -void +template void gnc_register_number_range_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, int value, int min, - int max, int step) + const char* doc_string, ValueType value, + ValueType min, ValueType max, ValueType step) { - GncOption option{GncOptionRangeValue{section, name, key, doc_string, - value, min, max, step}}; + GncOption option{GncOptionRangeValue{section, name, key, + doc_string, value, min, + max, step}}; db->register_option(section, std::move(option)); } @@ -928,10 +929,10 @@ gnc_register_taxtable_option(GncOptionDB* db, const char* section, void gnc_register_counter_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, int value) + const char* doc_string, double value) { - GncOption option{GncOptionRangeValue{section, name, key, doc_string, - value, 0, 999999999, 1}}; + GncOption option{GncOptionRangeValue{section, name, key, doc_string, + value, 0.0, 999999999.0, 1.0}}; db->register_option(section, std::move(option)); } @@ -1312,3 +1313,13 @@ gnc_option_db_set_glist_value(GncOptionDB*, const char*, const char*, GList*) { } +// Force creation of templates +template void gnc_register_number_range_option(GncOptionDB* 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(GncOptionDB* 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.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index b373aa7635..eeb1c565c4 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -138,10 +138,12 @@ void gnc_register_list_option(GncOptionDB* db, const char* section, const char* doc_string, const char* value, GncMultichoiceOptionChoices&& list); +template void gnc_register_number_range_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, - int value, int min, int max, int step); + ValueType value, ValueType min, + ValueType max, ValueType step); void gnc_register_number_plot_size_option(GncOptionDB* db, const char* section, const char* name, @@ -180,7 +182,7 @@ void gnc_register_taxtable_option(GncOptionDB* db, const char* section, void gnc_register_counter_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, int value); + const char* doc_string, double value); void gnc_register_counter_format_option(GncOptionDB* db, const char* section, const char* name, From d8f83d6ee6634bd5e843a1e9e6cc066976f30732 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:42:59 -0700 Subject: [PATCH 114/298] Boolean simple won't work if its UI type is INTERNAL. --- libgnucash/app-utils/gnc-optiondb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 2ee330443d..90f44e6e6b 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -726,7 +726,7 @@ gnc_register_simple_boolean_option(GncOptionDB* db, bool value) { GncOption option{section, name, key, doc_string, value, - GncOptionUIType::INTERNAL}; + GncOptionUIType::BOOLEAN}; db->register_option(section, std::move(option)); } From 4a4e5d36f8f837bdf3e44e5586fec81aaead100a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:44:38 -0700 Subject: [PATCH 115/298] Enable gnc_register_owner_option to handle the three types that GncOwner aliases. --- libgnucash/app-utils/gnc-optiondb.cpp | 34 +++++++++++++++++++++------ libgnucash/app-utils/gnc-optiondb.hpp | 3 ++- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 90f44e6e6b..c91574e913 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -896,23 +896,43 @@ gnc_register_internal_option(GncOptionDB* db, const char* section, db->register_option(section, std::move(option)); } +static inline GncOptionUIType +owner_type_to_ui_type(GncOwnerType type) +{ + switch (type) + { + case GNC_OWNER_NONE: + case GNC_OWNER_UNDEFINED: + case GNC_OWNER_JOB: + return GncOptionUIType::INTERNAL; + case GNC_OWNER_CUSTOMER: + return GncOptionUIType::CUSTOMER; + case GNC_OWNER_VENDOR: + return GncOptionUIType::VENDOR; + case GNC_OWNER_EMPLOYEE: + return GncOptionUIType::EMPLOYEE; + } +} + void gnc_register_owner_option(GncOptionDB* db, const char* section, - const char* name, const char* key, - const char* doc_string, GncOwner* value) + const char* name, const char* key, + const char* doc_string, GncOwner* value, + GncOwnerType type) { - GncOption option{section, name, key, doc_string, (const QofInstance*)value, - GncOptionUIType::INVOICE}; + GncOption option{section, name, key, doc_string, + (const QofInstance*)value->owner.undefined, + owner_type_to_ui_type(type)}; 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) + const char* name, const char* key, + const char* doc_string, GncInvoice* value) { GncOption option{section, name, key, doc_string, (const QofInstance*)value, - GncOptionUIType::OWNER}; + GncOptionUIType::INVOICE}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index eeb1c565c4..34b8122684 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -174,7 +174,8 @@ void gnc_register_invoice_option(GncOptionDB* db, const char* section, void gnc_register_owner_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, GncOwner* value); + const char* doc_string, GncOwner* value, + GncOwnerType type); void gnc_register_taxtable_option(GncOptionDB* db, const char* section, const char* name, const char* key, From 7cb27b26362aebfcdd842ec4deeb41fc75a2ad6a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:45:34 -0700 Subject: [PATCH 116/298] Make changes to controls enable the Apply & OK buttons. --- gnucash/gnome-utils/dialog-options.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 78a67fae4e..328ca26ef2 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -270,6 +270,7 @@ 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 From 2dd8d782e5f692e8096bfc29022d8fb888197b85 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:46:47 -0700 Subject: [PATCH 117/298] Don't crash if the option set doesn't set a default section. --- gnucash/gnome-utils/dialog-options.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 328ca26ef2..f3f329ad4f 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -542,13 +542,14 @@ gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox, auto num_sections = odb->num_sections(); auto default_section = odb->get_default_section(); - PINFO("Default Section name is %s", default_section->get_name().c_str()); + PINFO("Default Section name is %s", + default_section ? default_section->get_name().c_str() : "NULL"); odb->foreach_section( [propertybox, default_section, &default_page] (GncOptionSectionPtr& section) { auto page = gnc_options_dialog_append_page(propertybox, section); - if (section.get() == default_section) + if (default_section && section.get() == default_section) default_page = page; }); From e3b5a7d833f2a470e47225b9c8201336e0c71f62 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:48:41 -0700 Subject: [PATCH 118/298] Fix boolean controls so that the set visitor recognizes the value. --- gnucash/gnome-utils/dialog-options.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index f3f329ad4f..8e736832d9 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -926,7 +926,7 @@ public: void set_option_from_ui_item(GncOption& option) noexcept override { auto widget{GTK_TOGGLE_BUTTON(get_widget())}; - option.set_value(gtk_toggle_button_get_active(widget)); + option.set_value(static_cast(gtk_toggle_button_get_active(widget))); } }; @@ -944,7 +944,7 @@ create_option_widget (GncOption& option, gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); auto widget = gtk_check_button_new (); - auto ui_item{std::make_unique(GncGtkBooleanUIItem{widget})}; + auto ui_item{std::make_unique(widget)}; g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(gnc_option_changed_widget_cb), &option); From 79fdb4124e6810cc4cba90f48acf376991b0f91c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 14:50:17 -0700 Subject: [PATCH 119/298] Fix some errant UI Types and a mis-formatted function. --- gnucash/gnome-utils/dialog-options.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 8e736832d9..0e9a42b7f9 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1938,7 +1938,7 @@ class GncGtkListUIItem : public GncOptionGtkUIItem { public: GncGtkListUIItem(GtkWidget* widget) : - GncOptionGtkUIItem{widget, GncOptionUIType::MULTICHOICE} {} + GncOptionGtkUIItem{widget, GncOptionUIType::LIST} {} void set_ui_item_from_option(GncOption& option) noexcept override { auto widget{GTK_TREE_VIEW(get_widget())}; @@ -2496,7 +2496,7 @@ class GncGtkDateFormatUIItem : public GncOptionGtkUIItem { public: GncGtkDateFormatUIItem(GtkWidget* widget) : - GncOptionGtkUIItem{widget, GncOptionUIType::STRING} {} + GncOptionGtkUIItem{widget, GncOptionUIType::DATE_FORMAT} {} void set_ui_item_from_option(GncOption& option) noexcept override { auto widget{GNC_DATE_FORMAT(get_widget())}; @@ -2723,10 +2723,13 @@ public: }; template<> GtkWidget * -create_option_widget (GncOption& option, GtkGrid *page_box, - GtkLabel *name_label, char *documentation, +create_option_widget (GncOption& option, + GtkGrid *page_box, + GtkLabel *name_label, + char *documentation, /* Return values */ - GtkWidget **enclosing, bool *packed) + GtkWidget **enclosing, + bool *packed) { *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); From 010ab1a965a87c25550f7b32207558d533ca9db7 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 17:25:46 -0700 Subject: [PATCH 120/298] Fix free of unallocated ptr crash. --- libgnucash/app-utils/gnc-optiondb.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index c91574e913..909b5c2948 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -588,7 +588,9 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept if (type == GncOptionUIType::BOOLEAN) { auto val{option.template get_value()}; - auto kvp{new KvpValue(val ? "t" : "f")}; + // ~KvpValue will g_free the value. + auto kvp{new KvpValue(val ? g_strdup("t") : + g_strdup("f"))}; qof_book_set_option(book, kvp, &list_head); } else if (type > GncOptionUIType::DATE_FORMAT) From c751e561847409444b25f819edd8b6292fb2e68c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 17:27:15 -0700 Subject: [PATCH 121/298] Fix fail to load or save number-range values in KVP. --- libgnucash/app-utils/gnc-optiondb.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 909b5c2948..a0f3fc235c 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -602,7 +602,9 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept } else if (type == GncOptionUIType::NUMBER_RANGE) { - auto kvp{new KvpValue(option.template get_value())}; + /* The Gtk control uses a double so that's what we + * have to store. */ + auto kvp{new KvpValue(option.template get_value())}; qof_book_set_option(book, kvp, &list_head); } else @@ -634,6 +636,9 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept 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; @@ -1166,7 +1171,7 @@ gnc_option_db_book_options(GncOptionDB* odb) //Accounts Tab - gnc_register_number_range_option(odb, OPTION_SECTION_ACCOUNTS, + 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); From 245a8fccc7886550b22cb6bfb95b9e63681b53a1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 17:47:26 -0700 Subject: [PATCH 122/298] Correctly select UI types for GUID output. --- libgnucash/app-utils/gnc-optiondb.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index a0f3fc235c..74fdc6cd5b 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -568,6 +568,28 @@ GncOptionDB::load_from_key_value(std::istream& iss) return iss; } +bool +is_qofinstance_ui_type(GncOptionUIType type) +{ + switch (type) + { + case CURRENCY: + case COMMODITY: + case ACCOUNT_SEL: + case BUDGET: + case OWNER: + case CUSTOMER: + case VENDOR: + case EMPLOYEE: + case INVOICE: + case TAX_TABLE: + case QUERY: + return true; + default: + return false; + } +} + void GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept { @@ -593,7 +615,7 @@ GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept g_strdup("f"))}; qof_book_set_option(book, kvp, &list_head); } - else if (type > GncOptionUIType::DATE_FORMAT) + else if (is_qofinstance_ui_type(type)) { const QofInstance* inst{QOF_INSTANCE(option.template get_value())}; auto guid = guid_copy(qof_instance_get_guid(inst)); From 6491c98563152043228891e24dc6d70cafc36695 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 17:49:34 -0700 Subject: [PATCH 123/298] Clean up some omitted type-forcing in set_option_from_ui_type() instances. --- gnucash/gnome-utils/dialog-options.cpp | 11 ++-- gnucash/gnome/dialog-report-column-view.c | 77 +++++++++------------- gnucash/gnome/dialog-report-column-view.h | 2 +- libgnucash/app-utils/gnc-option-uitype.hpp | 1 + 4 files changed, 38 insertions(+), 53 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 0e9a42b7f9..57cec62187 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -971,7 +971,7 @@ public: void set_option_from_ui_item(GncOption& option) noexcept override { auto widget{GTK_ENTRY(get_widget())}; - option.set_value(gtk_entry_get_text(widget)); + option.set_value(std::string{gtk_entry_get_text(widget)}); } }; @@ -2091,7 +2091,7 @@ public: } void set_option_from_ui_item(GncOption& option) noexcept override { - option.set_value(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget()))); + option.set_value(gtk_spin_button_get_value(GTK_SPIN_BUTTON(get_widget()))); } }; @@ -2217,13 +2217,14 @@ public: 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()); + 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(gtk_font_button_get_font_name(font_button)); + option.set_value(std::string{gtk_font_button_get_font_name(font_button)}); } }; @@ -2727,7 +2728,7 @@ create_option_widget (GncOption& option, GtkGrid *page_box, GtkLabel *name_label, char *documentation, - /* Return values */ + /* Return values */ GtkWidget **enclosing, bool *packed) { diff --git a/gnucash/gnome/dialog-report-column-view.c b/gnucash/gnome/dialog-report-column-view.c index 88c5925fcc..7d8fbec85a 100644 --- a/gnucash/gnome/dialog-report-column-view.c +++ b/gnucash/gnome/dialog-report-column-view.c @@ -60,12 +60,11 @@ struct gncp_column_view_edit GtkTreeView * available; GtkTreeView * contents; - SCM options; SCM view; - GNCOptionDB * odb; + GncOptionDB * odb; SCM available_list; - SCM contents_list; + GList* contents_list; int contents_selected; GtkWidget *add_button; @@ -82,26 +81,16 @@ 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) +gnc_column_view_set_option(GncOptionDB * odb, char * section, char * name, + GList* 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); - } + gnc_option_db_set_glist_value(section, name, new_value); } 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); @@ -175,12 +164,11 @@ 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); + GList* contents = gnc_option_db_lookup_glist_option(view->odb, + "__general", + "report-list"); SCM this_report; - SCM selection; - gchar *name; + gchar* selection; GtkListStore *store; GtkTreeIter iter; @@ -189,41 +177,38 @@ update_contents_lists(gnc_column_view_edit * view) /* 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)) + if (g_list_length(contents)) { 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)); + row = MIN (row, g_list_length(view->contents_list) - 1); + selection = g_list_nth_value(view->contents_list, row); } else - selection = SCM_UNDEFINED; + selection = NULL; - 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 (GList* node = contents; node; g_list_next(node)) { - for (int i = 0; !scm_is_null(contents); contents = SCM_CDR(contents), i++) - { - SCM contents_temp = SCM_CAR(contents); + gchar *name; + SCM contents_temp = SCM_CAR(node); + int id = scm_to_int(SCM_CAAR(node)); - 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)); - 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); + 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); @@ -324,7 +309,7 @@ gnc_column_view_edit_close_cb(GNCOptionWin * win, gpointer user_data) ********************************************************************/ GtkWidget * -gnc_column_view_edit_options(SCM options, SCM view) +gnc_column_view_edit_options(GncOptionDB* odb, SCM view) { SCM get_editor = scm_c_eval_string("gnc:report-editor-widget"); SCM ptr; @@ -366,12 +351,11 @@ gnc_column_view_edit_options(SCM options, SCM view) 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); + r->odb = odb; gnc_options_dialog_build_contents(r->optwin, r->odb); @@ -380,7 +364,6 @@ gnc_column_view_edit_options(SCM options, SCM view) 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); diff --git a/gnucash/gnome/dialog-report-column-view.h b/gnucash/gnome/dialog-report-column-view.h index 1b37b499ff..4153690e31 100644 --- a/gnucash/gnome/dialog-report-column-view.h +++ b/gnucash/gnome/dialog-report-column-view.h @@ -28,6 +28,6 @@ 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/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp index b053503117..5cc07c0581 100644 --- a/libgnucash/app-utils/gnc-option-uitype.hpp +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -53,6 +53,7 @@ enum GncOptionUIType INVOICE, TAX_TABLE, QUERY, + REPORT_LIST, MAX_VALUE, //Nake sure this one is always last }; From 5f9c66aa616a33cb0f6e6d31df3d952522672a8b Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Apr 2020 17:57:07 -0700 Subject: [PATCH 124/298] Save all options to KVP if clear_options is true. --- libgnucash/app-utils/gnc-optiondb.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 74fdc6cd5b..ff81482004 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -596,11 +596,11 @@ 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) + [clear_options, book](GncOptionSectionPtr& section) { section->foreach_option( - [book, §ion](auto& option) { - if (option.is_changed()) + [clear_options, book, §ion](auto& option) { + if (clear_options || option.is_changed()) { // qof_book_set_option wants a GSList path. Let's avoid // allocating and make one here. From 276d33975abed7c76066e36c6f48cb387314d4df Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 5 Apr 2020 09:07:34 -0700 Subject: [PATCH 125/298] Remove dialog-options.c, replaced by dialog-options.cpp. Adds new function reset_default_value to GncOption; doing it outside of the class requires knowing the Type of m_value. --- gnucash/gnome-utils/dialog-options.c | 4163 ---------------------- gnucash/gnome-utils/dialog-options.cpp | 44 +- libgnucash/app-utils/gnc-option-impl.hpp | 9 + libgnucash/app-utils/gnc-option.cpp | 6 + libgnucash/app-utils/gnc-option.hpp | 1 + 5 files changed, 48 insertions(+), 4175 deletions(-) delete mode 100644 gnucash/gnome-utils/dialog-options.c diff --git a/gnucash/gnome-utils/dialog-options.c b/gnucash/gnome-utils/dialog-options.c deleted file mode 100644 index ec0a9fda23..0000000000 --- a/gnucash/gnome-utils/dialog-options.c +++ /dev/null @@ -1,4163 +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 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; - -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 -}; - -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); -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); -} - -<<<<<<< HEAD -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); -} -======= ->>>>>>> f87d57145 (Remove the incomplete book-currency code.) - -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; -} - -<<<<<<< HEAD -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; -} - -======= ->>>>>>> f87d57145 (Remove the incomplete book-currency code.) -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); -} - -/* 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); - - component_id = gnc_register_gui_component (retval->component_class, - NULL, component_close_handler, - retval); - gnc_gui_component_set_session (component_id, gnc_get_current_session()); - - 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; -} - -/************************* - * 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)); - for (i = 0; i < index && list; i++) - list = list->next; - g_return_val_if_fail (list, TRUE); - - button = list->data; - g_list_free (list); - 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; -} - -/************************* - * 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); -} - -<<<<<<< HEAD -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)); -} - -======= ->>>>>>> f87d57145 (Remove the incomplete book-currency code.) -/************************************/ -/* 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 - }, - { 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 index 57cec62187..838fefae4d 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -241,17 +241,24 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive) 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); + GtkWidget* widget = GTK_WIDGET(it->data); + const gchar* name = gtk_widget_get_name(widget); - 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 (name, "ok_button") == 0 || + g_strcmp0 (name, "apply_button") == 0) + gtk_widget_set_sensitive (widget, sensitive); + else if (g_strcmp0 (name, "cancel_button") == 0) + gtk_button_set_label (GTK_BUTTON (widget), + sensitive ? _("_Cancel") : + _("_Close")); } g_list_free (children); } + break; // Found the button-box, no need to continue. } g_list_free (children); } + break; // Found the box, no need to continue. } g_list_free (children); } @@ -684,6 +691,7 @@ dialog_reset_cb(GtkWidget * w, gpointer data) { GNCOptionWin *win = static_cast(data); gpointer val; + bool dialog_changed = false; val = g_object_get_data(G_OBJECT(w), "section"); g_return_if_fail (val); @@ -691,12 +699,17 @@ dialog_reset_cb(GtkWidget * w, gpointer data) auto section = static_cast(val); section->foreach_option( - [](GncOption& 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(); - const_cast(option.get_ui_item())->set_dirty(true); }); - dialog_changed_internal (win->window, TRUE); + dialog_changed_internal (win->window, dialog_changed); } void @@ -768,6 +781,9 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title, 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; @@ -808,7 +824,10 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title, gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, retval); - gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(retval->window), parent); + // 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(retval->window), + parent); if (title) gtk_window_set_title(GTK_WINDOW(retval->window), title); @@ -942,7 +961,8 @@ create_option_widget (GncOption& option, *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); - auto widget = gtk_check_button_new (); + auto widget = + gtk_check_button_new_with_label (gtk_label_get_text(name_label)); auto ui_item{std::make_unique(widget)}; @@ -1791,8 +1811,8 @@ create_account_widget(GncOption& option, char *name) if (multiple_selection) { - /* Put the "Show hidden" checkbox on a separate line since the 4 buttons make - the dialog too wide. */ + /* 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); @@ -2824,7 +2844,7 @@ gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win) { gnc_options_dialog_set_help_cb(win, (GNCOptionWinCallback)gnc_book_options_help_cb, - NULL); + nullptr); } /* Dummy function impls. The following functions are declared in diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index de985b437c..4dddb407a9 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -129,6 +129,7 @@ public: ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } void set_value(ValueType new_value) { m_value = new_value; } + 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; } @@ -182,6 +183,7 @@ public: else throw std::invalid_argument("Validation failed, value not set."); } + void reset_default_value() { m_value = m_default_value; } bool is_changed() const noexcept { return m_value != m_default_value; } std::ostream& to_scheme(std::ostream&) const; std::istream& from_scheme(std::istream&); @@ -425,6 +427,7 @@ public: 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; } @@ -659,6 +662,7 @@ public: { return std::get<2>(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; } @@ -855,6 +859,7 @@ public: m_value = values; } GList* account_type_list() const noexcept; + 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; } @@ -1043,6 +1048,10 @@ public: { return gnc_relative_date_description(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; } diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index c50f0a04f4..30e874e083 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -123,6 +123,12 @@ GncOption::set_value(ValueType 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 { diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 72ba3fedd9..7b8e3d4c5c 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -74,6 +74,7 @@ public: template void set_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; From 2ccfe0c1910befd269c1001afd7ea38551b29eec Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 5 Apr 2020 09:09:08 -0700 Subject: [PATCH 126/298] Finish implementing the TaxTable option GUI. --- gnucash/gnome/business-options-gnome.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp index 2e26b992a3..e7ac983ab6 100644 --- a/gnucash/gnome/business-options-gnome.cpp +++ b/gnucash/gnome/business-options-gnome.cpp @@ -161,9 +161,14 @@ public: GncOptionGtkUIItem(widget, GncOptionUIType::TAX_TABLE) {} void set_ui_item_from_option(GncOption& option) noexcept override { + auto taxtable{option.get_value()}; + 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)); } }; @@ -179,18 +184,18 @@ create_option_widget(GncOption& option, *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"}; + 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))}; - g_object_unref(builder); 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); From 8440b9c99c9ced5e5e8cdd993cd996b723c1799a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 5 Apr 2020 10:02:20 -0700 Subject: [PATCH 127/298] Test QofInstance from option before trying to use it. Avoids GObject type error noise. --- gnucash/gnome-utils/dialog-options.cpp | 32 +++++++++++++----------- gnucash/gnome/business-options-gnome.cpp | 11 +++++--- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 838fefae4d..10ac44dde0 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1086,9 +1086,9 @@ public: void set_ui_item_from_option(GncOption& option) noexcept override { auto widget{GNC_CURRENCY_EDIT(get_widget())}; - auto currency = - GNC_COMMODITY(option.get_value()); - gnc_currency_edit_set_currency(widget, currency); + 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 { @@ -1126,9 +1126,9 @@ public: void set_ui_item_from_option(GncOption& option) noexcept override { auto widget{GNC_GENERAL_SELECT(get_widget())}; - auto commodity = - GNC_COMMODITY(option.get_value()); - gnc_general_select_set_selected(widget, commodity); + 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 { @@ -1872,9 +1872,9 @@ public: void set_ui_item_from_option(GncOption& option) noexcept override { auto widget{GNC_ACCOUNT_SEL(get_widget())}; - gnc_account_sel_set_account(widget, - GNC_ACCOUNT(option.get_value()), - FALSE); + auto instance{option.get_value()}; + if (instance) + gnc_account_sel_set_account(widget, GNC_ACCOUNT(instance), FALSE); } void set_option_from_ui_item(GncOption& option) noexcept override { @@ -2725,12 +2725,14 @@ public: { GtkTreeIter iter; auto widget{GTK_COMBO_BOX(get_widget())}; - auto budget{GNC_BUDGET(option.get_value())}; - auto tree_model{gtk_combo_box_get_model(widget)}; - if (gnc_tree_model_budget_get_iter_for_budget(tree_model, &iter, - budget)) - gtk_combo_box_set_active_iter(widget, &iter); - + 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 { diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp index e7ac983ab6..fc0204a71a 100644 --- a/gnucash/gnome/business-options-gnome.cpp +++ b/gnucash/gnome/business-options-gnome.cpp @@ -123,8 +123,10 @@ public: GncOptionGtkUIItem(widget, GncOptionUIType::INVOICE) {} void set_ui_item_from_option(GncOption& option) noexcept override { - gnc_general_search_set_selected(GNC_GENERAL_SEARCH(get_widget()), - GNC_INVOICE(option.get_value())); + 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 { @@ -162,8 +164,9 @@ public: void set_ui_item_from_option(GncOption& option) noexcept override { auto taxtable{option.get_value()}; - gnc_simple_combo_set_value(GTK_COMBO_BOX(get_widget()), - GNC_TAXTABLE(taxtable)); + 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 { From 507f35cc303e0bc6453d50d5a61d8e7e038c30de Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 5 Apr 2020 10:37:33 -0700 Subject: [PATCH 128/298] Fix wrong callback for TEXT control. --- gnucash/gnome-utils/dialog-options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 10ac44dde0..2f32c3d387 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1069,7 +1069,7 @@ create_option_widget (GncOption& option, GtkGrid *page_bo auto ui_item{std::make_unique(widget)}; auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); g_signal_connect(G_OBJECT(text_buffer), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), &option); + G_CALLBACK(gnc_option_changed_option_cb), &option); option.set_ui_item(std::move(ui_item)); option.set_ui_item_from_option(); From c6fce31795344a42a63967e68b4e2f7c980f09c1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 5 Apr 2020 10:38:24 -0700 Subject: [PATCH 129/298] Set the component_class on the GNCOptionWin, silencing component-manager error. --- gnucash/gnome-utils/dialog-options.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 2f32c3d387..a3785ae9dd 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -780,6 +780,7 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title, 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")); + retval->component_class = component_class ? component_class : DIALOG_OPTIONS_CM_CLASS; // 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"); From 1f0bfe0cfa22cc7572ccac3e4b858ff3424b6c1b Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 5 Apr 2020 10:39:48 -0700 Subject: [PATCH 130/298] Log an error if we try to create an option for an unregistered UI type. --- gnucash/gnome-utils/dialog-options.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index a3785ae9dd..c117eb63d6 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -151,6 +151,7 @@ GncOptionUIFactory::create(GncOption& option, GtkGrid* page, GtkLabel* name, auto func{s_registry[static_cast(type)]}; if (func) return func(option, page, name, description, enclosing, packed); + PERR("No function registered for type %d", type); return nullptr; } From d1cfd62f3177abf681724ff6010042d2791fe28c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 5 Apr 2020 10:44:37 -0700 Subject: [PATCH 131/298] Fix loading text fields from KVP into options. --- libgnucash/app-utils/gnc-optiondb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index ff81482004..452b7bca79 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -670,7 +670,7 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept if (option.get_ui_type() == GncOptionUIType::BOOLEAN) option.set_value(*str == 't' ? true : false); else - option.set_value(str); + option.set_value(std::string{str}); break; } case KvpValue::Type::GUID: From 7dab089d49b3b6e5aa43b866d0733a7e4de576b5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 8 Apr 2020 09:53:12 -0700 Subject: [PATCH 132/298] Fix miscalculation of prev_quarter_end for the first quarter. If the current quarter ends June 30 backing up 3 months from there gets March 30, one day off. Back up first and calculate the end of the actual quarter of interest. Changed pre_quarter_start for consistency, it doesn't matter. --- libgnucash/engine/gnc-date.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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); } /* ================================================= */ From 21398dfda1462591522be96b387a581db492bbd0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 8 Apr 2020 11:44:11 -0700 Subject: [PATCH 133/298] Testing fixups for using GncOptionDB* in register_option functions. --- .../app-utils/test/gtest-gnc-optiondb.cpp | 46 +++++++++--------- .../app-utils/test/test-gnc-optiondb.scm | 47 ++++++++++--------- 2 files changed, 50 insertions(+), 43 deletions(-) diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 963626b461..eaab1fa358 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -33,6 +33,8 @@ extern "C" #include } +using GncOptionDBPtr = std::unique_ptr; + class GncOptionDBTest : public ::testing::Test { protected: @@ -73,7 +75,7 @@ TEST_F(GncOptionDBTest, test_unregister_option) TEST_F(GncOptionDBTest, test_register_string_option) { - gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option", + gnc_register_string_option(m_db.get(), "foo", "bar", "baz", "Phony Option", std::string{"waldo"}); EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "bar").c_str()); } @@ -129,8 +131,8 @@ TEST_F(GncOptionDBTest, test_register_account_list_option) { AccountTestBook book; auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; - gnc_register_account_list_option(m_db, "foo", "bar", "baz", "Phony Option", - acclist); + gnc_register_account_list_option(m_db.get(), "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)); } @@ -139,7 +141,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option) { AccountTestBook book; auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; - gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", + gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz", "Phony Option", acclist, {ACCT_TYPE_STOCK}); EXPECT_EQ(4, m_db->find_option("foo", "bar")->get_value().size()); @@ -151,7 +153,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option) AccountTestBook book; auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; GncOptionAccountList accsel{acclist[2]}; - gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", + gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz", "Phony Option", accsel, {ACCT_TYPE_STOCK}); EXPECT_EQ(1, m_db->find_option("foo", "bar")->get_value().size()); @@ -163,10 +165,10 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct) AccountTestBook book; auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; GncOptionAccountList accsel{acclist[2]}; - gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", + gnc_register_account_list_limited_option(m_db.get(), "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", + gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz", "Phony Option", acclist, {ACCT_TYPE_BANK}); EXPECT_FALSE(m_db->find_option("foo", "bar")); @@ -179,8 +181,8 @@ TEST_F(GncOptionDBTest, test_register_multichoice_option) { "waldo", "pepper", "salt"}, { "pork", "sausage", "links"}, { "corge", "grault", "garply"}}; - gnc_register_multichoice_option(m_db, "foo", "bar", "baz", "Phony Option", - std::move(choices)); + gnc_register_multichoice_option(m_db.get(), "foo", "bar", "baz", + "Phony Option", std::move(choices)); ASSERT_TRUE(m_db->set_option("foo", "bar", std::string{"corge"})); EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str()); } @@ -197,7 +199,7 @@ time64_from_gdate(const GDate* g_date, DayPart when) TEST_F(GncOptionDBTest, test_register_relative_date_option) { - gnc_register_date_option(m_db, "foo", "bar", "baz", "Phony Option", + gnc_register_date_option(m_db.get(), "foo", "bar", "baz", "Phony Option", RelativeDatePeriod::START_ACCOUNTING_PERIOD); GDate prev_year_start; g_date_set_time_t(&prev_year_start, time(nullptr)); @@ -211,7 +213,8 @@ 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); + gnc_register_date_option(m_db.get(), "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); @@ -237,7 +240,8 @@ static const RelativeDatePeriodVec begin_dates TEST_F(GncOptionDBTest, test_register_start_date_option) { - gnc_register_start_date_option(m_db, "foo", "bar", "baz", "Phony Option"); + gnc_register_start_date_option(m_db.get(), "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); @@ -301,18 +305,18 @@ protected: 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", + gnc_register_string_option(m_db.get(), "foo", "bar", "baz", "Phony Option", std::string{"waldo"}); - gnc_register_date_option(m_db, "pork", "garply", "first", + gnc_register_text_option(m_db.get(), "foo", "sausage", "links", + "Phony Option", std::string{"waldo"}); + gnc_register_string_option(m_db.get(), "qux", "grault", "baz", + "Phony Option", std::string{""}); + gnc_register_text_option(m_db.get(), "qux", "garply", "fred", + "Phony Option", std::string{"waldo"}); + gnc_register_date_option(m_db.get(), "pork", "garply", "first", "Phony Date Option", RelativeDatePeriod::START_CURRENT_QUARTER); - gnc_register_account_list_option(m_db, "quux", "xyzzy", "second", + gnc_register_account_list_option(m_db.get(), "quux", "xyzzy", "second", "Phony AccountList Option", {aapl, hpe}); } diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 5a407f31b3..7df17cac07 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -46,7 +46,7 @@ (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" + (string-opt (gnc-register-string-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option" "waldo"))) (test-equal "waldo" (gnc-option-value option-db "foo" "bar")) @@ -89,38 +89,41 @@ (define (test-make-account-list-option book) (test-group "test-make-account-list-option" - (let ((optiondb (new-gnc-optiondb)) + (let ((option-db (new-gnc-optiondb)) (acctlist (gnc-account-list-from-types book (list ACCT-TYPE-STOCK)))) - (gnc-register-account-list-option optiondb "foo" "bar" "baz" + (gnc-register-account-list-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option" acctlist) - (let ((acct-list (gnc-option-value optiondb "foo" "bar"))) + (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-option" - (let ((optiondb (new-gnc-optiondb)) + (let ((option-db (new-gnc-optiondb)) (acctlist (gnc-account-list-from-types book (list ACCT-TYPE-STOCK)))) - (gnc-register-account-list-limited-option optiondb "foo" "bar" "baz" - "Phony Option" acctlist (list ACCT-TYPE-STOCK)) - (let ((acct-list (gnc-option-value optiondb "foo" "bar"))) + (gnc-register-account-list-limited-option + (GncOptionDBPtr-get 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)) (test-equal (cadr acctlist) (cadr acct-list))) - (gnc-register-account-list-limited-option optiondb "waldo" "pepper" "baz" - "Phony Option" acctlist (list ACCT-TYPE-BANK)) - (let ((acct-list (gnc-option-value optiondb "waldo" "pepper"))) + (gnc-register-account-list-limited-option + (GncOptionDBPtr-get 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 ((optiondb (new-gnc-optiondb)) + (let ((option-db (new-gnc-optiondb)) (acctlist (gnc-account-list-from-types book (list ACCT-TYPE-STOCK)))) - (gnc-register-account-sel-limited-option optiondb "salt" "pork" "baz" - "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK)) - (let ((acct (gnc-option-value optiondb "salt" "pork"))) + (gnc-register-account-sel-limited-option + (GncOptionDBPtr-get option-db) "salt" "pork" "baz" + "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK)) + (let ((acct (gnc-option-value option-db "salt" "pork"))) (test-equal (list (cadr acctlist)) acct))))) (let* ((book (gnc-option-test-book-new)) @@ -155,7 +158,7 @@ (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" + (multi-opt (gnc-register-multichoice-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option" multichoice))) (test-equal "plugh" (gnc-option-value option-db "foo" "bar")) @@ -170,7 +173,7 @@ (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" + (list-op (gnc-register-list-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option" "AvgBalPlot" value-list))) (test-equal "AvgBalPlot" (gnc-option-value option-db "foo" "bar")) @@ -182,7 +185,7 @@ (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" + (date-opt (gnc-register-date-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option" (RelativeDatePeriod-today))) (a-time (gnc-dmy2time64 11 07 2019))) @@ -195,7 +198,7 @@ (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" + (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option" (list (RelativeDatePeriod-today) (RelativeDatePeriod-start-this-month) (RelativeDatePeriod-start-prev-month) @@ -211,10 +214,10 @@ (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 option-db "foo" "bar" + (number-opt (gnc-register-number-range-option-double (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option" 15 5 30 1))) - (test-equal 15 (gnc-option-value option-db "foo" "bar")) + (test-equal 15.0 (gnc-option-value option-db "foo" "bar")) (gnc-set-option option-db "foo" "bar" 20) - (test-equal 20 (gnc-option-value option-db "foo" "bar"))) + (test-equal 20.0 (gnc-option-value option-db "foo" "bar"))) (test-end "test-gnc-number-range-option")) From 85db341afe0211b9be71a2ba678a106048cbd18f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 8 Apr 2020 12:05:19 -0700 Subject: [PATCH 134/298] Remove test-option-utils It no longer works. Since that's all that test-app-utils called, delete it too. --- libgnucash/app-utils/test/CMakeLists.txt | 2 - libgnucash/app-utils/test/test-app-utils.c | 54 ------- .../app-utils/test/test-option-util.cpp | 137 ------------------ 3 files changed, 193 deletions(-) delete mode 100644 libgnucash/app-utils/test/test-app-utils.c delete mode 100644 libgnucash/app-utils/test/test-option-util.cpp diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index eb6b27c0e3..7b2568e5bf 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -12,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) - macro(add_app_utils_test _TARGET _SOURCE_FILES) gnc_add_test(${_TARGET} "${_SOURCE_FILES}" APP_UTILS_TEST_INCLUDE_DIRS APP_UTILS_TEST_LIBS) endmacro() diff --git a/libgnucash/app-utils/test/test-app-utils.c b/libgnucash/app-utils/test/test-app-utils.c deleted file mode 100644 index ae63a01cf2..0000000000 --- a/libgnucash/app-utils/test/test-app-utils.c +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************** - * test-app-utils.c: GLib g_test test execution file. * - * 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, 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 void test_suite_option_util (void); -extern void test_suite_gnc_ui_util (void); - -static void -guile_main (void *closure, int argc, char **argv) -{ - int retval; - scm_c_use_module("gnucash app-utils"); - - test_suite_option_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; -} 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); -} From 31a0153fec66fcacc4348ba0f75efd488a37369b Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 8 Apr 2020 12:08:54 -0700 Subject: [PATCH 135/298] Another GncOptionDBPtr fix. --- libgnucash/app-utils/gnc-optiondb.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 48f4492339..b9472eb968 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -220,7 +220,7 @@ gnc_option_test_book_destroy(QofBook* book) %rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD; %rename(gnc_register_date_option_set) - gnc_register_date_option(const GncOptionDBPtr&, const char*, const char*, + gnc_register_date_option(GncOptionDB*, const char*, const char*, const char*, const char*, RelativeDatePeriodVec&, bool); From 52600bbd1f9a8a2c0bc04edca605572c8ccbd50b Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 8 Apr 2020 12:11:16 -0700 Subject: [PATCH 136/298] Instantiate gnc_register_number_range_option templates in SWIG. --- libgnucash/app-utils/gnc-optiondb.i | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index b9472eb968..06bac06d4f 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -142,6 +142,12 @@ 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 SCM scm_from_value(GncOptionAccountList value) { @@ -384,7 +390,10 @@ wrap_unique_ptr(GncOptionDBPtr, 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 %{ using GncOptionDBPtr = std::unique_ptr; From b19f3d383c9ad869f9ea6a76fb93f3880beffbde Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 8 Apr 2020 12:20:06 -0700 Subject: [PATCH 137/298] Protect some exceptions from being passed to Swig. --- libgnucash/app-utils/gnc-optiondb.cpp | 15 +++++++++++---- libgnucash/app-utils/gnc-optiondb.i | 13 +++++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 452b7bca79..70a8dc0612 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -888,10 +888,17 @@ gnc_register_number_range_option(GncOptionDB* db, const char* section, const char* doc_string, ValueType value, ValueType min, ValueType max, ValueType step) { - GncOption option{GncOptionRangeValue{section, name, key, - doc_string, value, min, - max, step}}; - db->register_option(section, std::move(option)); + 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 diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 06bac06d4f..a698fb5745 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -425,10 +425,19 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); auto db_opt = optiondb->find_option(section, name); if (!db_opt) { -// PWARN("Attempt to write non-existent option %s/%s", section, name); + std::cerr <<"Attempt to write non-existent option " << section + << "/" << name; return; } - GncOption_set_value_from_scm(db_opt, new_value); + 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 From 4f3fd665cfc1f7552f3b06b9de7bec5e86b3e4e8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 8 Apr 2020 12:20:12 -0700 Subject: [PATCH 138/298] Fix counter and counter_format storage in KVP. So that they go to their own slots instead of to the options slot. --- libgnucash/app-utils/gnc-optiondb.cpp | 171 ++++++++++++++++---------- 1 file changed, 109 insertions(+), 62 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 70a8dc0612..0cdaf0db42 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -568,7 +568,37 @@ GncOptionDB::load_from_key_value(std::istream& iss) return iss; } -bool +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) @@ -590,55 +620,73 @@ is_qofinstance_ui_type(GncOptionUIType type) } } +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( - [clear_options, book](GncOptionSectionPtr& section) + [book](GncOptionSectionPtr& section) { section->foreach_option( - [clear_options, book, §ion](auto& option) { - if (clear_options || option.is_changed()) + [book, §ion](auto& option) { + if (option.is_changed()) { - // qof_book_set_option wants a GSList path. Let's avoid - // allocating and make one here. - GSList list_tail{(void*)option.get_name().c_str(), nullptr}; - GSList list_head{(void*)section->get_name().c_str(), &list_tail}; + /* 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) - { - auto val{option.template get_value()}; - // ~KvpValue will g_free the value. - auto kvp{new KvpValue(val ? g_strdup("t") : - g_strdup("f"))}; - qof_book_set_option(book, kvp, &list_head); - } + kvp = kvp_value_from_bool_option(option); else if (is_qofinstance_ui_type(type)) - { - const QofInstance* inst{QOF_INSTANCE(option.template get_value())}; - auto guid = guid_copy(qof_instance_get_guid(inst)); - auto kvp{new KvpValue(guid)}; - qof_book_set_option(book, kvp, &list_head); - } + 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. */ - auto kvp{new KvpValue(option.template get_value())}; - qof_book_set_option(book, kvp, &list_head); - } + kvp = new KvpValue(option.template get_value()); else - { - auto kvp{new KvpValue{g_strdup(option.template get_value().c_str())}}; - qof_book_set_option(book, kvp, &list_head); - } + 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 { @@ -648,11 +696,15 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept section->foreach_option( [book, §ion](GncOption& option) { - /* qof_book_set_option wants a GSList path. Let's avoid allocating - * and make one here. - */ - GSList list_tail{(void*)option.get_name().c_str(), nullptr}; - GSList list_head{(void*)section->get_name().c_str(), &list_tail}; + // 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; @@ -665,20 +717,11 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept option.set_value(kvp->get()); break; case KvpValue::Type::STRING: - { - 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}); + fill_option_from_string_kvp(option, kvp); break; - } case KvpValue::Type::GUID: - { - auto guid{kvp->get()}; - option.set_value((const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type())); - break; - } + fill_option_from_guid_kvp(option, kvp); + break; default: return; break; @@ -1224,67 +1267,71 @@ gnc_option_db_book_options(GncOptionDB* odb) //Counters Tab gnc_register_counter_option(odb, counter_section, - N_("Customer number"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + 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"), "a", + 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"), "b", + N_("Vendor number format"), "gncVendorb", N_("The format string to use for generating vendor numbers. This is a printf-style format string."), empty_string); From 60c1d16e64d2ea327234dc91ab3bd6d232f2aa33 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 6 Sep 2020 13:15:36 -0700 Subject: [PATCH 139/298] Add/rename files in POTFILES. --- po/POTFILES.in | 1 - 1 file changed, 1 deletion(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 5e1439b19f..fb268e6b35 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -135,7 +135,6 @@ 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 From 90b8fce59fe02a0e0ccac05a2baaadf7317e07a0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 6 Sep 2020 13:15:51 -0700 Subject: [PATCH 140/298] Fix build after merge. --- bindings/guile/gnc-guile-bindings.c | 1 - libgnucash/app-utils/test/CMakeLists.txt | 16 ++++++---------- libgnucash/app-utils/test/test-gnc-optiondb.scm | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/bindings/guile/gnc-guile-bindings.c b/bindings/guile/gnc-guile-bindings.c index 4d40df5cdf..77dbf384be 100644 --- a/bindings/guile/gnc-guile-bindings.c +++ b/bindings/guile/gnc-guile-bindings.c @@ -44,6 +44,5 @@ gnc_guile_bindings_init(void) scm_init_sw_core_utils_module(); scm_init_sw_engine_module(); - is_initialized = 1; } } diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index 7b2568e5bf..f5358e7334 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -80,18 +80,14 @@ gnc_add_scheme_tests("${test_app_utils_scheme_SOURCES}") if (HAVE_SRFI64) gnc_add_scheme_test_targets(scm-test-app-utils-srfi64 - "${test_app_utils_scheme_SRFI64_SOURCES}" - "tests" - "${GUILE_DEPENDS};scm-srfi64-extras" - FALSE - ) + SOURCES "${test_app_utils_scheme_SRFI64_SOURCES}" + OUTPUT_DIR "tests" + DEPENDS "${GUILE_DEPENDS};scm-srfi64-extras") gnc_add_scheme_test_targets(scm-test-gnc-optiondb - "test-gnc-optiondb.scm" - "tests" - "swig-apputils-guile-cpp;scm-srfi64-extras" - FALSE - ) + SOURCES "test-gnc-optiondb.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_app_utils_scheme_SRFI64_SOURCES}") endif() diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 7df17cac07..56f3e9b160 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -26,7 +26,7 @@ (use-modules (gnucash gnc-module)) (eval-when (compile load eval expand) - (load-extension "_sw_app_utils" "scm_init_sw_app_utils_module")) + (load-extension "libgnc-app-utils" "scm_init_sw_app_utils_module")) (use-modules (gnucash engine)) (use-modules (sw_app_utils)) From 8c77ce967b500aeaaa02db7209b0c513c1cbc6d0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 6 Oct 2020 10:09:27 -0700 Subject: [PATCH 141/298] Implement and test obsolete option name aliasing. --- libgnucash/app-utils/gnc-optiondb.cpp | 85 +++++++++++++++++++++- libgnucash/app-utils/gnc-optiondb.i | 7 ++ libgnucash/app-utils/test/test-options.scm | 35 +++++---- 3 files changed, 108 insertions(+), 19 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 0cdaf0db42..f6750b1a39 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -34,6 +34,69 @@ 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()) + { + std::cerr << "No alias for " << old_name << " found.\n"; + return nullptr; + } + std::cerr << "Found " << alias->second.second << " as alias for " << old_name << ".\n"; + 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"}}, + // invoice.scm, renamed November 2018 + {"Individual Taxes", {nullptr, "Use Detailed Tax Summary"}}, + // income-gst-statement.scm + {"default format", {nullptr, "Default Format"}} +}; static bool operator==(const std::string& str, const char* cstr) @@ -76,7 +139,13 @@ GncOptionSection::find_option(const char* name) const [name](auto& option) -> bool { return option.get_name() == name; }); - return (option == m_options.end() ? nullptr : &*option); + 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{} {} @@ -133,9 +202,19 @@ const GncOption* GncOptionDB::find_option(const std::string& section, const char* name) const { auto db_section = const_cast(this)->find_section(section); - if (!db_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) + return find_option(alias->first, alias->second); return nullptr; - return db_section->find_option(name); } std::string diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index a698fb5745..7ccc7214e0 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -446,6 +446,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); 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); + } %} #endif //SWIGGUILE diff --git a/libgnucash/app-utils/test/test-options.scm b/libgnucash/app-utils/test/test-options.scm index 27b970692d..f7e55eeb3c 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,17 @@ (test-end "test-options")) (define (test-lookup-option) - (let ((options (gnc:new-options))) - (gnc:register-option - options - (gnc:make-simple-boolean-option - "Section" "Start Date" "sort-tag" "docstring" 'default-val)) + (let* ((options (new-gnc-optiondb)) + (string-opt (gnc-register-string-option (GncOptionDBPtr-get options) + "Section" "Start Date" + "sort-tag" "docstring" "waldo") + )) - (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?")))) + (gnc-register-simple-boolean-option + (GncOptionDBPtr-get options) + "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)))) From 776d1aaa0ed8b711490edf18590a1ed59ea4d256 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 8 Oct 2020 16:48:00 -0700 Subject: [PATCH 142/298] GNC_DEFINE_TYPE_WITH_CODE: Don't use class as a symbol name. It conflicts with the C++ keyword. Even GObject code knows better. --- gnucash/gnome-utils/gnc-gobject-utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; \ From 7022f5222d6250b54ac7ef7e682a02c1de7b175f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 8 Oct 2020 16:50:41 -0700 Subject: [PATCH 143/298] Revert gnc_option_db_lookup|set_glist_value to ...scm_value. GLists and SCM lists are not interchangeable. --- libgnucash/app-utils/gnc-optiondb.cpp | 7 ++++--- libgnucash/app-utils/gnc-optiondb.h | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index f6750b1a39..1bc1f2fa3f 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -21,6 +21,7 @@ * * \********************************************************************/ +#include #include #include #include @@ -1484,14 +1485,14 @@ gnc_option_db_lookup_qofinstance_value(GncOptionDB*, const char*, const char*) return nullptr; } -GList* -gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*) +SCM +gnc_option_db_lookup_scm_value(GncOptionDB*, const char*, const char*) { return nullptr; } void -gnc_option_db_set_glist_value(GncOptionDB*, const char*, const char*, GList*) +gnc_option_db_set_scm_value(GncOptionDB*, const char*, const char*, SCM) { } diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h index f4b3d4246c..6511594ae4 100644 --- a/libgnucash/app-utils/gnc-optiondb.h +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -32,6 +32,9 @@ class GncOptionDB; typedef struct GncOption GncOption; typedef struct GncOptionDB GncOptionDB; #endif + +#include + #ifdef __cplusplus class GncOptionDB; extern "C" @@ -153,7 +156,7 @@ const QofInstance* gnc_option_db_lookup_qofinstance_value(GncOptionDB*, * @return the GList* of the value or nullptr if the option isn't found * or if its value isn't a string. */ -GList* gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*); +SCM gnc_option_db_lookup_scm_value(GncOptionDB*, const char*, const char*); /** * Set the GList* value of an option in the GncOptionDB. @@ -166,8 +169,8 @@ GList* gnc_option_db_lookup_glist_value(GncOptionDB*, const char*, const char*); * @param name the option name * @param value the value to be stored in the option. */ -void gnc_option_db_set_glist_value(GncOptionDB*, const char*, - const char*, GList*); +void gnc_option_db_set_scm_value(GncOptionDB*, const char*, + const char*, SCM); #ifdef __cplusplus } From e48416010dfd4fb47d9142e09f1f1f61f5fb1c68 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 8 Oct 2020 16:51:49 -0700 Subject: [PATCH 144/298] Remove redundant GncOptionDB decl. --- libgnucash/app-utils/gnc-optiondb.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h index 6511594ae4..6c5dfa9b5c 100644 --- a/libgnucash/app-utils/gnc-optiondb.h +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -36,11 +36,8 @@ typedef struct GncOptionDB GncOptionDB; #include #ifdef __cplusplus -class GncOptionDB; extern "C" { -#else -typedef struct GncOptionDB GncOptionDB; #endif #include #include From ae73b3855d27c769fd8055194452e75f81adf545 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 8 Oct 2020 17:37:56 -0700 Subject: [PATCH 145/298] Cast const char* args from guile to char*. Swig's guile typemaps lack a const char* freearg typemap. When a function argument is explicitly const char* Swig automatically discards the const in the temporary's decl so freeing it isn't a problem, but if the function arg is declared as something typedeffed to const char* the alias is used in the temporary's decl causing an error about discarding the const qualifier when it's time to free the temporary. Providing a const char* freearg typemap works around the shortcoming. --- gnucash/html/gnc-html.i | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gnucash/html/gnc-html.i b/gnucash/html/gnc-html.i index 573cf81cfb..4b76ff0502 100644 --- a/gnucash/html/gnc-html.i +++ b/gnucash/html/gnc-html.i @@ -34,6 +34,9 @@ #include %} #if defined(SWIGGUILE) + +%typemap (freearg) const char* "if (must_free$argnum && $1) SWIG_free((char*)$1);"; + %{ #include "guile-mappings.h" From 023db2335349a8d5f15843f199d6cdfd9a769f03 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 8 Oct 2020 17:43:07 -0700 Subject: [PATCH 146/298] Make URLType an alias for const char* instead of char*. Because it uses macros declared as string constants as values and C++ objects if one tries to initialize a non-const char* with a string constant. On the other hand one doesn't want to strdup into a const char* because then you can't free it, so all of the instances where it's strduped for inserting into a container must be changed from URLType to char*. --- gnucash/html/gnc-html-extras.h | 2 +- gnucash/html/gnc-html-history.h | 2 +- gnucash/html/gnc-html.c | 14 +++++++------- gnucash/html/gnc-html.h | 1 + 4 files changed, 10 insertions(+), 9 deletions(-) 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.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 70b4352c9a..45a4ce671b 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 From 472814d315cb424f488a9c7da208272501294fa3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 9 Oct 2020 14:08:18 -0700 Subject: [PATCH 147/298] Change the normal internal option ValueType to SCM. Internal options are used as a Scheme hack and don't need to be interpreted on the C side so there's no point in converting them to C types. --- libgnucash/app-utils/gnc-option-impl.hpp | 11 ++++++++--- libgnucash/app-utils/gnc-option.cpp | 5 +++++ libgnucash/app-utils/gnc-option.hpp | 2 ++ libgnucash/app-utils/gnc-optiondb.cpp | 2 +- libgnucash/app-utils/gnc-optiondb.hpp | 3 ++- libgnucash/app-utils/gnc-optiondb.i | 14 ++++++++++++++ 6 files changed, 32 insertions(+), 5 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 4dddb407a9..0abd8314f1 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -274,9 +274,14 @@ template>(std::istream& iss, OptType& opt) { std::decay_t value; - iss >> value; - opt.set_value(value); - return iss; + if constexpr (std::is_same_v, SCM>) + return iss; + else + { + iss >> value; + opt.set_value(value); + return iss; + } } template() const; template int GncOption::get_value() const; @@ -540,6 +542,7 @@ template const QofInstance* GncOption::get_value() const; template RelativeDatePeriod GncOption::get_value() const; template GncOptionAccountList GncOption::get_value() const; template GncMultichoiceOptionIndexVec GncOption::get_value() const; +template SCM GncOption::get_value() const; template bool GncOption::get_default_value() const; template int GncOption::get_default_value() const; @@ -551,6 +554,7 @@ template const QofInstance* GncOption::get_default_value() c template RelativeDatePeriod GncOption::get_default_value() const; template GncOptionAccountList GncOption::get_default_value() const; template GncMultichoiceOptionIndexVec GncOption::get_default_value() const; +template SCM GncOption::get_default_value() const; template void GncOption::set_value(bool); template void GncOption::set_value(int); @@ -564,6 +568,7 @@ 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(SCM); template void GncOption::get_limits(double&, double&, double&) const noexcept; template void GncOption::get_limits(int&, int&, int&) const noexcept; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 7b8e3d4c5c..5a9af10930 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -29,6 +29,7 @@ extern "C" #include } +#include #include #include #include @@ -51,6 +52,7 @@ using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue, + GncOptionValue, GncOptionAccountValue, GncOptionMultichoiceValue, GncOptionRangeValue, diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 1bc1f2fa3f..cc469d7a15 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1048,7 +1048,7 @@ gnc_register_query_option(GncOptionDB* db, const char* section, void gnc_register_internal_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, std::string value) + const char* doc_string, SCM value) { GncOption option{section, name, key, doc_string, value, GncOptionUIType::INTERNAL}; diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 34b8122684..75c0ac6967 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -29,6 +29,7 @@ #include #include #include +#include extern "C" { #include @@ -161,7 +162,7 @@ void gnc_register_color_option(GncOptionDB* db, const char* section, void gnc_register_internal_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, std::string value); + const char* doc_string, SCM value); void gnc_register_currency_option(GncOptionDB* db, const char* section, diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 7ccc7214e0..f620aabe18 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -64,6 +64,12 @@ template inline SCM /*{ return SCM_BOOL_F; }*/ +template <> inline SCM +scm_from_value(SCM value) +{ + return value; +} + template <> inline SCM scm_from_value(QofQuery* value) { @@ -118,6 +124,8 @@ scm_from_value(const QofInstance* value) template inline ValueType scm_to_value(SCM new_value) { + if constexpr (std::is_same_v, SCM>) + return new_value; return ValueType{}; } @@ -369,6 +377,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); { return std::visit([](const auto& option)->SCM { auto value{option.get_value()}; + if constexpr (std::is_same_v, + SCM>) + return value; return scm_from_value(static_cast(value)); }, swig_get_option($self)); } @@ -376,6 +387,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); { return std::visit([](const auto& option)->SCM { auto value{option.get_default_value()}; + if constexpr (std::is_same_v, + SCM>) + return value; return scm_from_value(static_cast(value)); }, swig_get_option($self)); } From ea835b31b791c1348a20f8bc255f6faceda511e8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 6 Oct 2020 10:14:10 -0700 Subject: [PATCH 148/298] Provide GncOptionPtr& overloads for register-option functions. Simplifies and makes safer calls from Scheme and C++ by not requiring a raw pointer. --- libgnucash/app-utils/gnc-optiondb.cpp | 6 +- libgnucash/app-utils/gnc-optiondb.hpp | 666 +++++++++++++++++- libgnucash/app-utils/gnc-optiondb.i | 35 +- .../app-utils/test/gtest-gnc-optiondb.cpp | 32 +- .../app-utils/test/test-gnc-optiondb.scm | 22 +- libgnucash/app-utils/test/test-options.scm | 5 +- 6 files changed, 731 insertions(+), 35 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index cc469d7a15..8ad69ddb86 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -215,7 +215,7 @@ GncOptionDB::find_option(const std::string& section, const char* name) const */ if (alias && alias->first) return find_option(alias->first, alias->second); - return nullptr; + return nullptr; } std::string @@ -1497,11 +1497,11 @@ gnc_option_db_set_scm_value(GncOptionDB*, const char*, const char*, SCM) } // Force creation of templates -template void gnc_register_number_range_option(GncOptionDB* db, +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(GncOptionDB* db, +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, diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 75c0ac6967..de0f13aff3 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -45,6 +45,7 @@ extern "C" class GncOptionDB; +using GncOptionDBPtr = std::unique_ptr; using GncOptionAccountList = std::vector; using GncOptionAccountTypeList = std::vector; @@ -72,43 +73,221 @@ 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, @@ -116,12 +295,59 @@ void gnc_register_account_list_limited_option(GncOptionDB* db, 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, @@ -129,16 +355,88 @@ void gnc_register_account_sel_limited_option(GncOptionDB* db, 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_sel_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_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, 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, + GncMultichoiceOptionChoices&& value) +{ + gnc_register_multichoice_option(db.get(), section, name, + key, doc_string, 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, @@ -146,56 +444,311 @@ void gnc_register_number_range_option(GncOptionDB* db, 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, 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, QofQuery* value) +{ + gnc_register_query_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); +} + +/** + * Create a new internal string option and register it in the options database. + * + * Internal options are used for passing state data in some reports. As the name + * suggests they do not create UI elements in options dialogs. + * @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_internal_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, SCM value); +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_internal_option(GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, + const char* doc_string, + SCM value) +{ + gnc_register_internal_option(db.get(), section, name, key, + doc_string, value); +} + +/** + * 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 owner-type 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 type The Owner-type (Customer, Employee, or Vendor) + */ void gnc_register_owner_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncOwner* value, GncOwnerType type); +/** + * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. + */ +inline void gnc_register_owner_option(const GncOptionDBPtr& db, + const char* section, const char* name, + const char* key, const char* doc_string, + GncOwner* value, GncOwnerType type) +{ + gnc_register_owner_option(db.get(), section, name, key, + doc_string, value, type); +} + +/** + * 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, @@ -203,6 +756,16 @@ enum RelativeDateUI : uint8_t 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, @@ -210,25 +773,126 @@ void gnc_register_date_option(GncOptionDB* db, const char* section, 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 index f620aabe18..cd752e7ce0 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -234,7 +234,7 @@ gnc_option_test_book_destroy(QofBook* book) %rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD; %rename(gnc_register_date_option_set) - gnc_register_date_option(GncOptionDB*, const char*, const char*, + gnc_register_date_option(GncOptionDBPtr&, const char*, const char*, const char*, const char*, RelativeDatePeriodVec&, bool); @@ -364,6 +364,39 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %ignore gnc_option_to_scheme; %ignore gnc_option_from_scheme; +/* 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 GncOptionAccountList&, GncOptionAccountTypeList&&); +%ignore gnc_register_multichoice_option(GncOptionDB*, 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_internal_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_owner_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncOwner*, GncOwnerType); +%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); %include "gnc-option-date.hpp" %include "gnc-option.hpp" diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index eaab1fa358..0acbb633bc 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -75,7 +75,7 @@ TEST_F(GncOptionDBTest, test_unregister_option) TEST_F(GncOptionDBTest, test_register_string_option) { - gnc_register_string_option(m_db.get(), "foo", "bar", "baz", "Phony 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()); } @@ -131,7 +131,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_option) { AccountTestBook book; auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; - gnc_register_account_list_option(m_db.get(), "foo", "bar", "baz", + 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)); @@ -141,7 +141,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option) { AccountTestBook book; auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; - gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz", + gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", acclist, {ACCT_TYPE_STOCK}); EXPECT_EQ(4, m_db->find_option("foo", "bar")->get_value().size()); @@ -153,7 +153,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option) AccountTestBook book; auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; GncOptionAccountList accsel{acclist[2]}; - gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz", + gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", accsel, {ACCT_TYPE_STOCK}); EXPECT_EQ(1, m_db->find_option("foo", "bar")->get_value().size()); @@ -165,10 +165,10 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct) AccountTestBook book; auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; GncOptionAccountList accsel{acclist[2]}; - gnc_register_account_list_limited_option(m_db.get(), "foo", "bar", "baz", "Phony Option", + 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.get(), "foo", "bar", "baz", + 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")); @@ -181,7 +181,7 @@ TEST_F(GncOptionDBTest, test_register_multichoice_option) { "waldo", "pepper", "salt"}, { "pork", "sausage", "links"}, { "corge", "grault", "garply"}}; - gnc_register_multichoice_option(m_db.get(), "foo", "bar", "baz", + gnc_register_multichoice_option(m_db, "foo", "bar", "baz", "Phony Option", std::move(choices)); ASSERT_TRUE(m_db->set_option("foo", "bar", std::string{"corge"})); EXPECT_STREQ("corge", m_db->lookup_string_option("foo", "bar").c_str()); @@ -199,7 +199,7 @@ time64_from_gdate(const GDate* g_date, DayPart when) TEST_F(GncOptionDBTest, test_register_relative_date_option) { - gnc_register_date_option(m_db.get(), "foo", "bar", "baz", "Phony 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)); @@ -213,7 +213,7 @@ 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.get(), "foo", "bar", "baz", "Phony Option", + 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)); @@ -240,7 +240,7 @@ static const RelativeDatePeriodVec begin_dates TEST_F(GncOptionDBTest, test_register_start_date_option) { - gnc_register_start_date_option(m_db.get(), "foo", "bar", "baz", + 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)); @@ -305,18 +305,18 @@ protected: create_account(expenses, ACCT_TYPE_EXPENSE, "Gas"); create_account(expenses, ACCT_TYPE_EXPENSE, "Rent"); - gnc_register_string_option(m_db.get(), "foo", "bar", "baz", + gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option", std::string{"waldo"}); - gnc_register_text_option(m_db.get(), "foo", "sausage", "links", + gnc_register_text_option(m_db, "foo", "sausage", "links", "Phony Option", std::string{"waldo"}); - gnc_register_string_option(m_db.get(), "qux", "grault", "baz", + gnc_register_string_option(m_db, "qux", "grault", "baz", "Phony Option", std::string{""}); - gnc_register_text_option(m_db.get(), "qux", "garply", "fred", + gnc_register_text_option(m_db, "qux", "garply", "fred", "Phony Option", std::string{"waldo"}); - gnc_register_date_option(m_db.get(), "pork", "garply", "first", + gnc_register_date_option(m_db, "pork", "garply", "first", "Phony Date Option", RelativeDatePeriod::START_CURRENT_QUARTER); - gnc_register_account_list_option(m_db.get(), "quux", "xyzzy", "second", + gnc_register_account_list_option(m_db, "quux", "xyzzy", "second", "Phony AccountList Option", {aapl, hpe}); } diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 56f3e9b160..2da34c98b6 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -23,7 +23,7 @@ (use-modules (srfi srfi-64)) (use-modules (tests srfi64-extras)) -(use-modules (gnucash gnc-module)) +;; 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")) @@ -46,7 +46,7 @@ (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 (GncOptionDBPtr-get option-db) "foo" "bar" "baz" + (string-opt (gnc-register-string-option option-db "foo" "bar" "baz" "Phony Option" "waldo"))) (test-equal "waldo" (gnc-option-value option-db "foo" "bar")) @@ -92,7 +92,7 @@ (let ((option-db (new-gnc-optiondb)) (acctlist (gnc-account-list-from-types book (list ACCT-TYPE-STOCK)))) - (gnc-register-account-list-option (GncOptionDBPtr-get option-db) "foo" "bar" "baz" + (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)) @@ -104,13 +104,13 @@ (acctlist (gnc-account-list-from-types book (list ACCT-TYPE-STOCK)))) (gnc-register-account-list-limited-option - (GncOptionDBPtr-get option-db) "foo" "bar" "baz" + 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)) (test-equal (cadr acctlist) (cadr acct-list))) (gnc-register-account-list-limited-option - (GncOptionDBPtr-get option-db) "waldo" "pepper" "baz" + 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)))))) @@ -121,7 +121,7 @@ (acctlist (gnc-account-list-from-types book (list ACCT-TYPE-STOCK)))) (gnc-register-account-sel-limited-option - (GncOptionDBPtr-get option-db) "salt" "pork" "baz" + option-db "salt" "pork" "baz" "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK)) (let ((acct (gnc-option-value option-db "salt" "pork"))) (test-equal (list (cadr acctlist)) acct))))) @@ -158,7 +158,7 @@ (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 (GncOptionDBPtr-get option-db) "foo" "bar" "baz" + (multi-opt (gnc-register-multichoice-option option-db "foo" "bar" "baz" "Phony Option" multichoice))) (test-equal "plugh" (gnc-option-value option-db "foo" "bar")) @@ -173,7 +173,7 @@ (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 (GncOptionDBPtr-get option-db) "foo" "bar" "baz" + (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")) @@ -185,7 +185,7 @@ (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 (GncOptionDBPtr-get option-db) "foo" "bar" + (date-opt (gnc-register-date-option option-db "foo" "bar" "baz" "Phony Option" (RelativeDatePeriod-today))) (a-time (gnc-dmy2time64 11 07 2019))) @@ -198,7 +198,7 @@ (test-begin "test-gnc-test-date-set-option") (let* ((option-db (new-gnc-optiondb)) (date-opt (gnc-register-date-option-set - (GncOptionDBPtr-get option-db) "foo" "bar" "baz" "Phony Option" + option-db "foo" "bar" "baz" "Phony Option" (list (RelativeDatePeriod-today) (RelativeDatePeriod-start-this-month) (RelativeDatePeriod-start-prev-month) @@ -214,7 +214,7 @@ (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 (GncOptionDBPtr-get option-db) "foo" "bar" + (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")) diff --git a/libgnucash/app-utils/test/test-options.scm b/libgnucash/app-utils/test/test-options.scm index f7e55eeb3c..8e28da9199 100644 --- a/libgnucash/app-utils/test/test-options.scm +++ b/libgnucash/app-utils/test/test-options.scm @@ -15,13 +15,12 @@ (define (test-lookup-option) (let* ((options (new-gnc-optiondb)) - (string-opt (gnc-register-string-option (GncOptionDBPtr-get options) - "Section" "Start Date" + (string-opt (gnc-register-string-option options "Section" "Start Date" "sort-tag" "docstring" "waldo") )) (gnc-register-simple-boolean-option - (GncOptionDBPtr-get options) + options "Filter" "Void Transactions" "sort-tag" "docstring" 'default-val) ;; Testing that the old option name aliases work. (let ((option (gnc-lookup-option options "Section" "From"))) From e2c87f23c3c8d0760f886fb6c89fa621820ff4aa Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 8 Oct 2020 16:45:15 -0700 Subject: [PATCH 149/298] Convert optiondb users in gnome and gnome-utils to C++. To enable using std::unique_ptr and avoid memory management problems. --- gnucash/gnome-utils/CMakeLists.txt | 2 +- gnucash/gnome-utils/dialog-options.h | 6 +- gnucash/gnome-utils/gnc-icons.c | 1 + ...{gnc-main-window.c => gnc-main-window.cpp} | 582 +++++++++--------- gnucash/gnome-utils/gnc-main-window.h | 20 + gnucash/gnome/CMakeLists.txt | 12 +- ...nt-hierarchy.c => assistant-hierarchy.cpp} | 178 +++--- gnucash/gnome/assistant-hierarchy.h | 7 + gnucash/gnome/dialog-custom-report.c | 1 - ...n-view.c => dialog-report-column-view.cpp} | 120 ++-- ...n-view.h => dialog-report-column-view.hpp} | 4 +- ...-sheet.c => dialog-report-style-sheet.cpp} | 40 +- gnucash/gnome/dialog-report-style-sheet.h | 11 +- gnucash/gnome/gnc-budget-view.c | 1 - gnucash/gnome/gnc-plugin-page-budget.c | 1 - ...ge-report.c => gnc-plugin-page-report.cpp} | 183 +++--- gnucash/gnome/gnc-plugin-page-report.h | 9 +- .../{window-report.c => window-report.cpp} | 36 +- gnucash/gnome/window-report.h | 8 +- gnucash/html/gnc-html-webkit2.c | 4 +- libgnucash/app-utils/option-util.c | 4 +- libgnucash/app-utils/option-util.h | 2 - libgnucash/engine/qofbook.h | 3 +- po/POTFILES.in | 12 +- 24 files changed, 638 insertions(+), 609 deletions(-) rename gnucash/gnome-utils/{gnc-main-window.c => gnc-main-window.cpp} (91%) rename gnucash/gnome/{assistant-hierarchy.c => assistant-hierarchy.cpp} (93%) rename gnucash/gnome/{dialog-report-column-view.c => dialog-report-column-view.cpp} (88%) rename gnucash/gnome/{dialog-report-column-view.h => dialog-report-column-view.hpp} (99%) rename gnucash/gnome/{dialog-report-style-sheet.c => dialog-report-style-sheet.cpp} (95%) rename gnucash/gnome/{gnc-plugin-page-report.c => gnc-plugin-page-report.cpp} (94%) rename gnucash/gnome/{window-report.c => window-report.cpp} (91%) diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt index 960651b529..3d2e354cec 100644 --- a/gnucash/gnome-utils/CMakeLists.txt +++ b/gnucash/gnome-utils/CMakeLists.txt @@ -74,7 +74,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 diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h index 82d0fdd681..8c3890a785 100644 --- a/gnucash/gnome-utils/dialog-options.h +++ b/gnucash/gnome-utils/dialog-options.h @@ -32,9 +32,9 @@ using GNCOptionDB = GncOptionDB; extern "C" { #else -#include "option-util.h" -typedef GNCOption GncOption; -typedef GNCOptionDB GncOptionDB; +typedef struct GncOption GncOption; +typedef struct GncOptionDB GncOptionDB; +typedef GncOptionDB GNCOptionDB; #endif #include #include 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 91% rename from gnucash/gnome-utils/gnc-main-window.c rename to gnucash/gnome-utils/gnc-main-window.cpp index a8bda02f06..5c4aba19ed 100644 --- a/gnucash/gnome-utils/gnc-main-window.c +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -32,6 +32,9 @@ @author Copyright (C) 2003 Jan Arne Petersen @author Copyright (C) 2003,2005,2006 David Hampton */ +#include +extern "C" +{ #include #include @@ -43,7 +46,6 @@ #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" @@ -70,8 +72,7 @@ #include "gnc-warnings.h" #include "gnc-window.h" #include "gnc-prefs.h" -// +JSLED -//#include "gnc-html.h" +#include #include "gnc-autosave.h" #include "print-session.h" #ifdef MAC_INTEGRATION @@ -82,6 +83,8 @@ # include # include // for stat(2) #endif +} +#include "dialog-options.h" /** Names of signals generated by the main window. */ enum @@ -132,12 +135,12 @@ extern gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options); /** 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; @@ -282,27 +285,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", @@ -343,7 +346,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) }, @@ -351,12 +354,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", @@ -366,14 +369,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) }, @@ -381,12 +384,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) }, @@ -404,7 +407,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) }, @@ -417,17 +420,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 }, @@ -440,16 +443,16 @@ 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. */ @@ -462,7 +465,7 @@ static guint n_radio_entries = G_N_ELEMENTS (radio_entries); static const gchar *gnc_menu_important_actions[] = { "FileCloseAction", - NULL, + nullptr, }; @@ -473,7 +476,7 @@ static const gchar *gnc_menu_important_actions[] = static const gchar *always_insensitive_actions[] = { "FilePrintAction", - NULL + nullptr }; @@ -483,7 +486,7 @@ static const gchar *always_insensitive_actions[] = static const gchar *initially_insensitive_actions[] = { "FileCloseAction", - NULL + nullptr }; @@ -495,7 +498,7 @@ static const gchar *always_hidden_actions[] = { "ViewSortByAction", "ViewFilterByAction", - NULL + nullptr }; @@ -504,7 +507,7 @@ static const gchar *always_hidden_actions[] = static const gchar *immutable_page_actions[] = { "FileCloseAction", - NULL + nullptr }; @@ -513,7 +516,7 @@ static const gchar *immutable_page_actions[] = static const gchar *multiple_page_actions[] = { "WindowMovePageAction", - NULL + nullptr }; @@ -558,19 +561,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); } } @@ -595,9 +593,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, @@ -616,7 +614,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; @@ -635,7 +634,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); @@ -687,7 +686,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da gboolean max, visible, desired_visibility; gchar *window_group; gint page_start, page_count, i; - GError *error = NULL; + GError *error = nullptr; /* Setup */ ENTER("window %p, data %p (key file %p, window %d)", @@ -742,7 +741,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); @@ -761,7 +760,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) { @@ -783,7 +782,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) { @@ -822,7 +821,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) { @@ -839,7 +838,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) { @@ -855,7 +854,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) { @@ -871,7 +870,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) { @@ -900,7 +899,7 @@ 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) { @@ -911,7 +910,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da { /* 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++) { @@ -945,9 +944,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. */ @@ -965,14 +963,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(); } @@ -986,7 +985,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); @@ -1066,7 +1065,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); @@ -1152,7 +1151,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; } @@ -1169,14 +1168,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; @@ -1202,7 +1201,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)) { @@ -1215,7 +1214,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)) @@ -1281,7 +1280,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 */ @@ -1294,7 +1293,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; @@ -1320,7 +1319,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 @@ -1328,7 +1327,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); @@ -1338,7 +1337,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); @@ -1428,21 +1427,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; @@ -1581,10 +1580,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; @@ -1601,7 +1600,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(""); @@ -1677,7 +1676,7 @@ gnc_main_window_update_all_titles (void) { g_list_foreach(active_windows, (GFunc)gnc_main_window_update_title, - NULL); + nullptr); } static void @@ -1699,7 +1698,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(); @@ -1713,7 +1712,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; @@ -1736,8 +1735,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()) { @@ -1745,7 +1744,7 @@ static gchar *generate_statusbar_lastmodified_message() } if (!(uri && strlen (uri))) - return NULL; + return nullptr; else { if (gnc_uri_targets_local_fs (uri)) @@ -1761,30 +1760,37 @@ 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); g_object_unref (info); g_object_unref (file); +#else + return nullptr; +#endif } // If the URI is not a file but a database, we can maybe also show // something useful, but I have no idea how to obtain this information. @@ -1797,11 +1803,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)) { @@ -1819,7 +1825,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 @@ -1873,7 +1879,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(" "); } @@ -2008,10 +2014,10 @@ gnc_main_window_update_all_menu_items (void) /* 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; @@ -2047,11 +2053,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"); @@ -2126,13 +2130,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); @@ -2192,11 +2193,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"); @@ -2249,7 +2249,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) { @@ -2272,7 +2272,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; @@ -2297,7 +2297,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) { @@ -2325,7 +2325,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; @@ -2366,14 +2366,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); @@ -2409,7 +2409,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(" "); @@ -2424,7 +2424,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); @@ -2449,9 +2449,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); @@ -2509,7 +2509,7 @@ main_window_update_page_set_read_only_icon (GncPluginPage *page, for (children = gtk_container_get_children (GTK_CONTAINER(tab_widget)); children; children = children->next) { - GtkWidget *widget = children->data; + GtkWidget *widget = static_cast(children->data); if (GTK_IS_IMAGE(widget)) image = widget; } @@ -2639,7 +2639,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"); @@ -2664,7 +2664,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); @@ -2684,24 +2684,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); } @@ -2738,7 +2738,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); @@ -2760,10 +2760,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); @@ -2780,37 +2780,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. @@ -2839,7 +2839,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); @@ -2856,7 +2856,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(); @@ -2868,7 +2868,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 (); @@ -2937,13 +2937,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; @@ -3075,9 +3072,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)); @@ -3088,7 +3085,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 */ @@ -3099,10 +3096,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 @@ -3123,14 +3120,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); } @@ -3160,7 +3157,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 @@ -3195,18 +3192,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); @@ -3235,7 +3232,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); @@ -3277,7 +3274,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); @@ -3343,7 +3340,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) { @@ -3397,7 +3394,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); @@ -3428,18 +3425,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); @@ -3447,11 +3443,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, @@ -3486,17 +3482,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); @@ -3534,7 +3529,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); @@ -3557,18 +3552,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; } @@ -3623,7 +3617,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; @@ -3636,7 +3630,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; @@ -3720,7 +3714,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); @@ -3741,7 +3735,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); @@ -3756,7 +3750,7 @@ gnc_main_window_setup_window (GncMainWindow *window) guint merge_id; GncPluginManager *manager; GList *plugins; - GError *error = NULL; + GError *error = nullptr; gchar *filename; ENTER(" "); @@ -3782,7 +3776,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); @@ -3862,21 +3856,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); @@ -3935,7 +3929,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; @@ -4010,7 +4004,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. */ @@ -4020,10 +4014,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)); } @@ -4053,7 +4047,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); @@ -4063,20 +4057,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 */ @@ -4125,7 +4119,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); @@ -4186,15 +4180,15 @@ gnc_book_options_dialog_apply_helper(GncOptionDB * options) gboolean use_split_action_for_num_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", @@ -4230,7 +4224,7 @@ static void gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin, gpointer user_data) { - GncOptionDB * options = user_data; + auto options{static_cast(user_data)}; if (!options) return; @@ -4242,7 +4236,7 @@ static void gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin, gpointer user_data) { - GncOptionDB * options = user_data; + auto options{static_cast(user_data)}; gnc_options_dialog_destroy(optionwin); gnc_option_db_destroy(options); @@ -4271,7 +4265,7 @@ static gboolean show_handler (const char *class_name, gint component_id, gpointer user_data, gpointer iter_data) { - GNCOptionWin *optwin = user_data; + auto optwin{static_cast(user_data)}; GtkWidget *widget; if (!optwin) @@ -4297,9 +4291,9 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent) /* 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, @@ -4323,7 +4317,7 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent) 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 @@ -4399,13 +4393,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); + } } } @@ -4483,7 +4479,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); } } @@ -4618,17 +4615,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) { @@ -4638,7 +4635,7 @@ get_file (const gchar *partial) g_free(text); } g_free (filename); - return NULL; + return nullptr; } @@ -4648,7 +4645,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 ** @@ -4658,7 +4655,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); @@ -4697,7 +4694,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(), @@ -4721,7 +4718,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); @@ -4733,7 +4730,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); @@ -4753,15 +4750,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 @@ -4773,13 +4770,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 * @@ -4796,13 +4793,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; } @@ -4814,7 +4811,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); } @@ -4830,7 +4827,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); @@ -4849,7 +4846,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); @@ -4860,25 +4857,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); @@ -4936,7 +4929,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; @@ -4972,7 +4965,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; } @@ -5006,12 +4999,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 fe11c42ecf..106fef53ff 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" @@ -443,6 +447,22 @@ void gnc_main_window_show_all_windows(void); GtkWidget *gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow *parent); +/** + * 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. + **/ +gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options); + +#ifdef __cplusplus +} +#endif #endif /* __GNC_MAIN_WINDOW_H */ /** @} */ diff --git a/gnucash/gnome/CMakeLists.txt b/gnucash/gnome/CMakeLists.txt index 00e28d5c08..d48762487e 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-editor2.h @@ -69,7 +69,7 @@ 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 business-options-gnome.cpp @@ -98,8 +98,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-editor2.c dialog-sx-from-trans.c @@ -120,7 +120,7 @@ set (gnc_gnome_SOURCES gnc-plugin-page-owner-tree.c gnc-plugin-page-register.c gnc-plugin-page-register2.c - gnc-plugin-page-report.c + gnc-plugin-page-report.cpp gnc-plugin-page-sx-list.c gnc-split-reg.c gnc-split-reg2.c @@ -129,7 +129,7 @@ set (gnc_gnome_SOURCES top-level.c window-reconcile.c window-reconcile2.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 93% rename from gnucash/gnome/assistant-hierarchy.c rename to gnucash/gnome/assistant-hierarchy.cpp index 4682477ec6..3dd7c55295 100644 --- a/gnucash/gnome/assistant-hierarchy.c +++ b/gnucash/gnome/assistant-hierarchy.cpp @@ -22,10 +22,12 @@ * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ +#include +extern "C" +{ #include #include -#include #if PLATFORM(WINDOWS) #include #endif @@ -36,11 +38,9 @@ #include #include #include - #ifdef MAC_INTEGRATION #include #endif - #include "gnc-account-merge.h" #include "dialog-new-user.h" #include "dialog-options.h" @@ -66,6 +66,10 @@ #include "gnc-plugin-page-account-tree.h" #include "gnc-engine.h" +} + +#include + static QofLogModule log_module = GNC_MOD_IMPORT; #define GNC_PREFS_GROUP "dialogs.new-hierarchy" @@ -156,7 +160,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); } @@ -168,21 +172,19 @@ 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; } } 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); @@ -674,8 +675,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)); @@ -753,11 +754,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", @@ -769,7 +770,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); @@ -777,7 +778,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); @@ -791,7 +792,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 (); @@ -830,7 +831,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); @@ -859,7 +860,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)) @@ -909,7 +910,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), @@ -944,11 +945,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; } } @@ -974,7 +975,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)); @@ -1013,7 +1014,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); @@ -1027,7 +1028,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); } @@ -1060,7 +1061,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); @@ -1122,7 +1123,7 @@ balance_cell_data_func (GtkTreeViewColumn *tree_column, "text", string, "editable", allow_value, "sensitive", allow_value, - NULL); + nullptr); } static void @@ -1136,20 +1137,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 @@ -1161,7 +1162,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 @@ -1209,7 +1210,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); @@ -1224,7 +1225,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); } @@ -1236,16 +1237,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; } @@ -1261,7 +1261,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 @@ -1285,7 +1285,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); @@ -1328,17 +1328,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); } @@ -1347,16 +1347,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); } @@ -1366,16 +1366,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); } @@ -1412,7 +1412,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 ()); } @@ -1503,7 +1504,7 @@ static void book_options_dialog_close_cb(GNCOptionWin * optionwin, gpointer user_data) { - GncOptionDB * options = user_data; + auto options{static_cast(user_data)}; gnc_options_dialog_destroy(optionwin); gnc_option_db_destroy(options); @@ -1524,7 +1525,7 @@ assistant_insert_book_options_page (hierarchy_data *data) /* 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); + DIALOG_BOOK_OPTIONS_CM_CLASS, nullptr); gnc_options_dialog_build_contents_full (data->optionwin, data->options, FALSE); gnc_options_dialog_set_close_cb (data->optionwin, @@ -1620,12 +1621,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); @@ -1642,7 +1643,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* @@ -1656,7 +1657,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 @@ -1672,5 +1673,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/dialog-custom-report.c b/gnucash/gnome/dialog-custom-report.c index f2af3b0efd..248eb6ef0f 100644 --- a/gnucash/gnome/dialog-custom-report.c +++ b/gnucash/gnome/dialog-custom-report.c @@ -32,7 +32,6 @@ #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.cpp similarity index 88% rename from gnucash/gnome/dialog-report-column-view.c rename to gnucash/gnome/dialog-report-column-view.cpp index 7d8fbec85a..cf14268592 100644 --- a/gnucash/gnome/dialog-report-column-view.c +++ b/gnucash/gnome/dialog-report-column-view.cpp @@ -21,22 +21,26 @@ * Boston, MA 02110-1301, USA gnu@gnu.org * ********************************************************************/ +#include +extern "C" +{ #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" +} + +#include "dialog-report-column-view.hpp" +#include "dialog-options.h" +#include enum available_cols { @@ -64,7 +68,7 @@ struct gncp_column_view_edit GncOptionDB * odb; SCM available_list; - GList* contents_list; + SCM contents_list; int contents_selected; GtkWidget *add_button; @@ -81,10 +85,10 @@ 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, - GList* new_value) +gnc_column_view_set_option(GncOptionDB* odb, const char* section, + const char* name, SCM new_value) { - gnc_option_db_set_glist_value(section, name, new_value); + gnc_option_db_set_scm_value(odb, section, name, new_value); } static void @@ -164,11 +168,10 @@ static void update_contents_lists(gnc_column_view_edit * view) { SCM report_menu_name = scm_c_eval_string("gnc:report-menu-name"); - GList* contents = gnc_option_db_lookup_glist_option(view->odb, - "__general", - "report-list"); - SCM this_report; - gchar* selection; + SCM contents = gnc_option_db_lookup_scm_value(view->odb, "__general", + "report-list"); + SCM this_report; + SCM selection = SCM_UNDEFINED; GtkListStore *store; GtkTreeIter iter; @@ -177,44 +180,46 @@ update_contents_lists(gnc_column_view_edit * view) /* Update the list of selected reports (right selection box). */ tree_selection = gtk_tree_view_get_selection(view->contents); - if (g_list_length(contents)) + if (scm_is_list(view->contents_list) && !scm_is_null (view->contents_list)) { int row = view->contents_selected; - row = MIN (row, g_list_length(view->contents_list) - 1); - selection = g_list_nth_value(view->contents_list, row); + row = MIN (row, scm_ilength (view->contents_list) - 1); + selection = scm_list_ref (view->contents_list, scm_from_int (row)); } - else - selection = NULL; + 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); - for (GList* node = contents; node; g_list_next(node)) + if (!scm_is_list(contents)) + return; + + for (int i = 0; !scm_is_null(contents); + contents = SCM_CDR(contents), ++i) { - gchar *name; - SCM contents_temp = SCM_CAR(node); - int id = scm_to_int(SCM_CAAR(node)); + 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)); + 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, - scm_to_int(SCM_CADR(contents_temp)), - CONTENTS_COL_REPORT_ROWS, - scm_to_int(SCM_CADDR(contents_temp)), - -1); + 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); + if (scm_is_equal (contents_temp, selection)) + gtk_tree_selection_select_iter (tree_selection, &iter); - g_free (name); - } + g_free (name); } } @@ -242,7 +247,7 @@ gnc_column_view_update_buttons_cb (GtkTreeSelection *selection, if (is_selected) { - int len = scm_ilength (r->contents_list); + int len = scm_ilength (reinterpret_cast(r->contents_list)); gtk_tree_model_get (model, &iter, CONTENTS_COL_ROW, &r->contents_selected, -1); @@ -270,19 +275,20 @@ 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; + auto win{static_cast(user_data)}; + GList *results = nullptr, *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); + GtkWidget *dialog = + gtk_message_dialog_new(GTK_WINDOW(gnc_options_dialog_widget(w)), + 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); @@ -295,7 +301,7 @@ gnc_column_view_edit_apply_cb(GNCOptionWin * w, gpointer user_data) static void gnc_column_view_edit_close_cb(GNCOptionWin * win, gpointer user_data) { - gnc_column_view_edit * r = 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); @@ -323,17 +329,17 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view) if (ptr != SCM_BOOL_F) { #define FUNC_NAME "gtk_window_present" - GtkWindow * w = SWIG_MustGetPtr(ptr, SWIG_TypeQuery("_p_GtkWidget"), 1, 0); + auto w{static_cast(SWIG_MustGetPtr(ptr, SWIG_TypeQuery("_p_GtkWidget"), 1, 0))}; gtk_window_present(w); #undef FUNC_NAME - return NULL; + return nullptr; } 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))); + r->optwin = gnc_options_dialog_new (nullptr, GTK_WINDOW(gnc_ui_get_main_window (nullptr))); /* Hide the generic dialog page list. */ gtk_widget_hide(gnc_options_page_list(r->optwin)); @@ -377,7 +383,7 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view) renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("", renderer, "text", AVAILABLE_COL_NAME, - NULL); + nullptr); gtk_tree_view_append_column(r->available, column); /* use the selection cb to update buttons */ @@ -394,19 +400,19 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view) renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Report"), renderer, "text", CONTENTS_COL_NAME, - NULL); + 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, - NULL); + 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, - NULL); + nullptr); gtk_tree_view_append_column(r->contents, column); /* use the selection cb to update buttons */ @@ -435,7 +441,7 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view) void gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data) { - gnc_column_view_edit * r = 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?!"); SCM template_name; @@ -511,7 +517,7 @@ gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data) void gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data) { - gnc_column_view_edit * r = user_data; + auto r = static_cast(user_data); SCM newlist = SCM_EOL; SCM oldlist = r->contents_list; int count; @@ -553,7 +559,7 @@ gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data) void gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data) { - gnc_column_view_edit * r = user_data; + auto r = static_cast(user_data); SCM oldlist = r->contents_list; SCM newlist = SCM_EOL; SCM temp; @@ -591,7 +597,7 @@ 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) { - gnc_column_view_edit * r = user_data; + auto r = static_cast(user_data); SCM oldlist = r->contents_list; SCM newlist = SCM_EOL; SCM temp; @@ -629,7 +635,7 @@ gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data) void gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data) { - gnc_column_view_edit * r = user_data; + auto r = static_cast(user_data); GtkWidget * rowspin; GtkWidget * colspin; GtkWidget * dlg; diff --git a/gnucash/gnome/dialog-report-column-view.h b/gnucash/gnome/dialog-report-column-view.hpp similarity index 99% rename from gnucash/gnome/dialog-report-column-view.h rename to gnucash/gnome/dialog-report-column-view.hpp index 4153690e31..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(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 95% rename from gnucash/gnome/dialog-report-style-sheet.c rename to gnucash/gnome/dialog-report-style-sheet.cpp index a9c5d84151..60d4287d7a 100644 --- a/gnucash/gnome/dialog-report-style-sheet.c +++ b/gnucash/gnome/dialog-report-style-sheet.cpp @@ -22,13 +22,15 @@ * Boston, MA 02110-1301, USA gnu@gnu.org * ********************************************************************/ +#include +extern "C" +{ #include #include #include #include "dialog-report-style-sheet.h" -#include "dialog-options.h" #include "dialog-utils.h" #include "gnc-component-manager.h" #include "gnc-session.h" @@ -37,6 +39,9 @@ #include "gnc-guile-utils.h" #include "gnc-report.h" #include "gnc-ui.h" +} +#include "dialog-options.h" +#include #define DIALOG_STYLE_SHEETS_CM_CLASS "style-sheets-dialog" #define GNC_PREFS_GROUP "dialogs.style-sheet" @@ -82,9 +87,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"); @@ -118,14 +123,14 @@ 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); @@ -135,7 +140,7 @@ static void gnc_style_sheet_options_close_cb (GNCOptionWin * propertybox, gpointer user_data) { - ss_info * ssi = user_data; + auto ssi{static_cast(user_data)}; GtkTreeIter iter; if (gtk_tree_row_reference_valid (ssi->row_ref)) @@ -182,7 +187,7 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss, gnc_options_dialog_build_contents (ssinfo->odialog, ssinfo->odb); - gnc_options_dialog_set_style_sheet_options_help_cb (ssinfo->odialog); +// 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, @@ -260,9 +265,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 +447,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 0b288da2f8..89a96d70b5 100644 --- a/gnucash/gnome/gnc-budget-view.c +++ b/gnucash/gnome/gnc-budget-view.c @@ -61,7 +61,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 8a968bdf0e..35496027e5 100644 --- a/gnucash/gnome/gnc-plugin-page-budget.c +++ b/gnucash/gnome/gnc-plugin-page-budget.c @@ -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 94% rename from gnucash/gnome/gnc-plugin-page-report.c rename to gnucash/gnome/gnc-plugin-page-report.cpp index bd1e5f40bc..8b6137854d 100644 --- a/gnucash/gnome/gnc-plugin-page-report.c +++ b/gnucash/gnome/gnc-plugin-page-report.cpp @@ -36,13 +36,14 @@ @author Copyright (C) 2004 Joshua Sled @author Copyright (C) 2005 David Hampton */ - +#include +extern "C" +{ #include #include #include #include -#include #include #include @@ -68,12 +69,12 @@ #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 "gnc-icons.h" #include "print-session.h" +} /* NW: you can add GNC_MOD_REPORT to gnc-engine.h or simply define it locally. Any unique string with @@ -81,13 +82,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 @@ -272,7 +273,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; @@ -293,29 +294,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 @@ -352,8 +343,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); @@ -362,7 +353,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)", @@ -387,8 +378,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 */ @@ -442,8 +433,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); @@ -457,7 +448,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 ); @@ -467,7 +458,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 @@ -477,7 +468,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()); @@ -488,7 +479,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); @@ -502,7 +493,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); @@ -557,7 +548,7 @@ gnc_plugin_page_report_setup( GncPluginPage *ppage ) priv->edited_reports = SCM_EOL; priv->name_change_cb_id = SCM_BOOL_F; - 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); @@ -659,14 +650,14 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, */ } - 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 = nullptr; } if (priv->cur_report != SCM_BOOL_F) @@ -680,7 +671,7 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, priv->option_change_cb_id = gnc_option_db_register_change_callback(priv->cur_odb, gnc_plugin_page_report_option_change_cb, - report, NULL, NULL); + report, nullptr, nullptr); */ if (gnc_html_history_forward_p(gnc_html_get_history(priv->html))) { @@ -767,8 +758,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; } @@ -808,7 +799,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; } @@ -868,8 +859,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); @@ -942,14 +933,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); @@ -959,7 +950,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++) @@ -975,7 +966,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); @@ -984,7 +975,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) @@ -1000,7 +991,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); @@ -1008,7 +999,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 ); @@ -1035,7 +1026,7 @@ 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); @@ -1107,10 +1098,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)); } @@ -1120,13 +1109,13 @@ gnc_plugin_page_report_destroy(GncPluginPageReportPrivate * priv) { //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); @@ -1147,12 +1136,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 @@ -1212,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) }, @@ -1220,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", @@ -1230,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", @@ -1246,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) }, @@ -1294,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 */ @@ -1321,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 ); @@ -1361,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)); } @@ -1402,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); @@ -1469,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; } @@ -1488,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; @@ -1531,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; @@ -1573,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); @@ -1603,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. */ @@ -1613,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) @@ -1624,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; } } @@ -1742,7 +1728,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); @@ -1805,8 +1791,8 @@ 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"); @@ -1830,7 +1816,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); } @@ -1909,7 +1895,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); @@ -1957,7 +1943,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); @@ -1968,15 +1954,15 @@ 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 && g_file_test(dirname, - G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) + (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { - gchar *tmp = g_build_filename(dirname, job_name, NULL); + gchar *tmp = g_build_filename(dirname, job_name, nullptr); g_free(job_name); job_name = tmp; } @@ -2005,7 +1991,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/window-report.c b/gnucash/gnome/window-report.cpp similarity index 91% rename from gnucash/gnome/window-report.c rename to gnucash/gnome/window-report.cpp index 25fb3f6aa0..10a3d23f1a 100644 --- a/gnucash/gnome/window-report.c +++ b/gnucash/gnome/window-report.cpp @@ -24,25 +24,26 @@ * Boston, MA 02110-1301, USA gnu@gnu.org * * * ********************************************************************/ - +#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 "dialog-report-column-view.hpp" /******************************************************************** * @@ -51,9 +52,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); } /******************************************************************** @@ -73,15 +74,15 @@ gnc_options_dialog_apply_cb(GNCOptionWin * propertybox, 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)}; + GList *results = nullptr, *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 (win->win), - 0, + static_cast(0), GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", @@ -100,7 +101,7 @@ gnc_options_dialog_help_cb(GNCOptionWin * propertybox, 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), @@ -110,7 +111,7 @@ gnc_options_dialog_help_cb(GNCOptionWin * propertybox, "%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); } @@ -118,7 +119,7 @@ static void gnc_options_dialog_close_cb(GNCOptionWin * propertybox, 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); @@ -135,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; @@ -155,10 +155,10 @@ gnc_report_window_default_params_editor(GncOptionDB* odb, 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 = @@ -212,7 +212,7 @@ gnc_report_edit_options(SCM report, GtkWindow *parent) SCM ptr; SCM options; GncOptionDB* odb; - GtkWidget *options_widget = NULL; + GtkWidget *options_widget = nullptr; /* If the options editor widget already exists we simply raise it */ if (gnc_report_raise_editor (report)) @@ -233,11 +233,9 @@ gnc_report_edit_options(SCM report, GtkWindow *parent) if (scm_is_string(ptr)) { gchar *rpt_type = gnc_scm_to_utf8_string (ptr); -#if 0 if (g_strcmp0 (rpt_type, "d8ba4a2e89e8479ca9f6eccdeb164588") == 0) options_widget = gnc_column_view_edit_options (odb, report); else -#endif options_widget = gnc_report_window_default_params_editor (odb, report, parent); g_free (rpt_type); } diff --git a/gnucash/gnome/window-report.h b/gnucash/gnome/window-report.h index 17ff0195c2..1ecaf626ea 100644 --- a/gnucash/gnome/window-report.h +++ b/gnucash/gnome/window-report.h @@ -24,7 +24,10 @@ #define GNC_REPORT_WINDOW_H #include - +#ifdef __cplusplus +extern "C" +{ +#endif //#include "gnc-html.h" #include "qof.h" #include @@ -42,4 +45,7 @@ GtkWidget * gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report void reportWindow(int id, GtkWindow *parent); gboolean gnc_report_edit_options(SCM report, GtkWindow *parent); +#ifdef __cplusplus +} +#endif #endif 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/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c index 45228b697f..ee0e1d64af 100644 --- a/libgnucash/app-utils/option-util.c +++ b/libgnucash/app-utils/option-util.c @@ -702,7 +702,7 @@ gnc_option_default_getter (GNCOption *option) * Args: option - the GNCOption * * Returns: SCM handle to function * \********************************************************************/ -SCM +static SCM gnc_option_value_validator(GNCOption *option) { initialize_getters (); @@ -723,7 +723,7 @@ gnc_option_value_validator(GNCOption *option) * Returns: SCM handle to function * * If no such function exists, returns SCM_UNDEFINED. * \********************************************************************/ -SCM +static SCM gnc_option_widget_changed_proc_getter(GNCOption *option) { SCM cb; diff --git a/libgnucash/app-utils/option-util.h b/libgnucash/app-utils/option-util.h index 4c1676e35e..953eb651d7 100644 --- a/libgnucash/app-utils/option-util.h +++ b/libgnucash/app-utils/option-util.h @@ -97,8 +97,6 @@ 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); diff --git a/libgnucash/engine/qofbook.h b/libgnucash/engine/qofbook.h index 5932ecab15..0a7f90e86e 100644 --- a/libgnucash/engine/qofbook.h +++ b/libgnucash/engine/qofbook.h @@ -44,8 +44,7 @@ #include //To preempt it being included extern "C" in a later header. class GncOptionDB; #else -#include -typedef GNCOptionDB GncOptionDB; +typedef struct GncOptionDB GncOptionDB; #endif #ifdef __cplusplus extern "C" diff --git a/po/POTFILES.in b/po/POTFILES.in index fb268e6b35..b0e1da8fad 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -48,7 +48,7 @@ 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/business-gnome-utils.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-editor2.c gnucash/gnome/dialog-sx-editor.c gnucash/gnome/dialog-sx-from-trans.c @@ -96,7 +96,7 @@ gnucash/gnome/gnc-plugin-page-invoice.c gnucash/gnome/gnc-plugin-page-owner-tree.c gnucash/gnome/gnc-plugin-page-register2.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-register2.c gnucash/gnome/gnc-plugin-register.c @@ -112,7 +112,7 @@ gnucash/gnome/top-level.c gnucash/gnome/window-autoclear.c gnucash/gnome/window-reconcile2.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 @@ -172,7 +172,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 From 76b0001cbe6547136f40a1bea8712baf0c7f26c5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 18 Dec 2020 16:02:11 -0800 Subject: [PATCH 150/298] Remove option-util. --- gnucash/gnome-utils/gnc-main-window.cpp | 2 +- libgnucash/app-utils/CMakeLists.txt | 2 - libgnucash/app-utils/gnc-optiondb.cpp | 10 - libgnucash/app-utils/gnc-optiondb.h | 7 - libgnucash/app-utils/option-util.c | 2295 ----------------------- libgnucash/app-utils/option-util.h | 254 --- libgnucash/engine/qofbookslots.h | 1 - po/POTFILES.in | 1 - 8 files changed, 1 insertion(+), 2571 deletions(-) delete mode 100644 libgnucash/app-utils/option-util.c delete mode 100644 libgnucash/app-utils/option-util.h diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index 5c4aba19ed..8f4707ac2e 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -4284,7 +4284,7 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent) GncOptionDB *options; GNCOptionWin *optionwin; - options = gnc_option_db_new_for_type (QOF_ID_BOOK); + options = gnc_option_db_new(); qof_book_load_options (book, gnc_option_db_load, options); gnc_option_db_clean (options); diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt index 142976109e..573c93db4c 100644 --- a/libgnucash/app-utils/CMakeLists.txt +++ b/libgnucash/app-utils/CMakeLists.txt @@ -38,7 +38,6 @@ set (app_utils_HEADERS 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 @@ -80,7 +79,6 @@ set (app_utils_SOURCES 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}) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 8ad69ddb86..12aa71d8ab 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1250,16 +1250,6 @@ gnc_option_db_new(void) return new GncOptionDB; } -GncOptionDB* -gnc_option_db_new_for_type(QofIdType type) -{ - if (strcmp(type, QOF_ID_BOOK)) - return nullptr; - auto db = new GncOptionDB; - gnc_option_db_book_options(db); - return db; -} - void gnc_option_db_destroy(GncOptionDB* odb) { diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h index 6c5dfa9b5c..885715622b 100644 --- a/libgnucash/app-utils/gnc-optiondb.h +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -54,13 +54,6 @@ extern "C" */ GncOptionDB* gnc_option_db_new(void); -/** - * Convenence function duplicating an option-util function. We need this temporarily to make gnc-main-window and assistant-hierarchy happy. - * @param type The QofType - * @return a new GncOptionDB*. Transfers ownership. - */ -GncOptionDB* gnc_option_db_new_for_type(QofIdType type); - /** * Destruct and release a GncOptionDB. * @param odb The GncOptionDB. diff --git a/libgnucash/app-utils/option-util.c b/libgnucash/app-utils/option-util.c deleted file mode 100644 index ee0e1d64af..0000000000 --- a/libgnucash/app-utils/option-util.c +++ /dev/null @@ -1,2295 +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; -}; - - -/****** 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); -} - -#if 0 -/********************************************************************\ - * 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; -} -#endif -/* 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; -} -#if 0 -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); -} -#endif -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_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 * -\********************************************************************/ -static 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. * -\********************************************************************/ -static 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_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; -} -#if 0 -/********************************************************************\ - * 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; -} -#endif - -/********************************************************************\ - * _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; -} - -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; -} - -#if 0 -/********************************************************************\ - * 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; -} -#endif - -/********************************************************************\ - * 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_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_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_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; -} - -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 953eb651d7..0000000000 --- a/libgnucash/app-utils/option-util.h +++ /dev/null @@ -1,254 +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; -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_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); - -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); - -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); - -#endif /* OPTION_UTIL_H */ diff --git a/libgnucash/engine/qofbookslots.h b/libgnucash/engine/qofbookslots.h index 4ec7b0f301..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 */ diff --git a/po/POTFILES.in b/po/POTFILES.in index b0e1da8fad..75b34e8e68 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -537,7 +537,6 @@ 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 From cb7270cafe53189b6d88374658ae097f4adcad24 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 22 Dec 2020 16:24:15 -0800 Subject: [PATCH 151/298] Fix myriad gcc10 complaints. --- gnucash/gnome-utils/dialog-options.cpp | 28 +++++---------- gnucash/gnome-utils/gnc-main-window.cpp | 35 +++++++++---------- gnucash/gnome/gnc-plugin-page-report.cpp | 15 ++++---- libgnucash/app-utils/gnc-option-date.cpp | 9 +++++ libgnucash/app-utils/gnc-option-date.hpp | 4 +-- libgnucash/app-utils/gnc-option-impl.cpp | 1 + libgnucash/app-utils/gnc-option.hpp | 2 +- libgnucash/app-utils/gnc-optiondb.cpp | 1 + libgnucash/app-utils/gnc-optiondb.i | 4 +++ .../app-utils/test/gtest-gnc-option.cpp | 6 ++-- .../app-utils/test/gtest-gnc-optiondb.cpp | 22 ++++++------ 11 files changed, 67 insertions(+), 60 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index c117eb63d6..0cb06fa98c 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1187,7 +1187,7 @@ create_multichoice_widget(GncOption& option) auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); /* Add values to the list store, entry and tooltip */ - for (auto i = 0; i < num_values; i++) + for (decltype(num_values) i = 0; i < num_values; i++) { GtkTreeIter iter; auto itemstring = option.permissible_value_name(i); @@ -1325,7 +1325,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) : auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); /* Add values to the list store, entry and tooltip */ auto num = option.num_permissible_values(); - for (auto index = 0; index < num; ++index) + for (decltype(num) index = 0; index < num; ++index) { GtkTreeIter iter; gtk_list_store_append (store, &iter); @@ -1503,7 +1503,7 @@ date_set_relative_cb(GtkWidget *widget, gpointer data1) } } -GtkWidget* +static GtkWidget* create_date_option_widget(GncOption& option, GtkGrid *page_box, GtkLabel *name_label, char *documentation, /* Return values */ @@ -1682,8 +1682,8 @@ public: const GncOptionAccountList& accounts = option.get_value(); for (auto account : accounts) - g_list_prepend(acc_list, static_cast(const_cast(account))); - g_list_reverse(acc_list); + acc_list = g_list_prepend(acc_list, static_cast(const_cast(account))); + acc_list = g_list_reverse(acc_list); gnc_tree_view_account_set_selected_accounts(widget, acc_list, TRUE); g_list_free(acc_list); } @@ -1699,7 +1699,7 @@ public: } }; -GtkWidget* +static GtkWidget* create_account_widget(GncOption& option, char *name) { bool multiple_selection; @@ -2022,7 +2022,7 @@ create_list_widget(GncOption& option, char *name) gtk_tree_view_set_headers_visible(view, FALSE); auto num_values = option.num_permissible_values(); - for (auto i = 0; i < num_values; i++) + for (decltype(num_values) i = 0; i < num_values; i++) { auto raw_string = option.permissible_value_name(i); auto string = (raw_string && *raw_string) ? _(raw_string) : ""; @@ -2442,7 +2442,7 @@ public: g_list_free(list); auto val{g_object_get_data (G_OBJECT (button), "gnc_radiobutton_index")}; - g_return_if_fail (GPOINTER_TO_INT (val) == index); + g_return_if_fail (GPOINTER_TO_UINT (val) == index); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); } @@ -2473,7 +2473,7 @@ create_radiobutton_widget(char *name, GncOption& option) gtk_container_add (GTK_CONTAINER (frame), box); /* Iterate over the options and create a radio button for each one */ - for (auto i = 0; i < num_values; i++) + for (decltype(num_values) i = 0; i < num_values; i++) { auto label = option.permissible_value_name(i); auto tip = option.permissible_value_description(i); @@ -2850,13 +2850,3 @@ gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win) (GNCOptionWinCallback)gnc_book_options_help_cb, nullptr); } - -/* Dummy function impls. The following functions are declared in - * dialog_options.h and used by clients but they're made obsolete by the new - * options system. They're here to satisfy the linker. - */ -GtkWidget* const -gnc_option_get_gtk_widget (GncOption *option) -{ - return nullptr; -} diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index 8f4707ac2e..454f74fabe 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -456,7 +456,7 @@ static GtkRadioActionEntry radio_entries [] = }; /** 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. @@ -685,7 +685,7 @@ 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; + gsize page_start, page_count, i; GError *error = nullptr; /* Setup */ @@ -903,7 +903,7 @@ gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *da } 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 @@ -1902,7 +1902,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); @@ -1910,12 +1910,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 @@ -1923,15 +1923,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), + (void*)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), + (void*)gnc_main_window_cmd_window_raise, window); } g_free(action_name); @@ -1956,13 +1956,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; } @@ -1973,7 +1973,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 @@ -1983,7 +1983,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); @@ -2008,7 +2008,6 @@ 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 */ @@ -2021,10 +2020,10 @@ gnc_main_window_update_all_menu_items (void) /* 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, @@ -4588,7 +4587,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) */ diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp index 8b6137854d..1554962231 100644 --- a/gnucash/gnome/gnc-plugin-page-report.cpp +++ b/gnucash/gnome/gnc-plugin-page-report.cpp @@ -1156,14 +1156,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) @@ -1719,8 +1718,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); @@ -1743,8 +1744,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 " + "pexport-error.")); } result = TRUE; } diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp index e7f1132265..332c6bcd7d 100644 --- a/libgnucash/app-utils/gnc-option-date.cpp +++ b/libgnucash/app-utils/gnc-option-date.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include extern "C" { @@ -551,3 +553,10 @@ gnc_relative_date_to_time64(RelativeDatePeriod period) set_day_and_time(now, checked_reldate(period).m_type); return static_cast(GncDateTime(now)); } + +std::ostream& +operator<<(std::ostream& ostr, RelativeDatePeriod per) +{ + ostr << 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 index 954e0a1cc8..8d4e65529c 100644 --- a/libgnucash/app-utils/gnc-option-date.hpp +++ b/libgnucash/app-utils/gnc-option-date.hpp @@ -30,7 +30,7 @@ extern "C" } #include - +#include /** * Reporting periods relative to the current date. * @@ -83,6 +83,6 @@ const char* gnc_relative_date_display_string(RelativeDatePeriod); const char* gnc_relative_date_description(RelativeDatePeriod); RelativeDatePeriod gnc_relative_date_from_storage_string(const char*); time64 gnc_relative_date_to_time64(RelativeDatePeriod); - +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 index 38b1f8e048..5c92c214fb 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -25,6 +25,7 @@ #include "gnc-option-impl.hpp" #include #include +#include extern "C" { diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 5a9af10930..a39c91c246 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -34,7 +34,7 @@ extern "C" #include #include #include -#include "gnc-option-uitype.hpp" +#include "gnc-option-ui.hpp" #include "gnc-option-date.hpp" class GncOptionUIItem; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 12aa71d8ab..452e9e62a1 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1071,6 +1071,7 @@ owner_type_to_ui_type(GncOwnerType type) case GNC_OWNER_EMPLOYEE: return GncOptionUIType::EMPLOYEE; } + return GncOptionUIType::INTERNAL; } void diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index cd752e7ce0..e5cc556741 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -444,6 +444,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %inline %{ 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, diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 790303b7ff..503576ebce 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -1198,7 +1198,7 @@ TEST(GncOptionDate, test_gnc_relative_date_description) TEST(GncOptionDate, test_gnc_relative_date_from_storage_string) { - EXPECT_EQ(RelativeDatePeriod::ABSOLUTE, gnc_relative_date_from_storage_string("foo")); + // 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")); @@ -1327,7 +1327,7 @@ TEST_F(GncDateOptionList, test_set_and_get_relative) 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()); - auto index(std::find(c_begin_dates.begin(), c_begin_dates.end(), + 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 @@ -1335,7 +1335,7 @@ TEST_F(GncDateOptionList, test_set_and_get_relative) 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(5, m_option.get_value()); + EXPECT_EQ(5u, m_option.get_value()); } TEST_F(GncDateOption, test_stream_out) diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 0acbb633bc..3575372863 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -53,7 +53,7 @@ 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(m_db->num_sections(), 1); + EXPECT_EQ(1u, m_db->num_sections()); } TEST_F(GncOptionDBTest, test_lookup_string_option) @@ -144,7 +144,7 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option) gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", acclist, {ACCT_TYPE_STOCK}); - EXPECT_EQ(4, m_db->find_option("foo", "bar")->get_value().size()); + 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)); } @@ -156,7 +156,7 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option) gnc_register_account_list_limited_option(m_db, "foo", "bar", "baz", "Phony Option", accsel, {ACCT_TYPE_STOCK}); - EXPECT_EQ(1, m_db->find_option("foo", "bar")->get_value().size()); + 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)); } @@ -256,19 +256,19 @@ TEST_F(GncOptionDBTest, test_register_start_date_option) m_db->set_option("foo", "bar", RelativeDatePeriod::START_THIS_MONTH); EXPECT_EQ(RelativeDatePeriod::START_THIS_MONTH, m_db->find_option("foo", "bar")->get_value()); - - auto index(std::find(begin_dates.begin(), begin_dates.end(), - RelativeDatePeriod::START_THIS_MONTH) - begin_dates.begin()); - /* If this fails check that the begin_dates vector above matches the one in + 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(index, + 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(5, m_db->find_option("foo", "bar")->get_value()); + EXPECT_EQ(5u, m_db->find_option("foo", "bar")->get_value()); } @@ -398,7 +398,7 @@ TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input) EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value()[0]); m_db->load_option_scheme(iss); EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value()[1]); - EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get_value().size()); + EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value().size()); } @@ -438,7 +438,7 @@ TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input) EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value()); EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value()[1]); - EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get_value().size()); + EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value().size()); } From 7fa6778b4b71bca4129d60c98b1746ba0bcddae7 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 4 Jan 2021 17:08:03 -0800 Subject: [PATCH 152/298] Add a register_option overload to GncOptionDB For compatibility with existing scheme code. --- libgnucash/app-utils/gnc-optiondb-impl.hpp | 1 + libgnucash/app-utils/gnc-optiondb.cpp | 7 +++++++ libgnucash/app-utils/gnc-optiondb.i | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index 058f308be3..472b1ca24f 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -84,6 +84,7 @@ public: 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; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 452e9e62a1..eae05d49b7 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -168,6 +168,13 @@ GncOptionDB::register_option(const char* sectname, GncOption&& option) m_sections.back()->add_option(std::move(option)); } +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) { diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index e5cc556741..4aca8778ad 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -364,6 +364,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %ignore gnc_option_to_scheme; %ignore gnc_option_from_scheme; +/* 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. From 43f4bcb610ca24830b7b87b38800c458153a78fe Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 4 Jan 2021 17:13:53 -0800 Subject: [PATCH 153/298] Make GncOptionUIType an enum class. So that Swig will create properly constrained identifiers for its values. --- gnucash/gnome/business-options-gnome.cpp | 2 +- libgnucash/app-utils/gnc-option-uitype.hpp | 2 +- libgnucash/app-utils/gnc-optiondb.cpp | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp index fc0204a71a..519df3889b 100644 --- a/gnucash/gnome/business-options-gnome.cpp +++ b/gnucash/gnome/business-options-gnome.cpp @@ -63,7 +63,7 @@ ui_type_to_owner_type(GncOptionUIType ui_type) return GNC_OWNER_EMPLOYEE; std::ostringstream oss; - oss << "UI type " << ui_type << " could not be converted to owner type\n"; + oss << "UI type " << static_cast(ui_type) << " could not be converted to owner type\n"; throw std::invalid_argument(oss.str()); } diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp index 5cc07c0581..b1e784a455 100644 --- a/libgnucash/app-utils/gnc-option-uitype.hpp +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -23,7 +23,7 @@ #ifndef GNC_OPTION_UITYPE_HPP__ #define GNC_OPTION_UITYPE_HPP__ -enum GncOptionUIType +enum class GncOptionUIType : unsigned int { INTERNAL, BOOLEAN, diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index eae05d49b7..d2f64c2524 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -690,17 +690,17 @@ is_qofinstance_ui_type(GncOptionUIType type) { switch (type) { - case CURRENCY: - case COMMODITY: - case ACCOUNT_SEL: - case BUDGET: - case OWNER: - case CUSTOMER: - case VENDOR: - case EMPLOYEE: - case INVOICE: - case TAX_TABLE: - case QUERY: + case GncOptionUIType::CURRENCY: + case GncOptionUIType::COMMODITY: + 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; From a602f64b170290d05febe8d5e8ae89b8e6f45b2f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 4 Jan 2021 17:19:02 -0800 Subject: [PATCH 154/298] SFINAE-constrain the GncOption constructor templates. So that one can't instantiate an invalid constructor. Unfortunately Swig doesn't understand SFINAE and will try to create the invalid constructors anyway but at least this generates a compile-time error when it tries to. --- libgnucash/app-utils/gnc-option.cpp | 5 ++++- libgnucash/app-utils/gnc-option.hpp | 11 +++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 5ba5b57314..71c52377ee 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -33,7 +33,10 @@ extern "C" #include } -template +template >, + int>> GncOption::GncOption(const char* section, const char* name, const char* key, const char* doc_string, ValueType value, GncOptionUIType ui_type) : diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index a39c91c246..ce3f2f0207 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -37,6 +37,7 @@ extern "C" #include "gnc-option-ui.hpp" #include "gnc-option-date.hpp" +struct OptionClassifier; class GncOptionUIItem; using GncOptionUIItemPtr = std::unique_ptr; struct QofInstance_s; @@ -65,10 +66,16 @@ using GncOptionVariantPtr = std::unique_ptr; class GncOption { public: - template + template >, + int> = 0> GncOption(OptionType option) : m_option{std::make_unique(option)} {} - template + template >, + int> = 0> GncOption(const char* section, const char* name, const char* key, const char* doc_string, ValueType value, From 0b7ccfbd5bc6ceaea40ec026ffa074f496fe4eb9 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 4 Jan 2021 17:21:31 -0800 Subject: [PATCH 155/298] Create gnc-make-foo-option functions. For compatibility with current Scheme code. --- libgnucash/app-utils/gnc-option.cpp | 18 ++++ libgnucash/app-utils/gnc-option.hpp | 7 ++ libgnucash/app-utils/gnc-optiondb.i | 162 +++++++++++++++++++++++++++- 3 files changed, 186 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 71c52377ee..da5bc40362 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -584,3 +584,21 @@ template bool GncOption::validate(std::string) const; template bool GncOption::validate(const QofInstance*) const; template bool GncOption::validate(RelativeDatePeriod) const; template bool GncOption::validate(GncMultichoiceOptionIndexVec) 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); +template GncOption* gnc_make_option(const char*, + const char*, + const char*, + const char*, + const QofInstance*, + GncOptionUIType); diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index ce3f2f0207..798e539a33 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -134,5 +134,12 @@ operator>>(std::istream& iss, GncOption& opt) return opt.in_stream(iss); } +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.i b/libgnucash/app-utils/gnc-optiondb.i index 4aca8778ad..7a8f0ceb50 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -406,11 +406,17 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %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_qofinstance_option) gnc_make_option; %extend GncOption { - SCM get_scm_value() { return std::visit([](const auto& option)->SCM { @@ -448,6 +454,160 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %template(gnc_register_number_range_option_int) gnc_register_number_range_option; %inline %{ + 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{GncOptionAccountValue{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; + } + } + + 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{GncOptionAccountValue{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; + } + } + + GncOption* gnc_make_account_sel_limited_option(const char* section, + const char* name, + const char* key, + const char* doc_string, + const GncOptionAccountList& value, + GncOptionAccountTypeList&& allowed) + { + try + { + return new GncOption{GncOptionAccountValue{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; + } + } + + GncOption* gnc_make_multichoice_option(const char* section, + const char* name, const char* key, + const char* doc_string, + GncMultichoiceOptionChoices&& choices) + { + try { + return new GncOption{GncOptionMultichoiceValue{section, name, key, + doc_string, + choices.empty() ? "None" : + std::get<0>(choices.at(0)).c_str(), + std::move(choices)}}; + } + catch (const std::exception& err) + { + std::cerr << "Make multichoice option threw unexpected exception " << err.what() << ", option not created." << std::endl; + return nullptr; + } + } + + GncOption* gnc_make_list_option(const char* section, + const char* name, const char* key, + const char* doc_string, const char* value, + GncMultichoiceOptionChoices&& list) + { + try { + return new GncOption{GncOptionMultichoiceValue{section, name, key, + doc_string, value, 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; + } + } + + 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; + } + } + + 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; + } + } + + 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{GncOptionValidatedValue{ + section, name, key, doc_string, (const QofInstance*)value, + [](const QofInstance* new_value) -> bool + { + return GNC_IS_COMMODITY (new_value) && + gnc_commodity_is_currency(GNC_COMMODITY(new_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(); From 18b83874fdb08cd691a02de814e17a775f8ca570 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 15 Feb 2021 15:49:03 -0800 Subject: [PATCH 156/298] Another bit of Remove test-option-utils --- libgnucash/app-utils/test/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index f5358e7334..f4c7425daa 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -92,9 +92,6 @@ if (HAVE_SRFI64) 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(test_autoclear_SOURCES test-autoclear.cpp ) From 55a2ed1df8e2a2a170f754555723c9cf1ebaf2bd Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 15 Feb 2021 16:08:46 -0800 Subject: [PATCH 157/298] gnc-optiondb.i uses functions declared in gnc-optiondb.h. So it needs it declared in the swig file. --- libgnucash/app-utils/gnc-optiondb.i | 1 + 1 file changed, 1 insertion(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 7a8f0ceb50..0626f105eb 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -40,6 +40,7 @@ namespace std { //%module sw_gnc_optiondb %{ +#include "gnc-optiondb.h" #include "gnc-optiondb.hpp" #include "gnc-optiondb-impl.hpp" #include "gnc-option-date.hpp" From fba024854821ab5ee7dbd2ddb3c3137fd8bf3cc5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 18 Feb 2021 17:10:16 -0800 Subject: [PATCH 158/298] Support different GncOptionMultichoiceValue key types. Scheme can use strings, symbols, or ints as keys in multichoice options, but C++ can handle only strings. Add conversion and tracking so that the right key type gets sent back to Scheme. --- libgnucash/app-utils/gnc-option-impl.hpp | 7 ++- libgnucash/app-utils/gnc-option.hpp | 7 +++ libgnucash/app-utils/gnc-optiondb.hpp | 3 +- libgnucash/app-utils/gnc-optiondb.i | 66 +++++++++++++++++++++++- 4 files changed, 78 insertions(+), 5 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 0abd8314f1..d2ae693dd7 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -500,7 +500,8 @@ operator>> (std::istream& iss, OptType& opt) using GncMultichoiceOptionEntry = std::tuple; + const std::string, + GncOptionMultichoiceKeyType>; using GncMultichoiceOptionIndexVec = std::vector; using GncMultichoiceOptionChoices = std::vector; @@ -575,7 +576,8 @@ public: if (vec.size() == 1) return std::get<0>(m_choices.at(vec[0])); else - return c_list_string; + throw std::length_error("Retrieving multiple values from a multichoice isn't implemented."); + } const std::string& get_default_value() const { @@ -671,6 +673,7 @@ public: 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<3>(m_choices.at(i)); } private: std::size_t find_key (const std::string& key) const noexcept { diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 798e539a33..342cda4fad 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -63,6 +63,13 @@ using GncOptionVariant = std::variant, using GncOptionVariantPtr = std::unique_ptr; +enum class GncOptionMultichoiceKeyType +{ + SYMBOL, + STRING, + NUMBER, +}; + class GncOption { public: diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index de0f13aff3..0d05c1ab78 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -51,7 +51,8 @@ using GncOptionAccountList = std::vector; using GncOptionAccountTypeList = std::vector; using GncMultichoiceOptionEntry = std::tuple; + const std::string, + GncOptionMultichoiceKeyType>; using GncMultichoiceOptionChoices = std::vector; /** diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 0626f105eb..95016fda8e 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -256,14 +256,34 @@ gnc_option_test_book_destroy(QofBook* book) %typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices) { + using KeyType = GncOptionMultichoiceKeyType; auto len = scm_to_size_t(scm_length($input)); for (std::size_t i = 0; i < len; ++i) { SCM vec = scm_list_ref($input, scm_from_size_t(i)); - std::string key{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 0))}; + 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))}; std::string desc{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 2))}; - choices.push_back({std::move(key), std::move(name), std::move(desc)}); + choices.push_back({std::move(key), std::move(name), std::move(desc), keytype}); } $1 = &choices; } @@ -404,6 +424,45 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %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 GncOptionMultichoiceKeyType; + /* Replace GncOptionMultichoiceValue::get_value with one that restores the keytype that Scheme sent it. */ +%inline %{ + SCM get_scm_value(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)); + }; + }; + + auto indexes = option.get_multiple(); + if (indexes.empty()) + indexes = option.get_default_multiple(); + if (indexes.empty()) + return SCM_BOOL_F; + if (indexes.size() == 1) // FIXME: Should use bool member to decide + return scm_value(option.permissible_value(indexes[0]), + option.get_keytype(indexes[0])); + auto values{scm_list_1(SCM_UNDEFINED)}; + for(auto index : indexes) + { + auto val{scm_list_1(scm_value(option.permissible_value(index), + option.get_keytype(index)))}; + values = scm_append(scm_list_2(val, values)); + } + return scm_reverse(values); + } + %} + %include "gnc-option-date.hpp" %include "gnc-option.hpp" %include "gnc-option-impl.hpp" @@ -421,6 +480,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); SCM get_scm_value() { return std::visit([](const auto& option)->SCM { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + return get_scm_value(option); auto value{option.get_value()}; if constexpr (std::is_same_v, SCM>) From 08d1eebba251d123f68a02fa2a5ab7b1208e836d Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 18 Feb 2021 17:27:37 -0800 Subject: [PATCH 159/298] Use GUID strings instead of QofInstance* for scheme value of QofInstance. Because that's what gets used everywhere else. --- libgnucash/app-utils/gnc-optiondb.cpp | 6 ++++-- libgnucash/app-utils/gnc-optiondb.i | 26 ++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index d2f64c2524..699a8eadf6 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1478,9 +1478,11 @@ gnc_option_db_set_string_value(GncOptionDB*, const char*, } const QofInstance* -gnc_option_db_lookup_qofinstance_value(GncOptionDB*, const char*, const char*) +gnc_option_db_lookup_qofinstance_value(GncOptionDB* odb, const char* section, + const char* name) { - return nullptr; + auto option{odb->find_option(section, name)}; + return option->get_value(); } SCM diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 95016fda8e..dcc8de6b72 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -116,10 +116,13 @@ scm_from_value(double value) template <> inline SCM scm_from_value(const QofInstance* value) { - auto guid = guid_to_string(qof_instance_get_guid(value)); - auto scm_guid = scm_from_utf8_string(guid); - g_free(guid); - return scm_guid; + if (!value) return SCM_BOOL_F; + auto type{qof_collection_get_type(qof_instance_get_collection(value))}; + auto guid_str{guid_to_string(qof_instance_get_guid(value))}; + auto scm_guid = scm_from_utf8_string(guid_str); + auto scm_type = scm_from_utf8_string(type); + free(guid_str); + return scm_cons(scm_type, scm_guid); } template inline ValueType @@ -157,6 +160,21 @@ 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 || !scm_is_pair(new_value)) + return nullptr; + auto guid_str{scm_to_utf8_stringn(scm_car(new_value), nullptr)}; + auto type{scm_to_utf8_stringn(scm_cdr(new_value), nullptr)}; + GncGUID new_guid; + string_to_guid(guid_str, &new_guid); + free(guid_str); + auto coll{qof_book_get_collection(qof_session_get_book(gnc_get_current_session()), type)}; + free(type); + return qof_collection_lookup_entity(coll, &new_guid); +} + template <>inline SCM scm_from_value(GncOptionAccountList value) { From 53ad0ba440e6bd088f05916db257539bb4343813 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 18 Feb 2021 17:28:17 -0800 Subject: [PATCH 160/298] Prevent SWIG from trying to call delete on a std::unique_ptr. --- libgnucash/app-utils/gnc-optiondb.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index dcc8de6b72..37626d6604 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -24,7 +24,7 @@ namespace std { pointer get () const; // operator bool () const; - ~unique_ptr(); + ~unique_ptr() = delete; //Otherwise swig takes the unique_ptr and calls delete on it. }; } From 86102e1be7b1b4742c2acc194190af72dad43931 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 18 Feb 2021 17:32:09 -0800 Subject: [PATCH 161/298] Return SCM_BOOL_F instead of nullptr to Scheme optiondb-lookup. Because unlike C, Scheme thinks (if 0) should return true. Besides, Swig hides pointers so a null check doesn't even work. --- libgnucash/app-utils/gnc-optiondb.cpp | 7 +++++-- libgnucash/app-utils/gnc-optiondb.i | 22 ++++++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 699a8eadf6..fd465a859e 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1486,14 +1486,17 @@ gnc_option_db_lookup_qofinstance_value(GncOptionDB* odb, const char* section, } SCM -gnc_option_db_lookup_scm_value(GncOptionDB*, const char*, const char*) +gnc_option_db_lookup_scm_value(GncOptionDB* odb, const char* section, + const char* name) { - return nullptr; + std::cerr << "Use gnc_option_db_lookup_value." << std::endl; + return SCM_BOOL_F; } void gnc_option_db_set_scm_value(GncOptionDB*, const char*, const char*, SCM) { + std::cerr << "Use gnc_set_option." << std::endl; } // Force creation of templates diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 37626d6604..c7b2946003 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -392,11 +392,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %ignore swig_get_option(GncOption&); %inline %{ +#include #include "gnc-option.hpp" #include "gnc-option-ui.hpp" GncOptionVariant& swig_get_option(GncOption* option) { + assert(option); return *option->m_option; } %} @@ -442,6 +444,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %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); +%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;" %ignore GncOptionMultichoiceKeyType; /* Replace GncOptionMultichoiceValue::get_value with one that restores the keytype that Scheme sent it. */ @@ -497,6 +501,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %extend GncOption { SCM get_scm_value() { + if (!$self) + return SCM_BOOL_F; return std::visit([](const auto& option)->SCM { if constexpr (std::is_same_v, GncOptionMultichoiceValue>) @@ -510,6 +516,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } SCM get_scm_default_value() { + if (!$self) + return SCM_BOOL_F; return std::visit([](const auto& option)->SCM { auto value{option.get_default_value()}; if constexpr (std::is_same_v, @@ -705,11 +713,21 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); 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); + auto db_opt{optiondb->find_option(section, name)}; if (!db_opt) return SCM_BOOL_F; return GncOption_get_scm_default_value(db_opt); @@ -719,7 +737,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); gnc_set_option(const GncOptionDBPtr& optiondb, const char* section, const char* name, SCM new_value) { - auto db_opt = optiondb->find_option(section, name); + auto db_opt{optiondb->find_option(section, name)}; if (!db_opt) { std::cerr <<"Attempt to write non-existent option " << section From 8c2a8edbed5f68987d77cb96a403ce44e57740a8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 18 Feb 2021 17:36:52 -0800 Subject: [PATCH 162/298] Implement gnc_option_db_set_string_value and gnc_optiondb_lookup_string_value. --- libgnucash/app-utils/gnc-optiondb.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index fd465a859e..e6b82fa2a6 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1466,15 +1466,19 @@ gnc_option_db_book_options(GncOptionDB* odb) } const char* -gnc_option_db_lookup_string_value(GncOptionDB*, const char*, 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*, const char*, - const char*, const char*) +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* From a21f329b1e3b45273b0f85a12ade98a3ee04655c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 18 Feb 2021 17:38:10 -0800 Subject: [PATCH 163/298] Use 100% instead of 20000 px for default Pixmap size. --- libgnucash/app-utils/gnc-optiondb.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index e6b82fa2a6..ecd1ce838d 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1037,8 +1037,9 @@ gnc_register_number_plot_size_option(GncOptionDB* db, 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, 100, 20000, 5}}; + value, 10, 100, 1}}; db->register_option(section, std::move(option)); } From d41292fdddc91ca2e754f838179dd56ca22c2132 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 18 Feb 2021 17:13:09 -0800 Subject: [PATCH 164/298] Make set_selectable a GncOptionsGtkUIItem member function. Callable from the option object with set_ui_item_selectable and from Scheme as gnc-option-db-set-option-selectable-by-name. --- gnucash/gnome-utils/dialog-options.cpp | 25 ++++++------------- gnucash/gnome-utils/dialog-options.hpp | 1 + libgnucash/app-utils/gnc-option-ui.hpp | 1 + libgnucash/app-utils/gnc-option.cpp | 7 ++++++ libgnucash/app-utils/gnc-option.hpp | 1 + libgnucash/app-utils/gnc-optiondb.i | 10 ++++++++ .../app-utils/test/gtest-gnc-option.cpp | 1 + 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 0cb06fa98c..87c2f2b714 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -170,6 +170,13 @@ GncOptionGtkUIItem::~GncOptionGtkUIItem() 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() { @@ -290,24 +297,6 @@ gnc_option_changed_option_cb(GtkWidget *dummy, GncOption* option) } -/* - * set_selectable * - * Change the selectable state of the widget that represents a - * GUI option. - * - * 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. - */ -static void -set_selectable (GncOption& option, bool selectable) -{ - auto widget = gnc_option_get_gtk_widget(&option); - if (widget) - gtk_widget_set_sensitive (widget, selectable); -} - // This do-nothing template is specialized for each GncOptionUIType. template GtkWidget* create_option_widget(GncOption& option, GtkGrid*, GtkLabel*, char*, GtkWidget**, diff --git a/gnucash/gnome-utils/dialog-options.hpp b/gnucash/gnome-utils/dialog-options.hpp index 7daef23517..c2fb570c24 100644 --- a/gnucash/gnome-utils/dialog-options.hpp +++ b/gnucash/gnome-utils/dialog-options.hpp @@ -50,6 +50,7 @@ public: GncOptionGtkUIItem(const GncOptionGtkUIItem& item); GncOptionGtkUIItem(GncOptionGtkUIItem&&) = default; virtual ~GncOptionGtkUIItem() override; + virtual void set_selectable(bool) const noexcept override; void clear_ui_item() override; void set_widget(GtkWidget* widget); virtual GtkWidget* const get_widget() const { return m_widget; } diff --git a/libgnucash/app-utils/gnc-option-ui.hpp b/libgnucash/app-utils/gnc-option-ui.hpp index dbd44e8b9a..a40381bba7 100644 --- a/libgnucash/app-utils/gnc-option-ui.hpp +++ b/libgnucash/app-utils/gnc-option-ui.hpp @@ -51,6 +51,7 @@ public: 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; diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index da5bc40362..5053c62971 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -193,6 +193,13 @@ GncOption::set_ui_item(GncOptionUIItemPtr&& ui_item) 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 { diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 342cda4fad..4e88f3412e 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -98,6 +98,7 @@ public: 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(); diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index c7b2946003..7f6caed507 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -768,6 +768,16 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); { return optiondb->find_option(section, name); } + + 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); + } %} #endif //SWIGGUILE diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 503576ebce..39f1aee6a6 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -526,6 +526,7 @@ public: ~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 { From 7c1b4b794a74f018417b0eed4bd043eaf45fcb55 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Feb 2021 13:18:39 -0800 Subject: [PATCH 165/298] Convert from Scheme date-period symbols to C++ RelativeDatePeriod enum. Create an alist mapping the Scheme symbols to the corresponding Swig functions that alias the enumerations. When a symbol is received look up the corresponding function and evaluate it, retrieving the enumeration. --- libgnucash/app-utils/gnc-optiondb.i | 94 +++++++++++++++++++---------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 7f6caed507..89e0e81227 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -216,41 +216,10 @@ gnc_option_test_book_destroy(QofBook* book) %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(absolute) RelativeDatePeriod::ABSOLUTE; -%rename(today) RelativeDatePeriod::TODAY; -%rename(one_week_ago) RelativeDatePeriod::ONE_WEEK_AGO; -%rename(one_week_ahead) RelativeDatePeriod::ONE_WEEK_AHEAD; -%rename(one_month_ago) RelativeDatePeriod::ONE_MONTH_AGO; -%rename(one_month_ahead) RelativeDatePeriod::ONE_MONTH_AHEAD; -%rename(three_months_ago) RelativeDatePeriod::THREE_MONTHS_AGO; -%rename(three_months_ahead) RelativeDatePeriod::THREE_MONTHS_AHEAD; -%rename(six_months_ago) RelativeDatePeriod::SIX_MONTHS_AGO; -%rename(six_months_ahead) RelativeDatePeriod::SIX_MONTHS_AHEAD; -%rename(one_year_ago) RelativeDatePeriod::ONE_YEAR_AGO; -%rename(one_year_ahead) RelativeDatePeriod::ONE_YEAR_AHEAD; -%rename(start_this_month) RelativeDatePeriod::START_THIS_MONTH; -%rename(end_this_month) RelativeDatePeriod::END_THIS_MONTH; -%rename(start_prev_month) RelativeDatePeriod::START_PREV_MONTH; -%rename(end_prev_month) RelativeDatePeriod::END_PREV_MONTH; -%rename(start_next_month) RelativeDatePeriod::START_NEXT_MONTH; -%rename(end_next_month) RelativeDatePeriod::END_NEXT_MONTH; -%rename(start_current_quarter) RelativeDatePeriod::START_CURRENT_QUARTER; -%rename(end_current_quarter) RelativeDatePeriod::END_CURRENT_QUARTER; -%rename(start_prev_quarter) RelativeDatePeriod::START_PREV_QUARTER; -%rename(end_prev_quarter) RelativeDatePeriod::END_PREV_QUARTER; -%rename(start_next_quarter) RelativeDatePeriod::START_NEXT_QUARTER; -%rename(end_next_quarter) RelativeDatePeriod::END_NEXT_QUARTER; -%rename(start_cal_year) RelativeDatePeriod::START_CAL_YEAR; -%rename(end_cal_year) RelativeDatePeriod::END_CAL_YEAR; -%rename(start_prev_year) RelativeDatePeriod::START_PREV_YEAR; -%rename(end_prev_year) RelativeDatePeriod::END_PREV_YEAR; -%rename(start_next_year) RelativeDatePeriod::START_NEXT_YEAR; -%rename(end_next_year) RelativeDatePeriod::END_NEXT_YEAR; -%rename(start_accounting_period) RelativeDatePeriod::START_ACCOUNTING_PERIOD; -%rename(end_accounting_period) RelativeDatePeriod::END_ACCOUNTING_PERIOD; %rename(gnc_register_date_option_set) gnc_register_date_option(GncOptionDBPtr&, const char*, const char*, @@ -447,8 +416,44 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %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 const SCM reldate_values = scm_c_eval_string( + "'((absolute RelativeDatePeriod-ABSOLUTE)" + "(today RelativeDatePeriod-TODAY)" + "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)" + "(one-week-ahead RelativeDatePeriod-ONE-WEEK-AHEAD)" + "(one-month-ago RelativeDatePeriod-ONE-MONTH-AGO)" + "(one-month-ahead RelativeDatePeriod-ONE-MONTH-AHEAD)" + "(three-months-ago RelativeDatePeriod-THREE-MONTHS-AGO)" + "(three-months-ahead RelativeDatePeriod-THREE-MONTHS-AHEAD)" + "(six-months-ago RelativeDatePeriod-SIX-MONTHS-AGO)" + "(six-months-ahead RelativeDatePeriod-SIX-MONTHS-AHEAD)" + "(one-year-ago RelativeDatePeriod-ONE-YEAR-AGO)" + "(one-year-ahead RelativeDatePeriod-ONE-YEAR-AHEAD)" + "(start-this-month RelativeDatePeriod-START-THIS-MONTH)" + "(end-this-month RelativeDatePeriod-END-THIS-MONTH)" + "(start-prev-month RelativeDatePeriod-START-PREV-MONTH)" + "(end-prev-month RelativeDatePeriod-END-PREV-MONTH)" + "(start-next-month RelativeDatePeriod-START-NEXT-MONTH)" + "(end-next-month RelativeDatePeriod-END-NEXT-MONTH)" + "(start-current-quarter RelativeDatePeriod-START-CURRENT-QUARTER)" + "(end-current-quarter RelativeDatePeriod-END-CURRENT-QUARTER)" + "(start-prev-quarter RelativeDatePeriod-START-PREV-QUARTER)" + "(end-prev-quarter RelativeDatePeriod-END-PREV-QUARTER)" + "(start-next-quarter RelativeDatePeriod-START-NEXT-QUARTER)" + "(end-next-quarter RelativeDatePeriod-END-NEXT-QUARTER)" + "(start-cal-year RelativeDatePeriod-START-CAL-YEAR)" + "(end-cal-year RelativeDatePeriod-END-CAL-YEAR)" + "(start-prev-year RelativeDatePeriod-START-PREV-YEAR)" + "(end-prev-year RelativeDatePeriod-END-PREV-YEAR)" + "(start-next-year RelativeDatePeriod-START-NEXT-YEAR)" + "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)" + "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)" + "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))"); + %} + %ignore GncOptionMultichoiceKeyType; - /* Replace GncOptionMultichoiceValue::get_value with one that restores the keytype that Scheme sent it. */ + %inline %{ SCM get_scm_value(const GncOptionMultichoiceValue& option) { @@ -528,11 +533,34 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } void set_value_from_scm(SCM new_value) { + if (!$self) + return; std::visit([new_value](auto& option) { + if constexpr (std::is_same_v, + GncOptionDateValue>) + { + if (scm_is_pair(new_value)) + { + auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(new_value)))}; + if (strcmp(car, "relative") == 0) + { + auto lookup{scm_assq_ref(reldate_values, + scm_cdr(new_value))}; + auto reldate{scm_primitive_eval(lookup)}; + option.set_value(static_cast(scm_to_int(reldate))); + } + else + { + option.set_value(scm_to_int64(scm_cdr(new_value))); + } + } + return; + } option.set_value(scm_to_value>(new_value)); }, swig_get_option($self)); } }; + %extend GncOptionDB { %template(set_option_string) set_option; %template(set_option_int) set_option; From 16a36d91044b00ef653073bdb7ed193a992b3de3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Feb 2021 13:50:13 -0800 Subject: [PATCH 166/298] Correctly set value of a GncOptionMultichoiceValue. Accounting for the 3 types of SCM object that we might be handed. --- libgnucash/app-utils/gnc-optiondb.i | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 89e0e81227..d07ebefcdd 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -556,6 +556,23 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } return; } + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + { + if (scm_is_integer(new_value)) + { + option.set_value(scm_to_int(new_value)); + return; + } + std::string new_value_str{}; + if (scm_is_symbol(new_value)) + new_value_str = scm_to_utf8_string(scm_symbol_to_string(new_value)); + else if (scm_is_string(new_value)) + new_value_str = scm_to_utf8_string(new_value); + if (!new_value_str.empty()) + option.set_value(new_value_str); + return; + } option.set_value(scm_to_value>(new_value)); }, swig_get_option($self)); } From 81d261897e9f48227e9b06e672a12c9036eea30a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Feb 2021 16:13:59 -0800 Subject: [PATCH 167/298] scm_to_value() handle Swigged ptrs as well as GUIDs. Some swigged engine functions used to set options return Swigged ptrs. --- libgnucash/app-utils/gnc-optiondb.i | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index d07ebefcdd..0380a597de 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -164,7 +164,16 @@ template <> inline const QofInstance* scm_to_value(SCM new_value) { if (new_value == SCM_BOOL_F || !scm_is_pair(new_value)) - return nullptr; + { + void* ptr{}; + SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p_QofInstance_s, 0); + if (ptr) + return static_cast(ptr); + SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p_gnc_commodity, 0); + if (ptr) + return static_cast(ptr); + } + auto guid_str{scm_to_utf8_stringn(scm_car(new_value), nullptr)}; auto type{scm_to_utf8_stringn(scm_cdr(new_value), nullptr)}; GncGUID new_guid; From f7f2d22909c8a205018d07dcc944db242bcffd80 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 20 Feb 2021 11:42:42 -0800 Subject: [PATCH 168/298] Implement gnc-optiondb-foreach. --- libgnucash/app-utils/gnc-optiondb.i | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 0380a597de..58019706a1 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -832,6 +832,23 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); auto option{odb->find_option(section, name)}; option->set_ui_item_selectable(selectable); } + + 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); + }); + }); + } %} #endif //SWIGGUILE From b5c0477143850f0aae7ff393c2da0f22909aa979 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 21 Feb 2021 14:25:20 -0800 Subject: [PATCH 169/298] Use the SWIGGED pointer for the SCM value of QofInstance* Values. This effectively reverts b7dd7f. Note that two cases aren't handled because the types aren't GObjects and so don't have type macros to decipher them: GncOwner and QofQuery. Since they're not GObjects they're obviously not QofInstances either and we need to rethink this value type a bit. --- libgnucash/app-utils/gnc-optiondb.i | 60 +++++++++++++++++------------ 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 58019706a1..110be68a43 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -117,12 +117,24 @@ template <> inline SCM scm_from_value(const QofInstance* value) { if (!value) return SCM_BOOL_F; - auto type{qof_collection_get_type(qof_instance_get_collection(value))}; - auto guid_str{guid_to_string(qof_instance_get_guid(value))}; - auto scm_guid = scm_from_utf8_string(guid_str); - auto scm_type = scm_from_utf8_string(type); - free(guid_str); - return scm_cons(scm_type, scm_guid); + + auto ptr{static_cast(const_cast(value))}; + swig_type_info *type = SWIGTYPE_p_QofInstance_s; + 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_GncBudget; + else if (GNC_IS_INVOICE(value)) + type = SWIGTYPE_p_GncInvoice; + else if (GNC_IS_TAXTABLE(value)) + type = SWIGTYPE_p_GncTaxTable; +/* There is no type macro for QofQuery, it's not a GObject. + else if (GNC_IS_QOFQUERY(value)) + type = SWIGTYPE_p_Query; +*/ + return SWIG_NewPointerObj(ptr, type, FALSE); } template inline ValueType @@ -163,25 +175,23 @@ scm_to_value(SCM new_value) template <> inline const QofInstance* scm_to_value(SCM new_value) { - if (new_value == SCM_BOOL_F || !scm_is_pair(new_value)) - { - void* ptr{}; - SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p_QofInstance_s, 0); - if (ptr) - return static_cast(ptr); - SWIG_ConvertPtr(new_value, &ptr, SWIGTYPE_p_gnc_commodity, 0); - if (ptr) - return static_cast(ptr); - } + if (new_value == SCM_BOOL_F) + return nullptr; - auto guid_str{scm_to_utf8_stringn(scm_car(new_value), nullptr)}; - auto type{scm_to_utf8_stringn(scm_cdr(new_value), nullptr)}; - GncGUID new_guid; - string_to_guid(guid_str, &new_guid); - free(guid_str); - auto coll{qof_book_get_collection(qof_session_get_book(gnc_get_current_session()), type)}; - free(type); - return qof_collection_lookup_entity(coll, &new_guid); + static const std::array types{ + SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity, + SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice, + SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account + }; + 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 SCM @@ -371,6 +381,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %ignore swig_get_option(GncOption&); %inline %{ #include +#include +#include #include "gnc-option.hpp" #include "gnc-option-ui.hpp" From f20c358ce1856f7e8c51bdebff8aaf80ded6e90a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 21 Feb 2021 14:30:30 -0800 Subject: [PATCH 170/298] Return a pair in scheme for GncOptionRangeValue. This is a bit of a hack to handle PlotSize options. They're the only RangeValues that use ints; the rest use doubles because the control is a GtkSpinButton and that uses doubles. The chart code expects a pair with either 'pixel or 'percent saying what to put in front of the number. We hack that too: if value <= 100 then it's percent because 100px is about 3cm on modern monitors and 15mm on HiDPI ones. Bigger numbers are pixels. --- libgnucash/app-utils/gnc-optiondb.i | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 110be68a43..2e7d6ea912 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -183,7 +183,7 @@ scm_to_value(SCM new_value) SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice, SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account }; - void* ptr{}; + void* ptr{}; auto pos = std::find_if(types.begin(), types.end(), [&new_value, &ptr](auto type){ SWIG_ConvertPtr(new_value, &ptr, type, 0); @@ -509,7 +509,15 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } return scm_reverse(values); } - %} + + 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)); + } + + %} %include "gnc-option-date.hpp" %include "gnc-option.hpp" @@ -531,7 +539,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return SCM_BOOL_F; return std::visit([](const auto& option)->SCM { if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) + GncOptionMultichoiceValue> || + std::is_same_v, + GncOptionRangeValue>) return get_scm_value(option); auto value{option.get_value()}; if constexpr (std::is_same_v, From efc734649015917fd8628dd3d49a7a57ad844689 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 22 Feb 2021 14:08:14 -0800 Subject: [PATCH 171/298] Implement scm_to_value. --- libgnucash/app-utils/gnc-optiondb.i | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 2e7d6ea912..1d5a85b3bd 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -194,6 +194,26 @@ scm_to_value(SCM new_value) 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) + retval.push_back(static_cast(account)); + next = scm_cdr(next); + if (scm_is_null(next)) + break; + } + return retval; +} + template <>inline SCM scm_from_value(GncOptionAccountList value) { @@ -604,7 +624,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_value(new_value_str); return; } - option.set_value(scm_to_value>(new_value)); + auto value{scm_to_value>(new_value)}; //Can't inline, set_value takes arg by reference. + option.set_value(value); }, swig_get_option($self)); } }; From c62b526ba0cc29974f5eaee4a1c45975939020f4 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 23 Feb 2021 10:36:58 -0800 Subject: [PATCH 172/298] Implement scm_to_value. --- libgnucash/app-utils/gnc-optiondb.i | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 1d5a85b3bd..c7c45817ed 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -145,6 +145,12 @@ scm_to_value(SCM 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) { From 534a7c28937de165a2c287df86e3778b4f7a3a24 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 25 Feb 2021 10:34:43 -0800 Subject: [PATCH 173/298] GncOptionMultichoiceValue allow setting a default selection. Instead of arbitrarily using the first allowed value. Also update tests for the Scheme type addition to GncMultichoiceOptionChoices, intercept more cases where the value needs to be transformed, and go back to emitting a string instead of throwing in GncOptionMultichoiceValue::get_value when m_values has more than one value. --- libgnucash/app-utils/gnc-option-impl.hpp | 2 +- libgnucash/app-utils/gnc-optiondb.cpp | 14 +++- libgnucash/app-utils/gnc-optiondb.hpp | 5 +- libgnucash/app-utils/gnc-optiondb.i | 64 +++++++++++++++---- .../app-utils/test/gtest-gnc-option.cpp | 36 ++++++----- .../app-utils/test/gtest-gnc-optiondb.cpp | 13 ++-- .../app-utils/test/test-gnc-optiondb.scm | 23 +++++-- 7 files changed, 113 insertions(+), 44 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index d2ae693dd7..9e09b14132 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -576,7 +576,7 @@ public: if (vec.size() == 1) return std::get<0>(m_choices.at(vec[0])); else - throw std::length_error("Retrieving multiple values from a multichoice isn't implemented."); + return c_list_string; } const std::string& get_default_value() const diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index ecd1ce838d..82b52928ab 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -990,11 +990,19 @@ gnc_register_account_sel_limited_option(GncOptionDB* db, void gnc_register_multichoice_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, + 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, - std::get<0>(choices.at(0)).c_str(), std::move(choices)}}; + defval.c_str(), std::move(choices)}}; db->register_option(section, std::move(option)); } @@ -1471,7 +1479,7 @@ gnc_option_db_lookup_string_value(GncOptionDB* odb, const char* section, const c { auto value{odb->lookup_string_option(section, name)}; if (value.empty()) - return nullptr; + return nullptr; return strdup(value.c_str()); } diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 0d05c1ab78..3e8a96ee53 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -383,6 +383,7 @@ inline void gnc_register_account_sel_limited_option(GncOptionDBPtr& db, 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); /** @@ -391,10 +392,12 @@ void gnc_register_multichoice_option(GncOptionDB* db, 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, std::move(value)); + key, doc_string, default_val, + std::move(value)); } /** diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index c7c45817ed..65d0b2a4de 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -441,7 +441,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %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 GncOptionAccountList&, GncOptionAccountTypeList&&); -%ignore gnc_register_multichoice_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncMultichoiceOptionChoices&&); +%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*); @@ -497,12 +497,16 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)" "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)" "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))"); + + return reldate_values; + } %} %ignore GncOptionMultichoiceKeyType; %inline %{ - SCM get_scm_value(const GncOptionMultichoiceValue& option) + inline SCM scm_from_multichoices (const GncMultichoiceOptionIndexVec& indexes, + const GncOptionMultichoiceValue& option) { using KeyType = GncOptionMultichoiceKeyType; auto scm_value = [](const char* value, KeyType keytype) -> SCM { @@ -514,15 +518,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); case KeyType::STRING: return scm_str; case KeyType::NUMBER: - return scm_string_to_number(scm_str, scm_from_int(10)); + return scm_string_to_number(scm_str, + scm_from_int(10)); }; + return SCM_BOOL_F; }; - auto indexes = option.get_multiple(); - if (indexes.empty()) - indexes = option.get_default_multiple(); - if (indexes.empty()) - return SCM_BOOL_F; if (indexes.size() == 1) // FIXME: Should use bool member to decide return scm_value(option.permissible_value(indexes[0]), option.get_keytype(indexes[0])); @@ -536,6 +537,27 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return scm_reverse(values); } + + 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); + } + + 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); + } + SCM get_scm_value(const GncOptionRangeValue& option) { auto val{option.get_value()}; @@ -543,6 +565,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return scm_cons(desig, scm_from_int(val)); } + 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)); + } + %} %include "gnc-option-date.hpp" @@ -581,6 +610,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); if (!$self) return SCM_BOOL_F; return std::visit([](const auto& option)->SCM { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue> || + std::is_same_v, + GncOptionRangeValue>) + return get_scm_default_value(option); auto value{option.get_default_value()}; if constexpr (std::is_same_v, SCM>) @@ -705,14 +739,20 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); 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, - choices.empty() ? "None" : - std::get<0>(choices.at(0)).c_str(), - std::move(choices)}}; + doc_string, defval.c_str(), std::move(choices)}}; } catch (const std::exception& err) { diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 39f1aee6a6..e4667d7f10 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -949,18 +949,20 @@ TEST_F(GncOptionAccountTest, test_account_list_from_scheme) 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", "thud"}, - {"waldo", "pepper", "salt"}, - {"pork", "sausage", "links"}, - {"corge", "grault", "garply"} - }}} {} + m_option{GncOptionMultichoiceValue + {"foo", "bar", "baz", + "Phony Option", "plugh", + { + {"plugh", "xyzzy", "thud", KT::STRING}, + {"waldo", "pepper", "salt", KT::STRING}, + {"pork", "sausage", "links", KT::STRING}, + {"corge", "grault", "garply", KT::STRING} + }}} {} GncOption m_option; }; @@ -1052,15 +1054,15 @@ class GncOptionListTest : public ::testing::Test { protected: GncOptionListTest() : - m_option{GncOptionMultichoiceValue{"foo", "bar", "baz", - "Phony Option", - GncMultichoiceOptionIndexVec{0, 2}, - { - {"plugh", "xyzzy", "thud"}, - {"waldo", "pepper", "salt"}, - {"pork", "sausage", "links"}, - {"corge", "grault", "garply"} - }}} {} + m_option{GncOptionMultichoiceValue{ + "foo", "bar", "baz", "Phony Option", + GncMultichoiceOptionIndexVec{0, 2}, + { + {"plugh", "xyzzy", "thud", KT::STRING}, + {"waldo", "pepper", "salt", KT::STRING}, + {"pork", "sausage", "links", KT::STRING}, + {"corge", "grault", "garply", KT::STRING} + }}} {} GncOption m_option; }; diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 3575372863..bae79b04ad 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -174,15 +174,18 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct) EXPECT_FALSE(m_db->find_option("foo", "bar")); } +using KT = GncOptionMultichoiceKeyType; TEST_F(GncOptionDBTest, test_register_multichoice_option) { GncMultichoiceOptionChoices choices{ - { "plugh", "xyzzy", "thud"}, - { "waldo", "pepper", "salt"}, - { "pork", "sausage", "links"}, - { "corge", "grault", "garply"}}; + { "plugh", "xyzzy", "thud", KT::STRING}, + { "waldo", "pepper", "salt", KT::STRING}, + { "pork", "sausage", "links", KT::STRING}, + { "corge", "grault", "garply", KT::STRING}}; gnc_register_multichoice_option(m_db, "foo", "bar", "baz", - "Phony Option", std::move(choices)); + "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()); } diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 2da34c98b6..0d26222ae3 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -23,6 +23,19 @@ (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) @@ -154,17 +167,17 @@ (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 '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" multichoice))) + (multi-opt (gnc:register-multichoice-option option-db "foo" "bar" "baz" + "Phony Option" 'waldo multichoice))) - (test-equal "plugh" (gnc-option-value option-db "foo" "bar")) + (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 "plugh" (gnc-option-default-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) From 9bd3baff63d41186aff4f3341f6373098d19e1fd Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 25 Feb 2021 10:40:18 -0800 Subject: [PATCH 174/298] Change the reldate_values alist from a static to a singleton. A static has to be initialized at library load and Guile isn't necessarily running then. The singleton gets evaluated at runtime only if the getter is called from Guile. --- libgnucash/app-utils/gnc-optiondb.i | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 65d0b2a4de..40ac774838 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -464,7 +464,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %typemap(out) GncOption* "$result = ($1) ? scm_from_pointer($1, nullptr) : SCM_BOOL_F;" %header %{ - static const SCM reldate_values = scm_c_eval_string( + static const SCM get_reldate_values() { + + static SCM reldate_values = SCM_BOOL_F; + + if (scm_is_false(reldate_values)) + reldate_values = scm_c_eval_string( "'((absolute RelativeDatePeriod-ABSOLUTE)" "(today RelativeDatePeriod-TODAY)" "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)" @@ -626,7 +631,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); { if (!$self) return; - std::visit([new_value](auto& option) { + auto reldate_values{get_reldate_values()}; + std::visit([new_value, reldate_values](auto& option) { if constexpr (std::is_same_v, GncOptionDateValue>) { From 43cd81ba78b235c7cc333e0c987f962f60085949 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 25 Feb 2021 10:41:44 -0800 Subject: [PATCH 175/298] Fix a couple of RangeValue retrieval issues. --- libgnucash/app-utils/gnc-optiondb.i | 79 ++++++++++++++++------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 40ac774838..85db0e16eb 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -409,6 +409,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); #include #include #include +#include #include "gnc-option.hpp" #include "gnc-option-ui.hpp" @@ -470,38 +471,38 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); if (scm_is_false(reldate_values)) reldate_values = scm_c_eval_string( - "'((absolute RelativeDatePeriod-ABSOLUTE)" - "(today RelativeDatePeriod-TODAY)" - "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)" - "(one-week-ahead RelativeDatePeriod-ONE-WEEK-AHEAD)" - "(one-month-ago RelativeDatePeriod-ONE-MONTH-AGO)" - "(one-month-ahead RelativeDatePeriod-ONE-MONTH-AHEAD)" - "(three-months-ago RelativeDatePeriod-THREE-MONTHS-AGO)" - "(three-months-ahead RelativeDatePeriod-THREE-MONTHS-AHEAD)" - "(six-months-ago RelativeDatePeriod-SIX-MONTHS-AGO)" - "(six-months-ahead RelativeDatePeriod-SIX-MONTHS-AHEAD)" - "(one-year-ago RelativeDatePeriod-ONE-YEAR-AGO)" - "(one-year-ahead RelativeDatePeriod-ONE-YEAR-AHEAD)" - "(start-this-month RelativeDatePeriod-START-THIS-MONTH)" - "(end-this-month RelativeDatePeriod-END-THIS-MONTH)" - "(start-prev-month RelativeDatePeriod-START-PREV-MONTH)" - "(end-prev-month RelativeDatePeriod-END-PREV-MONTH)" - "(start-next-month RelativeDatePeriod-START-NEXT-MONTH)" - "(end-next-month RelativeDatePeriod-END-NEXT-MONTH)" - "(start-current-quarter RelativeDatePeriod-START-CURRENT-QUARTER)" - "(end-current-quarter RelativeDatePeriod-END-CURRENT-QUARTER)" - "(start-prev-quarter RelativeDatePeriod-START-PREV-QUARTER)" - "(end-prev-quarter RelativeDatePeriod-END-PREV-QUARTER)" - "(start-next-quarter RelativeDatePeriod-START-NEXT-QUARTER)" - "(end-next-quarter RelativeDatePeriod-END-NEXT-QUARTER)" - "(start-cal-year RelativeDatePeriod-START-CAL-YEAR)" - "(end-cal-year RelativeDatePeriod-END-CAL-YEAR)" - "(start-prev-year RelativeDatePeriod-START-PREV-YEAR)" - "(end-prev-year RelativeDatePeriod-END-PREV-YEAR)" - "(start-next-year RelativeDatePeriod-START-NEXT-YEAR)" - "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)" - "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)" - "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))"); + "'((absolute RelativeDatePeriod-ABSOLUTE)" + "(today RelativeDatePeriod-TODAY)" + "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)" + "(one-week-ahead RelativeDatePeriod-ONE-WEEK-AHEAD)" + "(one-month-ago RelativeDatePeriod-ONE-MONTH-AGO)" + "(one-month-ahead RelativeDatePeriod-ONE-MONTH-AHEAD)" + "(three-months-ago RelativeDatePeriod-THREE-MONTHS-AGO)" + "(three-months-ahead RelativeDatePeriod-THREE-MONTHS-AHEAD)" + "(six-months-ago RelativeDatePeriod-SIX-MONTHS-AGO)" + "(six-months-ahead RelativeDatePeriod-SIX-MONTHS-AHEAD)" + "(one-year-ago RelativeDatePeriod-ONE-YEAR-AGO)" + "(one-year-ahead RelativeDatePeriod-ONE-YEAR-AHEAD)" + "(start-this-month RelativeDatePeriod-START-THIS-MONTH)" + "(end-this-month RelativeDatePeriod-END-THIS-MONTH)" + "(start-prev-month RelativeDatePeriod-START-PREV-MONTH)" + "(end-prev-month RelativeDatePeriod-END-PREV-MONTH)" + "(start-next-month RelativeDatePeriod-START-NEXT-MONTH)" + "(end-next-month RelativeDatePeriod-END-NEXT-MONTH)" + "(start-current-quarter RelativeDatePeriod-START-CURRENT-QUARTER)" + "(end-current-quarter RelativeDatePeriod-END-CURRENT-QUARTER)" + "(start-prev-quarter RelativeDatePeriod-START-PREV-QUARTER)" + "(end-prev-quarter RelativeDatePeriod-END-PREV-QUARTER)" + "(start-next-quarter RelativeDatePeriod-START-NEXT-QUARTER)" + "(end-next-quarter RelativeDatePeriod-END-NEXT-QUARTER)" + "(start-cal-year RelativeDatePeriod-START-CAL-YEAR)" + "(end-cal-year RelativeDatePeriod-END-CAL-YEAR)" + "(start-prev-year RelativeDatePeriod-START-PREV-YEAR)" + "(end-prev-year RelativeDatePeriod-END-PREV-YEAR)" + "(start-next-year RelativeDatePeriod-START-NEXT-YEAR)" + "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)" + "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)" + "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))"); return reldate_values; } @@ -519,9 +520,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); switch (keytype) { case KeyType::SYMBOL: - return scm_string_to_symbol(scm_str); + return scm_string_to_symbol(scm_str); case KeyType::STRING: - return scm_str; + return scm_str; case KeyType::NUMBER: return scm_string_to_number(scm_str, scm_from_int(10)); @@ -651,6 +652,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_value(scm_to_int64(scm_cdr(new_value))); } } + option.set_value(scm_to_int64(new_value)); return; } if constexpr (std::is_same_v, @@ -670,6 +672,15 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_value(new_value_str); return; } + if constexpr (std::is_same_v, + GncOptionRangeValue>) + { + 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; + } auto value{scm_to_value>(new_value)}; //Can't inline, set_value takes arg by reference. option.set_value(value); }, swig_get_option($self)); From d9984f75ab8a91ef23ee85af099ca0901c9c2553 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 25 Feb 2021 10:42:58 -0800 Subject: [PATCH 176/298] Update test-gnc-optiondb.scm for not renaming the RelativeDatePeriod enums. --- .../app-utils/test/test-gnc-optiondb.scm | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 0d26222ae3..ae01a460c9 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -200,7 +200,7 @@ (let* ((option-db (new-gnc-optiondb)) (date-opt (gnc-register-date-option option-db "foo" "bar" "baz" "Phony Option" - (RelativeDatePeriod-today))) + (RelativeDatePeriod-TODAY))) (a-time (gnc-dmy2time64 11 07 2019))) (test-equal (current-time) (gnc-option-value option-db "foo" "bar")) (gnc-set-option option-db "foo" "bar" a-time) @@ -212,14 +212,15 @@ (let* ((option-db (new-gnc-optiondb)) (date-opt (gnc-register-date-option-set option-db "foo" "bar" "baz" "Phony Option" - (list (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)) #t))) + (list (RelativeDatePeriod-TODAY) + (RelativeDatePeriod-START-THIS-MONTH) + (RelativeDatePeriod-START-PREV-MONTH) + (RelativeDatePeriod-START-CURRENT-QUARTER) + (RelativeDatePeriod-START-PREV-QUARTER) + (RelativeDatePeriod-START-CAL-YEAR) + (RelativeDatePeriod-START-CAL-YEAR) + (RelativeDatePeriod-START-PREV-YEAR) + (RelativeDatePeriod-START-ACCOUNTING-PERIOD)) #t))) (test-equal (gnc-accounting-period-fiscal-start) (gnc-option-value option-db "foo" "bar"))) (test-end "test-gnc-test-date-set-option")) From 4a305998e4cb41b19de80df4c0d424715be4e5f3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 2 Mar 2021 14:14:06 -0800 Subject: [PATCH 177/298] GncOptionDateValue set_value_from_scm: 'else' required. --- libgnucash/app-utils/gnc-optiondb.i | 1 + 1 file changed, 1 insertion(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 85db0e16eb..20aa66e0cc 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -652,6 +652,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_value(scm_to_int64(scm_cdr(new_value))); } } + else option.set_value(scm_to_int64(new_value)); return; } From 3aa86ca9925b85ea9316a54c6e2e64e35c4f0708 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 2 Mar 2021 14:15:43 -0800 Subject: [PATCH 178/298] QofInstance* needs a typemap to match the from/to SCM logic. --- libgnucash/app-utils/gnc-optiondb.i | 51 ++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 20aa66e0cc..64773857f5 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -59,6 +59,55 @@ SCM scm_init_sw_gnc_optiondb_module(void); * resolution is more strict so the overload prefers the exact decltype(value) * to implicit conversion candidates. */ + +%typemap (out) QofInstance_s* { + if ($1 == nullptr) + { + $result = SCM_BOOL_F; + } + else + { + + auto ptr{static_cast(const_cast($1))}; + swig_type_info *type = SWIGTYPE_p_QofInstance_s; + if (GNC_IS_COMMODITY($1)) + type = SWIGTYPE_p_gnc_commodity; + else if (GNC_IS_ACCOUNT($1)) + type = SWIGTYPE_p_Account; + else if (GNC_IS_BUDGET($1)) + type = SWIGTYPE_p_GncBudget; + else if (GNC_IS_INVOICE($1)) + type = SWIGTYPE_p_GncInvoice; + else if (GNC_IS_TAXTABLE($1)) + type = SWIGTYPE_p_GncTaxTable; + $result = SWIG_NewPointerObj(ptr, type, FALSE); + } +} + +%typemap (in) QofInstance_s* { + if (scm_is_true($input)) + { + static const std::array types { + SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity, + SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice, + SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account + }; + void* ptr{}; + auto pos = std::find_if(types.begin(), types.end(), + [&$input, &ptr](auto type){ + SWIG_ConvertPtr($input, &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); @@ -184,7 +233,7 @@ scm_to_value(SCM new_value) if (new_value == SCM_BOOL_F) return nullptr; - static const std::array types{ + static const std::array types{ SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity, SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice, SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account From e7309f077bb47768071088a57580164b158c6721 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 2 Mar 2021 14:20:12 -0800 Subject: [PATCH 179/298] Replace gnc_make_option with gnc_make_commodity_option. To handle cases where we try to create commodity options with a symbol as the default instead of a gnc_commodity*. --- libgnucash/app-utils/gnc-optiondb.i | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 64773857f5..ac893a53a3 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -881,6 +881,36 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } + GncOption* gnc_make_commodity_option(const char* section, + const char* name, const char* key, + const char* doc_string, + gnc_commodity *value) + { + return new GncOption{GncOptionValue{ + section, name, key, doc_string, (const QofInstance*)value}}; + } + + 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; + } + GncOption* gnc_make_currency_option(const char* section, const char* name, const char* key, const char* doc_string, From c04f4a00e0d34d66ed5409fd9e3875e40c13d3c4 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 2 Mar 2021 14:24:15 -0800 Subject: [PATCH 180/298] Replace gnc_make_option with gnc_make_scm_option To finesse SWIG_Guile's typedef unsigned long SCM, which causes SFINAE issues when trying to resolve the template. --- libgnucash/app-utils/gnc-option.cpp | 10 ++++++++++ libgnucash/app-utils/gnc-option.hpp | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 5053c62971..701b805414 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -517,6 +517,16 @@ GncOption::from_scheme(std::istream& iss) }, *m_option); } +GncOption* +gnc_make_SCM_option(const char* section, const char* name, + const char* key, const char* doc_string, + SCM value, GncOptionUIType ui_type) +{ + return new GncOption(section, name, key, doc_string, + reinterpret_cast(value), ui_type); +} + + /* We must instantiate all of the templates we need here because we don't expose * the template implementation in the public header. */ diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 4e88f3412e..aa7fc37467 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -150,4 +150,9 @@ gnc_make_option(const char* section, const char* name, return new GncOption(section, name, key, doc_string, value, ui_type); } +/* To work around SWIG_Guile's typedef of SCM to unsigned long: */ +GncOption* gnc_make_SCM_option(const char* section, const char* name, + const char* key, const char* doc_string, + SCM value, GncOptionUIType ui_type); + #endif //GNC_OPTION_HPP_ From 5c743378107ea6f34d2920f025efc07e0be60d51 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 5 Mar 2021 15:56:34 -0800 Subject: [PATCH 181/298] Add function GncOption::set_default_value. Allows reports to derive from other reports and then change the option default values to suit. If they change only the values and not the defaults then it's possible to create saved report configs that don't include the options whose values are changed to the base report's default value. See https://bugs.gnucash.org/show_bug.cgi?id=642292. --- libgnucash/app-utils/gnc-option-impl.cpp | 9 +++ libgnucash/app-utils/gnc-option-impl.hpp | 72 ++++++++++++++++++++++- libgnucash/app-utils/gnc-option.cpp | 35 +++++++++++ libgnucash/app-utils/gnc-option.hpp | 1 + libgnucash/app-utils/gnc-optiondb.i | 75 ++++++++++++++++++++++++ 5 files changed, 191 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 5c92c214fb..16310597fa 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -131,6 +131,15 @@ GncOptionDateValue::set_value(size_t index) noexcept m_period = m_period_set[index]; } +void +GncOptionDateValue::set_default_value(size_t index) noexcept +{ + assert(!m_period_set.empty()); + assert(index < m_period_set.size()); + m_date = m_default_date = INT64_MAX; + m_period = m_default_period = m_period_set[index]; +} + size_t GncOptionDateValue::permissible_value_index(const char* key) const noexcept { diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 9e09b14132..02f58c2127 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -91,7 +91,6 @@ protected: }; */ - static const char* commodity_scm_intro{"'(commodity-scm "}; #ifndef SWIG size_t constexpr classifier_size_max{50}; @@ -129,6 +128,9 @@ public: ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } void set_value(ValueType new_value) { m_value = new_value; } + void set_default_value(ValueType new_value) { + m_value = m_default_value = new_value; + } 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; } @@ -183,6 +185,13 @@ public: 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 reset_default_value() { m_value = m_default_value; } bool is_changed() const noexcept { return m_value != m_default_value; } std::ostream& to_scheme(std::ostream&) const; @@ -426,6 +435,13 @@ public: 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; @@ -649,6 +665,40 @@ public: else throw std::invalid_argument("One of the supplied indexes was out of range."); } + 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_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(); @@ -866,6 +916,11 @@ public: //throw! m_value = values; } + void set_default_value (const 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 { return m_value != m_default_value; } @@ -1039,6 +1094,21 @@ public: } } 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; + } + } + void set_default_value(size_t index) noexcept; std::size_t num_permissible_values() const noexcept { return m_period_set.size(); diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 701b805414..4ef3af971d 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -126,6 +126,27 @@ GncOption::set_value(ValueType value) }, *m_option); } +template void +GncOption::set_default_value(ValueType value) +{ + std::visit([value](auto& option) { + if constexpr + (std::is_same_v, + std::decay_t> || + (std::is_same_v, + GncOptionDateValue> && + (std::is_same_v, + RelativeDatePeriod> || + std::is_same_v, size_t>))) + option.set_default_value(value); + if constexpr + (std::is_same_v, + GncOptionMultichoiceValue> && + std::is_same_v, + GncMultichoiceOptionIndexVec>) + option.set_multiple(value); + }, *m_option); +} void GncOption::reset_default_value() { @@ -590,6 +611,20 @@ template void GncOption::set_value(GncOptionAccountList); template void GncOption::set_value(GncMultichoiceOptionIndexVec); template void GncOption::set_value(SCM); +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(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(SCM); + 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; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index aa7fc37467..401c84f3a7 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -88,6 +88,7 @@ public: 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(); diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index ac893a53a3..dcf9571360 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -735,6 +735,81 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_value(value); }, swig_get_option($self)); } + + void set_default_value_from_scm(SCM new_value) + { + if (!$self) + return; + auto reldate_values{get_reldate_values()}; + std::visit([new_value, reldate_values](auto& option) { + if constexpr (std::is_same_v, + GncOptionDateValue>) + { + if (scm_is_pair(new_value)) + { + auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(new_value)))}; + if (strcmp(car, "relative") == 0) + { + auto lookup{scm_assq_ref(reldate_values, + scm_cdr(new_value))}; + auto reldate{scm_primitive_eval(lookup)}; + option.set_default_value(static_cast(scm_to_int(reldate))); + } + else + { + option.set_value(scm_to_int64(scm_cdr(new_value))); + } + } + else + option.set_default_value(scm_to_int64(new_value)); + return; + } + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + { + if (scm_is_integer(new_value)) + { + option.set_default_value(scm_to_int(new_value)); + return; + } + std::string new_value_str{}; + if (scm_is_symbol(new_value)) + new_value_str = scm_to_utf8_string(scm_symbol_to_string(new_value)); + else if (scm_is_string(new_value)) + new_value_str = scm_to_utf8_string(new_value); + if (!new_value_str.empty()) + option.set_default_value(new_value_str); + return; + } + if constexpr (std::is_same_v, + GncOptionRangeValue>) + { + 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; + } + 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)); + } + + SCM get_type() + { + if (!self) + return SCM_BOOL_F; + return std::visit([](const auto& option)->SCM { + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + 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 { From 42185c0ec8dc926912a440ced0fae83755d97086 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 5 Mar 2021 16:13:09 -0800 Subject: [PATCH 182/298] typemap for std::size_t Unaccountably missing from swig_guile. --- libgnucash/app-utils/gnc-optiondb.i | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index dcf9571360..cfd4ec771f 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -38,6 +38,9 @@ namespace std { %enddef +%typemap(in) std::size_t "$1 = scm_to_ulong($input);"; +%typemap(out) std::size_t "$result = scm_from_ulong($1);"; + //%module sw_gnc_optiondb %{ #include "gnc-optiondb.h" From 63502900f3dc9992a38bbd140a3e1a4830aa32cd Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 5 Mar 2021 16:15:12 -0800 Subject: [PATCH 183/298] Permit GncOptionAccountValue to have a nil default value. --- libgnucash/app-utils/gnc-option-impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 16310597fa..961769de7b 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -40,7 +40,7 @@ bool GncOptionAccountValue::validate(const GncOptionAccountList& values) const { if (values.empty()) - return false; + return true; if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) && values.size() != 1) return false; From 693e966bf958b540017e6cc0c1e6c20d3c10f5fd Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 8 Mar 2021 11:13:26 -0800 Subject: [PATCH 184/298] Extract Scheme date handling functions and apply them. Including in RelativeDatePeriodVec typemap, making it work correctly. --- libgnucash/app-utils/gnc-optiondb.i | 77 ++++++++++++++--------------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index cfd4ec771f..60d86eea0a 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -329,11 +329,11 @@ gnc_option_test_book_destroy(QofBook* book) %typemap(in) RelativeDatePeriodVec& (RelativeDatePeriodVec period_set) { - auto len = scm_to_size_t(scm_length($input)); + 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((RelativeDatePeriod)scm_to_int(s_reldateperiod)); + period_set.push_back(scm_relative_date_get_period(s_reldateperiod)); } $1 = &period_set; } @@ -517,8 +517,9 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %typemap(out) GncOption* "$result = ($1) ? scm_from_pointer($1, nullptr) : SCM_BOOL_F;" %header %{ - static const SCM get_reldate_values() { + inline static RelativeDatePeriod scm_relative_date_get_period(SCM date) + { static SCM reldate_values = SCM_BOOL_F; if (scm_is_false(reldate_values)) @@ -556,8 +557,32 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)" "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))"); - return reldate_values; + auto reldate_scm{scm_is_pair(date) ? scm_cdr(date) : date}; + auto reldate{scm_primitive_eval(scm_assq_ref(reldate_values, + reldate_scm))}; + return static_cast(scm_to_int(reldate)); + + } + + inline static bool scm_date_absolute(SCM date) + { + if (scm_is_pair(date)) + { + auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(date)))}; + if (strcmp(car, "relative") == 0) + return false; } + return true; + } + + 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; @@ -684,28 +709,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); { if (!$self) return; - auto reldate_values{get_reldate_values()}; - std::visit([new_value, reldate_values](auto& option) { + std::visit([new_value](auto& option) { if constexpr (std::is_same_v, GncOptionDateValue>) { - if (scm_is_pair(new_value)) - { - auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(new_value)))}; - if (strcmp(car, "relative") == 0) - { - auto lookup{scm_assq_ref(reldate_values, - scm_cdr(new_value))}; - auto reldate{scm_primitive_eval(lookup)}; - option.set_value(static_cast(scm_to_int(reldate))); - } - else - { - option.set_value(scm_to_int64(scm_cdr(new_value))); - } - } + if (scm_date_absolute(new_value)) + option.set_value(scm_absolute_date_to_time64(new_value)); else - option.set_value(scm_to_int64(new_value)); + option.set_value(scm_relative_date_get_period(new_value)); return; } if constexpr (std::is_same_v, @@ -743,28 +754,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); { if (!$self) return; - auto reldate_values{get_reldate_values()}; - std::visit([new_value, reldate_values](auto& option) { + std::visit([new_value](auto& option) { if constexpr (std::is_same_v, GncOptionDateValue>) { - if (scm_is_pair(new_value)) - { - auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(new_value)))}; - if (strcmp(car, "relative") == 0) - { - auto lookup{scm_assq_ref(reldate_values, - scm_cdr(new_value))}; - auto reldate{scm_primitive_eval(lookup)}; - option.set_default_value(static_cast(scm_to_int(reldate))); - } - else - { - option.set_value(scm_to_int64(scm_cdr(new_value))); - } - } + if (scm_date_absolute(new_value)) + option.set_default_value(scm_absolute_date_to_time64(new_value)); else - option.set_default_value(scm_to_int64(new_value)); + option.set_default_value(scm_relative_date_get_period(new_value)); return; } if constexpr (std::is_same_v, From 86a2f1551fc5910096f9c21e87711598abee876f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 8 Mar 2021 11:14:01 -0800 Subject: [PATCH 185/298] Implement gnc-make-date-option. --- libgnucash/app-utils/gnc-optiondb.i | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 60d86eea0a..1dc5fd05d5 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -878,6 +878,51 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } + 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; + } + } + GncOption* gnc_make_multichoice_option(const char* section, const char* name, const char* key, const char* doc_string, From 7d0cdf7c94099aac372409c86d26ec004d0e7f01 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 8 Mar 2021 11:16:35 -0800 Subject: [PATCH 186/298] Handle #f input to option typemap(in)s. --- libgnucash/app-utils/gnc-optiondb.i | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 1dc5fd05d5..130e50878b 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -341,7 +341,7 @@ gnc_option_test_book_destroy(QofBook* book) %typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices) { using KeyType = GncOptionMultichoiceKeyType; - auto len = scm_to_size_t(scm_length($input)); + 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)); @@ -375,7 +375,7 @@ gnc_option_test_book_destroy(QofBook* book) %typemap(in) GncOptionAccountList { - auto len = scm_to_size_t(scm_length($input)); + 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)); @@ -387,7 +387,7 @@ gnc_option_test_book_destroy(QofBook* book) %typemap(in) GncOptionAccountTypeList& (GncOptionAccountTypeList types) { - auto len = scm_to_size_t(scm_length($input)); + 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)); @@ -399,7 +399,7 @@ gnc_option_test_book_destroy(QofBook* book) %typemap(in) GncOptionAccountTypeList&& (GncOptionAccountTypeList types) { - auto len = scm_to_size_t(scm_length($input)); + 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)); @@ -411,7 +411,7 @@ gnc_option_test_book_destroy(QofBook* book) %typemap(in) GncOptionAccountList { - auto len = scm_to_size_t(scm_length($input)); + 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)); @@ -423,7 +423,7 @@ gnc_option_test_book_destroy(QofBook* book) %typemap(in) GncOptionAccountList& (GncOptionAccountList acclist) { - auto len = scm_to_size_t(scm_length($input)); + 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)); From 418eb066206c26c37f4d7d7af63b8d6f8cde7faf Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 11 Mar 2021 17:50:57 -0800 Subject: [PATCH 187/298] Remove GncOptionDateValue::set_default_value(size_t) Because it's ambiguous with set_default_value(time64). --- libgnucash/app-utils/gnc-option-impl.cpp | 9 --------- libgnucash/app-utils/gnc-option-impl.hpp | 1 - 2 files changed, 10 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 961769de7b..210ef99676 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -131,15 +131,6 @@ GncOptionDateValue::set_value(size_t index) noexcept m_period = m_period_set[index]; } -void -GncOptionDateValue::set_default_value(size_t index) noexcept -{ - assert(!m_period_set.empty()); - assert(index < m_period_set.size()); - m_date = m_default_date = INT64_MAX; - m_period = m_default_period = m_period_set[index]; -} - size_t GncOptionDateValue::permissible_value_index(const char* key) const noexcept { diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 02f58c2127..4b2f84de3a 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -1108,7 +1108,6 @@ public: m_date = m_default_date = time; } } - void set_default_value(size_t index) noexcept; std::size_t num_permissible_values() const noexcept { return m_period_set.size(); From 0a8f66ee9048bdffb75840f76ec3928e394e50af Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 11 Mar 2021 17:56:53 -0800 Subject: [PATCH 188/298] Fold GncOptionGncOwnerValue into GncOptionValue. Reflecting the way Scheme options handles it. --- libgnucash/app-utils/gnc-option-impl.cpp | 4 +- libgnucash/app-utils/gnc-option-uitype.hpp | 1 + libgnucash/app-utils/gnc-optiondb-impl.hpp | 5 ++- libgnucash/app-utils/gnc-optiondb.cpp | 32 ------------- libgnucash/app-utils/gnc-optiondb.h | 1 - libgnucash/app-utils/gnc-optiondb.hpp | 28 ------------ libgnucash/app-utils/gnc-optiondb.i | 52 +++++++++++++++------- 7 files changed, 43 insertions(+), 80 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 210ef99676..b12ca23aa8 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -211,8 +211,8 @@ qof_instance_from_guid(GncGUID* guid, GncOptionUIType type) case GncOptionUIType::BUDGET: qof_type = "Budget"; break; - case GncOptionUIType::OWNER: - qof_type = "gncOwner"; + case GncOptionUIType::JOB: + qof_type = "gncJob"; break; case GncOptionUIType::CUSTOMER: qof_type = "gncCustomer"; diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp index b1e784a455..22098bb72b 100644 --- a/libgnucash/app-utils/gnc-option-uitype.hpp +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -51,6 +51,7 @@ enum class GncOptionUIType : unsigned int VENDOR, EMPLOYEE, INVOICE, + JOB, TAX_TABLE, QUERY, REPORT_LIST, diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index 472b1ca24f..95f77a34bd 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -36,7 +36,10 @@ extern "C" #include #include #include -#include +#include +#include +#include +#include #include } diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 82b52928ab..5588499e09 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1070,38 +1070,6 @@ gnc_register_internal_option(GncOptionDB* db, const char* section, GncOptionUIType::INTERNAL}; db->register_option(section, std::move(option)); } - -static inline GncOptionUIType -owner_type_to_ui_type(GncOwnerType type) -{ - switch (type) - { - case GNC_OWNER_NONE: - case GNC_OWNER_UNDEFINED: - case GNC_OWNER_JOB: - return GncOptionUIType::INTERNAL; - case GNC_OWNER_CUSTOMER: - return GncOptionUIType::CUSTOMER; - case GNC_OWNER_VENDOR: - return GncOptionUIType::VENDOR; - case GNC_OWNER_EMPLOYEE: - return GncOptionUIType::EMPLOYEE; - } - return GncOptionUIType::INTERNAL; -} - -void -gnc_register_owner_option(GncOptionDB* db, const char* section, - const char* name, const char* key, - const char* doc_string, GncOwner* value, - GncOwnerType type) -{ - GncOption option{section, name, key, doc_string, - (const QofInstance*)value->owner.undefined, - owner_type_to_ui_type(type)}; - db->register_option(section, std::move(option)); -} - void gnc_register_invoice_option(GncOptionDB* db, const char* section, const char* name, const char* key, diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h index 885715622b..007bfd1f4f 100644 --- a/libgnucash/app-utils/gnc-optiondb.h +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -44,7 +44,6 @@ extern "C" #include #include #include -#include #include /** diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 3e8a96ee53..a5b6c427b9 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -37,7 +37,6 @@ extern "C" #include #include #include -#include #include } #include "gnc-option.hpp" @@ -620,33 +619,6 @@ inline void gnc_register_invoice_option(const GncOptionDBPtr& db, doc_string, value); } -/** - * Create a new owner-type 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 type The Owner-type (Customer, Employee, or Vendor) - */ -void gnc_register_owner_option(GncOptionDB* db, const char* section, - const char* name, const char* key, - const char* doc_string, GncOwner* value, - GncOwnerType type); - -/** - * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. - */ -inline void gnc_register_owner_option(const GncOptionDBPtr& db, - const char* section, const char* name, - const char* key, const char* doc_string, - GncOwner* value, GncOwnerType type) -{ - gnc_register_owner_option(db.get(), section, name, key, - doc_string, value, type); -} - /** * Create a new taxtable option and register it in the options database. * diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 130e50878b..0541644b32 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -64,25 +64,33 @@ SCM scm_init_sw_gnc_optiondb_module(void); */ %typemap (out) QofInstance_s* { + swig_type_info *type = $descriptor(QofInstance_s); if ($1 == nullptr) { - $result = SCM_BOOL_F; + $result = SWIG_NewPointerObj(nullptr, type, FALSE); } else { auto ptr{static_cast(const_cast($1))}; - swig_type_info *type = SWIGTYPE_p_QofInstance_s; if (GNC_IS_COMMODITY($1)) - type = SWIGTYPE_p_gnc_commodity; + type = $descriptor(gnc_commodity*); else if (GNC_IS_ACCOUNT($1)) - type = SWIGTYPE_p_Account; + type = $descriptor(Account*); else if (GNC_IS_BUDGET($1)) - type = SWIGTYPE_p_GncBudget; + type = $descriptor(GncBudget*); else if (GNC_IS_INVOICE($1)) - type = SWIGTYPE_p_GncInvoice; + type = $descriptor(GncInvoice*); else if (GNC_IS_TAXTABLE($1)) - type = SWIGTYPE_p_GncTaxTable; + 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); } } @@ -90,10 +98,12 @@ SCM scm_init_sw_gnc_optiondb_module(void); %typemap (in) QofInstance_s* { if (scm_is_true($input)) { - static const std::array types { - SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity, - SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice, - SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account + 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{}; auto pos = std::find_if(types.begin(), types.end(), @@ -168,10 +178,11 @@ scm_from_value(double value) template <> inline SCM scm_from_value(const QofInstance* value) { - if (!value) return SCM_BOOL_F; + swig_type_info *type = SWIGTYPE_p_QofInstance_s; + if (!value) + return SWIG_NewPointerObj(nullptr, type, FALSE); auto ptr{static_cast(const_cast(value))}; - swig_type_info *type = SWIGTYPE_p_QofInstance_s; if (GNC_IS_COMMODITY(value)) type = SWIGTYPE_p_gnc_commodity; else if (GNC_IS_ACCOUNT(value)) @@ -182,6 +193,14 @@ scm_from_value(const QofInstance* 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; /* There is no type macro for QofQuery, it's not a GObject. else if (GNC_IS_QOFQUERY(value)) type = SWIGTYPE_p_Query; @@ -236,10 +255,12 @@ scm_to_value(SCM new_value) if (new_value == SCM_BOOL_F) return nullptr; - static const std::array types{ + static const std::array types{ SWIGTYPE_p_QofInstance_s, SWIGTYPE_p_gnc_commodity, SWIGTYPE_p_GncBudget, SWIGTYPE_p_GncInvoice, - SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account + 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(), @@ -502,7 +523,6 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %ignore gnc_register_internal_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_owner_option(GncOptionDB*, const char*, const char*, const char*, const char*, GncOwner*, GncOwnerType); %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); From f66a918be463bc36a3ecd6c5c8d28eac05837df8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 11 Mar 2021 18:03:36 -0800 Subject: [PATCH 189/298] Implement GncOptionValue QofQuery isn't a QofInstance and doesn't have a GUID. --- libgnucash/app-utils/gnc-option-impl.hpp | 30 ++++++++++++++++++++++++ libgnucash/app-utils/gnc-option.cpp | 7 ++++++ libgnucash/app-utils/gnc-option.hpp | 4 ++++ libgnucash/app-utils/gnc-optiondb.i | 18 ++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 4b2f84de3a..09eb8d20df 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -1157,4 +1157,34 @@ operator>> (std::istream& iss, return opt.in_stream(iss); } +/** QofQuery Options + */ + +inline std::istream& +gnc_option_from_scheme(std::istream& iss, GncOptionValue& opt) +{ +//FIXME: Implement or maybe rethink. + return iss; +} + +inline std::ostream& +gnc_option_to_scheme(std::ostream& oss, GncOptionValue& opt) +{ +//FIXME: Implement or maybe rethink. + return oss; +} + +inline std::istream& +gnc_option_from_scheme(std::istream& iss, GncOptionValidatedValue& opt) +{ +//FIXME: Implement or maybe rethink. + return iss; +} + +inline std::ostream& +gnc_option_to_scheme(std::ostream& oss, GncOptionValidatedValue& opt) +{ +//FIXME: Implement or maybe rethink. + return oss; +} #endif //GNC_OPTION_IMPL_HPP_ diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 4ef3af971d..6f708681bf 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -481,8 +481,12 @@ GncOption::from_scheme(std::istream& iss) (std::is_same_v, GncOptionMultichoiceValue>) || std::is_same_v, + GncOptionValue> || + std::is_same_v, GncOptionValue> || std::is_same_v, + GncOptionValidatedValue> || + std::is_same_v, GncOptionValidatedValue>) gnc_option_from_scheme(iss, option); else if constexpr @@ -571,6 +575,8 @@ template GncOption::GncOption(const char*, const char*, const char*, const char*, const QofInstance*, GncOptionUIType); template GncOption::GncOption(const char*, const char*, const char*, const char*, SCM, GncOptionUIType); +template GncOption::GncOption(const char*, const char*, const char*, + const char*, const QofQuery*, GncOptionUIType); template bool GncOption::get_value() const; template int GncOption::get_value() const; @@ -634,6 +640,7 @@ 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(const QofQuery*) const; template bool GncOption::validate(RelativeDatePeriod) const; template bool GncOption::validate(GncMultichoiceOptionIndexVec) const; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 401c84f3a7..5edbc9404c 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -40,6 +40,8 @@ extern "C" struct OptionClassifier; class GncOptionUIItem; using GncOptionUIItemPtr = std::unique_ptr; +struct _QofQuery; +using QofQuery = _QofQuery; struct QofInstance_s; using QofInstance = QofInstance_s; template class GncOptionValue; @@ -53,12 +55,14 @@ using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue, + GncOptionValue, GncOptionValue, GncOptionAccountValue, GncOptionMultichoiceValue, GncOptionRangeValue, GncOptionRangeValue, GncOptionValidatedValue, + GncOptionValidatedValue, GncOptionDateValue>; using GncOptionVariantPtr = std::unique_ptr; diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 0541644b32..f46446c262 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -208,6 +208,13 @@ scm_from_value(const QofInstance* value) return SWIG_NewPointerObj(ptr, type, FALSE); } +template <> inline SCM +scm_from_value(const QofQuery* value) +{ + auto ptr{static_cast(const_cast(value))}; + return SWIG_NewPointerObj(ptr, SWIGTYPE_p__QofQuery, FALSE); +} + template inline ValueType scm_to_value(SCM new_value) { @@ -273,6 +280,16 @@ scm_to_value(SCM new_value) return static_cast(ptr); } +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 GncOptionAccountList scm_to_value(SCM new_value) { @@ -689,6 +706,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %template(gnc_make_bool_option) gnc_make_option; %template(gnc_make_int64_option) gnc_make_option; %template(gnc_make_qofinstance_option) gnc_make_option; +%template(gnc_make_query_option) gnc_make_option; %extend GncOption { SCM get_scm_value() From 0f8446a1bb550c6ae62d61576d168b7c08df7d99 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 11 Mar 2021 18:04:12 -0800 Subject: [PATCH 190/298] Remove some no longer needed diagnostics. --- libgnucash/app-utils/gnc-optiondb.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 5588499e09..3bbd4e405e 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -51,11 +51,8 @@ public: return std::strcmp(old_name, alias.first) == 0; }); if (alias == c_option_aliases.end()) - { - std::cerr << "No alias for " << old_name << " found.\n"; return nullptr; - } - std::cerr << "Found " << alias->second.second << " as alias for " << old_name << ".\n"; + return &alias->second; } }; From a94f69d6e04329e92d94e844d4ca06e9c74d70fc Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 11 Mar 2021 18:06:31 -0800 Subject: [PATCH 191/298] Pretty up some SFINAE formatting for easier reading. --- libgnucash/app-utils/gnc-option-impl.hpp | 38 +++++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 09eb8d20df..edbf9e1918 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -334,7 +334,12 @@ operator>> >(std::istream& iss, opt.set_value(instr == "#t" ? true : false); return iss; } -template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue>, int> = 0> +template, + GncOptionValidatedValue> || + std::is_same_v, + GncOptionValue>, + int> = 0> inline std::ostream& gnc_option_to_scheme (std::ostream& oss, const OptType& opt) { @@ -363,7 +368,12 @@ gnc_option_to_scheme (std::ostream& oss, const OptType& opt) return oss; } -template, GncOptionValidatedValue> || std::is_same_v, GncOptionValue>, int> = 0> +template, + GncOptionValidatedValue> || + std::is_same_v, + GncOptionValue>, int> = 0> inline std::istream& gnc_option_from_scheme (std::istream& iss, OptType& opt) { @@ -791,15 +801,18 @@ operator>> (std::istream& iss, return iss; } -template, GncOptionMultichoiceValue>, int> = 0> +template, + GncOptionMultichoiceValue>, + int> = 0> inline std::ostream& gnc_option_to_scheme(std::ostream& oss, const OptType& opt) { auto indexes{opt.get_multiple()}; - if (indexes.size() > 1) + if (indexes.m_vec.size() > 1) oss << "'("; bool first = true; - for (auto index : indexes) + for (auto index : indexes.m_vec) { if (first) first = false; @@ -812,7 +825,10 @@ gnc_option_to_scheme(std::ostream& oss, const OptType& opt) return oss; } -template, GncOptionMultichoiceValue>, int> = 0> +template, + GncOptionMultichoiceValue>, + int> = 0> inline std::istream& gnc_option_from_scheme(std::istream& iss, OptType& opt) { @@ -971,7 +987,10 @@ operator>> (std::istream& iss, return iss; } -template, GncOptionAccountValue>, int> = 0> +template, + GncOptionAccountValue>, + int> = 0> inline std::ostream& gnc_option_to_scheme(std::ostream& oss, const OptType& opt) { @@ -990,7 +1009,10 @@ gnc_option_to_scheme(std::ostream& oss, const OptType& opt) return oss; } -template, GncOptionAccountValue>, int> = 0> +template, + GncOptionAccountValue>, + int> = 0> inline std::istream& gnc_option_from_scheme(std::istream& iss, OptType& opt) { From 27670a6e98865d2b8cb6c0702b8130f6a463af06 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 11 Mar 2021 18:13:35 -0800 Subject: [PATCH 192/298] Make the 'pixel and 'percent SCM symbols C++ constants. Improves efficiency and conciseness. --- libgnucash/app-utils/gnc-optiondb.i | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index f46446c262..88094f127a 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -623,6 +623,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %} %ignore GncOptionMultichoiceKeyType; +%ignore pixels; +%ignore percent; + +%header %{ + static const SCM pixels{(scm_from_utf8_symbol("pixels"))}; + static const SCM percent{(scm_from_utf8_symbol("percent"))}; + %} %inline %{ inline SCM scm_from_multichoices (const GncMultichoiceOptionIndexVec& indexes, @@ -681,14 +688,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); SCM get_scm_value(const GncOptionRangeValue& option) { auto val{option.get_value()}; - auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")}; + auto desig{val > 100 ? pixels : percent}; return scm_cons(desig, scm_from_int(val)); } SCM get_scm_default_value(const GncOptionRangeValue& option) { auto val{option.get_default_value()}; - auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")}; + auto desig{val > 100 ? pixels : percent}; return scm_cons(desig, scm_from_int(val)); } From be322a0d7cf863696e7c355517972f056bdc5ce1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 11 Mar 2021 18:15:57 -0800 Subject: [PATCH 193/298] Group GncOptionMultChoiceValue::set_multiple with set_default_multiple. Better readability. --- libgnucash/app-utils/gnc-option-impl.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index edbf9e1918..9193a22ed6 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -668,13 +668,6 @@ public: 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_value(const std::string& value) { auto index = find_key(value); @@ -702,6 +695,13 @@ public: 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)) From 12f8df1eab092bf6326e25a69497562f2eb087bc Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 27 Apr 2021 16:08:36 -0700 Subject: [PATCH 194/298] Fix test failure: Clear the stream before reusing it. --- libgnucash/app-utils/test/gtest-gnc-option.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index e4667d7f10..d715c268ee 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -936,6 +936,7 @@ TEST_F(GncOptionAccountTest, test_account_list_from_scheme) GncOptionAccountTypeList{ACCT_TYPE_BANK}}}; GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; acc_guids = make_account_list_SCM_str(acclistbad); + iss.clear(); iss.str(acc_guids); sel_option.from_scheme(iss); EXPECT_EQ(accsel, sel_option.get_value()); From 23461f1d6c1ee029b751e893956be87abf5dc8d8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 3 May 2021 09:38:43 -0700 Subject: [PATCH 195/298] The value of a list-option is a list. Duh. --- libgnucash/app-utils/test/test-gnc-optiondb.scm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index ae01a460c9..0cf743c431 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -189,10 +189,10 @@ (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" "GLPlot") - (test-equal "GLPlot" (gnc-option-value option-db "foo" "bar")) - (test-equal "AvgBalPlot" (gnc-option-default-value option-db "foo" "bar"))) + (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) From 7239eb809ac4b807aebd05020f9b317e4a5c341b Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 3 May 2021 09:39:19 -0700 Subject: [PATCH 196/298] Test more natural way of designating relative dates. --- .../app-utils/test/test-gnc-optiondb.scm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 0cf743c431..38215ce20a 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -212,15 +212,15 @@ (let* ((option-db (new-gnc-optiondb)) (date-opt (gnc-register-date-option-set option-db "foo" "bar" "baz" "Phony Option" - (list (RelativeDatePeriod-TODAY) - (RelativeDatePeriod-START-THIS-MONTH) - (RelativeDatePeriod-START-PREV-MONTH) - (RelativeDatePeriod-START-CURRENT-QUARTER) - (RelativeDatePeriod-START-PREV-QUARTER) - (RelativeDatePeriod-START-CAL-YEAR) - (RelativeDatePeriod-START-CAL-YEAR) - (RelativeDatePeriod-START-PREV-YEAR) - (RelativeDatePeriod-START-ACCOUNTING-PERIOD)) #t))) + `(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 (gnc-accounting-period-fiscal-start) (gnc-option-value option-db "foo" "bar"))) (test-end "test-gnc-test-date-set-option")) From 52ef53a40523587d8621ed18c5c8175a42a61a98 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 3 May 2021 09:46:57 -0700 Subject: [PATCH 197/298] Implement multichoice selection options. --- libgnucash/app-utils/gnc-option-impl.hpp | 7 +- libgnucash/app-utils/gnc-optiondb.i | 119 +++++++++++++++-------- 2 files changed, 80 insertions(+), 46 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 9193a22ed6..bcd0cc3654 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -283,7 +283,8 @@ template>(std::istream& iss, OptType& opt) { std::decay_t value; - if constexpr (std::is_same_v, SCM>) + if constexpr (std::is_same_v, SCM> || + std::is_same_v, const _QofQuery*>) return iss; else { @@ -809,10 +810,10 @@ inline std::ostream& gnc_option_to_scheme(std::ostream& oss, const OptType& opt) { auto indexes{opt.get_multiple()}; - if (indexes.m_vec.size() > 1) + if (indexes.size() > 1) oss << "'("; bool first = true; - for (auto index : indexes.m_vec) + for (auto index : indexes) { if (first) first = false; diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 88094f127a..95c1456966 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -376,6 +376,21 @@ gnc_option_test_book_destroy(QofBook* book) $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; @@ -618,22 +633,54 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); 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; -%ignore pixels; -%ignore percent; - -%header %{ - static const SCM pixels{(scm_from_utf8_symbol("pixels"))}; - static const SCM percent{(scm_from_utf8_symbol("percent"))}; - %} %inline %{ - inline SCM scm_from_multichoices (const GncMultichoiceOptionIndexVec& indexes, - const GncOptionMultichoiceValue& option) + 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 { @@ -651,20 +698,22 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return SCM_BOOL_F; }; - if (indexes.size() == 1) // FIXME: Should use bool member to decide + if (option.get_ui_type() == GncOptionUIType::MULTICHOICE) return scm_value(option.permissible_value(indexes[0]), option.get_keytype(indexes[0])); - auto values{scm_list_1(SCM_UNDEFINED)}; + auto values{SCM_BOOL_F}; for(auto index : indexes) { auto val{scm_list_1(scm_value(option.permissible_value(index), option.get_keytype(index)))}; - values = scm_append(scm_list_2(val, values)); + if (scm_is_true(values)) + values = scm_append(scm_list_2(val, values)); + else + values = val; } return scm_reverse(values); } - SCM get_scm_value(const GncOptionMultichoiceValue& option) { @@ -688,14 +737,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); SCM get_scm_value(const GncOptionRangeValue& option) { auto val{option.get_value()}; - auto desig{val > 100 ? pixels : percent}; + auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")}; return scm_cons(desig, scm_from_int(val)); } SCM get_scm_default_value(const GncOptionRangeValue& option) { auto val{option.get_default_value()}; - auto desig{val > 100 ? pixels : percent}; + auto desig{scm_c_eval_string(val > 100 ? "'pixels" : "'percent")}; return scm_cons(desig, scm_from_int(val)); } @@ -764,23 +813,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_value(scm_relative_date_get_period(new_value)); return; } + if constexpr (std::is_same_v, GncOptionMultichoiceValue>) { - if (scm_is_integer(new_value)) - { - option.set_value(scm_to_int(new_value)); - return; - } - std::string new_value_str{}; - if (scm_is_symbol(new_value)) - new_value_str = scm_to_utf8_string(scm_symbol_to_string(new_value)); - else if (scm_is_string(new_value)) - new_value_str = scm_to_utf8_string(new_value); - if (!new_value_str.empty()) - option.set_value(new_value_str); + option.set_multiple(scm_to_multichoices(new_value, option)); return; } + if constexpr (std::is_same_v, GncOptionRangeValue>) { @@ -790,6 +830,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_value(scm_to_int(new_value)); return; } + auto value{scm_to_value>(new_value)}; //Can't inline, set_value takes arg by reference. option.set_value(value); }, swig_get_option($self)); @@ -812,18 +853,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); if constexpr (std::is_same_v, GncOptionMultichoiceValue>) { - if (scm_is_integer(new_value)) - { - option.set_default_value(scm_to_int(new_value)); - return; - } - std::string new_value_str{}; - if (scm_is_symbol(new_value)) - new_value_str = scm_to_utf8_string(scm_symbol_to_string(new_value)); - else if (scm_is_string(new_value)) - new_value_str = scm_to_utf8_string(new_value); - if (!new_value_str.empty()) - option.set_default_value(new_value_str); + option.set_default_multiple(scm_to_multichoices(new_value, + option)); return; } if constexpr (std::is_same_v, @@ -984,7 +1015,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); 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)}}; + doc_string, defval.c_str(), std::move(choices), + GncOptionUIType::MULTICHOICE}}; } catch (const std::exception& err) { @@ -995,12 +1027,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); GncOption* gnc_make_list_option(const char* section, const char* name, const char* key, - const char* doc_string, const char* value, + const char* doc_string, + GncMultichoiceOptionIndexVec indexes, GncMultichoiceOptionChoices&& list) { try { return new GncOption{GncOptionMultichoiceValue{section, name, key, - doc_string, value, std::move(list), + doc_string, std::move(indexes), std::move(list), GncOptionUIType::LIST}}; } catch (const std::exception& err) From ce5fe577bf1b13ad55779109d0766b5de496fd86 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 15 May 2021 10:22:02 -0700 Subject: [PATCH 198/298] Fix failure to pass accounts list to gnc:make-account-list-option The Scheme way of generating options is somehow able to obtain the account-list even when running the generator before creating the account tree. The C++ options object doesn't have that ability so one must make sure to run the account creation first in a let* statement. --- .../report/reports/standard/test/test-equity-statement.scm | 4 ++-- gnucash/report/reports/standard/test/test-portfolios.scm | 4 ++-- gnucash/report/reports/standard/test/test-register.scm | 4 ++-- gnucash/report/reports/standard/test/test-trial-balance.scm | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gnucash/report/reports/standard/test/test-equity-statement.scm b/gnucash/report/reports/standard/test/test-equity-statement.scm index 90aff7ef3b..835af33938 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 12d6d1d77b..fdbda3ea4b 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-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")) From 7c6ecafd610ebe263131e538f7eabc06eacffdbd Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 11 Jul 2021 15:03:13 -0700 Subject: [PATCH 199/298] Rewrite options.scm to wrap options.hpp functions where needed. --- libgnucash/app-utils/options.scm | 2187 +++++------------------------- 1 file changed, 324 insertions(+), 1863 deletions(-) diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 2188c4eaa0..4acf907baa 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -25,1831 +25,89 @@ (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) -(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:value->string value) + (format #f "~s" value)) -(define gnc:*option-section-accounts* OPTION-SECTION-ACCOUNTS) -(define gnc:*option-name-trading-accounts* OPTION-NAME-TRADING-ACCOUNTS) +(define-public (gnc:lookup-option options section name) + (if options + (gnc-lookup-option options section name) + #f)) -(define (gnc:option-get-value book category key) +(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-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-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) + (format #f "#~a" (GncOption-get-scm-value opt))) + +(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 (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 (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)) - -(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)) - -(define (gnc:option-value option) - (let ((getter (gnc:option-getter option))) - (getter))) - -(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) - - (let* ((initial-budget (gnc-budget-get-default (gnc-get-current-book))) - (selection-budget initial-budget) - ) - - (gnc:make-option - section - name - sort-tag - 'budget - documentation-string - - ;; getter -- Return a budget pointer - (lambda () - selection-budget) - - ;; setter -- takes a budget - (lambda (x) - (set! selection-budget 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 (gncBudgetGetGUID selection-budget)) - " (gnc-get-current-book)))))")) - - ;; scm->kvp -- commit the change - ;; b -- book; p -- key-path - (lambda (b p) - (qof-book-set-option - b (gncBudgetGetGUID 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)) - (begin - (set! selection-budget (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))) - (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)) - -;; 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") - ;; 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))) - (if (not (equal? value default-value)) - (let ((save-fcn (gnc:option-scm->kvp option))) - (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:option-make-internal! options section name) + (let ((option (gnc-lookup-option options section name))) + (and option (GncOption-make-internal option)))) + +(define-public (gnc:option-type option) + (GncOption-get-type option)) + +;; Used only by test-stress-options.scm +(define-public (gnc:option-data option) + (let ((num-values (GncOption-num-permissible-values option)) + (retval '())) + (do ((i 0 (1+ i))) ((>= i num-values)) + (let ((value (GncOption-permissible-value option i)) + (name (GncOption-permissible-value-name option i)) + (desc (GncOption-permissible-value-description option i))) + (set! retval (cons retval (vector value name desc))))) + retval)) + +(define-public (gnc:new-options) + (new-gnc-optiondb)) + +(define-public (gnc:options-set-default-section optiondb section) + (GncOptionDB-set-default-section (GncOptionDBPtr-get optiondb) section)) + +(define-public (gnc:options-for-each func optdb) + (gnc-optiondb-foreach optdb 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 @@ -1862,58 +120,261 @@ 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)) +;; FIXME: Fake callback functions for boolean-complex and multichoice-callback -(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:options-register-callback section name callback options) 1) +(define-public (gnc:options-register-c-callback section name callback data options) 1) +(define-public (gnc:options-unregister-callback-id id) 0 options) -(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)) +;; 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) + (GncOption-get-section opt) opt)) -(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-public (gnc:unregister-option optdb section name) + (GncOptionDB-unregister-option (GncOptionDBPtr-get optdb) 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 + (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.") + (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-BUDGET))) +(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.") + (gnc-make-account-sel-limited-option section name key docstring (default) permitted)) +(define-public (gnc:make-account-sel-option section name key docstring default validator) + (let ((defval (if default (default) #f))) + (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-SCM-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")) + (gnc-make-SCM-option section name key desc default type))) +(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)))) + (defval (if getter (getter) #f))) + (format #t "Making owner option ~a:~a ~a ~a~%" section name defval ui-type)(force-output) + (gnc-make-qofinstance-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.") + (gnc-make-qofinstance-option section name key docstring #f (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 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 pagename name-from + (string-append sort-tag "a") info-from) + (gnc-register-end-date-option optiondb 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 +;; +;; 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. + +(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))) From 5a2ba091a3ef5f2f88ca20470232a0f1bba915bf Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 12 Jul 2021 13:09:25 -0700 Subject: [PATCH 200/298] Fix compilation error from latest rebase. --- gnucash/gnome-utils/gnc-main-window.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index 454f74fabe..df59ee33f8 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -1788,9 +1788,6 @@ static gchar *generate_statusbar_lastmodified_message() g_free(filepath); g_object_unref (info); g_object_unref (file); -#else - return nullptr; -#endif } // If the URI is not a file but a database, we can maybe also show // something useful, but I have no idea how to obtain this information. From bbe74aa0864aa1ef896372f410a04a96c8f73d6f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 12 Jul 2021 13:34:43 -0700 Subject: [PATCH 201/298] Remove tooltips from multichoice option values. This is the c++options equivalent to 02a6a0ae and e1525721. --- gnucash/gnome-utils/dialog-options.cpp | 33 +++++++------------ libgnucash/app-utils/gnc-option-impl.hpp | 16 ++------- libgnucash/app-utils/gnc-option.cpp | 14 -------- libgnucash/app-utils/gnc-option.hpp | 1 - libgnucash/app-utils/gnc-optiondb.hpp | 1 - libgnucash/app-utils/gnc-optiondb.i | 3 +- libgnucash/app-utils/options.scm | 5 ++- .../app-utils/test/gtest-gnc-option.cpp | 20 +++++------ .../app-utils/test/gtest-gnc-optiondb.cpp | 8 ++--- 9 files changed, 30 insertions(+), 71 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 87c2f2b714..7e2720679b 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -46,7 +46,6 @@ extern "C" #include "glib-guile.h" #include "gnc-account-sel.h" #include "gnc-tree-view-account.h" -#include "gnc-combott.h" #include "gnc-commodity-edit.h" #include "gnc-component-manager.h" #include "gnc-general-select.h" @@ -1180,15 +1179,12 @@ create_multichoice_widget(GncOption& option) { GtkTreeIter iter; auto itemstring = option.permissible_value_name(i); - auto description = option.permissible_value_description(i); gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, 0, - (itemstring && *itemstring) ? _(itemstring) : "", 1, - (description && *description) ? _(description) : "", -1); + 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(gnc_combott_new()); - g_object_set( G_OBJECT( widget ), "model", GTK_TREE_MODEL(store), NULL ); + auto widget{GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)))}; g_object_unref(store); g_signal_connect(G_OBJECT(widget), "changed", @@ -1204,13 +1200,13 @@ public: GncOptionGtkUIItem{widget, GncOptionUIType::MULTICHOICE} {} void set_ui_item_from_option(GncOption& option) noexcept override { - auto widget{GNC_COMBOTT(get_widget())}; - gnc_combott_set_active(widget, option.get_value()); + 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{GNC_COMBOTT(get_widget())}; - option.set_value(gnc_combott_get_active(widget)); + auto widget{GTK_COMBO_BOX(get_widget())}; + option.set_value(gtk_combo_box_get_active(widget)); } }; @@ -1300,9 +1296,7 @@ private: }; -RelativeDateEntry::RelativeDateEntry(GncOption& option) : - m_entry{GTK_WIDGET(gnc_combott_new())} - +RelativeDateEntry::RelativeDateEntry(GncOption& option) { /* GtkComboBox still does not support per-item tooltips, so have @@ -1319,12 +1313,11 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) : GtkTreeIter iter; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, - option.permissible_value_name(index), 1, - option.permissible_value_description(index), -1); + option.permissible_value_name(index), 1); } /* Create the new Combo with tooltip and add the store */ - g_object_set( G_OBJECT(m_entry), "model", GTK_TREE_MODEL(store), nullptr); + m_entry = GTK_WIDGET(gtk_combo_box_new_with_model(GTK_TREE_MODEL(store))); g_object_unref(store); g_signal_connect(G_OBJECT(m_entry), "changed", @@ -1334,13 +1327,13 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) : void RelativeDateEntry::set_entry_from_option(GncOption& option) { - gnc_combott_set_active(GNC_COMBOTT(m_entry), option.get_value()); + gtk_combo_box_set_active(GTK_COMBO_BOX(m_entry), option.get_value()); } void RelativeDateEntry::set_option_from_entry(GncOption& option) { - option.set_value(gnc_combott_get_active(GNC_COMBOTT(m_entry))); + option.set_value(gtk_combo_box_get_active(GTK_COMBO_BOX(m_entry))); } using AbsoluteDateEntryPtr = std::unique_ptr; @@ -2465,7 +2458,6 @@ create_radiobutton_widget(char *name, GncOption& option) for (decltype(num_values) i = 0; i < num_values; i++) { auto label = option.permissible_value_name(i); - auto tip = option.permissible_value_description(i); widget = gtk_radio_button_new_with_label_from_widget (widget ? @@ -2474,7 +2466,6 @@ create_radiobutton_widget(char *name, GncOption& option) label && *label ? _(label) : ""); g_object_set_data (G_OBJECT (widget), "gnc_radiobutton_index", GINT_TO_POINTER (i)); - gtk_widget_set_tooltip_text(widget, tip && *tip ? _(tip) : ""); g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(radiobutton_set_cb), &option); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0); diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index bcd0cc3654..38c9c0b675 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -526,7 +526,6 @@ operator>> (std::istream& iss, OptType& opt) } using GncMultichoiceOptionEntry = std::tuple; using GncMultichoiceOptionIndexVec = std::vector; @@ -535,9 +534,8 @@ using GncMultichoiceOptionChoices = std::vector; /** 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. The - * tuple contains three strings, a key, a display - * name and a brief description for the tooltip. Both name and description - * should be localized at the point of use. + * tuple contains three strings, a key, and a display + * name, which * should be localized at the point of use. * * */ @@ -726,15 +724,11 @@ public: { return std::get<1>(m_choices.at(index)).c_str(); } - const char* permissible_value_description(std::size_t index) const - { - return std::get<2>(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<3>(m_choices.at(i)); } + GncOptionMultichoiceKeyType get_keytype(unsigned i) const { return std::get<2>(m_choices.at(i)); } private: std::size_t find_key (const std::string& key) const noexcept { @@ -1144,10 +1138,6 @@ public: { return gnc_relative_date_display_string(m_period_set.at(index)); } - const char* permissible_value_description(std::size_t index) const - { - return gnc_relative_date_description(m_period_set.at(index)); - } void reset_default_value() { m_period = m_default_period; m_date = m_default_date; diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 6f708681bf..4ab4a1ddb4 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -361,20 +361,6 @@ GncOption::permissible_value_name(std::size_t index) const }, *m_option); } -const char* -GncOption::permissible_value_description(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_description(index); - else - return ""; - }, *m_option); -} - GList* GncOption::account_type_list() const noexcept { diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 5edbc9404c..dfc875befb 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -117,7 +117,6 @@ public: std::size_t permissible_value_index(const char* value) const; const char* permissible_value(std::size_t index) const; const char* permissible_value_name(std::size_t index) const; - const char* permissible_value_description(std::size_t index) const; GList* account_type_list() const noexcept; bool is_alternate() const noexcept; void set_alternate(bool) noexcept; diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index a5b6c427b9..92e04cf5e5 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -49,7 +49,6 @@ using GncOptionAccountList = std::vector; using GncOptionAccountTypeList = std::vector; using GncMultichoiceOptionEntry = std::tuple; using GncMultichoiceOptionChoices = std::vector; diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 95c1456966..52be11160e 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -419,8 +419,7 @@ gnc_option_test_book_destroy(QofBook* book) 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))}; - std::string desc{scm_to_utf8_string(SCM_SIMPLE_VECTOR_REF(vec, 2))}; - choices.push_back({std::move(key), std::move(name), std::move(desc), keytype}); + choices.push_back({std::move(key), std::move(name), keytype}); } $1 = &choices; } diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 4acf907baa..849a06e473 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -90,9 +90,8 @@ (retval '())) (do ((i 0 (1+ i))) ((>= i num-values)) (let ((value (GncOption-permissible-value option i)) - (name (GncOption-permissible-value-name option i)) - (desc (GncOption-permissible-value-description option i))) - (set! retval (cons retval (vector value name desc))))) + (name (GncOption-permissible-value-name option i))) + (set! retval (cons retval (vector value name))))) retval)) (define-public (gnc:new-options) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index d715c268ee..379617e13b 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -959,10 +959,10 @@ protected: {"foo", "bar", "baz", "Phony Option", "plugh", { - {"plugh", "xyzzy", "thud", KT::STRING}, - {"waldo", "pepper", "salt", KT::STRING}, - {"pork", "sausage", "links", KT::STRING}, - {"corge", "grault", "garply", KT::STRING} + {"plugh", "xyzzy", KT::STRING}, + {"waldo", "pepper", KT::STRING}, + {"pork", "sausage", KT::STRING}, + {"corge", "grault", KT::STRING} }}} {} GncOption m_option; }; @@ -1003,15 +1003,11 @@ TEST_F(GncMultichoiceOption, test_permissible_value_stuff) 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_STREQ("thud", - m_option.permissible_value_description(0)); }); 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_THROW({ auto result = m_option.permissible_value_description(4); }, - std::out_of_range); EXPECT_EQ(std::numeric_limits::max(), m_option.permissible_value_index("xyzzy")); } @@ -1059,10 +1055,10 @@ protected: "foo", "bar", "baz", "Phony Option", GncMultichoiceOptionIndexVec{0, 2}, { - {"plugh", "xyzzy", "thud", KT::STRING}, - {"waldo", "pepper", "salt", KT::STRING}, - {"pork", "sausage", "links", KT::STRING}, - {"corge", "grault", "garply", KT::STRING} + {"plugh", "xyzzy", KT::STRING}, + {"waldo", "pepper", KT::STRING}, + {"pork", "sausage", KT::STRING}, + {"corge", "grault", KT::STRING} }}} {} GncOption m_option; }; diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index bae79b04ad..ce0017646d 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -178,10 +178,10 @@ using KT = GncOptionMultichoiceKeyType; TEST_F(GncOptionDBTest, test_register_multichoice_option) { GncMultichoiceOptionChoices choices{ - { "plugh", "xyzzy", "thud", KT::STRING}, - { "waldo", "pepper", "salt", KT::STRING}, - { "pork", "sausage", "links", KT::STRING}, - { "corge", "grault", "garply", KT::STRING}}; + { "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)); From 7885353fe31665013f23294d2a7fd1079d47a9c5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 13 Jul 2021 09:46:07 -0700 Subject: [PATCH 202/298] Bring option aliases up to date. --- libgnucash/app-utils/gnc-optiondb.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 3bbd4e405e..8f8688fb8d 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -76,13 +76,13 @@ const OptionAliases Aliases::c_option_aliases {"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 + // 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: + // transaction.scm: {"Use Full Account Name?", {nullptr, "Use Full Account Name"}}, {"Use Full Other Account Name?", {nullptr, "Use Full Other Account Name"}}, @@ -90,10 +90,20 @@ const OptionAliases Aliases::c_option_aliases {"Void Transactions", {"Filter", "Void Transactions"}}, {"Account Substring", {"Filter", "Account Name Filter"}}, {"Enable links", {nullptr, "Enable Links"}}, - // invoice.scm, renamed November 2018 + // 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"}}, - // income-gst-statement.scm - {"default format", {nullptr, "Default Format"}} + // income-gst-statement.scm + {"default format", {nullptr, "Default Format"}}, + {"Report format", {nullptr, "Report Format"}}, }; static bool From 29a2365fdf17161f05c5cb2012b8f9ef450e6e06 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 13 Jul 2021 09:46:48 -0700 Subject: [PATCH 203/298] Avoid infinite recursion when the alias changes only the section name. --- libgnucash/app-utils/gnc-optiondb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 8f8688fb8d..abb80efed1 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -227,7 +227,7 @@ GncOptionDB::find_option(const std::string& section, const char* name) const * nullptr. GncOptionSection::find_option already checked if the alias * should have been in the same section. */ - if (alias && alias->first) + if (alias && alias->first && section != alias->first) return find_option(alias->first, alias->second); return nullptr; } From 6eb5dfed513e323d5781dba4039af364104fdcc1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 13 Jul 2021 09:47:58 -0700 Subject: [PATCH 204/298] make-internal fail if there is a ui_item, not if there isn't. --- libgnucash/app-utils/gnc-option.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 4ab4a1ddb4..8aa240622b 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -254,7 +254,7 @@ GncOption::set_option_from_ui_item() void GncOption::make_internal() { - if (!m_ui_item) + if (m_ui_item) { PERR("Option %s:%s has a UI Element, can't be INTERNAL.", get_section().c_str(), get_name().c_str()); From 3f89d063ebbac5d4dbc51feed65ebdce7de52c04 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 13 Jul 2021 09:49:27 -0700 Subject: [PATCH 205/298] Evaluate default function in gnc:make-account-sel-limited-option. --- libgnucash/app-utils/options.scm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 849a06e473..b70a099fb2 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -173,7 +173,8 @@ (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.") - (gnc-make-account-sel-limited-option section name key docstring (default) permitted)) + (let ((defval (if default (default) #f))) + (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) #f))) (gnc-make-account-sel-limited-option section name key docstring defval '()))) From c34986dad4dd2d07db1545585291bdb28ab331c4 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 13 Jul 2021 12:43:27 -0700 Subject: [PATCH 206/298] Change gnc:new-options to return a fake dispatch function. To placate test-report.scm --- libgnucash/app-utils/options.scm | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index b70a099fb2..0faa0a0659 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -37,7 +37,7 @@ (define-public (gnc:lookup-option options section name) (if options - (gnc-lookup-option options section name) + (gnc-lookup-option (options 'lookup) section name) #f)) (define-public (gnc:option-setter option) @@ -78,7 +78,7 @@ (qof-book-get-option book (acc category key))) (define-public (gnc:option-make-internal! options section name) - (let ((option (gnc-lookup-option options section name))) + (let ((option (gnc-lookup-option (options 'lookup) section name))) (and option (GncOption-make-internal option)))) (define-public (gnc:option-type option) @@ -94,14 +94,18 @@ (set! retval (cons retval (vector value name))))) retval)) +;; Create the database and return a dispatch function. (define-public (gnc:new-options) - (new-gnc-optiondb)) + (let ((optiondb (new-gnc-optiondb))) + (define (dispatch key) + optiondb) + dispatch)) (define-public (gnc:options-set-default-section optiondb section) - (GncOptionDB-set-default-section (GncOptionDBPtr-get optiondb) section)) + (GncOptionDB-set-default-section (GncOptionDBPtr-get (optiondb 'set-default-section)) section)) (define-public (gnc:options-for-each func optdb) - (gnc-optiondb-foreach optdb func)) + (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 @@ -121,18 +125,18 @@ ;; FIXME: Fake callback functions for boolean-complex and multichoice-callback -(define-public (gnc:options-register-callback section name callback options) 1) -(define-public (gnc:options-register-c-callback section name callback data options) 1) -(define-public (gnc:options-unregister-callback-id id) 0 options) +(define-public (gnc:options-register-callback section name callback options) (options 'register-callback) 1) +(define-public (gnc:options-register-c-callback section name callback data options) (options 'register-c-callback) 1) +(define-public (gnc:options-unregister-callback-id id) 0 (options 'unregister-callback-id)) ;; 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) + (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) 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.") @@ -256,12 +260,12 @@ (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 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 pagename name-from + (gnc-register-start-date-option (optiondb 'make-option) pagename name-from (string-append sort-tag "a") info-from) - (gnc-register-end-date-option optiondb pagename name-to + (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) From a1af86ed403d97c8f932c94c37537a43bcf87fb0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 13 Jul 2021 12:44:13 -0700 Subject: [PATCH 207/298] Implement gnc:generate-restore-forms, gnc-optiondb-save-to-scheme. --- libgnucash/app-utils/gnc-optiondb.i | 16 +++++++++++++++- libgnucash/app-utils/options.scm | 4 ++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 52be11160e..b22fd050c4 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -514,6 +514,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); #include #include #include +#include #include "gnc-option.hpp" #include "gnc-option-ui.hpp" @@ -530,7 +531,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); * overloads. The latter isn't useful to SWIG, ignore it. */ %ignore GncOptionDB::register_option(const char*, GncOption&&); - +/* GncOptionDB::save_to_scheme takes and returns a std::stream. Good luck + * converting *that* to anything useful! + */ +%ignore GncOptionDB::save_to_scheme(std::ostream&, const char*); +%ignore GncOptionDB::save_option_scheme(std::ostream&, const char*, const std::string&, const std::string&); +%ignore GncOptionDB::load_option_scheme(std::itream&); /* 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. @@ -1232,6 +1238,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); }); }); } + + std::string + gnc_optiondb_save_to_scheme(GncOptionDBPtr& odb, const char* prolog) + { + std::ostringstream oss; + odb->save_to_scheme(oss, prolog); + return oss.str(); + } %} #endif //SWIGGUILE diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 0faa0a0659..4e26f0dc57 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -35,6 +35,10 @@ (define-public (gnc:value->string value) (format #f "~s" value)) +(define-public (gnc:generate-restore-forms options name) + (let ((optiondb (options 'generate-restore-forms))) + (gnc-optiondb-save-to-scheme optiondb name))) + (define-public (gnc:lookup-option options section name) (if options (gnc-lookup-option (options 'lookup) section name) From 67dab6b320f8c93665b97bda9f9d00c5002c26bc Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 18 Jul 2021 14:13:29 -0700 Subject: [PATCH 208/298] Remove business-options.scm and business-prefs.scm. Their functions have been reimplemented elsewhere. --- libgnucash/app-utils/CMakeLists.txt | 31 +- libgnucash/app-utils/app-utils.scm | 1 - libgnucash/app-utils/business-options.scm | 386 ---------------------- libgnucash/app-utils/business-prefs.scm | 199 ----------- libgnucash/app-utils/gnc-optiondb.i | 44 ++- po/POTFILES.in | 2 - 6 files changed, 32 insertions(+), 631 deletions(-) delete mode 100644 libgnucash/app-utils/business-options.scm delete mode 100644 libgnucash/app-utils/business-prefs.scm diff --git a/libgnucash/app-utils/CMakeLists.txt b/libgnucash/app-utils/CMakeLists.txt index 573c93db4c..68379509be 100644 --- a/libgnucash/app-utils/CMakeLists.txt +++ b/libgnucash/app-utils/CMakeLists.txt @@ -42,10 +42,11 @@ set (app_utils_HEADERS # Command to generate the swig-app-utils-guile.c wrapper file set(SWIG_ARGS "-c++" "-procdoc" "sw-gnc-option-doc" "-procdocformat" "plain") -gnc_add_swig_guile_command (swig-apputils-guile-cpp - SWIG_APP_UTILS_GUILE_CPP swig-app-utils-guile.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/app-utils.i - ${CMAKE_CURRENT_SOURCE_DIR}/gnc-optiondb.i "" +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) @@ -188,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 ) @@ -217,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.scm b/libgnucash/app-utils/app-utils.scm index d0ed9635fc..be5c5d9db7 100644 --- a/libgnucash/app-utils/app-utils.scm +++ b/libgnucash/app-utils/app-utils.scm @@ -28,7 +28,6 @@ (load-and-reexport (sw_app_utils) (gnucash app-utils date-utilities) - (gnucash app-utils business-options) (gnucash app-utils options) (gnucash app-utils c-interface)) ;; gw-engine-spec.scm 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 84fc701fd1..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-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index b22fd050c4..9fbfa90834 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -41,7 +41,6 @@ namespace std { %typemap(in) std::size_t "$1 = scm_to_ulong($input);"; %typemap(out) std::size_t "$result = scm_from_ulong($1);"; - //%module sw_gnc_optiondb %{ #include "gnc-optiondb.h" #include "gnc-optiondb.hpp" @@ -53,6 +52,13 @@ SCM scm_init_sw_gnc_optiondb_module(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") /* Implementation Note: Plain overloads failed to compile because * auto value{option.get_value()}; @@ -133,18 +139,6 @@ scm_from_value(SCM value) return value; } -template <> inline SCM -scm_from_value(QofQuery* value) -{ - return SCM_BOOL_F; -} - -template <> inline SCM -scm_from_value(QofInstance* value) -{ - return SCM_BOOL_F; -} - template <> inline SCM scm_from_value(std::string value) { @@ -188,11 +182,11 @@ scm_from_value(const QofInstance* value) else if (GNC_IS_ACCOUNT(value)) type = SWIGTYPE_p_Account; else if (GNC_IS_BUDGET(value)) - type = SWIGTYPE_p_GncBudget; + type = SWIGTYPE_p_budget_s; else if (GNC_IS_INVOICE(value)) - type = SWIGTYPE_p_GncInvoice; + type = SWIGTYPE_p__gncInvoice; else if (GNC_IS_TAXTABLE(value)) - type = SWIGTYPE_p_GncTaxTable; + type = SWIGTYPE_p__gncTaxTable; else if (GNC_IS_CUSTOMER(value)) type = SWIGTYPE_p__gncCustomer; else if (GNC_IS_EMPLOYEE(value)) @@ -215,6 +209,18 @@ scm_from_value(const QofQuery* value) return SWIG_NewPointerObj(ptr, SWIGTYPE_p__QofQuery, FALSE); } +template <> inline SCM +scm_from_value(QofQuery* value) +{ + return scm_from_value(value); +} + +template <> inline SCM +scm_from_value(QofInstance* value) +{ + return scm_from_value(value); +} + template inline ValueType scm_to_value(SCM new_value) { @@ -261,11 +267,13 @@ 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_GncBudget, SWIGTYPE_p_GncInvoice, - SWIGTYPE_p_GncTaxTable, SWIGTYPE_p_Account, + 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 }; diff --git a/po/POTFILES.in b/po/POTFILES.in index 75b34e8e68..cdd4db4e49 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -509,8 +509,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.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 From c034a26ae75f25e9fc78e764a250da6d5c5c69e4 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 19 Jul 2021 10:40:48 -0700 Subject: [PATCH 209/298] Protect stored scheme option values from the garbage collector. --- libgnucash/app-utils/gnc-option-impl.cpp | 102 +++++++++++++++++++++++ libgnucash/app-utils/gnc-option-impl.hpp | 39 +++++++-- 2 files changed, 134 insertions(+), 7 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index b12ca23aa8..0f0c566645 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -268,3 +268,105 @@ qof_instance_to_string(const QofInstance* inst) gnc::GUID guid{*qof_instance_get_guid(inst)}; return guid.to_string(); } + +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; +} + +template <> void +GncOptionValue::set_value(SCM new_value) +{ + if (m_value) + scm_gc_unprotect_object(m_value); + m_value = new_value; + scm_gc_protect_object(m_value); +} + +template <> void +GncOptionValue::set_default_value(SCM new_value) +{ + if (m_value) + scm_gc_unprotect_object(m_value); + if (m_default_value) + scm_gc_unprotect_object(m_default_value); + m_value = m_default_value = new_value; + scm_gc_protect_object(m_value); + scm_gc_protect_object(m_default_value); +} + +template <> void +GncOptionValue::reset_default_value() +{ + if (m_value) + scm_gc_unprotect_object(m_value); + m_value = m_default_value; + scm_gc_protect_object(m_value); +} + +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 QofInstance*); +template void GncOptionValue::set_value(const QofQuery*); +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_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 QofInstance*); +template void GncOptionValue::set_default_value(const QofQuery*); +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::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(); diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 38c9c0b675..20d33f4c62 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -120,18 +120,43 @@ public: 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&) = default; + m_ui_type(ui_type), m_value{value}, m_default_value{value} { + if constexpr(std::is_same_v, SCM>) { + if (value) { + scm_gc_protect_object(m_value); + scm_gc_protect_object(m_default_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()} + { + if constexpr(std::is_same_v, SCM>) { + if (m_value) + scm_gc_protect_object(m_value); + if (m_default_value) + scm_gc_protect_object(m_default_value); + } + } GncOptionValue(GncOptionValue&&) = default; GncOptionValue& operator=(const GncOptionValue&) = default; GncOptionValue& operator=(GncOptionValue&&) = default; + ~GncOptionValue() { + if constexpr(std::is_same_v, SCM>) { + if (m_value) + scm_gc_unprotect_object(m_value); + if (m_default_value) + scm_gc_unprotect_object(m_default_value); + } + } ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } - void set_value(ValueType new_value) { m_value = new_value; } - void set_default_value(ValueType new_value) { - m_value = m_default_value = new_value; - } - void reset_default_value() { m_value = 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; } From f5d8d508eec31bf1fe900a967551d36edd383953 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 19 Jul 2021 10:42:24 -0700 Subject: [PATCH 210/298] Create the GncOptionVariant's GncOptionValue in place. Saves a temporary, prevents premature unprotecting of Scheme objects. --- libgnucash/app-utils/gnc-option.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 8aa240622b..1153e0c943 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -40,8 +40,9 @@ template (GncOptionValue { - section, name, key, doc_string, value, ui_type})} + m_option{std::make_unique( + std::in_place_type>, + section, name, key, doc_string, value, ui_type)} { } From 9d150b1ae70d2ad51f22054b7a681a4d67adc3e1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 19 Jul 2021 11:30:13 -0700 Subject: [PATCH 211/298] Always set budget option to default budget. --- libgnucash/app-utils/options.scm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 4e26f0dc57..97905c5db7 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -159,7 +159,10 @@ (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.") - (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-BUDGET))) + (let ((option (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-BUDGET)))) + (gnc:option-set-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)) From 05ac05644976aa8ada12961d93ed8874da39fbe2 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 30 Jul 2021 13:19:17 -0700 Subject: [PATCH 212/298] Make separate option type for GncOwner. GncOwner is not a QofInstance subclass. --- libgnucash/app-utils/gnc-option-impl.cpp | 4 ++ libgnucash/app-utils/gnc-option-impl.hpp | 15 +++++++ libgnucash/app-utils/gnc-option.cpp | 4 ++ libgnucash/app-utils/gnc-option.hpp | 5 +++ libgnucash/app-utils/gnc-optiondb.cpp | 14 ++++++- libgnucash/app-utils/gnc-optiondb.hpp | 29 +++++++++++++- libgnucash/app-utils/gnc-optiondb.i | 50 +++++++++++++++++------- libgnucash/app-utils/options.scm | 15 +++++-- 8 files changed, 114 insertions(+), 22 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 0f0c566645..ea4e52e23b 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -326,6 +326,7 @@ template GncOptionValue::GncOptionValue(const 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&); @@ -340,6 +341,7 @@ template void GncOptionValue::set_value(const char*); template void GncOptionValue::set_value(std::string); template void GncOptionValue::set_value(const QofInstance*); 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); @@ -353,6 +355,7 @@ template void GncOptionValue::set_default_value(const char*); template void GncOptionValue::set_default_value(std::string); template void GncOptionValue::set_default_value(const QofInstance*); 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); @@ -366,6 +369,7 @@ 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(); diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 20d33f4c62..485b1fcce2 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -309,6 +309,7 @@ std::istream& operator>>(std::istream& iss, OptType& opt) { std::decay_t value; if constexpr (std::is_same_v, SCM> || + std::is_same_v, const _gncOwner*> || std::is_same_v, const _QofQuery*>) return iss; else @@ -1198,6 +1199,20 @@ operator>> (std::istream& iss, /** QofQuery Options */ +inline std::istream& +gnc_option_from_scheme(std::istream& iss, GncOptionValue& opt) +{ +//FIXME: Implement or maybe rethink. + return iss; +} + +inline std::ostream& +gnc_option_to_scheme(std::ostream& oss, GncOptionValue& opt) +{ +//FIXME: Implement or maybe rethink. + return oss; +} + inline std::istream& gnc_option_from_scheme(std::istream& iss, GncOptionValue& opt) { diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 1153e0c943..812a2c1b7c 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -468,6 +468,8 @@ GncOption::from_scheme(std::istream& iss) (std::is_same_v, GncOptionMultichoiceValue>) || std::is_same_v, + GncOptionValue> || + std::is_same_v, GncOptionValue> || std::is_same_v, GncOptionValue> || @@ -564,6 +566,8 @@ template GncOption::GncOption(const char*, const char*, const char*, const char*, SCM, 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; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index dfc875befb..66e2ee25fd 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -40,6 +40,10 @@ extern "C" 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; @@ -56,6 +60,7 @@ using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue, + GncOptionValue, GncOptionValue, GncOptionAccountValue, GncOptionMultichoiceValue, diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index abb80efed1..3c0c1c79da 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1061,9 +1061,19 @@ gnc_register_number_plot_size_option(GncOptionDB* db, void gnc_register_query_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, QofQuery* value) + const char* doc_string, const QofQuery* value) { - GncOption option{section, name, key, doc_string, (const QofInstance*)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)); } diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 92e04cf5e5..e445f08370 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -501,18 +501,43 @@ inline void gnc_register_number_plot_size_option(const GncOptionDBPtr& db, */ void gnc_register_query_option(GncOptionDB* db, const char* section, const char* name, const char* key, - const char* doc_string, QofQuery* value); + 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, QofQuery* value) + 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. * diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 9fbfa90834..bf991c481d 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -53,6 +53,7 @@ SCM scm_init_sw_gnc_optiondb_module(void); %include %import %import (module="sw_engine") +%import (module="sw_engine") %import (module="sw_engine") %import (module="sw_engine") %import (module="sw_engine") @@ -195,13 +196,16 @@ scm_from_value(const QofInstance* value) type = SWIGTYPE_p__gncJob; else if (GNC_IS_VENDOR(value)) type = SWIGTYPE_p__gncVendor; -/* There is no type macro for QofQuery, it's not a GObject. - else if (GNC_IS_QOFQUERY(value)) - type = SWIGTYPE_p_Query; -*/ + 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 QofQuery* value) { @@ -216,9 +220,16 @@ scm_from_value(QofQuery* value) } template <> inline SCM -scm_from_value(QofInstance* value) +scm_from_value(const GncOwner* value) { - return scm_from_value(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 ValueType @@ -267,16 +278,16 @@ 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 - }; + 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){ @@ -298,6 +309,16 @@ scm_to_value(SCM new_value) 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) { @@ -776,6 +797,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %template(gnc_make_int64_option) gnc_make_option; %template(gnc_make_qofinstance_option) gnc_make_option; %template(gnc_make_query_option) gnc_make_option; +%template(gnc_make_owner_option) gnc_make_option; %extend GncOption { SCM get_scm_value() diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 97905c5db7..1ade4370f8 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -232,15 +232,22 @@ (gnc-make-SCM-option section name key desc default type))) (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 + (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)))) - (defval (if getter (getter) #f))) - (format #t "Making owner option ~a:~a ~a ~a~%" section name defval ui-type)(force-output) - (gnc-make-qofinstance-option section name key docstring defval ui-type))) + + (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.") (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-INVOICE))) From dd8e8b4efaa04dfa7f6c085f568116686cba4ff6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 30 Jul 2021 13:20:48 -0700 Subject: [PATCH 213/298] Evaluate the getter for making invoice options. --- libgnucash/app-utils/options.scm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 1ade4370f8..214458dec9 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -250,7 +250,8 @@ (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.") - (gnc-make-qofinstance-option section name key docstring #f (GncOptionUIType-INVOICE))) + (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))) From 18997db7202972be99d7d19ae7b367355219ef8e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 31 Jul 2021 17:43:48 -0700 Subject: [PATCH 214/298] Separate GncOptionAccountValue into GncOptionAccountListValue and GncOptionAccountSelValue. They have different get_value() return types so can't cohabit comfortably. --- libgnucash/app-utils/gnc-option-impl.cpp | 106 ++++++++++++- libgnucash/app-utils/gnc-option-impl.hpp | 149 ++++++++++++++++-- libgnucash/app-utils/gnc-option.cpp | 8 +- libgnucash/app-utils/gnc-option.hpp | 6 +- libgnucash/app-utils/gnc-optiondb.cpp | 8 +- libgnucash/app-utils/gnc-optiondb.hpp | 4 +- libgnucash/app-utils/gnc-optiondb.i | 22 ++- libgnucash/app-utils/options.scm | 4 +- .../app-utils/test/gtest-gnc-option.cpp | 36 ++--- .../app-utils/test/test-gnc-optiondb.scm | 4 +- 10 files changed, 293 insertions(+), 54 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index ea4e52e23b..b0d4132517 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -37,7 +37,7 @@ const std::string GncOptionMultichoiceValue::c_empty_string{""}; const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"}; bool -GncOptionAccountValue::validate(const GncOptionAccountList& values) const +GncOptionAccountListValue::validate(const GncOptionAccountList& values) const { if (values.empty()) return true; @@ -54,6 +54,42 @@ GncOptionAccountValue::validate(const GncOptionAccountList& values) const 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; + + 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(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 @@ -62,7 +98,73 @@ GncOptionAccountValue::validate(const GncOptionAccountList& values) const * @return an allocated GList* or nullptr if the list is empty. */ GList* -GncOptionAccountValue::account_type_list() const noexcept +GncOptionAccountListValue::account_type_list() const noexcept +{ + if (m_allowed.empty()) + return nullptr; + GList* retval; + 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 +{ + return m_value ? m_value : get_default_value(); +} + +const Account* +GncOptionAccountSelValue::get_default_value() const +{ + + if (m_default_value) + 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. + */ + 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; diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 485b1fcce2..482501fd94 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -908,30 +908,30 @@ using GncOptionAccountTypeList = std::vector; */ -class GncOptionAccountValue : public OptionClassifier +class GncOptionAccountListValue : public OptionClassifier { public: - GncOptionAccountValue(const char* section, const char* name, + 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} {} - GncOptionAccountValue(const char* section, const char* name, + 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} {} - GncOptionAccountValue(const char* section, const char* name, + 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} {} - GncOptionAccountValue(const char* section, const char* name, + GncOptionAccountListValue(const char* section, const char* name, const char* key, const char* doc_string, GncOptionUIType ui_type, const GncOptionAccountList& value, @@ -945,8 +945,8 @@ public: m_default_value = std::move(value); } - const GncOptionAccountList& get_value() const { return m_value; } - const GncOptionAccountList& get_default_value() const { return m_default_value; } + GncOptionAccountList get_value() const; + GncOptionAccountList get_default_value() const; bool validate (const GncOptionAccountList& values) const; void set_value (const GncOptionAccountList& values) { if (validate(values)) @@ -973,8 +973,8 @@ private: }; template<> inline std::ostream& -operator<< (std::ostream& oss, - const GncOptionAccountValue& opt) +operator<< (std::ostream& oss, + const GncOptionAccountListValue& opt) { auto values{opt.get_value()}; bool first = true; @@ -990,8 +990,8 @@ operator<< (std::ostream& oss, } template<> inline std::istream& -operator>> (std::istream& iss, - GncOptionAccountValue& opt) +operator>> (std::istream& iss, + GncOptionAccountListValue& opt) { GncOptionAccountList values; while (true) @@ -1010,7 +1010,7 @@ operator>> (std::istream& iss, template, - GncOptionAccountValue>, + GncOptionAccountListValue>, int> = 0> inline std::ostream& gnc_option_to_scheme(std::ostream& oss, const OptType& opt) @@ -1032,7 +1032,7 @@ gnc_option_to_scheme(std::ostream& oss, const OptType& opt) template, - GncOptionAccountValue>, + GncOptionAccountListValue>, int> = 0> inline std::istream& gnc_option_from_scheme(std::istream& iss, OptType& opt) @@ -1056,6 +1056,129 @@ gnc_option_from_scheme(std::istream& iss, OptType& opt) return iss; } +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{}, m_default_value{}, 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{const_cast(value)}, + m_default_value{const_cast(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{}, m_default_value{}, 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{}, m_default_value{}, m_allowed{std::move(allowed)} { + if (!validate(value)) + throw std::invalid_argument("Supplied Value not in allowed set."); + m_value = const_cast(value); + m_default_value = const_cast(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)) + //throw! + m_value = const_cast(value); + } + void set_default_value (const Account* value) { + if (validate(value)) + //throw! + m_value = m_default_value = const_cast(value); + } + GList* account_type_list() const noexcept; + 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; } +private: + GncOptionUIType m_ui_type; + Account* m_value; + Account* 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) +{ + const Account* value; + 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; +} + +template, + GncOptionAccountSelValue>, + int> = 0> +inline std::ostream& +gnc_option_to_scheme(std::ostream& oss, const OptType& opt) +{ + auto value{opt.get_value()}; + oss << "'(\""; + oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"'; + oss << ')'; + return oss; +} + +template, + GncOptionAccountSelValue>, + int> = 0> +inline std::istream& +gnc_option_from_scheme(std::istream& iss, OptType& opt) +{ + const Account* value; + iss.ignore(3, '"'); + while (true) + { + std::string str; + std::getline(iss, str, '"'); + if (!str.empty()) + { + value = (Account*)qof_instance_from_string(str, opt.get_ui_type()); + iss.ignore(2, '"'); + } + else + break; + } + opt.set_value(value); + iss.ignore(1, ')'); + return iss; +} + /** Date options * A legal date value is a pair of either and a RelativeDatePeriod, the absolute * flag and a time64, or for legacy purposes the absolute flag and a timespec. diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 812a2c1b7c..cebd526ed1 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -279,7 +279,7 @@ GncOption::is_multiselect() const noexcept { return std::visit([](const auto& option)->bool { if constexpr (std::is_same_v, - GncOptionAccountValue>) + GncOptionAccountListValue>) return option.is_multiselect(); else return false; @@ -367,7 +367,7 @@ GncOption::account_type_list() const noexcept { return std::visit([] (const auto& option) -> GList* { if constexpr (std::is_same_v, - GncOptionAccountValue>) + GncOptionAccountListValue>) return option.account_type_list(); else return nullptr; @@ -423,7 +423,7 @@ GncOption::to_scheme(std::ostream& oss) const return std::visit([&oss](auto& option) ->std::ostream& { if constexpr ((std::is_same_v, - GncOptionAccountValue>) || + GncOptionAccountListValue>) || (std::is_same_v, GncOptionMultichoiceValue>) || std::is_same_v, @@ -464,7 +464,7 @@ GncOption::from_scheme(std::istream& iss) return std::visit([&iss](auto& option) -> std::istream& { if constexpr ((std::is_same_v, - GncOptionAccountValue>) || + GncOptionAccountListValue>) || (std::is_same_v, GncOptionMultichoiceValue>) || std::is_same_v, diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 66e2ee25fd..158fdea5a6 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -49,7 +49,8 @@ using QofQuery = _QofQuery; struct QofInstance_s; using QofInstance = QofInstance_s; template class GncOptionValue; -class GncOptionAccountValue; +class GncOptionAccountListValue; +class GncOptionAccountSelValue; class GncOptionMultichoiceValue; template class GncOptionRangeValue; template class GncOptionValidatedValue; @@ -62,7 +63,8 @@ using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue, - GncOptionAccountValue, + GncOptionAccountListValue, + GncOptionAccountSelValue, GncOptionMultichoiceValue, GncOptionRangeValue, GncOptionRangeValue, diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 3c0c1c79da..ed7603c283 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -923,7 +923,7 @@ gnc_register_account_list_option(GncOptionDB* db, const char* section, const char* doc_string, const GncOptionAccountList& value) { - GncOption option{GncOptionAccountValue{section, name, key, doc_string, + GncOption option{GncOptionAccountListValue{section, name, key, doc_string, GncOptionUIType::ACCOUNT_LIST, value}}; db->register_option(section, std::move(option)); } @@ -938,7 +938,7 @@ gnc_register_account_list_limited_option(GncOptionDB* db, { try { - GncOption option{GncOptionAccountValue{section, name, key, doc_string, + GncOption option{GncOptionAccountListValue{section, name, key, doc_string, GncOptionUIType::ACCOUNT_LIST, value, std::move(allowed)}}; db->register_option(section, std::move(option)); } @@ -979,12 +979,12 @@ void gnc_register_account_sel_limited_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, - const GncOptionAccountList& value, + const Account* value, GncOptionAccountTypeList&& allowed) { try { - GncOption option{GncOptionAccountValue{section, name, key, doc_string, + GncOption option{GncOptionAccountSelValue{section, name, key, doc_string, GncOptionUIType::ACCOUNT_SEL, value, std::move(allowed)}}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index e445f08370..002efa9e3b 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -351,7 +351,7 @@ void gnc_register_account_sel_limited_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, - const GncOptionAccountList& value, + const Account* value, GncOptionAccountTypeList&& allowed); /** @@ -361,7 +361,7 @@ inline void gnc_register_account_sel_limited_option(GncOptionDBPtr& db, const char* section, const char* name, const char* key, const char* doc_string, - const GncOptionAccountList& value, + const Account* value, GncOptionAccountTypeList&& allowed) { gnc_register_account_sel_limited_option(db.get(), section, name, key, diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index bf991c481d..31b5acdda6 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -206,6 +206,12 @@ 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(const QofQuery* value) { @@ -299,6 +305,12 @@ scm_to_value(SCM new_value) return static_cast(ptr); } +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) { @@ -580,7 +592,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %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 GncOptionAccountList&, GncOptionAccountTypeList&&); +%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); @@ -939,7 +951,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); const GncOptionAccountList& value) { try { - return new GncOption{GncOptionAccountValue{section, name, key, + return new GncOption{GncOptionAccountListValue{section, name, key, doc_string, GncOptionUIType::ACCOUNT_LIST, value}}; } catch (const std::exception& err) @@ -958,7 +970,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); { try { - return new GncOption{GncOptionAccountValue{section, name, key, + return new GncOption{GncOptionAccountListValue{section, name, key, doc_string, GncOptionUIType::ACCOUNT_LIST, value, std::move(allowed)}}; } @@ -973,12 +985,12 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); const char* name, const char* key, const char* doc_string, - const GncOptionAccountList& value, + const Account* value, GncOptionAccountTypeList&& allowed) { try { - return new GncOption{GncOptionAccountValue{section, name, key, + return new GncOption{GncOptionAccountSelValue{section, name, key, doc_string, GncOptionUIType::ACCOUNT_SEL, value, std::move(allowed)}}; } diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 214458dec9..0e84c17f26 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -184,10 +184,10 @@ (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) #f))) + (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) #f))) + (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.") diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 379617e13b..2edc94af5e 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -736,7 +736,7 @@ TEST_F(GncOptionAccountTest, test_test_constructor_and_destructor) TEST_F(GncOptionAccountTest, test_option_no_value_constructor) { - GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", + GncOptionAccountListValue option{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST}; EXPECT_TRUE(option.get_value().empty()); EXPECT_TRUE(option.get_default_value().empty()); @@ -745,7 +745,7 @@ TEST_F(GncOptionAccountTest, test_option_no_value_constructor) TEST_F(GncOptionAccountTest, test_option_value_constructor) { GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; - GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", + 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()); @@ -756,11 +756,11 @@ 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})}; - GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option", + GncOptionAccountListValue option{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, GncOptionAccountTypeList{ACCT_TYPE_BANK}}; - EXPECT_TRUE(option.get_value().empty()); - EXPECT_TRUE(option.get_default_value().empty()); + 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)); } @@ -770,21 +770,21 @@ 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({ - GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", + GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, acclistbad, GncOptionAccountTypeList{ACCT_TYPE_BANK}); }, std::invalid_argument); EXPECT_THROW({ - GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", + GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_SEL, acclistgood, GncOptionAccountTypeList{ACCT_TYPE_BANK}); }, std::invalid_argument); EXPECT_NO_THROW({ - GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", + GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, acclistgood, GncOptionAccountTypeList{ACCT_TYPE_BANK}); @@ -792,12 +792,12 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor) EXPECT_NO_THROW({ GncOptionAccountList accsel{acclistgood[0]}; - GncOptionAccountValue option("foo", "bar", "baz", "Bogus Option", + GncOptionAccountListValue option("foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, accsel, GncOptionAccountTypeList{ACCT_TYPE_BANK}); }); - GncOptionAccountValue option {"foo", "bar", "baz", "Bogus Option", + GncOptionAccountListValue option {"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, acclistgood, GncOptionAccountTypeList{ACCT_TYPE_BANK}}; EXPECT_FALSE(option.get_value().empty()); @@ -809,7 +809,7 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor) TEST_F(GncOptionAccountTest, test_account_list_out) { GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; - GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", + GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, acclist}}; std::ostringstream oss; @@ -821,7 +821,7 @@ TEST_F(GncOptionAccountTest, test_account_list_out) EXPECT_EQ(acc_guids, oss.str()); GncOptionAccountList accsel{acclist[0]}; - GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", + GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, accsel, @@ -836,7 +836,7 @@ TEST_F(GncOptionAccountTest, test_account_list_out) TEST_F(GncOptionAccountTest, test_account_list_in) { GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; - GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", + GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, acclist}}; std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()}; @@ -848,7 +848,7 @@ TEST_F(GncOptionAccountTest, test_account_list_in) EXPECT_EQ(acclist, option.get_value()); GncOptionAccountList accsel{acclist[0]}; - GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", + GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, accsel, @@ -894,7 +894,7 @@ make_account_list_SCM_str(const GncOptionAccountList& acclist) TEST_F(GncOptionAccountTest, test_account_list_to_scheme) { GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; - GncOption option{GncOptionAccountValue {"foo", "bar", "baz", "Bogus Option", + GncOption option{GncOptionAccountListValue {"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, acclist}}; std::ostringstream oss; @@ -904,7 +904,7 @@ TEST_F(GncOptionAccountTest, test_account_list_to_scheme) EXPECT_EQ(acc_guids, oss.str()); GncOptionAccountList accsel{acclist[0]}; - GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", + GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, accsel, @@ -919,7 +919,7 @@ TEST_F(GncOptionAccountTest, test_account_list_to_scheme) TEST_F(GncOptionAccountTest, test_account_list_from_scheme) { GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; - GncOption option{GncOptionAccountValue{"foo", "bar", "baz", "Bogus Option", + GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, acclist}}; @@ -929,7 +929,7 @@ TEST_F(GncOptionAccountTest, test_account_list_from_scheme) EXPECT_EQ(acclist, option.get_value()); GncOptionAccountList accsel{acclist[0]}; - GncOption sel_option{GncOptionAccountValue{"foo", "bar", "baz", + GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, accsel, diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 38215ce20a..ad88824638 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -135,9 +135,9 @@ (list ACCT-TYPE-STOCK)))) (gnc-register-account-sel-limited-option option-db "salt" "pork" "baz" - "Phony Option" (list (cadr acctlist)) (list ACCT-TYPE-STOCK)) + "Phony Option" (cadr acctlist) (list ACCT-TYPE-STOCK)) (let ((acct (gnc-option-value option-db "salt" "pork"))) - (test-equal (list (cadr acctlist)) acct))))) + (test-equal (cadr acctlist) acct))))) (let* ((book (gnc-option-test-book-new)) (root-account (gnc-account-create-root book))) From 0f02236ebe751f32609b64b1a67fdad1ab4aa5e5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 1 Aug 2021 10:27:50 -0700 Subject: [PATCH 215/298] Fix gcc warnings. --- gnucash/gnome-utils/dialog-options.cpp | 5 +- libgnucash/app-utils/gnc-optiondb.i | 122 +++++++++++++------------ 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 7e2720679b..975b13543f 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -150,7 +150,7 @@ GncOptionUIFactory::create(GncOption& option, GtkGrid* page, GtkLabel* name, auto func{s_registry[static_cast(type)]}; if (func) return func(option, page, name, description, enclosing, packed); - PERR("No function registered for type %d", type); + PERR("No function registered for type %d", static_cast(type)); return nullptr; } @@ -1506,7 +1506,8 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box, 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", type); + PERR("Attempted to create date option widget with wrong UI type %d", + static_cast(type)); return nullptr; break; } diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 31b5acdda6..f081157b8a 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -760,7 +760,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return scm_reverse(values); } - SCM get_scm_value(const GncOptionMultichoiceValue& option) + static SCM + get_scm_value(const GncOptionMultichoiceValue& option) { auto indexes = option.get_multiple(); @@ -771,7 +772,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return scm_from_multichoices(indexes, option); } - SCM get_scm_default_value(const GncOptionMultichoiceValue& option) + static SCM + get_scm_default_value(const GncOptionMultichoiceValue& option) { auto indexes = option.get_default_multiple(); @@ -780,14 +782,16 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return scm_from_multichoices(indexes, option); } - SCM get_scm_value(const GncOptionRangeValue& 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)); } - SCM get_scm_default_value(const GncOptionRangeValue& option) + 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")}; @@ -945,10 +949,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %template(gnc_register_number_range_option_int) gnc_register_number_range_option; %inline %{ - GncOption* gnc_make_account_list_option(const char* section, - const char* name, const char* key, - const char* doc_string, - const GncOptionAccountList& value) + 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, @@ -961,12 +966,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } - 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) + 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 { @@ -981,12 +987,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } - 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) + 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 { @@ -1001,12 +1006,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } - 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) + 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 { @@ -1046,11 +1049,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } - GncOption* gnc_make_multichoice_option(const char* section, - const char* name, const char* key, - const char* doc_string, - const char* default_val, - GncMultichoiceOptionChoices&& choices) + 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}; @@ -1072,11 +1075,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } - GncOption* gnc_make_list_option(const char* section, - const char* name, const char* key, - const char* doc_string, - GncMultichoiceOptionIndexVec indexes, - GncMultichoiceOptionChoices&& list) + 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, @@ -1090,10 +1093,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } - 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) + 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 { @@ -1108,10 +1112,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } - 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) + 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 { @@ -1126,19 +1130,19 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } } - GncOption* gnc_make_commodity_option(const char* section, - const char* name, const char* key, - const char* doc_string, - gnc_commodity *value) + 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{GncOptionValue{ section, name, key, doc_string, (const QofInstance*)value}}; } - GncOption* gnc_make_commodity_option(const char* section, - const char* name, const char* key, - const char* doc_string, - const char *value) + 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())}; @@ -1156,10 +1160,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return nullptr; } - GncOption* gnc_make_currency_option(const char* section, - const char* name, const char* key, - const char* doc_string, - gnc_commodity *value) + static GncOption* + gnc_make_currency_option(const char* section, const char* name, + const char* key, const char* doc_string, + gnc_commodity *value) { try { @@ -1254,7 +1258,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return optiondb->find_option(section, name); } - void + static void gnc_option_db_set_option_selectable_by_name(GncOptionDBPtr& odb, const char* section, const char* name, @@ -1264,7 +1268,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option->set_ui_item_selectable(selectable); } - void + static void gnc_optiondb_foreach(GncOptionDBPtr& odb, SCM thunk) { odb->foreach_section( @@ -1281,7 +1285,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); }); } - std::string + static std::string gnc_optiondb_save_to_scheme(GncOptionDBPtr& odb, const char* prolog) { std::ostringstream oss; From 0bd9033bb3081ad94f80c507d455cf4ed04b54d8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 1 Aug 2021 11:11:20 -0700 Subject: [PATCH 216/298] Move includes of glib and gtk out or extern C in new cpp files. --- gnucash/gnome-utils/dialog-options.cpp | 5 +++-- gnucash/gnome-utils/gnc-main-window.cpp | 9 +++++---- gnucash/gnome/assistant-hierarchy.cpp | 7 ++++--- gnucash/gnome/business-options-gnome.cpp | 4 ++-- gnucash/gnome/dialog-report-column-view.cpp | 5 +++-- gnucash/gnome/dialog-report-style-sheet.cpp | 6 +++--- gnucash/gnome/gnc-plugin-page-report.cpp | 7 ++++--- gnucash/gnome/window-report.cpp | 3 ++- libgnucash/app-utils/app-utils.i | 1 + libgnucash/app-utils/gnc-option-impl.hpp | 1 + libgnucash/app-utils/gnc-option.hpp | 4 ---- libgnucash/app-utils/test/gtest-gnc-optiondb.cpp | 3 ++- 12 files changed, 30 insertions(+), 25 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 975b13543f..dfef570576 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -30,11 +30,12 @@ extern "C" #include // To include as C++ overriding later indirect includes #include -extern "C" -{ #include #include #include + +extern "C" +{ #include "swig-runtime.h" #include "gnc-tree-model-budget.h" //FIXME? diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index df59ee33f8..68b337977d 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -33,15 +33,16 @@ @author Copyright (C) 2003,2005,2006 David Hampton */ #include -extern "C" -{ -#include - #include #include #include #include +extern "C" +{ +#include + + #include "gnc-plugin.h" #include "gnc-plugin-manager.h" #include "gnc-main-window.h" diff --git a/gnucash/gnome/assistant-hierarchy.cpp b/gnucash/gnome/assistant-hierarchy.cpp index 3dd7c55295..6315e78a98 100644 --- a/gnucash/gnome/assistant-hierarchy.cpp +++ b/gnucash/gnome/assistant-hierarchy.cpp @@ -23,6 +23,10 @@ \********************************************************************/ #include +#include +#include +#include + extern "C" { #include @@ -32,9 +36,6 @@ extern "C" #include #endif -#include -#include -#include #include #include #include diff --git a/gnucash/gnome/business-options-gnome.cpp b/gnucash/gnome/business-options-gnome.cpp index 519df3889b..48144d9296 100644 --- a/gnucash/gnome/business-options-gnome.cpp +++ b/gnucash/gnome/business-options-gnome.cpp @@ -23,13 +23,13 @@ */ #include +#include +#include extern "C" { #include -#include -#include #include "swig-runtime.h" #include "guile-mappings.h" diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp index cf14268592..9e1adef41b 100644 --- a/gnucash/gnome/dialog-report-column-view.cpp +++ b/gnucash/gnome/dialog-report-column-view.cpp @@ -22,12 +22,13 @@ ********************************************************************/ #include +#include +#include + extern "C" { #include -#include -#include #include "swig-runtime.h" #include "dialog-utils.h" diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp index 60d4287d7a..68b90b3830 100644 --- a/gnucash/gnome/dialog-report-style-sheet.cpp +++ b/gnucash/gnome/dialog-report-style-sheet.cpp @@ -23,13 +23,13 @@ ********************************************************************/ #include +#include +#include + extern "C" { #include -#include -#include - #include "dialog-report-style-sheet.h" #include "dialog-utils.h" #include "gnc-component-manager.h" diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp index 1554962231..1365815cdc 100644 --- a/gnucash/gnome/gnc-plugin-page-report.cpp +++ b/gnucash/gnome/gnc-plugin-page-report.cpp @@ -37,13 +37,14 @@ @author Copyright (C) 2005 David Hampton */ #include +#include +#include +#include + extern "C" { #include -#include -#include -#include #include #include diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp index 10a3d23f1a..b5cc9393ca 100644 --- a/gnucash/gnome/window-report.cpp +++ b/gnucash/gnome/window-report.cpp @@ -25,11 +25,12 @@ * * ********************************************************************/ #include +#include + extern "C" { #include -#include #include #include diff --git a/libgnucash/app-utils/app-utils.i b/libgnucash/app-utils/app-utils.i index 2fc313df6a..ab8aead4cd 100644 --- a/libgnucash/app-utils/app-utils.i +++ b/libgnucash/app-utils/app-utils.i @@ -21,6 +21,7 @@ %module sw_app_utils %{ /* Includes the header in the wrapper code */ +#include #ifdef __cplusplus extern "C" { diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 482501fd94..d614b0dd20 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -42,6 +42,7 @@ extern "C" #include #include #include +#include #include "gnc-option-uitype.hpp" diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 158fdea5a6..089ff08250 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -24,11 +24,7 @@ #ifndef GNC_OPTION_HPP_ #define GNC_OPTION_HPP_ -extern "C" -{ #include -} - #include #include #include diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index ce0017646d..e00f0894a8 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -26,9 +26,10 @@ #include #include #include +#include + extern "C" { -#include #include #include } From 99eaa81a280e195a465290d6b9098db13b22a102 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 1 Aug 2021 12:43:14 -0700 Subject: [PATCH 217/298] Fix parse of commodity namespace from scheme istream. Just ignore 2 instead of ignoring 1 twice. No ifdef required. --- libgnucash/app-utils/gnc-option-impl.hpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index d614b0dd20..ae386afe3d 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -414,14 +414,10 @@ gnc_option_from_scheme (std::istream& iss, OptType& opt) { iss.ignore(strlen(commodity_scm_intro) + 1, '"'); std::getline(iss, name_space, '"'); - // libc++ doesn't consume the end character, libstdc++ does -#ifdef _LIBCPP_VERSION - iss.ignore(1, '"'); -#endif } else name_space = GNC_COMMODITY_NS_CURRENCY; - iss.ignore(1, '"'); + iss.ignore(2, '"'); std::getline(iss, mnemonic, '"'); if (type == GncOptionUIType::COMMODITY) From a2e1a3e1b8641968709ee2d82990152c8022990c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 2 Aug 2021 16:52:34 -0700 Subject: [PATCH 218/298] Extract the GncOptionDB* from the Scheme dispatch function closure. Scheme code keeps the GncOptionDB in a closure. Extract it for C/C++ use. Ownership remains with the closure, don't free the GncOptionDB*. --- gnucash/gnome/dialog-report-style-sheet.cpp | 4 +-- gnucash/gnome/gnc-plugin-page-report.cpp | 39 ++++++++++++--------- gnucash/gnome/window-report.cpp | 16 ++++++--- gnucash/gnome/window-report.h | 1 + libgnucash/app-utils/gnc-optiondb.cpp | 23 ++++++++++++ libgnucash/app-utils/gnc-optiondb.h | 14 ++++++++ libgnucash/app-utils/options.scm | 6 +++- 7 files changed, 78 insertions(+), 25 deletions(-) diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp index 68b90b3830..b56564a4c9 100644 --- a/gnucash/gnome/dialog-report-style-sheet.cpp +++ b/gnucash/gnome/dialog-report-style-sheet.cpp @@ -168,7 +168,7 @@ 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; @@ -176,7 +176,7 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss, title = g_strdup_printf(_("HTML Style Sheet Properties: %s"), name); ssinfo->odialog = gnc_options_dialog_new(title, parent); - ssinfo->odb = (GncOptionDB *)scm_to_pointer(scm_options); + ssinfo->odb = gnc_get_optiondb_from_dispatcher(scm_dispatch); ssinfo->stylesheet = sheet_info; ssinfo->row_ref = row_ref; g_free (title); diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp index 1365815cdc..2095142b5a 100644 --- a/gnucash/gnome/gnc-plugin-page-report.cpp +++ b/gnucash/gnome/gnc-plugin-page-report.cpp @@ -77,6 +77,9 @@ extern "C" #include "print-session.h" } + +#include + /* NW: you can add GNC_MOD_REPORT to gnc-engine.h or simply define it locally. Any unique string with a gnucash- prefix will do. Then just set a log level @@ -584,7 +587,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; @@ -640,8 +642,7 @@ 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 = - (GncOptionDB *)scm_to_pointer(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, @@ -666,8 +667,7 @@ 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 = (GncOptionDB *)scm_to_pointer(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, @@ -1032,20 +1032,25 @@ gnc_plugin_page_report_name_changed (GncPluginPage *page, const gchar *name) 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_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)) + if (priv->cur_odb) { - LEAVE("no change"); - return; - } - /* Store the new name for the report. */ - gnc_option_db_set_string_value(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); diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp index b5cc9393ca..4898c9cb82 100644 --- a/gnucash/gnome/window-report.cpp +++ b/gnucash/gnome/window-report.cpp @@ -26,6 +26,7 @@ ********************************************************************/ #include #include +#include extern "C" { @@ -208,10 +209,8 @@ 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; GncOptionDB* odb; GtkWidget *options_widget = nullptr; @@ -220,14 +219,13 @@ gnc_report_edit_options(SCM report, GtkWindow *parent) 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.")); return FALSE; } - odb = (GncOptionDB*)scm_to_pointer(options); /* Multi-column type reports need a special options dialog */ ptr = scm_call_1(get_report_type, report); @@ -249,3 +247,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 1ecaf626ea..e41cea70f8 100644 --- a/gnucash/gnome/window-report.h +++ b/gnucash/gnome/window-report.h @@ -47,5 +47,6 @@ gboolean gnc_report_edit_options(SCM report, GtkWindow *parent); #ifdef __cplusplus } +GncOptionDB* gnc_get_report_optiondb(SCM report_instance); #endif #endif diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index ed7603c283..7d804c2304 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -824,6 +824,29 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept }); } +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(); +} + void gnc_register_string_option(GncOptionDB* db, const char* section, const char* name, const char* key, diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h index 007bfd1f4f..d2fe65540c 100644 --- a/libgnucash/app-utils/gnc-optiondb.h +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -59,6 +59,20 @@ GncOptionDB* gnc_option_db_new(void); */ void gnc_option_db_destroy(GncOptionDB* odb); +/** + * 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); + /** * Write all changed ui_item values to their options. * @param odb The GncOptionDB. diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 0e84c17f26..e3707ff399 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -103,7 +103,11 @@ (let ((optiondb (new-gnc-optiondb))) (define (dispatch key) optiondb) - dispatch)) + dispatch)) + +;; Use the dispatch function to get the optiondb +(define-public (gnc:options-get dispatch) + (dispatch 'get)) (define-public (gnc:options-set-default-section optiondb section) (GncOptionDB-set-default-section (GncOptionDBPtr-get (optiondb 'set-default-section)) section)) From 58d090ff326aebe685dfaf4b726601a8d30f70b3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 3 Aug 2021 17:03:49 -0700 Subject: [PATCH 219/298] Protect strncmp from empty string. It crashes if given one. --- gnucash/gnome-utils/dialog-options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index dfef570576..b3e387bc25 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -454,7 +454,7 @@ gnc_options_dialog_append_page(GNCOptionWin * propertybox, GncOptionSectionPtr& section) { auto name = section->get_name().c_str(); - if (!name) + if (!name || *name == '\0') return -1; if (strncmp(name, "__", 2) == 0) From a97b3e0c6d21cd146d60326a6c59cc8143a804c5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 3 Aug 2021 17:05:04 -0700 Subject: [PATCH 220/298] Don't let C code destroy the ODB, it's owned by Guile. --- libgnucash/app-utils/gnc-optiondb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 7d804c2304..60f7ccd901 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1278,7 +1278,7 @@ gnc_option_db_new(void) void gnc_option_db_destroy(GncOptionDB* odb) { - delete odb; + PWARN("Direct Destroy called on GncOptionDB %" G_GUINT64_FORMAT, (uint64_t)odb); } GList* From 71955cc326fda6897af12e3093df0417d0598ed2 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 3 Aug 2021 17:06:22 -0700 Subject: [PATCH 221/298] Correctly terminate gtk_list_store_set(). --- gnucash/gnome-utils/dialog-options.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index b3e387bc25..43d14f6a81 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1182,7 +1182,7 @@ create_multichoice_widget(GncOption& option) auto itemstring = option.permissible_value_name(i); gtk_list_store_append (store, &iter); gtk_list_store_set(store, &iter, 0, - (itemstring && *itemstring) ? _(itemstring) : "", 1); + (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)))}; @@ -1314,7 +1314,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) GtkTreeIter iter; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, - option.permissible_value_name(index), 1); + option.permissible_value_name(index), -1); } /* Create the new Combo with tooltip and add the store */ From 914f5f359bcc486c40dd941f5f17908ed794dfa7 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 4 Aug 2021 12:05:02 -0700 Subject: [PATCH 222/298] Don't unref option UI elements. They're wrapped in unique_ptr and will take care of themselves. --- gnucash/gnome-utils/dialog-options.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 43d14f6a81..bc632648b1 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1254,7 +1254,7 @@ class AbsoluteDateEntry : public GncDateEntry { public: AbsoluteDateEntry(GncOption& option); - ~AbsoluteDateEntry() { g_object_unref(G_OBJECT(m_entry)); } + ~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); } @@ -1287,7 +1287,7 @@ class RelativeDateEntry : public GncDateEntry { public: RelativeDateEntry(GncOption& option); - ~RelativeDateEntry() { g_object_unref(G_OBJECT(m_entry)); }; + ~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; } @@ -1344,7 +1344,7 @@ class BothDateEntry : public GncDateEntry { public: BothDateEntry(GncOption& option); - ~BothDateEntry(); //The GncOptionGtkUIItem owns the widget + ~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; } @@ -1387,12 +1387,6 @@ BothDateEntry::BothDateEntry(GncOption& option) : } -BothDateEntry::~BothDateEntry() -{ - g_object_unref(G_OBJECT(m_abs_button)); - g_object_unref(G_OBJECT(m_rel_button)); -} - GtkWidget* BothDateEntry::get_entry() { From 26946d792c63aca947e77a9c01dccb3a84be302a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 4 Aug 2021 12:05:32 -0700 Subject: [PATCH 223/298] Configure the book options before trying to load them. --- gnucash/gnome-utils/gnc-main-window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index 68b337977d..91cb706204 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -4282,6 +4282,7 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent) GNCOptionWin *optionwin; 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); From b6622a386b8a3c6aa0b7dd42b296784202691922 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 4 Aug 2021 17:13:23 -0700 Subject: [PATCH 224/298] Pick up option aliases recently added in maint. --- libgnucash/app-utils/gnc-optiondb.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 60f7ccd901..c0629f7c4b 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -101,6 +101,10 @@ const OptionAliases Aliases::c_option_aliases {"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"}}, From f0ecc0e2ebd33f7286d22adf1b12c77f45fba771 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Aug 2021 15:41:03 -0700 Subject: [PATCH 225/298] std::string(char*) crashes if it's given a nullptr. --- gnucash/gnome-utils/dialog-options.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index bc632648b1..03aa010697 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -2320,8 +2320,11 @@ public: { auto string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(get_widget())); DEBUG("filename %s", string ? string : "(null)"); - option.set_value(std::string{string}); - g_free(string); + if (string) + { + option.set_value(std::string{string}); + g_free(string); + } } }; From c83a3f44dd0774a28c6e1bb22e809f7dce33c391 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Aug 2021 15:41:50 -0700 Subject: [PATCH 226/298] Fix paste error. Call get, not set, when we want to get. --- gnucash/gnome-utils/dialog-options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 03aa010697..5502ab5758 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -2177,7 +2177,7 @@ public: { GdkRGBA color; auto color_button = GTK_COLOR_CHOOSER(get_widget()); - gtk_color_chooser_set_rgba(color_button, &color); + gtk_color_chooser_get_rgba(color_button, &color); auto rgba_str = gdk_rgba_to_string(&color); option.set_value(std::string{rgba_str}); g_free(rgba_str); From 8079470c8aeed8601522a3336055aaec2760ae37 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Aug 2021 17:26:04 -0700 Subject: [PATCH 227/298] Fix Color chooser option value setting and getting. --- gnucash/gnome-utils/dialog-options.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 5502ab5758..d7864f3e58 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -2166,19 +2166,25 @@ public: void set_ui_item_from_option(GncOption& option) noexcept override { GdkRGBA color; - if (gdk_rgba_parse(&color, - option.get_value().c_str())) + auto rgba_str{g_strdup_printf("#%s", + option.get_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 = gdk_rgba_to_string(&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)); option.set_value(std::string{rgba_str}); g_free(rgba_str); } From 3c95ad8a0015f3605ac7516b140322378c9fdb87 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Aug 2021 18:20:19 -0700 Subject: [PATCH 228/298] Create and use renderer for multichoice option widgets. --- gnucash/gnome-utils/dialog-options.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index d7864f3e58..29017452d9 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1167,14 +1167,8 @@ create_multichoice_widget(GncOption& option) auto num_values = option.num_permissible_values(); g_return_val_if_fail(num_values >= 0, NULL); - - /* GtkComboBox still does not support per-item tooltips, so have - created a basic one called Combott implemented in gnc-combott. - Have highlighted changes in this file with comments for when - the feature of per-item tooltips is implemented in gtk, - see https://bugs.gnucash.org/show_bug.cgi?id=303717 */ - - auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + 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++) { @@ -1186,6 +1180,9 @@ create_multichoice_widget(GncOption& option) } /* 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); g_signal_connect(G_OBJECT(widget), "changed", @@ -1223,7 +1220,6 @@ create_option_widget (GncOption& option, GtkGrid * auto widget = create_multichoice_widget(option); auto ui_item{std::make_unique(widget)}; -//GncCombott doesn't emit a changed signal option.set_ui_item(std::move(ui_item)); option.set_ui_item_from_option(); @@ -1300,12 +1296,7 @@ private: RelativeDateEntry::RelativeDateEntry(GncOption& option) { - /* GtkComboBox still does not support per-item tooltips, so have - created a basic one called Combott implemented in gnc-combott. - Have highlighted changes in this file with comments for when - the feature of per-item tooltips is implemented in gtk, - see https://bugs.gnucash.org/show_bug.cgi?id=303717 */ - + auto renderer = gtk_cell_renderer_text_new(); auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); /* Add values to the list store, entry and tooltip */ auto num = option.num_permissible_values(); @@ -1319,6 +1310,10 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) /* 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_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); g_signal_connect(G_OBJECT(m_entry), "changed", From e4c9900c9e7f0c6328b13feae80112f55bbee7be Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Aug 2021 18:21:53 -0700 Subject: [PATCH 229/298] Avoid infinite recursion in toggle buttons. Calling activate on a toggle button toggles it again. No need for that. --- gnucash/gnome-utils/dialog-options.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 29017452d9..e0ad8f3027 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1391,18 +1391,6 @@ BothDateEntry::get_entry() void BothDateEntry::toggle_relative(bool use_absolute) { - if (use_absolute) - { - gtk_widget_set_sensitive(m_abs_entry->get_entry(), TRUE); - gtk_widget_set_sensitive(m_rel_entry->get_entry(), FALSE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_abs_button), TRUE); - } - else - { - gtk_widget_set_sensitive(m_rel_entry->get_entry(), TRUE); - gtk_widget_set_sensitive(m_abs_entry->get_entry(), FALSE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_rel_button), TRUE); - } m_use_absolute = use_absolute; } From 8acf52a69beaf5dfa7a4560831b2fae46a4294c9 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 5 Aug 2021 18:22:37 -0700 Subject: [PATCH 230/298] Clean up variable declarations in create_list_widget. --- gnucash/gnome-utils/dialog-options.cpp | 35 +++++++++----------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index e0ad8f3027..007b34e155 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1955,36 +1955,25 @@ public: static GtkWidget * 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; - - frame = gtk_frame_new(name); - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + 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); - store = gtk_list_store_new(1, G_TYPE_STRING); - view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store))); + 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); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes("", renderer, - "text", 0, - NULL); + 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); @@ -1993,16 +1982,16 @@ create_list_widget(GncOption& option, char *name) gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(view), FALSE, FALSE, 0); - selection = gtk_tree_view_get_selection(view); + 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); - bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL); + 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); - button = gtk_button_new_with_label(_("Select All")); + 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.")); From 86d7637160b4ec77f568ebc34da40b3866957ae9 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 8 Aug 2021 15:22:53 -0700 Subject: [PATCH 231/298] Handle GncOptionMultichoiceValue::set_value parameters that don't match the get_value return type. --- libgnucash/app-utils/gnc-option.cpp | 32 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index cebd526ed1..a1051aa6b3 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -117,13 +117,19 @@ GncOption::set_value(ValueType value) (std::is_same_v, RelativeDatePeriod> || std::is_same_v, size_t>))) - option.set_value(value); + option.set_value(value); if constexpr (std::is_same_v, - GncOptionMultichoiceValue> && - std::is_same_v, - GncMultichoiceOptionIndexVec>) - option.set_multiple(value); + GncOptionMultichoiceValue>) + { + if constexpr (std::is_same_v, + GncMultichoiceOptionIndexVec>) + option.set_multiple(value); + else if constexpr (std::is_same_v || + std::is_same_v, std::string> || + std::is_same_v, char*>) + option.set_value(value); + } }, *m_option); } @@ -139,13 +145,19 @@ GncOption::set_default_value(ValueType value) (std::is_same_v, RelativeDatePeriod> || std::is_same_v, size_t>))) - option.set_default_value(value); + option.set_default_value(value); if constexpr (std::is_same_v, - GncOptionMultichoiceValue> && - std::is_same_v, - GncMultichoiceOptionIndexVec>) - option.set_multiple(value); + GncOptionMultichoiceValue>) + { + if constexpr (std::is_same_v, + GncMultichoiceOptionIndexVec>) + option.set_multiple(value); + else if constexpr (std::is_same_v || + std::is_same_v, std::string> || + std::is_same_v, char*>) + option.set_default_value(value); + } }, *m_option); } void From e8f855e6043f674c5d28a35a41ae672d825410d9 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 8 Aug 2021 15:24:08 -0700 Subject: [PATCH 232/298] Oops, bailed on failure when finding the dialog box buttons. --- gnucash/gnome-utils/dialog-options.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 007b34e155..4f8c353dcf 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -261,13 +261,13 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive) _("_Close")); } g_list_free (children); - } break; // Found the button-box, no need to continue. } - g_list_free (children); } + g_list_free (children); break; // Found the box, no need to continue. } + } g_list_free (children); } } From a7f0476af91688d381fa43afd6f1a70ceb5cad53 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 8 Aug 2021 15:25:09 -0700 Subject: [PATCH 233/298] GncMultichoiceUIItem: Cast the selection index to a size_t. To get correct match with template type. --- gnucash/gnome-utils/dialog-options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 4f8c353dcf..b5275c46e2 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1204,7 +1204,7 @@ public: void set_option_from_ui_item(GncOption& option) noexcept override { auto widget{GTK_COMBO_BOX(get_widget())}; - option.set_value(gtk_combo_box_get_active(widget)); + option.set_value(static_cast(gtk_combo_box_get_active(widget))); } }; From 86da12d844a4749a0334cf129ded53140b5cc351 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 12 Aug 2021 14:28:44 -0700 Subject: [PATCH 234/298] Use RGB instead of RGBA for color set for color --- gnucash/gnome-utils/dialog-options.cpp | 15 ++++++++++----- libgnucash/app-utils/options.scm | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index b5275c46e2..23c0ff7e35 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -261,12 +261,12 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive) _("_Close")); } g_list_free (children); - break; // Found the button-box, no need to continue. + break; // Found the button-box, no need to continue. + } } - } g_list_free (children); - break; // Found the box, no need to continue. - } + break; // Found the box, no need to continue. + } } g_list_free (children); } @@ -2157,7 +2157,12 @@ public: (uint8_t)(color.green * 255), (uint8_t)(color.blue * 255), (uint8_t)(color.alpha * 255)); - option.set_value(std::string{rgba_str}); + 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); } }; diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index e3707ff399..1dd35f963f 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -157,7 +157,7 @@ (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 + (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)))) From 1af97ebb9ad43ce39402108ab8adc1b2f3fe6f33 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 12 Aug 2021 14:29:56 -0700 Subject: [PATCH 235/298] Implement registering/unregistering/calling change callbacks. Drives reloading the report when the Apply or OK button is pressed. --- gnucash/gnome/gnc-plugin-page-report.cpp | 33 +++++++++------------- libgnucash/app-utils/gnc-optiondb-impl.hpp | 23 +++++++++++++++ libgnucash/app-utils/gnc-optiondb.cpp | 28 ++++++++++++++++++ libgnucash/app-utils/gnc-optiondb.i | 1 + 4 files changed, 66 insertions(+), 19 deletions(-) diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp index 2095142b5a..2d47cd0df4 100644 --- a/gnucash/gnome/gnc-plugin-page-report.cpp +++ b/gnucash/gnome/gnc-plugin-page-report.cpp @@ -79,6 +79,7 @@ extern "C" #include +#include /* NW: you can add GNC_MOD_REPORT to gnc-engine.h or simply define it locally. Any unique string with @@ -111,14 +112,14 @@ typedef struct GncPluginPageReportPrivate SCM cur_report; /// The Option DB for this report. GncOptionDB *cur_odb; - SCM option_change_cb_id; + 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; + size_t name_change_cb_id; /* keep a list of edited reports so that we can destroy them when * the window is closed. */ @@ -550,7 +551,7 @@ 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, nullptr ); @@ -643,22 +644,17 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, scm_call_2(set_needs_save, inst_report, SCM_BOOL_T); 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 != 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->unregister_callback(priv->option_change_cb_id); + priv->option_change_cb_id = 0; priv->cur_odb = nullptr; } @@ -668,12 +664,11 @@ gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, scm_gc_protect_object(priv->cur_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, nullptr, nullptr); -*/ + 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))) { gnc_plugin_page_report_set_fwd_button(report, TRUE); diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index 95f77a34bd..d06f4a4520 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -63,6 +63,25 @@ public: }; using GncOptionSectionPtr = std::shared_ptr; +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 { @@ -138,10 +157,14 @@ public: 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; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index c0629f7c4b..763e7ec78a 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -22,6 +22,7 @@ \********************************************************************/ #include +#include #include #include #include @@ -666,6 +667,31 @@ GncOptionDB::load_from_key_value(std::istream& 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) { @@ -1306,6 +1332,8 @@ gnc_option_db_commit(GncOptionDB* odb) (void*)option.get_name().c_str()); } }); }); + if (!errors) + odb->run_callbacks(); return errors; } diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index f081157b8a..2ca72e6cab 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -799,6 +799,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } %} +%ignore GncOptionDBCallback; %include "gnc-option-date.hpp" %include "gnc-option.hpp" From e43ff93279be4d9ea2cf2d73bf89d6b1dcf0ca05 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 12 Aug 2021 14:30:13 -0700 Subject: [PATCH 236/298] Fix some overly-long lines. --- libgnucash/app-utils/gnc-option.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index a1051aa6b3..784a65b1df 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -125,9 +125,10 @@ GncOption::set_value(ValueType value) if constexpr (std::is_same_v, GncMultichoiceOptionIndexVec>) option.set_multiple(value); - else if constexpr (std::is_same_v || - std::is_same_v, std::string> || - std::is_same_v, char*>) + else if constexpr + (std::is_same_v || + std::is_same_v, std::string> || + std::is_same_v, char*>) option.set_value(value); } }, *m_option); @@ -150,12 +151,14 @@ GncOption::set_default_value(ValueType value) (std::is_same_v, GncOptionMultichoiceValue>) { - if constexpr (std::is_same_v, - GncMultichoiceOptionIndexVec>) + if constexpr + (std::is_same_v, + GncMultichoiceOptionIndexVec>) option.set_multiple(value); - else if constexpr (std::is_same_v || - std::is_same_v, std::string> || - std::is_same_v, char*>) + else if constexpr + (std::is_same_v || + std::is_same_v, std::string> || + std::is_same_v, char*>) option.set_default_value(value); } }, *m_option); From 852b2ffc2effe61d13585141cd62505eb1770fdd Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 13 Aug 2021 14:01:27 -0700 Subject: [PATCH 237/298] Handle bare currency mnemonics, catch invalid GUID string exceptions. --- libgnucash/app-utils/gnc-option-impl.cpp | 38 +++++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index b0d4132517..a20970303f 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -33,6 +33,8 @@ extern "C" #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"}; @@ -348,20 +350,40 @@ qof_instance_from_guid(GncGUID* guid, GncOptionUIType type) QofInstance* qof_instance_from_string(const std::string& str, GncOptionUIType type) { + QofInstance* retval{nullptr}; + // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic if (type == GncOptionUIType::CURRENCY || type == GncOptionUIType::COMMODITY) { auto book{gnc_get_current_book()}; - auto sep{str.find(":")}; - auto name_space{str.substr(0, sep)}; - auto mnemonic{str.substr(sep + 1, -1)}; auto table = gnc_commodity_table_get_table(book); - return QOF_INSTANCE(gnc_commodity_table_lookup(table, - name_space.c_str(), - mnemonic.c_str())); + auto sep{str.find(":")}; + if (sep != std::string::npos) + { + auto name_space{str.substr(0, sep)}; + auto mnemonic{str.substr(sep + 1, -1)}; + retval = QOF_INSTANCE(gnc_commodity_table_lookup(table, + name_space.c_str(), + mnemonic.c_str())); + } + if (!retval && type == GncOptionUIType::CURRENCY) + retval = QOF_INSTANCE(gnc_commodity_table_lookup(table, + "CURRENCY", + str.c_str())); } - auto guid{static_cast(gnc::GUID::from_string(str))}; - return qof_instance_from_guid(&guid, type); + + if (!retval) + { + 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 From 1cd2cf211c4e1d46c81d9c119d4d52d12aad4445 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 13 Aug 2021 14:05:49 -0700 Subject: [PATCH 238/298] Specialize QofInstance* options set_value, catch validation exceptions. GncOptionValue.get_value() returns a QofInstance* but reports store them as strings, either commodity mnemonics or GUIDs. Specialize set_value/set_default_value from scheme to handle those possibilities. GncOptionValidatedValue throws an invalid_argument exception if it's fed an invalid argument. Catch that so that it doesn't crash the program. --- libgnucash/app-utils/gnc-optiondb.i | 168 ++++++++++++++++++---------- 1 file changed, 110 insertions(+), 58 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 2ca72e6cab..5462a0d2aa 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -47,6 +47,8 @@ namespace std { #include "gnc-optiondb-impl.hpp" #include "gnc-option-date.hpp" +static const QofLogModule log_module = "gnc.optiondb"; + SCM scm_init_sw_gnc_optiondb_module(void); %} @@ -855,72 +857,122 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); { if (!$self) return; - std::visit([new_value](auto& option) { - if constexpr (std::is_same_v, - GncOptionDateValue>) - { - 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; - } + try { + std::visit([new_value](auto& option) { + if constexpr (std::is_same_v, + GncOptionDateValue>) + { + 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 (std::is_same_v, - GncOptionMultichoiceValue>) - { - option.set_multiple(scm_to_multichoices(new_value, option)); - return; - } + if constexpr (std::is_same_v, + GncOptionMultichoiceValue>) + { + option.set_multiple(scm_to_multichoices(new_value, option)); + return; + } - if constexpr (std::is_same_v, - GncOptionRangeValue>) - { - 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; - } - - auto value{scm_to_value>(new_value)}; //Can't inline, set_value takes arg by reference. - option.set_value(value); - }, swig_get_option($self)); + if constexpr (std::is_same_v, + GncOptionRangeValue>) + { + 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 (std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>) + { + 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; + } + auto value{scm_to_value>(new_value)}; //Can't inline, set_value takes arg by reference. + option.set_value(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; - std::visit([new_value](auto& option) { - if constexpr (std::is_same_v, - GncOptionDateValue>) - { - 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 (std::is_same_v, - GncOptionMultichoiceValue>) - { - option.set_default_multiple(scm_to_multichoices(new_value, - option)); - return; - } - if constexpr (std::is_same_v, - GncOptionRangeValue>) - { - 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; - } - 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)); + try { + std::visit([new_value](auto& option) { + if constexpr (std::is_same_v, + GncOptionDateValue>) + { + 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 (std::is_same_v, + GncOptionMultichoiceValue>) + { + option.set_default_multiple(scm_to_multichoices(new_value, + option)); + return; + } + if constexpr (std::is_same_v, + GncOptionRangeValue>) + { + 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 (std::is_same_v, + GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>) + { + 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; + } + 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() From 474bc360f4cb731099bdb1dab837579c43813280 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 15 Aug 2021 14:25:52 -0700 Subject: [PATCH 239/298] Improve converting vectors to SCM lists. --- libgnucash/app-utils/gnc-optiondb.i | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 5462a0d2aa..969d8ec77f 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -358,12 +358,9 @@ scm_from_value(GncOptionAccountList value) { SCM s_list = SCM_EOL; for (auto acct : value) - { - SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0)); - s_list = scm_append(scm_list_2(s_list, elem)); - } - - return s_list; + s_list = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0), + s_list); + return scm_reverse(s_list); } QofBook* gnc_option_test_book_new(); @@ -533,20 +530,18 @@ gnc_option_test_book_destroy(QofBook* book) { $result = SCM_EOL; for (auto acct : $1) - { - SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0)); - $result = scm_append(scm_list_2($result, elem)); - } + $result = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0), + $result); + $result = scm_reverse($result); } %typemap(out) GncOptionAccountList& { $result = SCM_EOL; for (auto acct : *$1) - { - SCM elem = scm_list_1(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0)); - $result = scm_append(scm_list_2($result, elem)); - } + $result = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0), + $result); + $result = scm_reverse ($result) } wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); From e77b5ec03bb375765427c57da2b6ab23eb4b1aef Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 15 Aug 2021 14:27:59 -0700 Subject: [PATCH 240/298] Fix RelativeDateValue GtkListModel spec: No tooltips anymore. --- gnucash/gnome-utils/dialog-options.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 23c0ff7e35..70e1c4c060 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1297,7 +1297,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) { auto renderer = gtk_cell_renderer_text_new(); - auto store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + 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) From 53fac914c01f354475c36996d33e3a6245c476fd Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 17 Aug 2021 15:30:44 -0700 Subject: [PATCH 241/298] Re-enable connecting the buttons on the Stylesheet Edit select dialog. --- gnucash/gnome/dialog-report-style-sheet.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp index b56564a4c9..e92bf4a100 100644 --- a/gnucash/gnome/dialog-report-style-sheet.cpp +++ b/gnucash/gnome/dialog-report-style-sheet.cpp @@ -73,13 +73,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) * ************************************************************/ From c06924622ced87b4abfb38fb6f21f7e013db2f54 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 17 Aug 2021 15:34:45 -0700 Subject: [PATCH 242/298] Null-value protection for GUID and account values. --- gnucash/report/html-utilities.scm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gnucash/report/html-utilities.scm b/gnucash/report/html-utilities.scm index 81abad44bf..bd185dac68 100644 --- a/gnucash/report/html-utilities.scm +++ b/gnucash/report/html-utilities.scm @@ -79,10 +79,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))) From 42b4755aea92134211c3c1ea09c59aac651779c0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 17 Aug 2021 15:36:51 -0700 Subject: [PATCH 243/298] Set the initial value on Relative Date combo boxes. --- gnucash/gnome-utils/dialog-options.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 70e1c4c060..b31810fecc 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1310,6 +1310,7 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) /* 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); From bbe8ebbc15eb64b92a1f0e1d526270e136f801f0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 17 Aug 2021 15:38:07 -0700 Subject: [PATCH 244/298] Reserve space for the accounts in their vector. Instead of creating an empty vector and then adding the accounts to the end of it. --- gnucash/gnome-utils/dialog-options.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index b31810fecc..ee14d4934a 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1653,7 +1653,8 @@ public: { auto widget{GNC_TREE_VIEW_ACCOUNT(get_widget())}; auto acc_list = gnc_tree_view_account_get_selected_accounts(widget); - GncOptionAccountList acc_vec(g_list_length(acc_list)); + GncOptionAccountList acc_vec; + acc_vec.reserve(g_list_length(acc_list)); for (auto node = acc_list; node; node = g_list_next(node)) acc_vec.push_back(static_cast(node->data)); g_list_free(acc_list); From 3d1812aacd340193480542f2946461b27d4c18d3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 17 Aug 2021 15:41:26 -0700 Subject: [PATCH 245/298] Explicitly cast a value to the decltype of get_value(). Can't always rely on implicit conversion working. --- libgnucash/app-utils/gnc-optiondb.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 969d8ec77f..4350f1c83c 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -899,7 +899,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return; } auto value{scm_to_value>(new_value)}; //Can't inline, set_value takes arg by reference. - option.set_value(value); + option.set_value(static_cast(value)); }, swig_get_option($self)); } catch(const std::invalid_argument& err) From 97a317b50cafbb507bdf53206e748aae277befb1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 17 Aug 2021 15:47:11 -0700 Subject: [PATCH 246/298] Ensure that alpha values in colors aren't passed to html. html color tags don't like them. --- libgnucash/app-utils/options.scm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 1dd35f963f..33ee587bf1 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -72,7 +72,12 @@ (GncOption-get-scm-value option)) (define-public (gnc:color-option->html opt) - (format #f "#~a" (GncOption-get-scm-value 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))) From 10381d42e078658d685ac3ccdf994fe9f1bc2650 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 18 Aug 2021 13:38:06 -0700 Subject: [PATCH 247/298] Fix color option handling. Read and write color options the way legacy code does. Pass only values with no alpha to gdk_rgba_parse because it fails if a hex-string has alpha. --- gnucash/gnome-utils/dialog-options.cpp | 8 ++++++-- libgnucash/app-utils/gnc-option-impl.hpp | 9 +++++++-- libgnucash/app-utils/gnc-option.cpp | 5 +++++ libgnucash/app-utils/gnc-option.hpp | 19 +++++++++++++++++++ libgnucash/app-utils/gnc-optiondb.i | 23 ++++++++++++++++++++++- 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index ee14d4934a..7702c5485c 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -2140,8 +2140,12 @@ public: void set_ui_item_from_option(GncOption& option) noexcept override { GdkRGBA color; - auto rgba_str{g_strdup_printf("#%s", - option.get_value().c_str())}; + /* 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()); diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index ae386afe3d..663fba1288 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -388,11 +388,16 @@ gnc_option_to_scheme (std::ostream& oss, const OptType& opt) { oss << ")"; } + return oss; } - else + + if constexpr (std::is_same_v, std::string>) { - oss << "\"" << qof_instance_to_string(value) << "\""; + if (type == GncOptionUIType::COLOR) + return output_color_value(oss, value); } + + oss << "\"" << qof_instance_to_string(value) << "\""; return oss; } diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 784a65b1df..f249ff06b7 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -453,7 +453,12 @@ GncOption::to_scheme(std::ostream& oss) const else if constexpr (std::is_same_v, std::string>) + { + if (option.get_ui_type() == GncOptionUIType::COLOR) + output_color_value(oss, option.get_value()); + else oss << '"' << option << '"'; + } else if constexpr (std::is_same_v, GncOptionRangeValue> || diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 089ff08250..1677e7b5d2 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include "gnc-option-ui.hpp" @@ -149,6 +150,24 @@ 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; +} + template GncOption* gnc_make_option(const char* section, const char* name, const char* key, const char* doc_string, diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 4350f1c83c..d53cc02421 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -46,6 +46,8 @@ namespace std { #include "gnc-optiondb.hpp" #include "gnc-optiondb-impl.hpp" #include "gnc-option-date.hpp" +#include +#include static const QofLogModule log_module = "gnc.optiondb"; @@ -240,6 +242,24 @@ scm_from_value(GncOwner* value) return scm_from_value(value); } +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) { @@ -257,6 +277,8 @@ scm_to_value(SCM 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); @@ -552,7 +574,6 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); #include #include #include -#include #include "gnc-option.hpp" #include "gnc-option-ui.hpp" From b361582cf22f13c34758befe848468569cfa98b1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 18 Aug 2021 14:37:20 -0700 Subject: [PATCH 248/298] Ensure option UI is sorted: Tabs by names, items by keys. --- libgnucash/app-utils/gnc-option.hpp | 6 ++++++ libgnucash/app-utils/gnc-optiondb-impl.hpp | 7 +++++++ libgnucash/app-utils/gnc-optiondb.cpp | 8 ++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 1677e7b5d2..414d7e39f9 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -138,6 +138,12 @@ private: 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) { diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index d06f4a4520..97885c0fec 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -63,6 +63,13 @@ public: }; 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 diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 763e7ec78a..be005b1c10 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -132,7 +132,9 @@ GncOptionSection::foreach_option(std::function func) con void GncOptionSection::add_option(GncOption&& option) { - m_options.emplace_back(std::move(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 @@ -176,8 +178,10 @@ GncOptionDB::register_option(const char* sectname, GncOption&& option) return; } - m_sections.emplace_back(std::make_shared(sectname)); + 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 From 7392ac6fcf853299d9c16fbf2c89dd337b0bbd44 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 18 Aug 2021 15:51:19 -0700 Subject: [PATCH 249/298] Prevent running off the end of the GncOptionUI widget's parent tree. --- gnucash/gnome-utils/dialog-options.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 7702c5485c..fddf9400e5 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -228,9 +228,16 @@ gnc_option_get_gtk_widget (const GncOption* option) static void dialog_changed_internal (GtkWidget *widget, bool sensitive) { - while (widget && !GTK_IS_WINDOW(widget)) - widget = gtk_widget_get_parent(widget); - if (!widget) return; + g_return_if_fail(widget); + while (TRUE) + { + auto new_widget = gtk_widget_get_parent(widget); + if (new_widget && GTK_IS_WIDGET(new_widget) && + GTK_IS_WINDOW(new_widget)) + widget = new_widget; + else + break; + } /* 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 */ From b3f96701a0603a0a84bc425f6e81c1f2363a6219 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 18 Aug 2021 16:14:21 -0700 Subject: [PATCH 250/298] GncOptionAccountListValue: Make setter param cv match template. --- libgnucash/app-utils/gnc-option-impl.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 663fba1288..5aaf2e1785 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -947,15 +947,20 @@ public: 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 (const GncOptionAccountList& values) { + void set_value (GncOptionAccountList values) { if (validate(values)) //throw! m_value = values; } - void set_default_value (const GncOptionAccountList& values) { + void set_default_value (GncOptionAccountList values) { if (validate(values)) //throw! m_value = m_default_value = values; From 62ab148a3e3b7be1852a940e6209b9cdbfecdd1a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 19 Aug 2021 09:48:44 -0700 Subject: [PATCH 251/298] Output a pair when writing relative date values to scheme file. So that the can be properly recognized on input. --- libgnucash/app-utils/gnc-option-date.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp index 332c6bcd7d..1e6ae7560d 100644 --- a/libgnucash/app-utils/gnc-option-date.cpp +++ b/libgnucash/app-utils/gnc-option-date.cpp @@ -557,6 +557,6 @@ gnc_relative_date_to_time64(RelativeDatePeriod period) std::ostream& operator<<(std::ostream& ostr, RelativeDatePeriod per) { - ostr << gnc_relative_date_display_string(per); + ostr << "'reldate . " << gnc_relative_date_display_string(per); return ostr; } From 3935f1a91b53fd8d4013b44d14704882609effdc Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 19 Aug 2021 11:13:29 -0700 Subject: [PATCH 252/298] Don't put labels on checkboxes, they have their own. --- gnucash/gnome-utils/dialog-options.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index fddf9400e5..7af187e75e 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -348,13 +348,15 @@ gnc_option_set_ui_widget(GncOption& option, GtkGrid *page_box, gint grid_row) 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 */ - 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); - + /* 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 From a487ca3f9822cc8e65f0d28e2c267b64a2a2c697 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 25 Aug 2021 12:16:15 -0700 Subject: [PATCH 253/298] Improve template conditional readability with custom traits values. --- libgnucash/app-utils/gnc-option-impl.hpp | 61 ++++----- libgnucash/app-utils/gnc-option.cpp | 150 +++++++++++------------ libgnucash/app-utils/gnc-option.hpp | 39 +++++- libgnucash/app-utils/gnc-optiondb.i | 72 +++++------ 4 files changed, 165 insertions(+), 157 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 5aaf2e1785..373409be4e 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -237,6 +237,18 @@ QofInstance* qof_instance_from_string(const std::string& str, 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, GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>); +}; + +template inline constexpr bool +is_QofInstanceValue_v = is_QofInstanceValue::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 * >>. @@ -246,16 +258,9 @@ std::string qof_instance_to_string(const QofInstance* inst); */ #ifndef SWIG template> && - ! (std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue> || - std::is_same_v, - GncOptionRangeValue> || - std::is_same_v, - GncOptionRangeValue>), int> = 0> + typename std::enable_if_t && + ! (is_QofInstanceValue_v || + is_RangeValue_v), int> = 0> std::ostream& operator<<(std::ostream& oss, const OptType& opt) { oss << opt.get_value(); @@ -271,10 +276,7 @@ operator<< >(std::ostream& oss, } template, - GncOptionValidatedValue> || - std::is_same_v, - GncOptionValue >, int> = 0> + typename std::enable_if_t, int> = 0> inline std::ostream& operator<< (std::ostream& oss, const OptType& opt) { @@ -296,16 +298,9 @@ operator<< (std::ostream& oss, const OptType& opt) } template> && - !(std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue> || - std::is_same_v, - GncOptionRangeValue> || - std::is_same_v, - GncOptionRangeValue>), int> = 0> + typename std::enable_if_t && + !(is_QofInstanceValue_v || + is_RangeValue_v), int> = 0> std::istream& operator>>(std::istream& iss, OptType& opt) { std::decay_t value; @@ -322,11 +317,7 @@ std::istream& operator>>(std::istream& iss, OptType& opt) } template, - GncOptionValidatedValue> || - std::is_same_v, - GncOptionValue >, - int> = 0> + typename std::enable_if_t, int> = 0> std::istream& operator>> (std::istream& iss, OptType& opt) { @@ -507,11 +498,7 @@ private: }; template, - GncOptionRangeValue> || - std::is_same_v, - GncOptionRangeValue>, - int> = 0> + typename std::enable_if_t, int> = 0> inline std::ostream& operator<< (std::ostream& oss, const OptType& opt) { @@ -522,11 +509,7 @@ operator<< (std::ostream& oss, const OptType& opt) } template, - GncOptionRangeValue> || - std::is_same_v, - GncOptionRangeValue>, - int> = 0> + typename std::enable_if_t, int> = 0> inline std::istream& operator>> (std::istream& iss, OptType& opt) { diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index f249ff06b7..b28a925791 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -34,8 +34,7 @@ extern "C" } template >, + typename std::enable_if_t, int>> GncOption::GncOption(const char* section, const char* name, const char* key, const char* doc_string, @@ -49,31 +48,30 @@ GncOption::GncOption(const char* section, const char* name, template ValueType GncOption::get_value() const { - return std::visit([](const auto option)->ValueType { - if constexpr (std::is_same_v, std::decay_t>) + return std::visit( + [](const auto option)->ValueType { + if constexpr (is_same_decayed_v) return option.get_value(); - if constexpr (std::is_same_v, - GncOptionDateValue> && - std::is_same_v, + if constexpr (is_same_decayed_v) + { + if constexpr (is_same_decayed_v) return option.get_period(); - if constexpr (std::is_same_v, - GncOptionDateValue> && - std::is_same_v, - size_t>) + if constexpr (std::is_same_v) return option.get_period_index(); - if constexpr - (std::is_same_v, - GncOptionMultichoiceValue> && - std::is_same_v, - size_t>) + return ValueType{}; + } + if constexpr (is_same_decayed_v) + { + if constexpr (std::is_same_v) return option.get_index(); - if constexpr - (std::is_same_v, - GncOptionMultichoiceValue> && - std::is_same_v, + if constexpr (is_same_decayed_v) return option.get_multiple(); + } return ValueType {}; }, *m_option); } @@ -81,23 +79,25 @@ GncOption::get_value() const template ValueType GncOption::get_default_value() const { - return std::visit([](const auto option)->ValueType { - if constexpr (std::is_same_v, std::decay_t>) + return std::visit( + [](const auto option)->ValueType { + if constexpr (is_same_decayed_v) return option.get_default_value(); - if constexpr (std::is_same_v, - GncOptionDateValue> && - std::is_same_v, + if constexpr (is_same_decayed_v) + { + if constexpr (is_same_decayed_v) return option.get_default_period(); - if constexpr (std::is_same_v, - GncOptionDateValue> && - std::is_same_v, - size_t>) + if constexpr (std::is_same_v) return option.get_default_period_index(); + return ValueType{}; + } if constexpr - (std::is_same_v, + (is_same_decayed_v && - std::is_same_v, + is_same_decayed_v) return option.get_default_multiple(); return ValueType {}; @@ -108,26 +108,24 @@ GncOption::get_default_value() const template void GncOption::set_value(ValueType value) { - std::visit([value](auto& option) { + std::visit( + [value](auto& option) { if constexpr - (std::is_same_v, - std::decay_t> || - (std::is_same_v, + (is_same_decayed_v || + (is_same_decayed_v && - (std::is_same_v, - RelativeDatePeriod> || - std::is_same_v, size_t>))) + (is_same_decayed_v || + std::is_same_v))) option.set_value(value); - if constexpr - (std::is_same_v, + if constexpr (is_same_decayed_v) { - if constexpr (std::is_same_v, + if constexpr (is_same_decayed_v) option.set_multiple(value); else if constexpr (std::is_same_v || - std::is_same_v, std::string> || + is_same_decayed_v || std::is_same_v, char*>) option.set_value(value); } @@ -137,27 +135,23 @@ GncOption::set_value(ValueType value) template void GncOption::set_default_value(ValueType value) { - std::visit([value](auto& option) { + std::visit( + [value](auto& option) { if constexpr - (std::is_same_v, - std::decay_t> || - (std::is_same_v, - GncOptionDateValue> && - (std::is_same_v, - RelativeDatePeriod> || - std::is_same_v, size_t>))) + (is_same_decayed_v|| + (is_same_decayed_v && + (is_same_decayed_v || + std::is_same_v))) option.set_default_value(value); - if constexpr - (std::is_same_v, + if constexpr (is_same_decayed_v) { - if constexpr - (std::is_same_v, + if constexpr (is_same_decayed_v) - option.set_multiple(value); + option.set_default_multiple(value); else if constexpr (std::is_same_v || - std::is_same_v, std::string> || + is_same_decayed_v || std::is_same_v, char*>) option.set_default_value(value); } @@ -174,7 +168,7 @@ GncOption::get_limits(ValueType& max, ValueType& min, ValueType& step) const noe { std::visit([&max, &min, &step](const auto& option) { if constexpr - (std::is_same_v, + (is_same_decayed_v>) option.get_limits(max, min, step); }, *m_option); @@ -292,8 +286,9 @@ GncOption::is_changed() const noexcept bool GncOption::is_multiselect() const noexcept { - return std::visit([](const auto& option)->bool { - if constexpr (std::is_same_v, + return std::visit( + [](const auto& option)->bool { + if constexpr (is_same_decayed_v) return option.is_multiselect(); else @@ -304,16 +299,17 @@ GncOption::is_multiselect() const noexcept template bool GncOption::validate(ValueType value) const { - return std::visit([value] (const auto& option) -> bool { - if constexpr ((std::is_same_v, + return std::visit( + [value] (const auto& option) -> bool { + if constexpr ((is_same_decayed_v && - std::is_same_v, + is_same_decayed_v) || - (std::is_same_v, + (is_same_decayed_v && - std::is_same_v, + is_same_decayed_v) || - std::is_same_v, + is_same_decayed_v>) return option.validate(value); else @@ -324,10 +320,11 @@ GncOption::validate(ValueType value) const std::size_t GncOption::num_permissible_values() const { - return std::visit([] (const auto& option) -> size_t { - if constexpr (std::is_same_v, + return std::visit( + [] (const auto& option) -> size_t { + if constexpr (is_same_decayed_v || - std::is_same_v, + is_same_decayed_v) return option.num_permissible_values(); else @@ -338,10 +335,11 @@ GncOption::num_permissible_values() const std::size_t GncOption::permissible_value_index(const char* value) const { - return std::visit([&value] (const auto& option) -> size_t { - if constexpr (std::is_same_v, + return std::visit( + [&value] (const auto& option) -> size_t { + if constexpr (is_same_decayed_v || - std::is_same_v, + is_same_decayed_v) return option.permissible_value_index(value); else @@ -393,10 +391,7 @@ bool GncOption::is_alternate() const noexcept { return std::visit([](auto& option) -> bool { - if constexpr(std::is_same_v, - GncOptionRangeValue> || - std::is_same_v, - GncOptionRangeValue>) + if constexpr(is_RangeValue_v) return option.is_alternate(); return false; }, *m_option); @@ -406,10 +401,7 @@ void GncOption::set_alternate(bool alt) noexcept { std::visit([alt](auto& option) { - if constexpr(std::is_same_v, - GncOptionRangeValue> || - std::is_same_v, - GncOptionRangeValue>) + if constexpr(is_RangeValue_v) option.set_alternate(alt); }, *m_option); } diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 414d7e39f9..e873b0d70d 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -53,6 +53,38 @@ template class GncOptionRangeValue; template class GncOptionValidatedValue; class GncOptionDateValue; +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, @@ -82,14 +114,13 @@ class GncOption { public: template >, + typename std::enable_if_t, int> = 0> + GncOption(OptionType option) : m_option{std::make_unique(option)} {} template >, + typename std::enable_if_t, int> = 0> GncOption(const char* section, const char* name, const char* key, const char* doc_string, diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index d53cc02421..09595b1b29 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -263,7 +263,7 @@ scm_color_list_to_string(SCM list) template inline ValueType scm_to_value(SCM new_value) { - if constexpr (std::is_same_v, SCM>) + if constexpr (is_same_decayed_v) return new_value; return ValueType{}; } @@ -816,7 +816,26 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return scm_cons(desig, scm_from_int(val)); } - %} +template +struct is_MultichoiceOrRange +{ + static constexpr bool value = + is_same_decayed_v || + is_same_decayed_v>; +}; + +template +inline constexpr bool is_MultichoiceOrRange_v = is_MultichoiceOrRange::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; %include "gnc-option-date.hpp" @@ -840,16 +859,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); if (!$self) return SCM_BOOL_F; return std::visit([](const auto& option)->SCM { - if constexpr (std::is_same_v, - GncOptionMultichoiceValue> || - std::is_same_v, - GncOptionRangeValue>) + if constexpr (is_MultichoiceOrRange_v) return get_scm_value(option); auto value{option.get_value()}; - if constexpr (std::is_same_v, - SCM>) - return value; - return scm_from_value(static_cast(value)); + return return_scm_value(value); }, swig_get_option($self)); } SCM get_scm_default_value() @@ -857,25 +870,20 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); if (!$self) return SCM_BOOL_F; return std::visit([](const auto& option)->SCM { - if constexpr (std::is_same_v, - GncOptionMultichoiceValue> || - std::is_same_v, - GncOptionRangeValue>) + if constexpr (is_MultichoiceOrRange_v) return get_scm_default_value(option); auto value{option.get_default_value()}; - if constexpr (std::is_same_v, - SCM>) - return value; - return scm_from_value(static_cast(value)); + return return_scm_value(value); }, swig_get_option($self)); } + void set_value_from_scm(SCM new_value) { if (!$self) return; try { std::visit([new_value](auto& option) { - if constexpr (std::is_same_v, + if constexpr (is_same_decayed_v) { if (scm_date_absolute(new_value)) @@ -885,14 +893,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return; } - if constexpr (std::is_same_v, + if constexpr (is_same_decayed_v) { option.set_multiple(scm_to_multichoices(new_value, option)); return; } - if constexpr (std::is_same_v, + if constexpr (is_same_decayed_v>) { if (scm_is_pair(new_value)) @@ -901,10 +909,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_value(scm_to_int(new_value)); return; } - if constexpr (std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>) + if constexpr (is_QofInstanceValue_v) { if (scm_is_string(new_value)) { @@ -937,7 +942,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); return; try { std::visit([new_value](auto& option) { - if constexpr (std::is_same_v, + if constexpr (is_same_decayed_v) { if (scm_date_absolute(new_value)) @@ -946,14 +951,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_default_value(scm_relative_date_get_period(new_value)); return; } - if constexpr (std::is_same_v, + if constexpr (is_same_decayed_v) { option.set_default_multiple(scm_to_multichoices(new_value, option)); return; } - if constexpr (std::is_same_v, + if constexpr (is_same_decayed_v>) { if (scm_is_pair(new_value)) @@ -962,10 +967,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); option.set_default_value(scm_to_int(new_value)); return; } - if constexpr (std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>) + if constexpr (is_QofInstanceValue_v) { if (scm_is_string(new_value)) { @@ -996,11 +998,11 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); if (!self) return SCM_BOOL_F; return std::visit([](const auto& option)->SCM { - if constexpr (std::is_same_v, - GncOptionMultichoiceValue>) + if constexpr (is_same_decayed_v) return scm_c_eval_string("'multichoice"); else if constexpr (std::is_same_v) + bool>) return scm_c_eval_string("'boolean"); else return SCM_BOOL_F; From bed44f404f0c30ca6a36f9863250134dfb9e6612 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 28 Aug 2021 16:52:36 -0700 Subject: [PATCH 254/298] Remove the always questionable Scheme generation and parsing code. It's more reasonable to do that in Scheme than in C++. --- libgnucash/app-utils/gnc-option-impl.hpp | 274 ----------------- libgnucash/app-utils/gnc-option.cpp | 257 +++++----------- libgnucash/app-utils/gnc-option.hpp | 3 - libgnucash/app-utils/gnc-optiondb-impl.hpp | 16 - libgnucash/app-utils/gnc-optiondb.cpp | 287 ------------------ libgnucash/app-utils/gnc-optiondb.i | 16 +- .../app-utils/test/gtest-gnc-option.cpp | 284 ----------------- .../app-utils/test/gtest-gnc-optiondb.cpp | 109 ------- 8 files changed, 73 insertions(+), 1173 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 373409be4e..f3294bd1e5 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -220,8 +220,6 @@ public: } void reset_default_value() { m_value = m_default_value; } bool is_changed() const noexcept { return m_value != m_default_value; } - std::ostream& to_scheme(std::ostream&) const; - std::istream& from_scheme(std::istream&); GncOptionUIType get_ui_type() const noexcept { return m_ui_type; } void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; } private: @@ -353,85 +351,6 @@ operator>> >(std::istream& iss, opt.set_value(instr == "#t" ? true : false); return iss; } -template, - GncOptionValidatedValue> || - std::is_same_v, - GncOptionValue>, - int> = 0> -inline std::ostream& -gnc_option_to_scheme (std::ostream& oss, const OptType& opt) -{ - auto value = opt.get_value(); - auto type = opt.get_ui_type(); - if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) - { - if (type == GncOptionUIType::COMMODITY) - { - oss << commodity_scm_intro; - oss << "\"" << - gnc_commodity_get_namespace(GNC_COMMODITY(value)) << "\" "; - } - - oss << "\"" << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)) << "\""; - - if (type == GncOptionUIType::COMMODITY) - { - oss << ")"; - } - return oss; - } - - if constexpr (std::is_same_v, std::string>) - { - if (type == GncOptionUIType::COLOR) - return output_color_value(oss, value); - } - - oss << "\"" << qof_instance_to_string(value) << "\""; - return oss; -} - -template, - GncOptionValidatedValue> || - std::is_same_v, - GncOptionValue>, int> = 0> -inline std::istream& -gnc_option_from_scheme (std::istream& iss, OptType& opt) -{ - std::string instr; - auto type = opt.get_ui_type(); - if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) - { - std::string name_space, mnemonic; - if (type == GncOptionUIType::COMMODITY) - { - iss.ignore(strlen(commodity_scm_intro) + 1, '"'); - std::getline(iss, name_space, '"'); - } - else - name_space = GNC_COMMODITY_NS_CURRENCY; - iss.ignore(2, '"'); - std::getline(iss, mnemonic, '"'); - - if (type == GncOptionUIType::COMMODITY) - iss.ignore(2, ')'); - else - iss.ignore(1, '"'); - - instr = name_space + ":"; - instr += mnemonic; - } - else - { - iss.ignore(1, '"'); - std::getline(iss, instr, '"'); - } - opt.set_value(qof_instance_from_string(instr, opt.get_ui_type())); - return iss; -} #endif // SWIG /** @@ -807,70 +726,6 @@ operator>> (std::istream& iss, return iss; } -template, - GncOptionMultichoiceValue>, - int> = 0> -inline std::ostream& -gnc_option_to_scheme(std::ostream& oss, const OptType& opt) -{ - auto indexes{opt.get_multiple()}; - if (indexes.size() > 1) - oss << "'("; - bool first = true; - for (auto index : indexes) - { - if (first) - first = false; - else - oss << " "; - oss << "'" << opt.permissible_value(index); - } - if (indexes.size() > 1) - oss << ')'; - return oss; -} - -template, - GncOptionMultichoiceValue>, - int> = 0> -inline std::istream& -gnc_option_from_scheme(std::istream& iss, OptType& opt) -{ - iss.ignore(3, '\''); - auto c{iss.peek()}; - if (static_cast(c) == '(') - { - GncMultichoiceOptionIndexVec values; - iss.ignore(3, '\''); - while (true) - { - std::string str; - std::getline(iss, str, ' '); - if (!str.empty()) - { - if (str.back() == ')') - { - str.pop_back(); - break; - } - values.push_back(opt.permissible_value_index(str.c_str())); - iss.ignore(2, '\''); - } - else - break; - } - opt.set_multiple(values); - } - else - { - std::string str; - std::getline(iss, str, ' '); - opt.set_value(str); - } - return iss; -} using GncOptionAccountList = std::vector; @@ -998,54 +853,6 @@ operator>> (std::istream& iss, return iss; } -template, - GncOptionAccountListValue>, - int> = 0> -inline std::ostream& -gnc_option_to_scheme(std::ostream& oss, const OptType& opt) -{ - auto values{opt.get_value()}; - oss << "'(\""; - bool first = true; - for (auto value : values) - { - if (first) - first = false; - else - oss << " \""; - oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"'; - } - oss << ')'; - return oss; -} - -template, - GncOptionAccountListValue>, - int> = 0> -inline std::istream& -gnc_option_from_scheme(std::istream& iss, OptType& opt) -{ - GncOptionAccountList values; - iss.ignore(3, '"'); - while (true) - { - std::string str; - std::getline(iss, str, '"'); - if (!str.empty()) - { - values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type())); - iss.ignore(2, '"'); - } - else - break; - } - opt.set_value(values); - iss.ignore(1, ')'); - return iss; -} - class GncOptionAccountSelValue : public OptionClassifier { public: @@ -1129,46 +936,6 @@ operator>> (std::istream& iss, return iss; } -template, - GncOptionAccountSelValue>, - int> = 0> -inline std::ostream& -gnc_option_to_scheme(std::ostream& oss, const OptType& opt) -{ - auto value{opt.get_value()}; - oss << "'(\""; - oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"'; - oss << ')'; - return oss; -} - -template, - GncOptionAccountSelValue>, - int> = 0> -inline std::istream& -gnc_option_from_scheme(std::istream& iss, OptType& opt) -{ - const Account* value; - iss.ignore(3, '"'); - while (true) - { - std::string str; - std::getline(iss, str, '"'); - if (!str.empty()) - { - value = (Account*)qof_instance_from_string(str, opt.get_ui_type()); - iss.ignore(2, '"'); - } - else - break; - } - opt.set_value(value); - iss.ignore(1, ')'); - return iss; -} - /** Date options * A legal date value is a pair of either and a RelativeDatePeriod, the absolute * flag and a time64, or for legacy purposes the absolute flag and a timespec. @@ -1312,45 +1079,4 @@ operator>> (std::istream& iss, /** QofQuery Options */ -inline std::istream& -gnc_option_from_scheme(std::istream& iss, GncOptionValue& opt) -{ -//FIXME: Implement or maybe rethink. - return iss; -} - -inline std::ostream& -gnc_option_to_scheme(std::ostream& oss, GncOptionValue& opt) -{ -//FIXME: Implement or maybe rethink. - return oss; -} - -inline std::istream& -gnc_option_from_scheme(std::istream& iss, GncOptionValue& opt) -{ -//FIXME: Implement or maybe rethink. - return iss; -} - -inline std::ostream& -gnc_option_to_scheme(std::ostream& oss, GncOptionValue& opt) -{ -//FIXME: Implement or maybe rethink. - return oss; -} - -inline std::istream& -gnc_option_from_scheme(std::istream& iss, GncOptionValidatedValue& opt) -{ -//FIXME: Implement or maybe rethink. - return iss; -} - -inline std::ostream& -gnc_option_to_scheme(std::ostream& oss, GncOptionValidatedValue& opt) -{ -//FIXME: Implement or maybe rethink. - return oss; -} #endif //GNC_OPTION_IMPL_HPP_ diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index b28a925791..b96405de0c 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -52,28 +52,28 @@ GncOption::get_value() const [](const auto option)->ValueType { if constexpr (is_same_decayed_v) - return option.get_value(); + return option.get_value(); if constexpr (is_same_decayed_v) { if constexpr (is_same_decayed_v) - return option.get_period(); + RelativeDatePeriod>) + return option.get_period(); if constexpr (std::is_same_v) - return option.get_period_index(); + return option.get_period_index(); return ValueType{}; } if constexpr (is_same_decayed_v) { if constexpr (std::is_same_v) - return option.get_index(); + return option.get_index(); if constexpr (is_same_decayed_v) - return option.get_multiple(); + GncMultichoiceOptionIndexVec>) + return option.get_multiple(); } - return ValueType {}; - }, *m_option); + return ValueType {}; + }, *m_option); } template ValueType @@ -83,25 +83,25 @@ GncOption::get_default_value() const [](const auto option)->ValueType { if constexpr (is_same_decayed_v) - return option.get_default_value(); + return option.get_default_value(); if constexpr (is_same_decayed_v) { if constexpr (is_same_decayed_v) - return option.get_default_period(); + RelativeDatePeriod>) + return option.get_default_period(); if constexpr (std::is_same_v) - return option.get_default_period_index(); + return option.get_default_period_index(); return ValueType{}; } - if constexpr + if constexpr (is_same_decayed_v && + GncOptionMultichoiceValue> && is_same_decayed_v) - return option.get_default_multiple(); - return ValueType {}; - }, *m_option); + GncMultichoiceOptionIndexVec>) + return option.get_default_multiple(); + return ValueType {}; + }, *m_option); } @@ -110,26 +110,26 @@ GncOption::set_value(ValueType value) { std::visit( [value](auto& option) { - if constexpr + if constexpr (is_same_decayed_v || (is_same_decayed_v && + GncOptionDateValue> && (is_same_decayed_v || std::is_same_v))) - option.set_value(value); + option.set_value(value); if constexpr (is_same_decayed_v) - { + GncOptionMultichoiceValue>) + { if constexpr (is_same_decayed_v) - option.set_multiple(value); - else if constexpr - (std::is_same_v || + GncMultichoiceOptionIndexVec>) + 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); + std::is_same_v, char*>) + option.set_value(value); + } + }, *m_option); } template void @@ -137,25 +137,25 @@ GncOption::set_default_value(ValueType value) { std::visit( [value](auto& option) { - if constexpr + if constexpr (is_same_decayed_v|| (is_same_decayed_v && (is_same_decayed_v || std::is_same_v))) - option.set_default_value(value); + option.set_default_value(value); if constexpr (is_same_decayed_v) - { + GncOptionMultichoiceValue>) + { if constexpr (is_same_decayed_v) + GncMultichoiceOptionIndexVec>) option.set_default_multiple(value); - else if constexpr - (std::is_same_v || + else if constexpr + (std::is_same_v || is_same_decayed_v || - std::is_same_v, char*>) - option.set_default_value(value); - } - }, *m_option); + std::is_same_v, char*>) + option.set_default_value(value); + } + }, *m_option); } void GncOption::reset_default_value() @@ -289,11 +289,11 @@ 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); + GncOptionAccountListValue>) + return option.is_multiselect(); + else + return false; + }, *m_option); } template bool @@ -302,19 +302,19 @@ GncOption::validate(ValueType value) const return std::visit( [value] (const auto& option) -> bool { if constexpr ((is_same_decayed_v && + GncOptionMultichoiceValue> && is_same_decayed_v) || + std::string>) || (is_same_decayed_v && + GncOptionMultichoiceValue> && is_same_decayed_v) || + GncMultichoiceOptionIndexVec>) || is_same_decayed_v>) - return option.validate(value); - else - return false; - }, *m_option); + GncOptionValidatedValue>) + return option.validate(value); + else + return false; + }, *m_option); } std::size_t @@ -323,13 +323,13 @@ GncOption::num_permissible_values() const return std::visit( [] (const auto& option) -> size_t { if constexpr (is_same_decayed_v || + GncOptionMultichoiceValue> || is_same_decayed_v) - return option.num_permissible_values(); - else - return size_t_max; - }, *m_option); + GncOptionDateValue>) + return option.num_permissible_values(); + else + return size_t_max; + }, *m_option); } std::size_t @@ -338,13 +338,13 @@ GncOption::permissible_value_index(const char* value) const return std::visit( [&value] (const auto& option) -> size_t { if constexpr (is_same_decayed_v || + GncOptionMultichoiceValue> || is_same_decayed_v) - return option.permissible_value_index(value); - else - return size_t_max;; - }, *m_option); + GncOptionDateValue>) + return option.permissible_value_index(value); + else + return size_t_max;; + }, *m_option); } const char* @@ -424,125 +424,6 @@ GncOption::in_stream(std::istream& iss) }, *m_option); } -std::ostream& -GncOption::to_scheme(std::ostream& oss) const -{ - return std::visit([&oss](auto& option) ->std::ostream& { - if constexpr - ((std::is_same_v, - GncOptionAccountListValue>) || - (std::is_same_v, - GncOptionMultichoiceValue>) || - std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>) - gnc_option_to_scheme(oss, option); - else if constexpr - (std::is_same_v, - GncOptionDateValue>) - oss << "'(" << option << ")"; - else if constexpr - (std::is_same_v, - std::string>) - { - if (option.get_ui_type() == GncOptionUIType::COLOR) - output_color_value(oss, option.get_value()); - else - oss << '"' << option << '"'; - } - else if constexpr - (std::is_same_v, - GncOptionRangeValue> || - std::is_same_v, - GncOptionRangeValue>) - { - if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE) - oss << "'(" << (option.is_alternate() ? - "\"percent\" . " : "\"pixels\" . "); - oss << option.get_value(); - if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE) - oss << ")"; - } - else - oss << option; - return oss; - }, *m_option); -} - -std::istream& -GncOption::from_scheme(std::istream& iss) -{ - return std::visit([&iss](auto& option) -> std::istream& { - if constexpr - ((std::is_same_v, - GncOptionAccountListValue>) || - (std::is_same_v, - GncOptionMultichoiceValue>) || - std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue> || - std::is_same_v, - GncOptionValidatedValue>) - gnc_option_from_scheme(iss, option); - else if constexpr - (std::is_same_v, - GncOptionDateValue>) - { - iss.ignore(2, '('); - iss >> option; - //operator >> clears the trailing ')' - } - else if constexpr - (std::is_same_v, - std::string>) - { - iss.ignore(1, '"'); - std::string input; - std::getline(iss, input, '"'); - option.set_value(input); - } - else if constexpr - (std::is_same_v, - GncOptionRangeValue> || - std::is_same_v, - GncOptionRangeValue>) - { - if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE) - { - iss.ignore(3, '"'); - std::string alt; - iss >> alt; - option.set_alternate( - strncmp(alt.c_str(), "pixels", - strlen("pixels")) == 0); - iss.ignore(4, ' '); - } - if constexpr(std::is_same_v, - GncOptionRangeValue>) - { - int val; - iss >> val; - option.set_value(val); - } - else - { - double val; - iss >> val; - option.set_value(val); - } - } - else - iss >> option; - return iss; - }, *m_option); -} - GncOption* gnc_make_SCM_option(const char* section, const char* name, const char* key, const char* doc_string, diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index e873b0d70d..9374959e2b 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -157,9 +157,6 @@ public: void set_alternate(bool) noexcept; std::ostream& out_stream(std::ostream& oss) const; std::istream& in_stream(std::istream& iss); - std::ostream& to_scheme(std::ostream& oss) const; - std::istream& from_scheme(std::istream& iss); - friend GncOptionVariant& swig_get_option(GncOption*); diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index 97885c0fec..5fa8114702 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -148,18 +148,10 @@ public: 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_scheme(std::ostream& oss, - const char* options_prolog) const noexcept; - std::istream& load_from_scheme(std::istream& iss) noexcept; 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_scheme(std::ostream& oss, - const char* option_prolog, - const std::string& section, - const std::string& name) const noexcept; - std::istream& load_option_scheme(std::istream& iss); std::ostream& save_option_key_value(std::ostream& oss, const std::string& section, const std::string& name) const noexcept; @@ -175,14 +167,6 @@ private: std::function m_get_ui_value; std::function m_set_ui_value; - static constexpr char const* const scheme_tags[] - { - "(let ((option (gnc:lookup-option ", - " ", - ")))", - " ((lambda (o) (if o (gnc:option-set-value o ", - "))) option))" - }; }; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index be005b1c10..133949c6aa 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -261,26 +261,6 @@ GncOptionDB::make_internal(const char* section, const char* name) db_opt->make_internal(); } -std::ostream& -GncOptionDB::save_option_scheme(std::ostream& oss, - const char* option_prolog, - 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 << scheme_tags[0] << option_prolog << "\n"; - oss << scheme_tags[1] << '"' << section.substr(0, classifier_size_max) << "\"\n"; - oss << scheme_tags[1] << '"' << name.substr(0, classifier_size_max) << '"'; - oss << scheme_tags[2] << "\n" << scheme_tags[3]; - db_opt->to_scheme(oss); - oss << scheme_tags[4] << "\n\n"; - - return oss; -} - static inline bool constexpr is_eol(char c) { @@ -330,273 +310,6 @@ is_delim(char c) is_single_quote(c) || is_double_quote(c) || is_semicolon(c); } -static std::string -scan_scheme_symbol_from_streambuf(std::streambuf* sbuf) -{ - std::string retval; - while(sbuf->in_avail() && !is_delim(sbuf->sgetc())) - retval += sbuf->sbumpc(); - return retval; -} - -#ifdef _LIBCPP_VERSION -static inline void constexpr -#else -static inline void -#endif -consume_scheme_comment(std::streambuf* sbuf) -{ - while (sbuf->in_avail() && !is_eol(sbuf->sgetc())) - sbuf->sbumpc(); -} - -static inline std::string -scan_scheme_string_from_streambuf(std::streambuf* sbuf) -{ - std::string retval{static_cast(sbuf->sbumpc())}; - while(sbuf->in_avail() && !is_double_quote(sbuf->sgetc())) - retval += sbuf->sbumpc(); - retval += sbuf->sbumpc(); // Add the closing quote. - return retval; -} - -#ifdef _LIBCPP_VERSION -static inline void constexpr -#else -static inline void -#endif -consume_scheme_whitespace(std::streambuf* sbuf) -{ - while (sbuf->in_avail() && is_whitespace(sbuf->sgetc())) - sbuf->sbumpc(); -} - -enum class IdentType -{ - NAME, //no introducing mark - CONST, //introduced with single quote - STRING, //delimited by double-quotes. - LIST, //introduced ' and delimited by parentheses - FORM //delimited by parentheses without ' introduction. -}; - -struct SchemeId -{ - IdentType m_type; - std::string m_name; - std::vector m_ids; -}; - -/** - * Scheme Parse Tree - * An identifier is a string and a type (name, const, string, or form). - */ - -static void scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id); - -static void -scan_scheme_form_from_streambuf(std::streambuf* sbuf, SchemeId& id) -{ - sbuf->sbumpc(); - if (!sbuf->in_avail()) - return; - char c = sbuf->sgetc(); - while (sbuf->in_avail() && !is_end_paren(c)) - { - SchemeId next_id; - scan_scheme_id_from_streambuf(sbuf, next_id); - if (id.m_name.empty() && next_id.m_type == IdentType::NAME) - { - id.m_name = std::move(next_id.m_name); - continue; - } - id.m_ids.emplace_back(std::move(next_id)); - if (!sbuf->in_avail()) - { - std::string err{"End of streambuf before end of form "}; - err += id.m_name; - throw std::runtime_error(err); - } - c = sbuf->sgetc(); - } - sbuf->sbumpc(); -} - -static void -scan_scheme_list_from_streambuf(std::streambuf* sbuf, std::string& str) -{ - - consume_scheme_whitespace(sbuf); - if (!sbuf->in_avail()) - return; - char c = sbuf->sgetc(); - while (sbuf->in_avail() && !is_end_paren(c)) - { - str += static_cast(sbuf->sbumpc()); - if (!sbuf->in_avail()) - return; - c = sbuf->sgetc(); - } - str += static_cast(sbuf->sbumpc()); -} - -static void -scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id) -{ - consume_scheme_whitespace(sbuf); - if (!sbuf->in_avail()) - return; - auto c{sbuf->sgetc()}; - switch(c) - { - case ';': - consume_scheme_comment(sbuf); - break; - case '"': - id.m_type = IdentType::STRING; - id.m_name = scan_scheme_string_from_streambuf(sbuf); - break; - case '\'': - { - std::string value{static_cast(sbuf->sbumpc())}; - if (sbuf->sgetc() == '(') - { - id.m_type == IdentType::LIST; - scan_scheme_list_from_streambuf(sbuf, value); - if (value.back() != ')') - throw std::runtime_error("End of streambuf before end of form "); - } - else if (sbuf->sgetc() == '"') - throw std::runtime_error("Malformed scheme particle starts '\""); - else - { - id.m_type = IdentType::CONST; - value += scan_scheme_symbol_from_streambuf(sbuf); - } - id.m_name = std::move(value); - break; - } - case '(': - id.m_type = IdentType::FORM; - scan_scheme_form_from_streambuf(sbuf, id); - break; - default: - id.m_type = IdentType::NAME; - id.m_name = scan_scheme_symbol_from_streambuf(sbuf); - break; - } - return; -} - -static inline std::string -unquote_scheme_string(const std::string& str) -{ - if (str.front() == '"' && str.back() == '"') - return str.substr(1, str.size() - 2); - - return str; -} - -static std::optional> -find_form(const SchemeId& toplevel, IdentType type, const char* name) -{ - if (toplevel.m_type == type && toplevel.m_name == name) - return std::ref(toplevel); - for (const auto& id : toplevel.m_ids) - { - if (id.m_type == type && id.m_name == name) - return std::ref(id); - auto child{find_form(id, type, name)}; - if (child) - return child; - } - return std::nullopt; -} - -std::istream& -GncOptionDB::load_option_scheme(std::istream& iss) -{ - auto sbuf{iss.rdbuf()}; - SchemeId toplevel; - std::optional> lookup_id; - bool form_found = false; - while (sbuf->in_avail() && !lookup_id) - { - scan_scheme_id_from_streambuf(sbuf, toplevel); - lookup_id = find_form(toplevel, IdentType::FORM, "gnc:lookup-option"); - } - - if (!lookup_id) - { - iss.setstate(std::ios_base::eofbit); - return iss; // No options - } - const auto& classifier = lookup_id->get().m_ids; - if (classifier.size() != 3) - throw std::runtime_error("Malformed option classifier."); - const auto& section = unquote_scheme_string(classifier[1].m_name); - const auto& name = unquote_scheme_string(classifier[2].m_name); - auto option = find_option(section, name.c_str()); - std::string option_str{section}; - option_str += ':'; - option_str += name; - if (!option) - { - std::string err{"Option not found: "}; - err += option_str; - throw std::runtime_error(err); - } - auto value_id = find_form(toplevel, IdentType::FORM, "gnc:option-set-value"); - if (!(value_id && value_id->get().m_ids.size() == 2)) - { - std::string err{"Option "}; - err += option_str; - throw std::runtime_error(err + " malformed value lambda form."); - } - std::istringstream value_iss{value_id->get().m_ids[1].m_name}; - option->from_scheme(value_iss); - return iss; -} - -std::ostream& -GncOptionDB::save_to_scheme(std::ostream& oss, const char* options_prolog) const noexcept -{ - foreach_section( - [&oss, options_prolog](const GncOptionSectionPtr& section) - { - oss << "\n; Section: " << section->get_name() << "\n\n"; - section->foreach_option( - [&oss, options_prolog, §ion](auto& option) - { - if (!option.is_changed()) - return; - oss << scheme_tags[0] << options_prolog << "\n"; - oss << scheme_tags[1] << '"' << section->get_name().substr(0, classifier_size_max) << "\"\n"; - oss << scheme_tags[1] << '"' << option.get_name().substr(0, classifier_size_max) << '"'; - oss << scheme_tags[2] << "\n" << scheme_tags[3]; - option.to_scheme(oss); - oss << scheme_tags[4] << "\n\n"; - }); - }); - return oss; -} - -std::istream& -GncOptionDB::load_from_scheme(std::istream& iss) noexcept -{ - try { - while (iss.good()) - load_option_scheme(iss); - iss.clear(); //unset eofbit and maybe failbit - } - catch (const std::runtime_error& err) - { - std::cerr << "Load of options from Scheme failed: " << - err.what() << std::endl; - } - return iss; -} - std::ostream& GncOptionDB::save_option_key_value(std::ostream& oss, const std::string& section, diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 09595b1b29..9bc3c13414 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -584,18 +584,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); } %} -%ignore gnc_option_to_scheme; -%ignore gnc_option_from_scheme; /* 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&&); -/* GncOptionDB::save_to_scheme takes and returns a std::stream. Good luck - * converting *that* to anything useful! - */ -%ignore GncOptionDB::save_to_scheme(std::ostream&, const char*); -%ignore GncOptionDB::save_option_scheme(std::ostream&, const char*, const std::string&, const std::string&); -%ignore GncOptionDB::load_option_scheme(std::itream&); /* 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. @@ -837,6 +829,8 @@ inline SCM return_scm_value(ValueType value) %} %ignore GncOptionDBCallback; +%ignore operator<(const GncOption&, const GncOption&); +%ignore operator<(const GncOptionSectionPtr&, const GncOptionSectionPtr&); %include "gnc-option-date.hpp" %include "gnc-option.hpp" @@ -1356,12 +1350,10 @@ inline SCM return_scm_value(ValueType value) }); } - static std::string + std::string gnc_optiondb_save_to_scheme(GncOptionDBPtr& odb, const char* prolog) { - std::ostringstream oss; - odb->save_to_scheme(oss, prolog); - return oss.str(); + return prolog; } %} diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 2edc94af5e..a6de6ebe2c 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -91,26 +91,6 @@ TEST(GncOption, test_string_stream_in) EXPECT_EQ(pepper, option.get_value()); } -TEST(GncOption, test_string_to_scheme) -{ - GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); - std::ostringstream oss; - option.to_scheme(oss); - std::string scheme_str{"\""}; - scheme_str += option.get_value() + "\""; - EXPECT_EQ(oss.str(), scheme_str); -} - -TEST(GncOption, test_string_from_scheme) -{ - GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"}); - std::string pepper{"pepper"}; - std::string scheme_str{"\"pepper\""}; - std::istringstream iss{scheme_str}; - option.from_scheme(iss); - EXPECT_EQ(pepper, option.get_value()); -} - TEST(GncOption, test_int64_t_value) { GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); @@ -136,23 +116,6 @@ TEST(GncOption, test_int64_stream_in) EXPECT_EQ(INT64_C(987654321), option.get_value()); } -TEST(GncOption, test_int64_to_scheme) -{ - GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); - std::ostringstream oss; - option.to_scheme(oss); - EXPECT_STREQ(oss.str().c_str(), "123456789"); -} - -TEST(GncOption, test_int64_from_scheme) -{ - GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); - std::string number{"987654321"}; - std::istringstream iss{number}; - option.from_scheme(iss); - EXPECT_EQ(INT64_C(987654321), option.get_value()); -} - TEST(GncOption, test_bool_stream_out) { GncOption option("foo", "bar", "baz", "Phony Option", false); @@ -176,28 +139,6 @@ TEST(GncOption, test_bool_stream_in) EXPECT_FALSE(option.get_value()); } -TEST(GncOption, test_bool_to_scheme) -{ - GncOption option("foo", "bar", "baz", "Phony Option", false); - std::ostringstream oss; - oss << option; - EXPECT_STREQ(oss.str().c_str(), "#f"); - oss.str(""); - option.set_value(true); - option.to_scheme(oss); - EXPECT_STREQ(oss.str().c_str(), "#t"); -} - -TEST(GncOption, test_bool_from_scheme) -{ - GncOption option("foo", "bar", "baz", "Phony Option", false); - std::istringstream iss("#t"); - iss >> option; - EXPECT_TRUE(option.get_value()); - iss.str("#f"); - option.from_scheme(iss); - EXPECT_FALSE(option.get_value()); -} class GncOptionTest : public ::testing::Test { @@ -242,33 +183,6 @@ TEST_F(GncOptionTest, test_budget_in) gnc_budget_destroy(budget); } -TEST_F(GncOptionTest, test_budget_to_scheme) -{ - auto budget = gnc_budget_new(m_book); - GncOption option{"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; - budget_guid.insert(0, "\""); - budget_guid += "\""; - option.to_scheme(oss); - EXPECT_EQ(budget_guid, oss.str()); - gnc_budget_destroy(budget); -} - -TEST_F(GncOptionTest, test_budget_from_scheme) -{ - auto budget = gnc_budget_new(m_book); - auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()}; - budget_guid.insert(0, "\""); - budget_guid += "\""; - std::istringstream iss{budget_guid}; - GncOption option{GncOptionValue{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}}; - option.from_scheme(iss); - 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.", @@ -462,50 +376,6 @@ TEST_F(GncOptionCommodityTest, test_commodity_in) EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); } -TEST_F(GncOptionCommodityTest, test_currency_to_scheme) -{ - auto option = make_currency_option("foo", "bar", "baz", "Phony Option", - m_eur, true); - - std::string eur_str{make_currency_SCM_str(m_eur)}; - std::ostringstream oss; - option.to_scheme(oss); - EXPECT_EQ(eur_str, oss.str()); -} - -TEST_F(GncOptionCommodityTest, test_commodity_to_scheme) -{ - GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe, - GncOptionUIType::COMMODITY}; - - std::string hpe_str{make_commodity_SCM_str(m_hpe)}; - std::ostringstream oss; - option.to_scheme(oss); - EXPECT_EQ(hpe_str, oss.str()); -} - -TEST_F(GncOptionCommodityTest, test_currency_from_scheme) -{ - auto option = make_currency_option("foo", "bar", "baz", "Phony Option", - m_eur, true); - - std::string usd_str{make_currency_SCM_str(m_usd)}; - std::istringstream iss{usd_str}; - option.from_scheme(iss); - EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value()); -} - -TEST_F(GncOptionCommodityTest, test_commodity_from_scheme) -{ - GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl, - GncOptionUIType::COMMODITY}; - - std::string hpe_str{make_commodity_SCM_str(m_hpe)}; - std::istringstream iss{hpe_str}; - option.from_scheme(iss); - EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value()); -} - class GncUIType { public: @@ -891,64 +761,6 @@ make_account_list_SCM_str(const GncOptionAccountList& acclist) return retval; } -TEST_F(GncOptionAccountTest, test_account_list_to_scheme) -{ - 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{make_account_list_SCM_str(acclist)}; - - option.to_scheme(oss); - 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 = make_account_list_SCM_str(accsel); - - oss.str(""); - sel_option.to_scheme(oss); - EXPECT_EQ(acc_guids, oss.str()); -} - -TEST_F(GncOptionAccountTest, test_account_list_from_scheme) -{ - GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})}; - GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", - GncOptionUIType::ACCOUNT_LIST, - acclist}}; - - std::string acc_guids{make_account_list_SCM_str(acclist)}; - std::istringstream iss{acc_guids}; - option.from_scheme(iss); - 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 = make_account_list_SCM_str(acclistbad); - iss.clear(); - iss.str(acc_guids); - sel_option.from_scheme(iss); - EXPECT_EQ(accsel, sel_option.get_value()); - - iss.clear(); //Reset the failedbit from the invalid selection type. - acc_guids = make_account_list_SCM_str(GncOptionAccountList{acclist[1]}); - EXPECT_NO_THROW({ - iss.str(acc_guids); - sel_option.from_scheme(iss); - }); - EXPECT_EQ(acclist[1], sel_option.get_value()[0]); -} using KT = GncOptionMultichoiceKeyType; class GncOptionMultichoiceTest : public ::testing::Test @@ -1031,22 +843,6 @@ TEST_F(GncMultichoiceOption, test_multichoice_in) EXPECT_EQ(iss.str(), m_option.get_value()); } -TEST_F(GncMultichoiceOption, test_multichoice_scheme_out) -{ - std::ostringstream oss; - m_option.to_scheme(oss); - std::string scm_str{'\''}; - scm_str += m_option.get_value(); - EXPECT_EQ(scm_str, oss.str()); -} - -TEST_F(GncMultichoiceOption, test_multichoice_scheme_in) -{ - std::istringstream iss{"'pork"}; - m_option.from_scheme(iss); - EXPECT_STREQ("pork", m_option.get_value().c_str()); -} - class GncOptionListTest : public ::testing::Test { protected: @@ -1116,26 +912,6 @@ TEST_F(GncListOption, test_list_in) EXPECT_EQ(iss.str(), m_option.get_value()); } -TEST_F(GncListOption, test_list_scheme_out) -{ - std::ostringstream oss; - m_option.to_scheme(oss); - std::string value{"'('"}; - auto vec{m_option.get_value()}; - value += m_option.permissible_value(vec[0]); - value += " '"; - value += m_option.permissible_value(vec[1]); - value += ")"; - EXPECT_EQ(value, oss.str()); -} - -TEST_F(GncListOption, test_list_scheme_in) -{ - std::istringstream iss{"'('pork 'waldo)"}; - m_option.from_scheme(iss); - EXPECT_STREQ("pork", m_option.get_value().c_str()); -} - static time64 time64_from_gdate(const GDate* g_date, DayPart when) { @@ -1572,63 +1348,3 @@ TEST_F(GncDateOption, test_stream_in_prev_year_end) EXPECT_EQ(time1, m_option.get_value()); } -/* We only need wiggle tests for to_scheme and from_scheme as they just wrap or -unwrap the normal stream output with "'(" and ")". -*/ - -TEST_F(GncDateOption, test_date_option_to_scheme) -{ - 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(); - timestr += ')'; - oss.str(""); - m_option.to_scheme(oss); - EXPECT_EQ(oss.str(), timestr); - - m_option.set_value(RelativeDatePeriod::TODAY); - oss.str(""); - m_option.to_scheme(oss); - EXPECT_STREQ(oss.str().c_str(), "'(relative . today)"); - - m_option.set_value(RelativeDatePeriod::START_THIS_MONTH); - oss.str(""); - m_option.to_scheme(oss); - EXPECT_STREQ(oss.str().c_str(), "'(relative . start-this-month)"); - -} - -TEST_F(GncDateOption, test_date_option_from_scheme) -{ - 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(); - timestr += ')'; - - std::istringstream iss{timestr}; - m_option.from_scheme(iss); - EXPECT_EQ(time1, m_option.get_value()); - - GDate month_start; - g_date_set_time_t(&month_start, time(nullptr)); - gnc_gdate_set_month_start(&month_start); - time1 = time64_from_gdate(&month_start, DayPart::start); - iss.clear(); - iss.str("'(relative . start-this-month)"); - m_option.from_scheme(iss); - EXPECT_EQ(time1, m_option.get_value()); - - GDate date; - g_date_set_time_t(&date, time(nullptr)); - gnc_gdate_set_month_end(&date); - time1 = time64_from_gdate(&date, DayPart::end); - iss.clear(); - iss.str("'(relative . end-this-month)"); - m_option.from_scheme(iss); - EXPECT_EQ(time1, m_option.get_value()); -} diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index e00f0894a8..15a3cca4ea 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -337,115 +337,6 @@ protected: GncOptionDBPtr m_db; }; -TEST_F(GncOptionDBIOTest, test_option_scheme_output) -{ - std::ostringstream oss; - m_db->save_option_scheme(oss, "option", "foo", "sausage"); - EXPECT_STREQ("", oss.str().c_str()); - oss.clear(); - 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")->is_changed()); - oss.flush(); - m_db->save_option_scheme(oss, "option", "foo", "sausage"); - EXPECT_STREQ("(let ((option (gnc:lookup-option option\n" - " \"foo\"\n" - " \"sausage\")))\n" - " ((lambda (o) (if o (gnc:option-set-value o \"pepper\"" - "))) option))\n\n", oss.str().c_str()); -} - -TEST_F(GncOptionDBIOTest, test_string_option_scheme_input) -{ - const char* input{"(let ((option (gnc:lookup-option option\n" - " \"foo\"\n" - " \"sausage\")))\n" - " ((lambda (o) (if o (gnc:option-set-value o \"pepper\"" - "))) option))\n\n"}; - std::istringstream iss{input}; - EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "sausage").c_str()); - m_db->load_option_scheme(iss); - EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); -} - -TEST_F(GncOptionDBIOTest, test_date_interval_option_scheme_input) -{ - const char* input{"(let ((option (gnc:lookup-option option\n" - " \"pork\"\n" - " \"garply\")))\n" - " ((lambda (o) (if o (gnc:option-set-value o " - "'(relative . end-prev-month)" - "))) option))\n\n"}; - std::istringstream iss{input}; - GDate month_end; - g_date_set_time_t(&month_end, time(nullptr)); - g_date_subtract_months(&month_end, 1); - gnc_gdate_set_month_end(&month_end); - auto time1 = time64_from_gdate(&month_end, DayPart::end); - m_db->load_option_scheme(iss); - EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value()); - -} - -TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input) -{ - auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})}; - auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))}; - auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))}; - std::string input{"(let ((option (gnc:lookup-option option\n" - " \"quux\"\n" - " \"xyzzy\")))\n" - " ((lambda (o) (if o (gnc:option-set-value o '(\""}; - input += hpe_guid + "\" \""; - input += msft_guid + "\")))) option))\n\n"; - std::istringstream iss{input}; - EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value()[0]); - m_db->load_option_scheme(iss); - EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value()[1]); - EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value().size()); - -} - -TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input) -{ - auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})}; - auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))}; - auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))}; - std::string input{";; Foo\n\n" - "(let ((option (gnc:lookup-option option\n" - " \"foo\"\n" - " \"sausage\")))\n" - " ((lambda (o) (if o (gnc:option-set-value o \"pepper\"" - "))) option))\n\n" - ";; Pork\n\n" - "(let ((option (gnc:lookup-option option\n" - " \"pork\"\n" - " \"garply\")))\n" - " ((lambda (o) (if o (gnc:option-set-value o " - "'(relative . end-prev-month)" - "))) option))\n\n" - ";; Quux\n\n" - "(let ((option (gnc:lookup-option option\n" - " \"quux\"\n" - " \"xyzzy\")))\n" - " ((lambda (o) (if o (gnc:option-set-value o '(\""}; - input += hpe_guid + "\" \""; - input += msft_guid + "\")))) option))\n\n"; - std::istringstream iss{input}; - GDate month_end; - g_date_set_time_t(&month_end, time(nullptr)); - g_date_subtract_months(&month_end, 1); - gnc_gdate_set_month_end(&month_end); - auto time1 = time64_from_gdate(&month_end, DayPart::end); - EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value()[0]); - m_db->load_from_scheme(iss); - EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str()); - EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value()); - EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value()[1]); - EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value().size()); - -} - TEST_F(GncOptionDBIOTest, test_option_key_value_output) { std::ostringstream oss; From 00c2e99d2ee866f21f4c78d7b01cd65711723ef9 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 30 Aug 2021 13:14:19 -0700 Subject: [PATCH 255/298] Convert the Scheme RelativeDatePeriod lookup table to a std::vector. From a Scheme alist. The vector can be used to find the scheme symbol as a direct lookup, which isn't possible with an alist, and can be searched for the Scheme symbol match more quickly than an alist can. --- libgnucash/app-utils/gnc-option-date.hpp | 3 + libgnucash/app-utils/gnc-optiondb.i | 223 ++++++++++++++---- .../app-utils/test/test-gnc-optiondb.scm | 8 +- 3 files changed, 180 insertions(+), 54 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp index 8d4e65529c..82f963d607 100644 --- a/libgnucash/app-utils/gnc-option-date.hpp +++ b/libgnucash/app-utils/gnc-option-date.hpp @@ -73,6 +73,9 @@ enum class RelativeDatePeriod : int END_ACCOUNTING_PERIOD, }; +constexpr unsigned relative_date_periods = + static_cast(RelativeDatePeriod::END_ACCOUNTING_PERIOD) + 2; + using RelativeDatePeriodVec = std::vector; bool gnc_relative_date_is_single(RelativeDatePeriod); diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 9bc3c13414..1fbbf575a5 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -427,6 +427,18 @@ gnc_option_test_book_destroy(QofBook* book) $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; @@ -626,61 +638,149 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %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) { - static SCM reldate_values = SCM_BOOL_F; - - if (scm_is_false(reldate_values)) - reldate_values = scm_c_eval_string( - "'((absolute RelativeDatePeriod-ABSOLUTE)" - "(today RelativeDatePeriod-TODAY)" - "(one-week-ago RelativeDatePeriod-ONE-WEEK-AGO)" - "(one-week-ahead RelativeDatePeriod-ONE-WEEK-AHEAD)" - "(one-month-ago RelativeDatePeriod-ONE-MONTH-AGO)" - "(one-month-ahead RelativeDatePeriod-ONE-MONTH-AHEAD)" - "(three-months-ago RelativeDatePeriod-THREE-MONTHS-AGO)" - "(three-months-ahead RelativeDatePeriod-THREE-MONTHS-AHEAD)" - "(six-months-ago RelativeDatePeriod-SIX-MONTHS-AGO)" - "(six-months-ahead RelativeDatePeriod-SIX-MONTHS-AHEAD)" - "(one-year-ago RelativeDatePeriod-ONE-YEAR-AGO)" - "(one-year-ahead RelativeDatePeriod-ONE-YEAR-AHEAD)" - "(start-this-month RelativeDatePeriod-START-THIS-MONTH)" - "(end-this-month RelativeDatePeriod-END-THIS-MONTH)" - "(start-prev-month RelativeDatePeriod-START-PREV-MONTH)" - "(end-prev-month RelativeDatePeriod-END-PREV-MONTH)" - "(start-next-month RelativeDatePeriod-START-NEXT-MONTH)" - "(end-next-month RelativeDatePeriod-END-NEXT-MONTH)" - "(start-current-quarter RelativeDatePeriod-START-CURRENT-QUARTER)" - "(end-current-quarter RelativeDatePeriod-END-CURRENT-QUARTER)" - "(start-prev-quarter RelativeDatePeriod-START-PREV-QUARTER)" - "(end-prev-quarter RelativeDatePeriod-END-PREV-QUARTER)" - "(start-next-quarter RelativeDatePeriod-START-NEXT-QUARTER)" - "(end-next-quarter RelativeDatePeriod-END-NEXT-QUARTER)" - "(start-cal-year RelativeDatePeriod-START-CAL-YEAR)" - "(end-cal-year RelativeDatePeriod-END-CAL-YEAR)" - "(start-prev-year RelativeDatePeriod-START-PREV-YEAR)" - "(end-prev-year RelativeDatePeriod-END-PREV-YEAR)" - "(start-next-year RelativeDatePeriod-START-NEXT-YEAR)" - "(end-next-year RelativeDatePeriod-END-NEXT-YEAR)" - "(start-accounting-period RelativeDatePeriod-START-ACCOUNTING-PERIOD)" - "(end-accounting-period RelativeDatePeriod-END-ACCOUNTING-PERIOD))"); - - auto reldate_scm{scm_is_pair(date) ? scm_cdr(date) : date}; - auto reldate{scm_primitive_eval(scm_assq_ref(reldate_values, - reldate_scm))}; - return static_cast(scm_to_int(reldate)); + 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)) { - auto car{scm_to_utf8_string(scm_symbol_to_string(scm_car(date)))}; - if (strcmp(car, "relative") == 0) - return false; + 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 true; + return (!(scm_is_symbol(date) || scm_is_string(date))); } inline static time64 scm_absolute_date_to_time64(SCM date) @@ -808,16 +908,39 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); 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_MultichoiceOrRange +struct is_MultichoiceDateOrRange { static constexpr bool value = is_same_decayed_v || - is_same_decayed_v>; + is_same_decayed_v> || + is_same_decayed_v; }; template -inline constexpr bool is_MultichoiceOrRange_v = is_MultichoiceOrRange::value; +inline constexpr bool is_MultichoiceDateOrRange_v = is_MultichoiceDateOrRange::value; template inline SCM return_scm_value(ValueType value) @@ -853,7 +976,7 @@ inline SCM return_scm_value(ValueType value) if (!$self) return SCM_BOOL_F; return std::visit([](const auto& option)->SCM { - if constexpr (is_MultichoiceOrRange_v) + if constexpr (is_MultichoiceDateOrRange_v) return get_scm_value(option); auto value{option.get_value()}; return return_scm_value(value); @@ -864,7 +987,7 @@ inline SCM return_scm_value(ValueType value) if (!$self) return SCM_BOOL_F; return std::visit([](const auto& option)->SCM { - if constexpr (is_MultichoiceOrRange_v) + if constexpr (is_MultichoiceDateOrRange_v) return get_scm_default_value(option); auto value{option.get_default_value()}; return return_scm_value(value); diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index ad88824638..30b5da1d03 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -202,9 +202,9 @@ "baz" "Phony Option" (RelativeDatePeriod-TODAY))) (a-time (gnc-dmy2time64 11 07 2019))) - (test-equal (current-time) (gnc-option-value option-db "foo" "bar")) + (test-equal '(relative . today) (gnc-option-value option-db "foo" "bar")) (gnc-set-option option-db "foo" "bar" a-time) - (test-equal a-time (gnc-option-value option-db "foo" "bar"))) + (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) @@ -212,7 +212,7 @@ (let* ((option-db (new-gnc-optiondb)) (date-opt (gnc-register-date-option-set option-db "foo" "bar" "baz" "Phony Option" - `(today + '(today start-this-month start-prev-month start-current-quarter @@ -221,7 +221,7 @@ start-cal-year start-prev-year start-accounting-period) #t))) - (test-equal (gnc-accounting-period-fiscal-start) + (test-equal '(relative . start-accounting-period) (gnc-option-value option-db "foo" "bar"))) (test-end "test-gnc-test-date-set-option")) From 00a982d97d6a628d2750611fe8a5b533cb682535 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 30 Aug 2021 13:46:10 -0700 Subject: [PATCH 256/298] Use Scheme to generate and parse saved option files. The saved option files being Scheme executables. --- libgnucash/app-utils/gnc-optiondb.i | 96 +++- libgnucash/app-utils/options.scm | 59 ++- libgnucash/app-utils/test/CMakeLists.txt | 3 +- .../test/test-gnc-option-scheme-output.scm | 432 ++++++++++++++++++ 4 files changed, 577 insertions(+), 13 deletions(-) create mode 100644 libgnucash/app-utils/test/test-gnc-option-scheme-output.scm diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 1fbbf575a5..39a9f18606 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -54,6 +54,8 @@ 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") @@ -550,6 +552,7 @@ gnc_option_test_book_destroy(QofBook* book) %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)); @@ -737,7 +740,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); 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 { @@ -971,6 +974,12 @@ inline SCM return_scm_value(ValueType value) %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) @@ -994,6 +1003,41 @@ inline SCM return_scm_value(ValueType value) }, swig_get_option($self)); } + SCM save_scm_value() + { + const SCM plain_format_str{scm_from_utf8_string("~s")}; + const SCM ticked_format_str{scm_from_utf8_string("'~a")}; +//scm_simple_format needs a scheme list of arguments to match the format +//placeholders. + auto value{scm_list_1(GncOption_get_scm_value($self))}; + auto uitype{$self->get_ui_type()}; + if (uitype == GncOptionUIType::STRING || + uitype == GncOptionUIType::TEXT || + uitype == GncOptionUIType::FONT || + uitype == GncOptionUIType::BOOLEAN || + uitype == GncOptionUIType::PIXMAP + ) + { + return scm_simple_format(SCM_BOOL_F, plain_format_str, value); + } + else if (uitype == GncOptionUIType::ACCOUNT_LIST || + uitype == GncOptionUIType::ACCOUNT_SEL || + uitype == GncOptionUIType::INVOICE || + uitype == GncOptionUIType::TAX_TABLE || + uitype == GncOptionUIType::OWNER) + { + if (value && scm_is_true(value)) + return scm_simple_format(SCM_BOOL_F, plain_format_str, value); + else + return scm_simple_format(SCM_BOOL_F, plain_format_str, + SCM_BOOL_F); + } + else + { + return scm_simple_format(SCM_BOOL_F, ticked_format_str, value); + } + } + void set_value_from_scm(SCM new_value) { if (!$self) @@ -1066,7 +1110,7 @@ inline SCM return_scm_value(ValueType 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; + return; } if constexpr (is_same_decayed_v) @@ -1137,6 +1181,30 @@ inline SCM return_scm_value(ValueType value) %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_account_list_option(const char* section, const char* name, const char* key, @@ -1473,10 +1541,28 @@ inline SCM return_scm_value(ValueType value) }); } - std::string - gnc_optiondb_save_to_scheme(GncOptionDBPtr& odb, const char* prolog) + /** 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) { - return prolog; + 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); + }); + }); } %} diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 33ee587bf1..81bbb26a6f 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -32,12 +32,6 @@ (use-modules (ice-9 pretty-print)) -(define-public (gnc:value->string value) - (format #f "~s" value)) - -(define-public (gnc:generate-restore-forms options name) - (let ((optiondb (options 'generate-restore-forms))) - (gnc-optiondb-save-to-scheme optiondb name))) (define-public (gnc:lookup-option options section name) (if options @@ -136,6 +130,57 @@ (gnc:option-value src-option))))) src-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-public (gnc:generate-restore-forms options toplevel-name) + (define (section-op section-name) + (display + (string-append "\n; Section: " section-name "\n\n"))) + + (define (gnc:option-is-budget? option) + (GncOption-is-budget-option option)) + + (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)) + + ;; FIXME: Fake callback functions for boolean-complex and multichoice-callback (define-public (gnc:options-register-callback section name callback options) (options 'register-callback) 1) @@ -169,7 +214,7 @@ (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-value option + (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) diff --git a/libgnucash/app-utils/test/CMakeLists.txt b/libgnucash/app-utils/test/CMakeLists.txt index f4c7425daa..37071adf22 100644 --- a/libgnucash/app-utils/test/CMakeLists.txt +++ b/libgnucash/app-utils/test/CMakeLists.txt @@ -85,10 +85,11 @@ if (HAVE_SRFI64) DEPENDS "${GUILE_DEPENDS};scm-srfi64-extras") gnc_add_scheme_test_targets(scm-test-gnc-optiondb - SOURCES "test-gnc-optiondb.scm" + 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() 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..d0b3147e11 --- /dev/null +++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm @@ -0,0 +1,432 @@ + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; 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-pixmap-option-to-scheme) + (test-gnc-currency-option-to-scheme) + (test-gnc-budget-option-to-scheme) + (test-gnc-font-option-to-scheme) + (test-gnc-commodity-option-to-scheme) + (test-gnc-date-option-to-scheme) + (test-gnc-multichoice-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 '~s))) option)) + +" value)) + +(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 make-option-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 test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") value) + (test-equal (test-template value) + (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 gnc:make-string-option 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 gnc:make-string-option 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 gnc:make-font-option 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 gnc:make-currency-option test-literal-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* ((session (gnc-get-current-session)) + (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 test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") budget2) + (test-equal (gnc-budget-get-default book) budget1) + (test-equal (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* ((book (gnc-option-test-book-new)) + (AAPL (gnc-commodity-new book "Apple" "NASDAQ" "AAPL" "" 1)) + (FMAGX (gnc-commodity-new book "Fidelity Magellan Fund" "FUND" "FMAGX" "" 1000))) + (test-option-scheme-output gnc:make-commodity-option test-literal-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 gnc:make-simple-boolean-option 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 gnc:make-pixmap-option 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 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 (test-template (GncOption-get-scm-value option)) + (gnc:generate-restore-forms odb "options")) + (set! value '(relative . end-prev-year)) + (gnc:option-set-value option value) + (test-equal value (GncOption-get-scm-value option)) + (test-equal (test-template (GncOption-get-scm-value 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) + (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" acctlist + (lambda (ac) + (let ((type (xaccAccountGetAccountType ac))) + (or (eq type ACCT-TYPE-STOCK) + (eq type ACCT-TYPE-BANK)))) #t)) + (test-equal 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) + (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK)))) + (gnc-option-set-value option new-acclist) + (test-equal (test-template (GncOption-get-scm-value option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-account-list-option-to-scheme")) + + (define (test-gnc-account-sel-option-to-scheme) + (test-begin "test-gnc-account-sel-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" acctlist + (lambda (ac) + (let ((type (xaccAccountGetAccountType ac))) + (or (eq type ACCT-TYPE-STOCK) + (eq type ACCT-TYPE-BANK)))) #t)) + (test-equal 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) + (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK)))) + (gnc-option-set-value option new-acclist) + (test-equal (test-template (GncOption-get-scm-value 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) + (test-gnc-account-sel-option-to-scheme) + (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 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 (test-template (GncOption-get-scm-value 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 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 (test-template (GncOption-get-scm-value 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 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 (test-template (GncOption-get-scm-value 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 100) + (max-value 10000) + (dec-places 0) + (step 5)) + (gnc-register-option odb + (gnc:make-number-plot-size-option + "foo" "bar" "a" "baz" 490 min-value + max-value dec-places step)) + (test-equal 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 420) + (test-equal (test-template (GncOption-get-scm-value option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-number-plot-size-option-to-scheme")) + +(define (test-gnc-query-option-to-scheme) + (test-begin "test-gnc-number-plot-size-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 test-unchanged-section-output-template + (gnc:generate-restore-forms odb "options")) + (let ((option (gnc:lookup-option odb "__reg" "query")) + (test-template test-literal-output-template)) + (gnc-option-set-value option (gnc-scm2query query-scm)) + (test-equal (test-template (GncOption-get-scm-value option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-number-plot-size-option-to-scheme")) + +(define (test-gnc-color-option-to-scheme) + (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))) + (test-option-scheme-output gnc:make-color-option + test-literal-output-template + default-color new-color)) + (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)) + (invoice '"13b305236443451a86c5366b7f890ecb")) + (test-option-scheme-output gnc:make-color-option + test-literal-output-template + (lambda () '()) invoice)) + (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 () '()) #f + 'GNC-OWNER-CUSTOMER)) + (test-equal 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 '"13b305236443451a86c5366b7f890ecb") + (test-equal (test-template (GncOption-get-scm-value option)) + (gnc:generate-restore-forms odb "options")) + )) + (test-end "test-gnc-owner-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) From 96b09ded9fedb0a97bab468124e1aeafdda93b43 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 10 Nov 2021 18:01:55 -0800 Subject: [PATCH 257/298] Doxygen documentation for new options classes. --- gnucash/gnome-utils/dialog-options.hpp | 44 +++++++++++ gnucash/gnome/business-options-gnome.h | 9 +++ libgnucash/app-utils/gnc-option-date.hpp | 91 +++++++++++++++++++++- libgnucash/app-utils/gnc-option-impl.hpp | 88 +++++++++------------ libgnucash/app-utils/gnc-option-uitype.hpp | 15 ++++ libgnucash/app-utils/gnc-option.hpp | 38 ++++++++- libgnucash/app-utils/gnc-optiondb-impl.hpp | 18 +++++ libgnucash/app-utils/gnc-optiondb.h | 33 ++++++++ libgnucash/app-utils/gnc-optiondb.hpp | 9 +++ 9 files changed, 291 insertions(+), 54 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.hpp b/gnucash/gnome-utils/dialog-options.hpp index c2fb570c24..b0101c23d3 100644 --- a/gnucash/gnome-utils/dialog-options.hpp +++ b/gnucash/gnome-utils/dialog-options.hpp @@ -3,6 +3,7 @@ * 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 * @@ -21,6 +22,10 @@ * 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_ @@ -31,18 +36,49 @@ #include #include "dialog-options.h" +/** @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; }; +/** class GncOptionGtkUIItem + * Gtk-specific Interface class for Option Widget + */ class GncOptionGtkUIItem : public GncOptionUIItem { public: @@ -50,7 +86,9 @@ public: 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; } @@ -68,6 +106,10 @@ 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) { @@ -76,3 +118,5 @@ qof_instance_cast(Instance inst) } #endif // GNC_DIALOG_OPTIONS_HPP_ +/** @} + @} */ diff --git a/gnucash/gnome/business-options-gnome.h b/gnucash/gnome/business-options-gnome.h index 47d518f225..fb454c0f83 100644 --- a/gnucash/gnome/business-options-gnome.h +++ b/gnucash/gnome/business-options-gnome.h @@ -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/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp index 82f963d607..395dee3aa1 100644 --- a/libgnucash/app-utils/gnc-option-date.hpp +++ b/libgnucash/app-utils/gnc-option-date.hpp @@ -20,7 +20,14 @@ * 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_ @@ -78,14 +85,96 @@ constexpr unsigned relative_date_periods = 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 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_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. + * + * @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.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index f3294bd1e5..f71be8f0c5 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -21,6 +21,15 @@ * * \********************************************************************/ +/** @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_ @@ -46,51 +55,6 @@ extern "C" #include "gnc-option-uitype.hpp" -/* - * Unused base class to document the structure of the current Scheme option - * vector, re-expressed in C++. The comment-numbers on the right indicate which - * item in the Scheme vector each item implements. - * - * Not everything here needs to be implemented, nor will it necessarily be - * implemented the same way. For example, part of the purpose of this redesign - * is to convert from saving options as strings of Scheme code to some form of - * key-value pair in the book options, so generate_restore_form() will likely be - * supplanted with save_to_book(). - -template -class GncOptionBase -{ -public: - virtual ~GncOption = default; - virtual ValueType get_value() const = 0; //5 - virtual ValueType get_default_value() = 0; - virtual SCM get_SCM_value() = 0; - virtual SCM get_SCM_default_value() const = 0; //7 - virtual void set_value(ValueType) = 0; //6 -// generate_restore_form outputs a Scheme expression (a "form") that finds an -// option and sets it to the current value. e.g.: -//(let ((option (gnc:lookup-option options -// "Display" -// "Amount"))) -// ((lambda (option) (if option ((gnc:option-setter option) 'none))) option)) -// it uses gnc:value->string to generate the "'none" (or whatever the option's -// value would be as input to the scheme interpreter). - - virtual std::string generate_restore_form(); //8 - virtual void save_to_book(QofBook*) const noexcept; //9 - virtual void read_from_book(QofBook*); //10 - virtual std::vector get_option_strings(); //15 - virtual set_changed_callback(std::function); //14 -protected: - const std::string m_section; //0 - const std::string m_name; //1 - const std::string m_sort_tag; //2 - const std::type_info m_kvp_type; //3 - const std::string m_doc_string; //4 - std::function m_changed_callback; //Part of the make-option closure - std::functionm_option_widget_changed_callback; //16 -}; -*/ static const char* commodity_scm_intro{"'(commodity-scm "}; #ifndef SWIG @@ -98,6 +62,12 @@ 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; @@ -112,6 +82,9 @@ struct OptionClassifier 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 { @@ -167,6 +140,12 @@ private: ValueType m_default_value; }; +/** class GncOptionValidatedValue + * Validated values have an additional member function, provided as a + * constructor argument, that checks value parameters for some property before + * setting the object's value member. If the function returns false a + * std::invalid_argument exception is thrown. + */ template class GncOptionValidatedValue : public OptionClassifier { @@ -353,7 +332,7 @@ operator>> >(std::istream& iss, } #endif // SWIG -/** +/** @class GncOptionRangeValue * Used for numeric ranges and plot sizes. */ @@ -461,7 +440,8 @@ using GncMultichoiceOptionEntry = std::tuple; using GncMultichoiceOptionChoices = std::vector; -/** Multichoice options have a vector of valid options +/** @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. The * tuple contains three strings, a key, and a display @@ -731,7 +711,7 @@ using GncOptionAccountList = std::vector; using GncOptionAccountTypeList = std::vector; -/** Account options +/** @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 @@ -853,6 +833,10 @@ operator>> (std::istream& iss, return iss; } +/* @class GncOptionAccountSelValue + * Like GncOptionAccountListValue but contains only a single account. + */ + class GncOptionAccountSelValue : public OptionClassifier { public: @@ -936,8 +920,8 @@ operator>> (std::istream& iss, return iss; } -/** Date options - * A legal date value is a pair of either and a RelativeDatePeriod, the absolute +/** @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. */ /* @@ -1076,7 +1060,7 @@ operator>> (std::istream& iss, return opt.in_stream(iss); } -/** QofQuery Options - */ #endif //GNC_OPTION_IMPL_HPP_ +/**@} + @} */ diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp index 22098bb72b..76c6e48e03 100644 --- a/libgnucash/app-utils/gnc-option-uitype.hpp +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -23,6 +23,19 @@ #ifndef GNC_OPTION_UITYPE_HPP__ #define GNC_OPTION_UITYPE_HPP__ +/** @addtogroup Engine + @{ */ +/** @addtogroup Options + @{ */ +/** @file gnc-option-uitype.hpp + @brief OptionUITypes + @author Copyright 2019-2021 John Ralls +*/ + +/** @enum GncOptionUIType + * Used by GncOptionClassifier to indicate to dialog-options what control + * should be displayed for the option. + */ enum class GncOptionUIType : unsigned int { INTERNAL, @@ -59,3 +72,5 @@ enum class GncOptionUIType : unsigned int }; #endif // GNC_OPTION_UITYPE_H__ +/** @} + @} */ diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 9374959e2b..0913f6d6d5 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -20,6 +20,15 @@ * 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_ @@ -110,6 +119,12 @@ enum class GncOptionMultichoiceKeyType 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: @@ -144,14 +159,24 @@ public: 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; @@ -202,6 +227,11 @@ output_color_value(std::ostream& oss, const std::string& value) 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, @@ -210,9 +240,15 @@ gnc_make_option(const char* section, const char* name, return new GncOption(section, name, key, doc_string, value, ui_type); } -/* To work around SWIG_Guile's typedef of SCM to unsigned long: */ +/** + * Free function wrapping GncOption's constructor using an SCM value. + * To work around SWIG_Guile's typedef of SCM to unsigned long + */ GncOption* gnc_make_SCM_option(const char* section, const char* name, const char* key, const char* doc_string, SCM value, GncOptionUIType ui_type); #endif //GNC_OPTION_HPP_ + +/** @} + @} */ diff --git a/libgnucash/app-utils/gnc-optiondb-impl.hpp b/libgnucash/app-utils/gnc-optiondb-impl.hpp index 5fa8114702..5083531d95 100644 --- a/libgnucash/app-utils/gnc-optiondb-impl.hpp +++ b/libgnucash/app-utils/gnc-optiondb-impl.hpp @@ -20,6 +20,14 @@ * 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_ @@ -45,6 +53,10 @@ extern "C" 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; @@ -90,6 +102,10 @@ struct GncOptionDBCallback using GncCallbackVec = std::vector; +/** @class GncOptionDB + * Holds all of the options for a book, report, or stylesheet, organized by + * GncOptionSections. + */ class GncOptionDB { public: @@ -171,3 +187,5 @@ private: #endif // GNC_OPTIONDB_P_HPP_ +/** @} + @} */ diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h index d2fe65540c..f828536e98 100644 --- a/libgnucash/app-utils/gnc-optiondb.h +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -20,7 +20,37 @@ * 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_ @@ -179,3 +209,6 @@ void gnc_option_db_set_scm_value(GncOptionDB*, const char*, } #endif #endif //GNC_OPTIONDB_H_ + +/** @} + @} */ diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 002efa9e3b..b274155ad6 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -20,6 +20,15 @@ * 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_ From 4c43dac1b6b2d2e7840cc4d286c7637bc4c90042 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 13 Nov 2021 11:10:17 -0800 Subject: [PATCH 258/298] Restore setting swig engine initialization flag. --- bindings/guile/gnc-guile-bindings.c | 1 + 1 file changed, 1 insertion(+) diff --git a/bindings/guile/gnc-guile-bindings.c b/bindings/guile/gnc-guile-bindings.c index 77dbf384be..4d40df5cdf 100644 --- a/bindings/guile/gnc-guile-bindings.c +++ b/bindings/guile/gnc-guile-bindings.c @@ -44,5 +44,6 @@ gnc_guile_bindings_init(void) scm_init_sw_core_utils_module(); scm_init_sw_engine_module(); + is_initialized = 1; } } From f6c9e63e3d8b130f79c39e6db693541e9fc01ba0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 14 Nov 2021 15:28:22 -0800 Subject: [PATCH 259/298] Fix PR comments so far. Except for gnc-option-date's normalize_tm and set_day_and_time. --- gnucash/gnome-utils/gnc-gnome-utils.c | 33 ------------------------ gnucash/gnome-utils/gnc-main-window.cpp | 4 +-- gnucash/gnome/assistant-hierarchy.cpp | 2 -- gnucash/gnome/gnc-plugin-page-report.cpp | 2 +- libgnucash/app-utils/app-utils.i | 10 ------- libgnucash/app-utils/gnc-option-date.cpp | 8 +++--- libgnucash/app-utils/gnc-option-date.hpp | 13 ++++++++-- libgnucash/app-utils/gnc-option-impl.cpp | 4 +-- libgnucash/app-utils/gnc-option-impl.hpp | 13 +++++++--- 9 files changed, 29 insertions(+), 60 deletions(-) diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c index 0180b62bf6..00f8efe380 100644 --- a/gnucash/gnome-utils/gnc-gnome-utils.c +++ b/gnucash/gnome-utils/gnc-gnome-utils.c @@ -99,39 +99,6 @@ gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat) gnc_gnome_help (GTK_WINDOW(gnc_options_dialog_widget (win)), HF_HELP, HL_BOOK_OPTIONS); } -#if 0 // Reimplemented in dialog-options.cpp -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); - } -} -#endif - static void gnc_style_sheet_options_help_cb (GNCOptionWin *win, gpointer dat) { diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index b371a7b9a1..a8b2a255f8 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -1924,13 +1924,13 @@ gnc_main_window_update_radio_button (GncMainWindow *window) { first_action = static_cast(g_slist_last(action_list)->data); g_signal_handlers_block_by_func(G_OBJECT(first_action), - (void*)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), - (void*)gnc_main_window_cmd_window_raise, + (gpointer)gnc_main_window_cmd_window_raise, window); } g_free(action_name); diff --git a/gnucash/gnome/assistant-hierarchy.cpp b/gnucash/gnome/assistant-hierarchy.cpp index 54980f1027..6562552c4c 100644 --- a/gnucash/gnome/assistant-hierarchy.cpp +++ b/gnucash/gnome/assistant-hierarchy.cpp @@ -131,8 +131,6 @@ typedef struct } hierarchy_data; -extern gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options); - void on_prepare (GtkAssistant *assistant, GtkWidget *page, hierarchy_data *data); void on_choose_account_categories_prepare (hierarchy_data *data); diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp index 2d47cd0df4..ab5888207b 100644 --- a/gnucash/gnome/gnc-plugin-page-report.cpp +++ b/gnucash/gnome/gnc-plugin-page-report.cpp @@ -1748,7 +1748,7 @@ gnc_plugin_page_report_export_cb( GtkAction *action, GncPluginPageReport *report gnc_error_dialog (parent, "%s", _("This report must be upgraded to return a " "document object with export-string or " - "pexport-error.")); + "export-error.")); } result = TRUE; } diff --git a/libgnucash/app-utils/app-utils.i b/libgnucash/app-utils/app-utils.i index ab8aead4cd..ac2b01f4f0 100644 --- a/libgnucash/app-utils/app-utils.i +++ b/libgnucash/app-utils/app-utils.i @@ -65,10 +65,6 @@ PyObject* SWIG_init (void); %import "base-typemaps.i" - /* OBSOLETE -typedef void (*GNCOptionChangeCallback) (gpointer user_data); -typedef int GNCOptionDBHandle; - */ void gnc_prefs_init(); QofBook * gnc_get_current_book (void); @@ -99,9 +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); -/* Obsolete: Options are C++ now, no need for convoluted callbacks. -void gncp_option_invoke_callback(GNCOptionChangeCallback callback, void *data); -*/ GNCPrintAmountInfo gnc_default_print_info (gboolean use_symbol); GNCPrintAmountInfo gnc_account_print_info (const Account *account, gboolean use_symbol); @@ -128,9 +121,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); -/* OBSOLETE Options are C++, no SCM generators. -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/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp index 1e6ae7560d..f7d17b0694 100644 --- a/libgnucash/app-utils/gnc-option-date.cpp +++ b/libgnucash/app-utils/gnc-option-date.cpp @@ -426,7 +426,7 @@ reldate_offset(RelativeDatePeriod per) static constexpr int days_in_month[12]{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static void -normalize_tm(struct tm& now) +normalize_reldate_tm(struct tm& now) { auto tmp_mon = now.tm_mon + (now.tm_mon < 0 ? 12 : now.tm_mon > 11 ? -12 : 0); @@ -453,7 +453,7 @@ normalize_tm(struct tm& now) } static void -set_day_and_time(struct tm& now, RelativeDateType type) +reldate_set_day_and_time(struct tm& now, RelativeDateType type) { if (type == RelativeDateType::START) { @@ -549,8 +549,8 @@ gnc_relative_date_to_time64(RelativeDatePeriod period) else if (reldate_is_next(period)) now.tm_mday += 7; } - normalize_tm(now); - set_day_and_time(now, checked_reldate(period).m_type); + normalize_reldate_tm(now); + reldate_set_day_and_time(now, checked_reldate(period).m_type); return static_cast(GncDateTime(now)); } diff --git a/libgnucash/app-utils/gnc-option-date.hpp b/libgnucash/app-utils/gnc-option-date.hpp index 395dee3aa1..b87ea4d08a 100644 --- a/libgnucash/app-utils/gnc-option-date.hpp +++ b/libgnucash/app-utils/gnc-option-date.hpp @@ -127,7 +127,7 @@ 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 not localizable. + * 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 @@ -158,7 +158,16 @@ const char* gnc_relative_date_description(RelativeDatePeriod); 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. + * 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. diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index a20970303f..5a79194660 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -104,7 +104,7 @@ GncOptionAccountListValue::account_type_list() const noexcept { if (m_allowed.empty()) return nullptr; - GList* retval; + GList* retval{nullptr}; for (auto type : m_allowed) retval = g_list_prepend(retval, GINT_TO_POINTER(type)); return g_list_reverse(retval); @@ -170,7 +170,7 @@ GncOptionAccountSelValue::account_type_list() const noexcept { if (m_allowed.empty()) return nullptr; - GList* retval; + GList* retval{nullptr}; for (auto type : m_allowed) retval = g_list_prepend(retval, GINT_TO_POINTER(type)); return g_list_reverse(retval); diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index f71be8f0c5..68e7b5859d 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -443,9 +443,14 @@ 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. The - * tuple contains three strings, a key, and a display - * name, which * should be localized at the point of use. + * 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. * * */ @@ -910,7 +915,7 @@ template<> inline std::istream& operator>> (std::istream& iss, GncOptionAccountSelValue& opt) { - const Account* value; + Account* value{nullptr}; std::string str; std::getline(iss, str, ' '); if (!str.empty()) From 74fd716afbf0b23d3b4bf4af0f18f935c58a8ea5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Nov 2021 11:43:04 -0800 Subject: [PATCH 260/298] Clarify gnc_relative_date_to_time64 helper functions. --- libgnucash/app-utils/gnc-option-date.cpp | 52 ++++++++++++------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp index f7d17b0694..30847b2a53 100644 --- a/libgnucash/app-utils/gnc-option-date.cpp +++ b/libgnucash/app-utils/gnc-option-date.cpp @@ -425,31 +425,40 @@ reldate_offset(RelativeDatePeriod per) 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 tmp_mon = now.tm_mon + (now.tm_mon < 0 ? 12 : - now.tm_mon > 11 ? -12 : 0); - if (now.tm_mday < 1) - { - --now.tm_mon; - now.tm_mday += days_in_month[tmp_mon - 1]; - } - else if (now.tm_mday > days_in_month[tmp_mon]) - { - ++now.tm_mon; - now.tm_mday -= days_in_month[tmp_mon]; - } - if (now.tm_mon < 0) + auto factor{abs(now.tm_mon) / 12}; + now.tm_mon /= factor > 0 ? factor : 1; + now.tm_year += now.tm_mon < 0 ? -factor : factor; + + while (now.tm_mday < 1) + now.tm_mday += days_in_month[--now.tm_mon]; + + while (now.tm_mday > days_in_month[now.tm_mon]) + now.tm_mday -= days_in_month[now.tm_mon++]; + + while (now.tm_mon < 0) { now.tm_mon += 12; --now.tm_year; } - else if (now.tm_mon > 11) + 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 @@ -457,18 +466,13 @@ reldate_set_day_and_time(struct tm& now, RelativeDateType type) { if (type == RelativeDateType::START) { - now.tm_hour = now.tm_min = now.tm_sec = 0; + gnc_tm_set_day_start(&now); now.tm_mday = 1; } else if (type == RelativeDateType::END) { - now.tm_min = now.tm_sec = 59; - now.tm_hour = 23; - now.tm_mday = days_in_month[now.tm_mon]; - // Check for Februrary in a leap year - if (int year = now.tm_year + 1900; now.tm_mon == 1 && - year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) - ++now.tm_mday; + 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. }; @@ -510,14 +514,12 @@ gnc_relative_date_to_time64(RelativeDatePeriod period) now.tm_mon = 0; else if (gnc_relative_date_is_ending(period)) now.tm_mon = 11; - now.tm_mday = days_in_month[now.tm_mon]; break; case RelativeDateOffset::SIX: if (reldate_is_prev(period)) now.tm_mon -= 6; else if (reldate_is_next(period)) now.tm_mon += 6; - now.tm_mday = days_in_month[now.tm_mon]; break; case RelativeDateOffset::QUARTER: { @@ -534,14 +536,12 @@ gnc_relative_date_to_time64(RelativeDatePeriod period) now.tm_mon += 3; if (gnc_relative_date_is_ending(period)) now.tm_mon += 2; - now.tm_mday = days_in_month[now.tm_mon]; break; case RelativeDateOffset::MONTH: if (reldate_is_prev(period)) --now.tm_mon; else if (reldate_is_next(period)) ++now.tm_mon; - now.tm_mday = days_in_month[now.tm_mon]; break; case RelativeDateOffset::WEEK: if (reldate_is_prev(period)) From d1fe359e4722e168fcd3fda0be87538b4e2c7ed1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Nov 2021 11:44:57 -0800 Subject: [PATCH 261/298] gnc_numeric_to_decimal: Change can't round warning to a debug. Routinely used as a check so a warning isn't appropriate. --- libgnucash/engine/gnc-numeric.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; } } From dd7feb99887278982f5dfc898fac44feb770672c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Nov 2021 11:45:54 -0800 Subject: [PATCH 262/298] options.scm: Remove second license comment. --- libgnucash/app-utils/options.scm | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 0cdb633106..097a996e28 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -356,23 +356,6 @@ ;; ;; 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 From a3a381cfd0efdbbcf7c8bb1c28d2999502bad84f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Nov 2021 11:52:44 -0800 Subject: [PATCH 263/298] dialog-options.cpp: Fix passing bad widget ptr to dialog_changed_internal. GtkTreeSelection is not a widget. --- gnucash/gnome-utils/dialog-options.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 7af187e75e..755e932a73 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1805,6 +1805,14 @@ create_account_widget(GncOption& option, char *name) 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, @@ -1828,9 +1836,10 @@ create_option_widget(GncOption& option, *packed = TRUE; auto widget = gnc_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(gnc_option_changed_widget_cb), &option); + 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); From e17ee38c8049f64fcbf00faf83b742d99ebaac0a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Nov 2021 11:56:25 -0800 Subject: [PATCH 264/298] dialog-options.cpp: Replace hand-rolled loop to find toplevel with gtk_widget_get_toplevel. --- gnucash/gnome-utils/dialog-options.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 755e932a73..88dc993eeb 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -229,16 +229,13 @@ static void dialog_changed_internal (GtkWidget *widget, bool sensitive) { g_return_if_fail(widget); - while (TRUE) - { - auto new_widget = gtk_widget_get_parent(widget); - if (new_widget && GTK_IS_WIDGET(new_widget) && - GTK_IS_WINDOW(new_widget)) - widget = new_widget; - else - break; - } + auto toplevel{gtk_widget_get_toplevel(widget)}; + if (toplevel == widget && !GTK_IS_WINDOW(toplevel)) + return; + g_assert(toplevel && GTK_IS_WINDOW(toplevel)); + + widget = toplevel; /* 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)) @@ -246,7 +243,7 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive) 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))) + 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) @@ -271,7 +268,7 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive) break; // Found the button-box, no need to continue. } } - g_list_free (children); + g_list_free(children); break; // Found the box, no need to continue. } } From 9804928006b19552d00a9f1b478fcbd72abb2dda Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Nov 2021 11:58:22 -0800 Subject: [PATCH 265/298] Don't call gnc_option_db_clean after setting up the options dialog. The widgets have already been loaded and loading them again just sensitizes the buttons as if something had changed. --- gnucash/gnome/window-report.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp index 4898c9cb82..0e27fa0695 100644 --- a/gnucash/gnome/window-report.cpp +++ b/gnucash/gnome/window-report.cpp @@ -190,7 +190,6 @@ gnc_report_window_default_params_editor(GncOptionDB* odb, SCM report, scm_gc_protect_object(prm->cur_report); gnc_options_dialog_build_contents(prm->win, prm->odb); - gnc_option_db_clean(prm->odb); gnc_options_dialog_set_apply_cb(prm->win, gnc_options_dialog_apply_cb, From 19a2dd495272fd58751216c1b84b1645b956e688 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Nov 2021 14:34:55 -0800 Subject: [PATCH 266/298] dialog-options: Store the main buttons in GNCOptionWin. Instead of searching for them every time the dialog updates. --- gnucash/gnome-utils/dialog-options.cpp | 79 ++++++++++---------------- 1 file changed, 29 insertions(+), 50 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 88dc993eeb..ca6e8fea8c 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -105,6 +105,10 @@ struct gnc_option_win GtkWidget * notebook; GtkWidget * page_list_view; GtkWidget * page_list; + GtkButton * help_button; + GtkButton * cancel_button; + GtkButton * apply_button; + GtkButton * ok_button; bool toplevel; @@ -235,45 +239,14 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive) return; g_assert(toplevel && GTK_IS_WINDOW(toplevel)); - widget = toplevel; - /* 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) - { - GtkWidget* widget = GTK_WIDGET(it->data); - const gchar* name = gtk_widget_get_name(widget); - - if (g_strcmp0 (name, "ok_button") == 0 || - g_strcmp0 (name, "apply_button") == 0) - gtk_widget_set_sensitive (widget, sensitive); - else if (g_strcmp0 (name, "cancel_button") == 0) - gtk_button_set_label (GTK_BUTTON (widget), - sensitive ? _("_Cancel") : - _("_Close")); - } - g_list_free (children); - break; // Found the button-box, no need to continue. - } - } - g_list_free(children); - break; // Found the box, no need to continue. - } - } - g_list_free (children); - } + /* Can't static cast, no inheritance relationship. */ + auto option_win = + static_cast(g_object_get_data(G_OBJECT(toplevel), + "optionwin")); + gtk_widget_set_sensitive (GTK_WIDGET(option_win->apply_button), sensitive); + gtk_widget_set_sensitive (GTK_WIDGET(option_win->ok_button), sensitive); + gtk_button_set_label (option_win->cancel_button, + sensitive ? _("_Cancel") : _("_Close")); } void @@ -777,6 +750,7 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title, 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")); retval->component_class = component_class ? component_class : DIALOG_OPTIONS_CM_CLASS; + g_object_set_data(G_OBJECT(retval->window), "optionwin", retval); // 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"); @@ -810,14 +784,14 @@ gnc_options_dialog_new_modal(gboolean modal, gchar *title, G_CALLBACK (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); + retval->help_button = GTK_BUTTON(gtk_builder_get_object (builder, "helpbutton")); + g_signal_connect(retval->help_button, "clicked", G_CALLBACK(gnc_options_dialog_help_button_cb), retval); + retval->cancel_button = GTK_BUTTON(gtk_builder_get_object (builder, "cancelbutton")); + g_signal_connect(retval->cancel_button, "clicked", G_CALLBACK(gnc_options_dialog_cancel_button_cb), retval); + retval->apply_button = GTK_BUTTON(gtk_builder_get_object (builder, "applybutton")); + g_signal_connect(retval->apply_button, "clicked", G_CALLBACK(gnc_options_dialog_apply_button_cb), retval); + retval->ok_button = GTK_BUTTON(gtk_builder_get_object (builder, "okbutton")); + g_signal_connect(retval->ok_button, "clicked", G_CALLBACK(gnc_options_dialog_ok_button_cb), retval); gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, retval); @@ -1061,7 +1035,6 @@ create_option_widget (GncOption& option, GtkGrid *page_bo 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); - gtk_container_add (GTK_CONTAINER (scroll), widget); auto ui_item{std::make_unique(widget)}; auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); @@ -1070,6 +1043,7 @@ create_option_widget (GncOption& option, GtkGrid *page_bo option.set_ui_item(std::move(ui_item)); option.set_ui_item_from_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; @@ -1740,7 +1714,6 @@ create_account_widget(GncOption& option, char *name) 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); @@ -1799,6 +1772,7 @@ create_account_widget(GncOption& option, char *name) option.set_ui_item(std::make_unique(tree)); option.set_ui_item_from_option(); + gtk_container_add(GTK_CONTAINER(scroll_win), tree); return frame; } @@ -1997,6 +1971,9 @@ create_list_widget(GncOption& option, char *name) 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); @@ -2034,6 +2011,8 @@ create_list_widget(GncOption& option, char *name) 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; } @@ -2644,7 +2623,6 @@ create_option_widget (GncOption& option, gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE); g_object_set (G_OBJECT(hbox), "margin", 3, NULL); - gtk_container_add(GTK_CONTAINER(*enclosing), hbox); auto value_px = create_range_spinner(option); g_signal_connect(G_OBJECT(value_px), "changed", @@ -2679,6 +2657,7 @@ create_option_widget (GncOption& option, option.set_ui_item(std::make_unique(static_cast(hbox))); option.set_ui_item_from_option(); + gtk_container_add(GTK_CONTAINER(*enclosing), hbox); gtk_widget_show_all(*enclosing); return hbox; } From 8eddb63ef1f5693253cb94df38cf8986f9290f2f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Nov 2021 14:39:40 -0800 Subject: [PATCH 267/298] Remove unused global variables global_help_cb_data and global_help_cb. --- gnucash/gnome-utils/dialog-options.cpp | 11 ----------- gnucash/gnome-utils/dialog-options.h | 3 --- gnucash/gnome-utils/gnc-gnome-utils.c | 2 -- 3 files changed, 16 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index ca6e8fea8c..4d6516790a 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -205,9 +205,6 @@ GncOptionGtkUIItem::set_widget(GtkWidget* widget) } -static GNCOptionWinCallback global_help_cb = NULL; -gpointer global_help_cb_data = NULL; - static void dialog_reset_cb(GtkWidget * w, gpointer data); void dialog_list_select_cb (GtkTreeSelection *selection, gpointer data); @@ -862,14 +859,6 @@ gnc_options_dialog_set_close_cb(GNCOptionWin * win, GNCOptionWinCallback 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) diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h index 8c3890a785..010858a901 100644 --- a/gnucash/gnome-utils/dialog-options.h +++ b/gnucash/gnome-utils/dialog-options.h @@ -79,9 +79,6 @@ 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, diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c index 00f8efe380..4b10742414 100644 --- a/gnucash/gnome-utils/gnc-gnome-utils.c +++ b/gnucash/gnome-utils/gnc-gnome-utils.c @@ -73,8 +73,6 @@ 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) { From 5e84f118ddfe53452e2f1088ee4d5aea618b0aa6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Fri, 19 Nov 2021 16:10:13 -0800 Subject: [PATCH 268/298] Move gnc_options_dialog functions from gnc-gnome-utils to dialog-options. --- gnucash/gnome-utils/dialog-options.cpp | 20 ++++++++++++++++++ gnucash/gnome-utils/dialog-options.h | 10 +++++++++ gnucash/gnome-utils/gnc-gnome-utils.c | 28 -------------------------- gnucash/gnome-utils/gnc-gnome-utils.h | 16 --------------- gnucash/gnome/dialog-custom-report.c | 1 - gnucash/gnome/gnc-budget-view.c | 1 - gnucash/gnome/gnc-plugin-page-budget.c | 2 +- gnucash/gnome/top-level.c | 1 - gnucash/html/gnc-html.i | 1 - 9 files changed, 31 insertions(+), 49 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 4d6516790a..cc370730b2 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -2801,3 +2801,23 @@ gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win) (GNCOptionWinCallback)gnc_book_options_help_cb, nullptr); } + +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_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); +} diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h index 010858a901..abf59dc502 100644 --- a/gnucash/gnome-utils/dialog-options.h +++ b/gnucash/gnome-utils/dialog-options.h @@ -96,6 +96,16 @@ void gnc_options_dialog_set_book_options_help_cb (GNCOptionWin *win); */ void gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb); +/** 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 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); + /** * Initialize the option UI elements. */ diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c index 4b10742414..9adf77e820 100644 --- a/gnucash/gnome-utils/gnc-gnome-utils.c +++ b/gnucash/gnome-utils/gnc-gnome-utils.c @@ -85,32 +85,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); -} - -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 * @@ -739,8 +713,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/dialog-custom-report.c b/gnucash/gnome/dialog-custom-report.c index 670e8d3d9f..7f72ef563f 100644 --- a/gnucash/gnome/dialog-custom-report.c +++ b/gnucash/gnome/dialog-custom-report.c @@ -29,7 +29,6 @@ #include "swig-runtime.h" #include "dialog-custom-report.h" -#include "dialog-options.h" #include "dialog-utils.h" #include "gnc-main-window.h" #include "window-report.h" diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c index cffee8dc89..172ed20353 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" diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c index e55b6df393..6eda5b34c0 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" diff --git a/gnucash/gnome/top-level.c b/gnucash/gnome/top-level.c index 77080180e4..d0a18f5e65 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/html/gnc-html.i b/gnucash/html/gnc-html.i index 4b76ff0502..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 From c3b8b6cc493ded3360b760b4fa960611da045000 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 21 Nov 2021 16:10:28 -0800 Subject: [PATCH 269/298] Make a proper class of GncOptionsDialog. Removes the C functions, allocates with new and delete and cleans itself up. --- gnucash/gnome-utils/CMakeLists.txt | 1 - gnucash/gnome-utils/dialog-options.cpp | 610 +++++++++----------- gnucash/gnome-utils/dialog-options.h | 117 ---- gnucash/gnome-utils/dialog-options.hpp | 80 ++- gnucash/gnome-utils/gnc-gnome-utils.c | 2 - gnucash/gnome-utils/gnc-main-window.cpp | 45 +- gnucash/gnome/assistant-hierarchy.cpp | 32 +- gnucash/gnome/dialog-report-column-view.cpp | 46 +- gnucash/gnome/dialog-report-style-sheet.cpp | 37 +- gnucash/gnome/window-report.cpp | 62 +- 10 files changed, 439 insertions(+), 593 deletions(-) delete mode 100644 gnucash/gnome-utils/dialog-options.h diff --git a/gnucash/gnome-utils/CMakeLists.txt b/gnucash/gnome-utils/CMakeLists.txt index c40af8c7f0..9cf89ce748 100644 --- a/gnucash/gnome-utils/CMakeLists.txt +++ b/gnucash/gnome-utils/CMakeLists.txt @@ -132,7 +132,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.cpp b/gnucash/gnome-utils/dialog-options.cpp index cc370730b2..2f6b4ac322 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -99,37 +99,6 @@ static constexpr const char* GNC_PREFS_GROUP{"dialogs.options"}; /* A pointer to the last selected filename */ #define LAST_SELECTION "last-selection" -struct gnc_option_win -{ - GtkWidget * window; - GtkWidget * notebook; - GtkWidget * page_list_view; - GtkWidget * page_list; - GtkButton * help_button; - GtkButton * cancel_button; - GtkButton * apply_button; - GtkButton * ok_button; - - bool 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 */ - bool destroyed; -}; enum page_tree { @@ -140,6 +109,8 @@ enum page_tree //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) @@ -151,6 +122,11 @@ 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) @@ -206,8 +182,7 @@ GncOptionGtkUIItem::set_widget(GtkWidget* widget) static void dialog_reset_cb(GtkWidget * w, gpointer data); -void dialog_list_select_cb (GtkTreeSelection *selection, - gpointer data); +static void dialog_list_select_cb (GtkTreeSelection *selection, gpointer data); static void component_close_handler (gpointer data); static void @@ -215,8 +190,8 @@ section_reset_widgets(GncOptionSection* section) { } -GtkWidget* const -gnc_option_get_gtk_widget (const GncOption* option) +static inline GtkWidget* const +option_get_gtk_widget (const GncOption* option) { if (!option) return nullptr; auto ui_item{dynamic_cast(option->get_ui_item())}; @@ -238,20 +213,24 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive) /* Can't static cast, no inheritance relationship. */ auto option_win = - static_cast(g_object_get_data(G_OBJECT(toplevel), + static_cast(g_object_get_data(G_OBJECT(toplevel), "optionwin")); - gtk_widget_set_sensitive (GTK_WIDGET(option_win->apply_button), sensitive); - gtk_widget_set_sensitive (GTK_WIDGET(option_win->ok_button), sensitive); - gtk_button_set_label (option_win->cancel_button, + 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 -gnc_options_dialog_changed (GNCOptionWin *win) +GncOptionsDialog::changed() noexcept { - if (!win) return; - - dialog_changed_internal (win->window, TRUE); + set_sensitive(true); } void @@ -266,7 +245,7 @@ void gnc_option_changed_option_cb(GtkWidget *dummy, GncOption* option) { if (!option) return; - auto widget{gnc_option_get_gtk_widget(option)}; + auto widget{option_get_gtk_widget(option)}; gnc_option_changed_widget_cb(widget, option); } @@ -392,16 +371,17 @@ create_reset_button_box(GtkBox* page_content_box) } static int -setup_notebook_pages(GNCOptionWin* propertybox, GtkBox* page_content_box, +setup_notebook_pages(GncOptionsDialog* dlg, GtkBox* page_content_box, const char* name) { - auto page_count = gtk_notebook_page_num(GTK_NOTEBOOK(propertybox->notebook), + auto notebook{dlg->get_notebook()}; + auto page_count = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), GTK_WIDGET(page_content_box)); - if (propertybox->page_list_view) + if (dlg->get_page_list_view()) { /* Build the matching list item for selecting from large page sets */ - auto view = GTK_TREE_VIEW(propertybox->page_list_view); + 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); @@ -414,20 +394,19 @@ setup_notebook_pages(GNCOptionWin* propertybox, GtkBox* page_content_box, 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); + 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(propertybox->page_list); + gtk_widget_hide(dlg->get_page_list()); } return page_count; } static int -gnc_options_dialog_append_page(GNCOptionWin * propertybox, - GncOptionSectionPtr& section) +dialog_append_page(GncOptionsDialog* dlg, GncOptionSectionPtr& section) { auto name = section->get_name().c_str(); if (!name || *name == '\0') @@ -462,55 +441,32 @@ gnc_options_dialog_append_page(GNCOptionWin * propertybox, _("Reset all values to their defaults.")); g_signal_connect(G_OBJECT(reset_button), "clicked", - G_CALLBACK(dialog_reset_cb), propertybox); + 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(propertybox->notebook), + 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(propertybox, page_content_box, name); + return setup_notebook_pages(dlg, page_content_box, name); } -/********************************************************************\ - * gnc_options_dialog_build_contents * - * builds an options dialog given a property box and an options * - * database and make the dialog visible * - * * - * @param propertybox - gnome property box to use * - * @param odb - option database to use * -\********************************************************************/ -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 * - * * - * @param propertybox - gnome property box to use * +/** + * 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 -gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox, - GNCOptionDB *odb, gboolean show_dialog) +GncOptionsDialog::build_contents(GncOptionDB *odb, bool show_dialog) { gint default_page = -1; - gint page; - - g_return_if_fail (propertybox != NULL); g_return_if_fail (odb != NULL); - propertybox->option_db = odb; + m_option_db = odb; auto num_sections = odb->num_sections(); auto default_section = odb->get_default_section(); @@ -519,9 +475,9 @@ gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox, default_section ? default_section->get_name().c_str() : "NULL"); odb->foreach_section( - [propertybox, default_section, &default_page] + [this, default_section, &default_page] (GncOptionSectionPtr& section) { - auto page = gnc_options_dialog_append_page(propertybox, section); + auto page = dialog_append_page(this, section); if (default_section && section.get() == default_section) default_page = page; }); @@ -541,107 +497,112 @@ gnc_options_dialog_build_contents_full (GNCOptionWin *propertybox, }); }); - gtk_notebook_popup_enable(GTK_NOTEBOOK(propertybox->notebook)); + gtk_notebook_popup_enable(GTK_NOTEBOOK(m_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)); + auto selection{gtk_tree_view_get_selection(GTK_TREE_VIEW(m_page_list_view))}; GtkTreeIter iter; - GtkTreeModel *model; - model = gtk_tree_view_get_model(GTK_TREE_VIEW(propertybox->page_list_view)); + 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(propertybox->notebook), default_page); + gtk_notebook_set_current_page(GTK_NOTEBOOK(m_notebook), default_page); } - dialog_changed_internal(propertybox->window, FALSE); + dialog_changed_internal(m_window, FALSE); if (show_dialog) - gtk_widget_show(propertybox->window); + gtk_widget_show(m_window); } -GtkWidget * -gnc_options_dialog_widget(GNCOptionWin * win) +void GncOptionsDialog::call_apply_cb() noexcept { - return win->window; + 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); } -GtkWidget * -gnc_options_page_list(GNCOptionWin * win) +void GncOptionsDialog::call_help_cb() noexcept { - return win->page_list; + if (m_help_cb) + (m_help_cb)(this, m_help_cb_data); } -GtkWidget * -gnc_options_dialog_notebook(GNCOptionWin * win) +void GncOptionsDialog::call_close_cb() noexcept { - 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)); - 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) + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(m_window)); + if (m_close_cb) { - if (win->close_cb) - (win->close_cb)(win, win->close_cb_data); + gtk_window_close(GTK_WINDOW(m_window)); + (m_close_cb)(this, m_close_cb_data); + } + else + { + gtk_widget_hide(m_window); } } -static bool -gnc_options_dialog_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) +void GncOptionsDialog::call_book_help_cb() noexcept { - GNCOptionWin *win = static_cast(data); +/* 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) { @@ -655,7 +616,7 @@ gnc_options_dialog_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gp static void dialog_reset_cb(GtkWidget * w, gpointer data) { - GNCOptionWin *win = static_cast(data); + GncOptionsDialog *win = static_cast(data); gpointer val; bool dialog_changed = false; @@ -675,14 +636,14 @@ dialog_reset_cb(GtkWidget * w, gpointer data) option.set_ui_item_from_option(); }); - dialog_changed_internal (win->window, dialog_changed); + dialog_changed_internal (win->get_widget(), dialog_changed); } -void -dialog_list_select_cb (GtkTreeSelection *selection, - gpointer data) +// changed signal handler +static void +dialog_list_select_cb (GtkTreeSelection *selection, gpointer data) { - GNCOptionWin * win = static_cast(data); + GncOptionsDialog * win = static_cast(data); GtkTreeModel *list; GtkTreeIter iter; gint index = 0; @@ -693,192 +654,183 @@ dialog_list_select_cb (GtkTreeSelection *selection, PAGE_INDEX, &index, -1); PINFO("Index is %d", index); - gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), index); + gtk_notebook_set_current_page(GTK_NOTEBOOK(win->get_notebook()), index); } static void component_close_handler (gpointer data) { - GNCOptionWin *win = static_cast(data); - gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->window)); - gnc_options_dialog_cancel_button_cb (NULL, win); + GncOptionsDialog *win = static_cast(data); + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(win->get_widget())); + dialog_cancel_button_cb (NULL, win); } -/* gnc_options_dialog_new: +/** Constructs a GncOptionsDialog * - * - 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 + * 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. */ -GNCOptionWin * -gnc_options_dialog_new(gchar *title, GtkWindow *parent) +GncOptionsDialog::GncOptionsDialog(bool modal, const char* title, + const char* component_class, + GtkWindow *parent) : + m_component_class{component_class ? component_class : DIALOG_OPTIONS_CM_CLASS} { - return gnc_options_dialog_new_modal(FALSE, title, NULL, parent); -} - -/** - * - 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(); + auto 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")); - retval->component_class = component_class ? component_class : DIALOG_OPTIONS_CM_CLASS; - g_object_set_data(G_OBJECT(retval->window), "optionwin", retval); + 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(retval->window), "gnc-id-options"); + gtk_widget_set_name (GTK_WIDGET(m_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")); + m_page_list_view = GTK_WIDGET(gtk_builder_get_object (builder, "page_list_treeview")); - view = GTK_TREE_VIEW(retval->page_list_view); + auto view = GTK_TREE_VIEW(m_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); + 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); - 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); + 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); + 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 (dialog_list_select_cb), retval); - } + 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); - retval->help_button = GTK_BUTTON(gtk_builder_get_object (builder, "helpbutton")); - g_signal_connect(retval->help_button, "clicked", G_CALLBACK(gnc_options_dialog_help_button_cb), retval); - retval->cancel_button = GTK_BUTTON(gtk_builder_get_object (builder, "cancelbutton")); - g_signal_connect(retval->cancel_button, "clicked", G_CALLBACK(gnc_options_dialog_cancel_button_cb), retval); - retval->apply_button = GTK_BUTTON(gtk_builder_get_object (builder, "applybutton")); - g_signal_connect(retval->apply_button, "clicked", G_CALLBACK(gnc_options_dialog_apply_button_cb), retval); - retval->ok_button = GTK_BUTTON(gtk_builder_get_object (builder, "okbutton")); - g_signal_connect(retval->ok_button, "clicked", G_CALLBACK(gnc_options_dialog_ok_button_cb), retval); + 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, retval); + 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(retval->window), + gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(m_window), parent); if (title) - gtk_window_set_title(GTK_WINDOW(retval->window), title); + gtk_window_set_title(GTK_WINDOW(m_window), title); /* modal */ - if (modal == TRUE) - { - GtkWidget *apply_button; - - apply_button = GTK_WIDGET(gtk_builder_get_object (builder, "applybutton")); - gtk_widget_hide (apply_button); - } + if (modal) + gtk_widget_hide (GTK_WIDGET(m_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(); + auto hbox = GTK_WIDGET(gtk_builder_get_object (builder, + "notebook_placeholder")); + m_notebook = gtk_notebook_new(); - gtk_widget_set_vexpand (retval->notebook, TRUE); + gtk_widget_set_vexpand (m_notebook, TRUE); - gtk_widget_show(retval->notebook); - gtk_box_pack_start(GTK_BOX(hbox), retval->notebook, TRUE, TRUE, 5); + gtk_widget_show(m_notebook); + gtk_box_pack_start(GTK_BOX(hbox), m_notebook, TRUE, TRUE, 5); - component_id = gnc_register_gui_component (retval->component_class, - NULL, component_close_handler, - retval); + 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 (retval->window, "destroy", - G_CALLBACK(gnc_options_dialog_destroy_cb), retval); + g_signal_connect (m_window, "destroy", G_CALLBACK(dialog_destroy_cb), this); - g_signal_connect (retval->window, "key_press_event", - G_CALLBACK(gnc_options_dialog_window_key_press_cb), retval); + g_signal_connect (m_window, "key_press_event", + G_CALLBACK(dialog_window_key_press_cb), this); g_object_unref(G_OBJECT(builder)); +} - retval->destroyed = FALSE; - return retval; +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 -gnc_options_dialog_set_apply_cb(GNCOptionWin * win, GNCOptionWinCallback cb, - gpointer data) +GncOptionsDialog::set_apply_cb(GncOptionsDialogCallback cb, gpointer data) noexcept { - win->apply_cb = cb; - win->apply_cb_data = data; + m_apply_cb = cb; + m_apply_cb_data = data; } void -gnc_options_dialog_set_help_cb(GNCOptionWin * win, GNCOptionWinCallback cb, - gpointer data) +GncOptionsDialog::set_help_cb(GncOptionsDialogCallback cb, gpointer data) noexcept { - win->help_cb = cb; - win->help_cb_data = data; + m_help_cb = cb; + m_help_cb_data = data; } void -gnc_options_dialog_set_close_cb(GNCOptionWin * win, GNCOptionWinCallback cb, - gpointer data) +GncOptionsDialog::set_close_cb( GncOptionsDialogCallback cb, gpointer data) noexcept { - win->close_cb = cb; - win->close_cb_data = data; + 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); } -/* This is for global program preferences. */ void -gnc_options_dialog_destroy(GNCOptionWin * win) +GncOptionsDialog::set_book_help_cb() noexcept { - 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); + 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 */ /* ***************************************************************/ @@ -1461,7 +1413,7 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box, } option.set_ui_item_from_option(); - auto widget{gnc_option_get_gtk_widget(&option)}; + auto widget{option_get_gtk_widget(&option)}; if (type == GncOptionUIType::DATE_RELATIVE) { *enclosing = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); @@ -1541,7 +1493,7 @@ account_select_all_cb(GtkWidget *widget, gpointer data) GncTreeViewAccount *tree_view; GtkTreeSelection *selection; - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option)); + 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); @@ -1555,7 +1507,7 @@ account_clear_all_cb(GtkWidget *widget, gpointer data) GncTreeViewAccount *tree_view; GtkTreeSelection *selection; - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option)); + 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); @@ -1568,7 +1520,7 @@ account_select_children_cb(GtkWidget *widget, gpointer data) GncTreeViewAccount *tree_view; GList *acct_list = NULL, *acct_iter = NULL; - tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget (option)); + 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) @@ -1593,7 +1545,7 @@ show_hidden_toggled_cb(GtkWidget *widget, GncOption* option) option->get_ui_type() != GncOptionUIType::ACCOUNT_SEL) return; - auto tree_view = GNC_TREE_VIEW_ACCOUNT(gnc_option_get_gtk_widget(option)); + 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)); @@ -1795,7 +1747,7 @@ create_option_widget(GncOption& option, gtk_grid_attach (GTK_GRID(page_box), *enclosing, 1, grid_row, 1, 1); *packed = TRUE; - auto widget = gnc_option_get_gtk_widget(&option); + 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", @@ -1858,7 +1810,7 @@ create_option_widget (GncOption& option, static void list_changed_cb(GtkTreeSelection *selection, GncOption* option) { - GtkTreeView *view = GTK_TREE_VIEW(gnc_option_get_gtk_widget (option)); + GtkTreeView *view = GTK_TREE_VIEW(option_get_gtk_widget (option)); gnc_option_changed_widget_cb(GTK_WIDGET(view), option); } @@ -1869,7 +1821,7 @@ list_select_all_cb(GtkWidget *widget, gpointer data) GtkTreeView *view; GtkTreeSelection *selection; - view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option)); + 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); @@ -1882,7 +1834,7 @@ list_clear_all_cb(GtkWidget *widget, gpointer data) GtkTreeView *view; GtkTreeSelection *selection; - view = GTK_TREE_VIEW(gnc_option_get_gtk_widget(option)); + 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); @@ -2018,7 +1970,7 @@ create_option_widget (GncOption& option, (G_OBJECT(page_box), "options-grid-row")); *enclosing = create_list_widget(option, NULL); - auto value = gnc_option_get_gtk_widget(&option); + auto value = option_get_gtk_widget(&option); align_label (name_label); @@ -2351,7 +2303,7 @@ radiobutton_set_cb(GtkWidget *w, gpointer data) gpointer _current, _new_value; gint current, new_value; - auto widget = gnc_option_get_gtk_widget(option); + auto widget = option_get_gtk_widget(option); _current = g_object_get_data(G_OBJECT(widget), "gnc_radiobutton_index"); current = GPOINTER_TO_INT (_current); @@ -2513,7 +2465,7 @@ gnc_plot_size_option_set_select_method(GncOption& option, bool set_buttons) GtkWidget *px_widget, *p_widget; GtkWidget *widget; - widget = gnc_option_get_gtk_widget(&option); + widget = option_get_gtk_widget(&option); widget_list = gtk_container_get_children(GTK_CONTAINER(widget)); // px_button item 0 @@ -2724,8 +2676,8 @@ create_option_widget (GncOption& option, return widget; } -void -gnc_options_ui_initialize(void) +static void +gnc_options_ui_factory_initialize(void) { GncOptionUIFactory::set_func(GncOptionUIType::BOOLEAN, create_option_widget); @@ -2781,43 +2733,9 @@ gnc_options_dialog_set_new_book_option_values (GncOptionDB *odb) { auto option{odb->find_option(OPTION_SECTION_ACCOUNTS, OPTION_NAME_NUM_FIELD_SOURCE)}; - auto num_source_button{gnc_option_get_gtk_widget(option)}; + auto num_source_button{option_get_gtk_widget(option)}; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (num_source_button), num_split_action); } } - -static void -gnc_book_options_help_cb (GNCOptionWin *win, gpointer dat) -{ - gnc_gnome_help (GTK_WINDOW (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, - nullptr); -} - -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_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); -} diff --git a/gnucash/gnome-utils/dialog-options.h b/gnucash/gnome-utils/dialog-options.h deleted file mode 100644 index abf59dc502..0000000000 --- a/gnucash/gnome-utils/dialog-options.h +++ /dev/null @@ -1,117 +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 -#ifdef __cplusplus -class GncOption; -class GncOptionDB; -using GNCOption = GncOption; -using GNCOptionDB = GncOptionDB; -extern "C" -{ -#else -typedef struct GncOption GncOption; -typedef struct GncOptionDB GncOptionDB; -typedef GncOptionDB GNCOptionDB; -#endif -#include -#include - - -/** - * Retrieve the GtkWidget* used for packing the option control. - * - * This is not ncessarily the widget that has the input or handles signals. - * @param option The option - * @return a GtkWidget* const - */ -GtkWidget* const gnc_option_get_gtk_widget (const 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); -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_build_contents (GNCOptionWin *win, - GNCOptionDB *odb); -void gnc_options_dialog_build_contents_full (GNCOptionWin *win, - GNCOptionDB *odb, - gboolean show_dialog); -void gnc_options_ui_initialize (void); - -/** 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_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 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); - -/** - * Initialize the option UI elements. - */ -void gnc_options_ui_initialize(void); - -#ifdef __cplusplus -} -#endif -#endif /* OPTIONS_DIALOG_H */ diff --git a/gnucash/gnome-utils/dialog-options.hpp b/gnucash/gnome-utils/dialog-options.hpp index b0101c23d3..71c02c39d3 100644 --- a/gnucash/gnome-utils/dialog-options.hpp +++ b/gnucash/gnome-utils/dialog-options.hpp @@ -34,7 +34,6 @@ #include #include -#include "dialog-options.h" /** @fn WidgetCreateFunc * Function pointer for per-option-type GtkWidget constructors. @@ -74,6 +73,7 @@ public: GtkWidget**, bool*); private: static std::vector s_registry; + static bool s_initialized; }; /** class GncOptionGtkUIItem @@ -116,6 +116,84 @@ 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/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c index 9adf77e820..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" @@ -77,7 +76,6 @@ 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"); diff --git a/gnucash/gnome-utils/gnc-main-window.cpp b/gnucash/gnome-utils/gnc-main-window.cpp index a8b2a255f8..c61de37306 100644 --- a/gnucash/gnome-utils/gnc-main-window.cpp +++ b/gnucash/gnome-utils/gnc-main-window.cpp @@ -86,7 +86,7 @@ extern "C" # include // for stat(2) #endif } -#include "dialog-options.h" +#include "dialog-options.hpp" /** Names of signals generated by the main window. */ enum @@ -4220,7 +4220,7 @@ 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) { auto options{static_cast(user_data)}; @@ -4232,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) { auto options{static_cast(user_data)}; - gnc_options_dialog_destroy(optionwin); + delete optionwin; gnc_option_db_destroy(options); } @@ -4264,14 +4264,12 @@ static gboolean show_handler (const char *class_name, gint component_id, gpointer user_data, gpointer iter_data) { - auto optwin{static_cast(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); } @@ -4279,11 +4277,9 @@ 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(); + 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); @@ -4295,23 +4291,18 @@ gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent) { 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 diff --git a/gnucash/gnome/assistant-hierarchy.cpp b/gnucash/gnome/assistant-hierarchy.cpp index 6562552c4c..5f60c10426 100644 --- a/gnucash/gnome/assistant-hierarchy.cpp +++ b/gnucash/gnome/assistant-hierarchy.cpp @@ -44,7 +44,6 @@ extern "C" #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" @@ -68,7 +67,7 @@ extern "C" #include "gnc-engine.h" } - +#include #include static QofLogModule log_module = GNC_MOD_IMPORT; @@ -125,7 +124,7 @@ typedef struct gboolean new_book; /* presumably only used for new book creation but we check*/ GncOptionDB *options; - GNCOptionWin *optionwin; + GncOptionsDialog *optionwin; GncHierarchyAssistantFinishedCallback when_completed; @@ -1397,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); @@ -1445,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); @@ -1504,19 +1503,18 @@ 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) { 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); @@ -1527,17 +1525,17 @@ assistant_insert_book_options_page (hierarchy_data *data) 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, nullptr); - 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); diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp index 9e1adef41b..5c45dcea6b 100644 --- a/gnucash/gnome/dialog-report-column-view.cpp +++ b/gnucash/gnome/dialog-report-column-view.cpp @@ -40,7 +40,7 @@ extern "C" } #include "dialog-report-column-view.hpp" -#include "dialog-options.h" +#include #include enum available_cols @@ -61,7 +61,7 @@ enum contents_cols struct gncp_column_view_edit { - GNCOptionWin * optwin; + GncOptionsDialog * optwin; GtkTreeView * available; GtkTreeView * contents; @@ -95,7 +95,7 @@ gnc_column_view_set_option(GncOptionDB* odb, const char* section, static void gnc_column_view_edit_destroy(gnc_column_view_edit * view) { - gnc_options_dialog_destroy(view->optwin); + delete view->optwin; scm_gc_unprotect_object(view->view); gnc_option_db_destroy(view->odb); g_free(view); @@ -273,18 +273,17 @@ gnc_column_view_update_buttons_cb (GtkTreeSelection *selection, } static void -gnc_column_view_edit_apply_cb(GNCOptionWin * w, gpointer user_data) +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)}; - GList *results = nullptr, *iter; if (!win) return; - results = gnc_option_db_commit (win->odb); - for (iter = results; iter; iter = iter->next) + 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(gnc_options_dialog_widget(w)), + gtk_message_dialog_new(GTK_WINDOW(dlg->get_widget()), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, @@ -300,7 +299,7 @@ gnc_column_view_edit_apply_cb(GNCOptionWin * w, gpointer user_data) } static void -gnc_column_view_edit_close_cb(GNCOptionWin * win, gpointer user_data) +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!"); @@ -340,10 +339,10 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view) gnc_column_view_edit * r = g_new0(gnc_column_view_edit, 1); GtkBuilder *builder; - r->optwin = gnc_options_dialog_new (nullptr, GTK_WINDOW(gnc_ui_get_main_window (nullptr))); + r->optwin = new GncOptionsDialog(nullptr, GTK_WINDOW(gnc_ui_get_main_window (nullptr))); /* Hide the generic dialog page list. */ - gtk_widget_hide(gnc_options_page_list(r->optwin)); + gtk_widget_hide(r->optwin->get_page_list()); builder = gtk_builder_new(); gnc_builder_add_from_file (builder, "dialog-report.glade", "view_contents_table"); @@ -364,10 +363,9 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view) r->contents_list = SCM_EOL; r->odb = odb; - gnc_options_dialog_build_contents(r->optwin, r->odb); + r->optwin->build_contents(r->odb); - gtk_notebook_append_page(GTK_NOTEBOOK(gnc_options_dialog_notebook - (r->optwin)), + gtk_notebook_append_page(GTK_NOTEBOOK(r->optwin->get_notebook()), editor, gtk_label_new(_("Contents"))); @@ -424,18 +422,16 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view) 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); + 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(gnc_options_dialog_widget(r->optwin)); + 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 gnc_options_dialog_widget(r->optwin); + return r->optwin->get_widget(); } } @@ -509,7 +505,7 @@ gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data) gnc_column_view_set_option(r->odb, "__general", "report-list", r->contents_list); - gnc_options_dialog_changed (r->optwin); + r->optwin->changed (); } g_free (guid_str); update_contents_lists(r); @@ -552,7 +548,7 @@ gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data) gnc_column_view_set_option(r->odb, "__general", "report-list", r->contents_list); - gnc_options_dialog_changed (r->optwin); + r->optwin->changed(); } update_contents_lists(r); } @@ -589,7 +585,7 @@ gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data) gnc_column_view_set_option(r->odb, "__general", "report-list", r->contents_list); - gnc_options_dialog_changed (r->optwin); + r->optwin->changed(); update_contents_lists(r); } @@ -627,7 +623,7 @@ gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data) gnc_column_view_set_option(r->odb, "__general", "report-list", r->contents_list); - gnc_options_dialog_changed (r->optwin); + r->optwin->changed(); update_contents_lists(r); } @@ -684,7 +680,7 @@ gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data) scm_from_int (r->contents_selected), current); scm_gc_protect_object(r->contents_list); - gnc_options_dialog_changed (r->optwin); + r->optwin->changed(); update_contents_lists(r); } diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp index 980d507941..a1a8d651bc 100644 --- a/gnucash/gnome/dialog-report-style-sheet.cpp +++ b/gnucash/gnome/dialog-report-style-sheet.cpp @@ -39,8 +39,9 @@ extern "C" #include "gnc-guile-utils.h" #include "gnc-report.h" #include "gnc-ui.h" +#include } -#include "dialog-options.h" +#include #include #define DIALOG_STYLE_SHEETS_CM_CLASS "style-sheets-dialog" @@ -60,7 +61,7 @@ struct _stylesheetdialog typedef struct ss_info { - GNCOptionWin * odialog; + GncOptionsDialog * odialog; GncOptionDB * odb; SCM stylesheet; GtkTreeRowReference *row_ref; @@ -109,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; @@ -138,16 +139,16 @@ gnc_style_sheet_options_apply_cb (GNCOptionWin * propertybox, } static void -gnc_style_sheet_options_close_cb (GNCOptionWin * propertybox, +gnc_style_sheet_options_close_cb (GncOptionsDialog *opt_dialog, gpointer user_data) { auto ssi{static_cast(user_data)}; - GtkTreeIter iter; 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, @@ -155,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); @@ -171,32 +172,24 @@ gnc_style_sheet_dialog_create (StyleSheetDialog * ss, 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->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); diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp index 0e27fa0695..5a50e22016 100644 --- a/gnucash/gnome/window-report.cpp +++ b/gnucash/gnome/window-report.cpp @@ -36,7 +36,6 @@ extern "C" #include #include "swig-runtime.h" -#include "dialog-options.h" #include "gnc-guile-utils.h" #include "gnc-report.h" #include "gnc-ui.h" @@ -45,6 +44,7 @@ extern "C" #include "gnc-plugin-page-report.h" } +#include "dialog-options.hpp" #include "dialog-report-column-view.hpp" /******************************************************************** @@ -65,30 +65,29 @@ reportWindow(int report_id, GtkWindow *parent) struct report_default_params_data { - GNCOptionWin * win; + 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?!"); auto win{static_cast(user_data)}; - GList *results = nullptr, *iter; if (!win) return; - results = gnc_option_db_commit (win->odb); - 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), - static_cast(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); @@ -99,33 +98,32 @@ 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; 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, 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) { 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); + delete win->win; gnc_option_db_destroy(win->odb); g_free(win); } @@ -183,24 +181,18 @@ gnc_report_window_default_params_editor(GncOptionDB* odb, 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->cur_report); - gnc_options_dialog_build_contents(prm->win, prm->odb); + 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(); } } From 6f93a68bad9bf8840d783e861499a33c13f1b4be Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 2 Dec 2021 15:04:49 -0800 Subject: [PATCH 270/298] c++options: Serialize and deserialize to strings instead of streams. And use serialize to create values for gnc:generate-restore-forms and both of them for the meat of the stream functions. This fixes in particular QofInstance serialization where passing the option value directly to scheme format resulted in a notation about a swig pointer instead of the desired GUID string or commodity namespace and mnemonic strings. --- libgnucash/app-utils/gnc-option-impl.cpp | 301 +++++++++++++++++- libgnucash/app-utils/gnc-option-impl.hpp | 35 +- libgnucash/app-utils/gnc-option.cpp | 19 +- libgnucash/app-utils/gnc-option.hpp | 18 +- libgnucash/app-utils/gnc-optiondb.i | 85 ++++- libgnucash/app-utils/options.scm | 15 +- .../app-utils/test/gtest-gnc-option.cpp | 42 +-- .../test/test-gnc-option-scheme-output.scm | 54 +++- 8 files changed, 502 insertions(+), 67 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 5a79194660..e36d9b1a5a 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -26,6 +26,7 @@ #include #include #include +#include extern "C" { @@ -389,8 +390,28 @@ qof_instance_from_string(const std::string& str, GncOptionUIType type) std::string qof_instance_to_string(const QofInstance* inst) { - gnc::GUID guid{*qof_instance_get_guid(inst)}; - return guid.to_string(); + 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{*qof_instance_get_guid(inst)}; + retval = guid.to_string(); + } + return retval; } template void @@ -411,6 +432,41 @@ GncOptionValue::reset_default_value() m_value = m_default_value; } +template std::string +GncOptionValue::serialize() const noexcept +{ + if constexpr(std::is_same_v) + return qof_instance_to_string(m_value); + 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 ""; +} + +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())); + 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; +} + template <> void GncOptionValue::set_value(SCM new_value) { @@ -441,6 +497,201 @@ GncOptionValue::reset_default_value() scm_gc_protect_object(m_value); } +template std::string +GncOptionValidatedValue::serialize() const noexcept +{ + if constexpr(std::is_same_v) + return qof_instance_to_string(m_value); + 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 "Invalid Value Type"; +} + +template bool +GncOptionValidatedValue::deserialize(const std::string& str) noexcept +{ + if constexpr(std::is_same_v) + set_value(qof_instance_from_string(str, get_ui_type())); + 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 +{ + std::string retval; + bool first = true; + for (auto val : m_value) + { + if (!first) + retval += " "; + first = false; + retval += qof_instance_to_string(QOF_INSTANCE(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; + auto ptr = qof_instance_from_string(str.substr(pos, pos + GUID_ENCODING_LENGTH), get_ui_type()); + m_value.push_back(reinterpret_cast(ptr)); + pos += GUID_ENCODING_LENGTH; + } + return true; +} + +std::string +GncOptionAccountSelValue::serialize() const noexcept +{ + return qof_instance_to_string(QOF_INSTANCE(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 +{ + std::string retval; + bool first = true; + 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") + { + set_value(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; + } +} + template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); template GncOptionValue::GncOptionValue(const GncOptionValue&); @@ -498,3 +749,49 @@ 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 GncOptionValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::serialize() const noexcept; +template std::string GncOptionValidatedValue::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 GncOptionValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; +template bool GncOptionValidatedValue::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 index 68e7b5859d..fa92008a0d 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -134,6 +134,8 @@ public: 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; @@ -201,6 +203,8 @@ public: 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; @@ -299,19 +303,24 @@ std::istream& operator>> (std::istream& iss, OptType& opt) { std::string instr; - if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY || - type == GncOptionUIType::CURRENCY) + auto type = opt.get_ui_type(); + if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) { std::string name_space, mnemonic; - if (type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY) - { + if (type == GncOptionUIType::COMMODITY) iss >> name_space; - } else name_space = GNC_COMMODITY_NS_CURRENCY; - iss >> mnemonic; - instr = name_space + ":"; - instr += mnemonic; + if (name_space.find(":") == std::string::npos) + { + iss >> mnemonic; + instr = name_space + ":"; + instr += mnemonic; + } + else + { + instr = name_space; + } } else { @@ -385,6 +394,8 @@ public: 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; @@ -644,6 +655,8 @@ public: 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 { @@ -794,6 +807,8 @@ public: 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; @@ -895,6 +910,8 @@ public: 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; Account* m_value; @@ -1042,6 +1059,8 @@ public: 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; diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index b96405de0c..47248390f7 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -406,12 +406,21 @@ GncOption::set_alternate(bool alt) noexcept }, *m_option); } -std::ostream& -GncOption::out_stream(std::ostream& oss) const +std::string +GncOption::serialize() const { - return std::visit([&oss](auto& option) -> std::ostream& { - oss << option; - return oss; + 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); } diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 0913f6d6d5..2c93c9283f 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -180,9 +180,20 @@ public: GList* account_type_list() const noexcept; bool is_alternate() const noexcept; void set_alternate(bool) noexcept; - std::ostream& out_stream(std::ostream& oss) const; +/** 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: @@ -200,7 +211,8 @@ operator<(const GncOption& right, const GncOption& left) inline std::ostream& operator<<(std::ostream& oss, const GncOption& opt) { - return opt.out_stream(oss); + oss << opt.serialize(); + return oss; } inline std::istream& diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 39a9f18606..6e200e62b7 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -590,6 +590,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); #include #include #include "gnc-option.hpp" +#include "gnc-option-impl.hpp" #include "gnc-option-ui.hpp" GncOptionVariant& swig_get_option(GncOption* option) @@ -1009,7 +1010,9 @@ inline SCM return_scm_value(ValueType value) const SCM ticked_format_str{scm_from_utf8_string("'~a")}; //scm_simple_format needs a scheme list of arguments to match the format //placeholders. - auto value{scm_list_1(GncOption_get_scm_value($self))}; + auto serial{$self->serialize()}; + SCM value = serial.empty() ? SCM_BOOL_F : + scm_list_1(scm_from_utf8_string(serial.c_str())); auto uitype{$self->get_ui_type()}; if (uitype == GncOptionUIType::STRING || uitype == GncOptionUIType::TEXT || @@ -1030,11 +1033,48 @@ inline SCM return_scm_value(ValueType value) return scm_simple_format(SCM_BOOL_F, plain_format_str, value); else return scm_simple_format(SCM_BOOL_F, plain_format_str, - SCM_BOOL_F); + scm_list_1(SCM_BOOL_F)); + } + else if (uitype == GncOptionUIType::CURRENCY) + { + const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")}; + return scm_simple_format(SCM_BOOL_F, quoted_format_str, value); + } + else if (uitype == GncOptionUIType::COMMODITY) + { + const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")}; + auto sep{serial.find(':')}; + if (sep == std::string::npos) + sep = serial.find(' '); + if (sep == std::string::npos) + { + const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")}; + return scm_simple_format(SCM_BOOL_F, quoted_format_str, value); + } + auto name_space{serial.substr(0, sep)}; + auto symbol{serial.substr(sep + 1, std::string::npos)}; + auto commodity_val{scm_list_2(scm_from_utf8_string(name_space.c_str()), + scm_from_utf8_string(symbol.c_str()))}; + return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val); + } + else if (uitype == GncOptionUIType::DATE_ABSOLUTE || + uitype == GncOptionUIType::DATE_RELATIVE || + uitype == GncOptionUIType::DATE_BOTH) + { + const SCM date_fmt{scm_from_utf8_string("'~a")}; + return scm_simple_format(SCM_BOOL_F, date_fmt, value); } else { - return scm_simple_format(SCM_BOOL_F, ticked_format_str, value); + if (value && scm_is_true(value)) + { + return scm_simple_format(SCM_BOOL_F, ticked_format_str, value); + } + else + { + return scm_simple_format(SCM_BOOL_F, plain_format_str, + scm_list_1(SCM_BOOL_F)); + } } } @@ -1085,6 +1125,24 @@ inline SCM return_scm_value(ValueType value) } 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)); @@ -1143,6 +1201,24 @@ inline SCM return_scm_value(ValueType value) } 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)); @@ -1392,7 +1468,8 @@ inline SCM return_scm_value(ValueType value) gnc_commodity *value) { return new GncOption{GncOptionValue{ - section, name, key, doc_string, (const QofInstance*)value}}; + section, name, key, doc_string, (const QofInstance*)value, + GncOptionUIType::COMMODITY}}; } static GncOption* diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 097a996e28..52e55c2e00 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -87,14 +87,13 @@ ;; Used only by test-stress-options.scm (define-public (gnc:option-data option) - (let ((num-values (GncOption-num-permissible-values option)) - (retval '())) - (do ((i 0 (1+ i))) ((>= i num-values)) - (let ((value (GncOption-permissible-value option i)) - (name (GncOption-permissible-value-name option i))) - (set! retval (cons retval (vector value name))))) - retval)) - +; (define num-values (GncOption-num-permissible-values option)) +; (let loop ((i 0) (retval '())) +; (if (>= i num-values) (reverse retval) +; (let ((value (GncOption-permissible-value option i)) +; (name (GncOption-permissible-value-name option i))) +; (loop (1+ i) (cons (vector value name) retval)))))) + (list (vector 1 2))) ;; Create the database and return a dispatch function. (define-public (gnc:new-options) (let ((optiondb (new-gnc-optiondb))) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index a6de6ebe2c..e2e01e132d 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -121,11 +121,11 @@ 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(), "#f"); + EXPECT_STREQ(oss.str().c_str(), "False"); oss.str(""); option.set_value(true); oss << option; - EXPECT_STREQ(oss.str().c_str(), "#t"); + EXPECT_STREQ(oss.str().c_str(), "True"); } TEST(GncOption, test_bool_stream_in) @@ -301,7 +301,7 @@ static inline std::string make_currency_str(gnc_commodity* cur) static inline std::string make_commodity_str(gnc_commodity* com) { std::string com_str{gnc_commodity_get_namespace(com)}; - com_str += " "; + com_str += ":"; com_str += gnc_commodity_get_mnemonic(com); return com_str; } @@ -511,7 +511,7 @@ TEST_F(GncRangeOption, test_range_out) { std::ostringstream oss; oss << "Integer " << m_intoption << " Double " << m_doubleoption << "."; - EXPECT_STREQ("Integer 15 Double 1.5.", oss.str().c_str()); + EXPECT_STREQ("Integer 15 Double 1.500000.", oss.str().c_str()); } TEST_F(GncRangeOption, test_range_in) @@ -1120,8 +1120,8 @@ TEST_F(GncDateOption, test_stream_out) m_option.set_value(time1); std::ostringstream oss; oss << time1; - std::string timestr{"absolute . "}; - timestr += oss.str(); + std::string timestr{"(absolute . "}; + timestr += oss.str() + ")"; oss.str(""); oss << m_option; EXPECT_EQ(oss.str(), timestr); @@ -1129,77 +1129,77 @@ TEST_F(GncDateOption, test_stream_out) m_option.set_value(RelativeDatePeriod::TODAY); oss.str(""); oss << m_option; - EXPECT_STREQ(oss.str().c_str(), "relative . today"); + 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + EXPECT_STREQ(oss.str().c_str(), "(relative . end-prev-fin-year)"); } TEST_F(GncDateOption, test_stream_in_absolute) diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm index d0b3147e11..34d1389a62 100644 --- a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm +++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm @@ -66,10 +66,32 @@ (let ((option (gnc:lookup-option options \"foo\" \"bar\"))) - ((lambda (o) (if o (gnc:option-set-value o '~s))) option)) + ((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 \"~a\"))) option)) + +" value)) + +(define (test-commodity-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\" \"~a\"))) option)) + +" (string-split value #\:))) + (define (test-budget-output-template value) (format #f " ; Section: foo @@ -90,7 +112,7 @@ (test-equal test-unchanged-section-output-template (gnc:generate-restore-forms odb "options")) (gnc:option-set-value (gnc:lookup-option odb "foo" "bar") value) - (test-equal (test-template value) + (test-equal (test-template (GncOption-serialize (gnc:lookup-option odb "foo" "bar"))) (gnc:generate-restore-forms odb "options")))) (define (test-gnc-string-option-to-scheme) @@ -125,7 +147,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (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 gnc:make-currency-option test-literal-output-template + (test-option-scheme-output gnc:make-currency-option test-currency-output-template USD EUR) ;; Garbage collection has already eaten USD and EUR. (test-book-clear-data book "gnc-commodity-table") @@ -163,7 +185,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (let* ((book (gnc-option-test-book-new)) (AAPL (gnc-commodity-new book "Apple" "NASDAQ" "AAPL" "" 1)) (FMAGX (gnc-commodity-new book "Fidelity Magellan Fund" "FUND" "FMAGX" "" 1000))) - (test-option-scheme-output gnc:make-commodity-option test-literal-output-template + (test-option-scheme-output gnc:make-commodity-option test-currency-output-template AAPL FMAGX)) (test-end "test-gnc-commodity-option-to-scheme")) @@ -181,19 +203,19 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (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 test-unchanged-section-output-template + (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 (test-template (GncOption-get-scm-value option)) + (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 value (GncOption-get-scm-value option)) - (test-equal (test-template (GncOption-get-scm-value option)) + (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")) @@ -248,7 +270,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (test-template test-literal-output-template) (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK)))) (gnc-option-set-value option new-acclist) - (test-equal (test-template (GncOption-get-scm-value option)) + (test-equal (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) (test-end "test-gnc-account-list-option-to-scheme")) @@ -271,7 +293,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (test-template test-literal-output-template) (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK)))) (gnc-option-set-value option new-acclist) - (test-equal (test-template (GncOption-get-scm-value option)) + (test-equal (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) (test-end "test-gnc-account-sel-option-to-scheme")) @@ -301,7 +323,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (gnc:generate-restore-forms odb "options")) (let ((option (gnc:lookup-option odb "foo" "bar"))) (gnc:option-set-value option value) - (test-equal (test-template (GncOption-get-scm-value option)) + (test-equal (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")))) (test-end "test-gnc-multichoice-option-to-scheme")) @@ -319,7 +341,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (let ((option (gnc:lookup-option odb "foo" "bar")) (test-template test-literal-output-template)) (gnc-option-set-value option '(ugly)) - (test-equal (test-template (GncOption-get-scm-value option)) + (test-equal (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) (test-end "test-gnc-list-option-to-scheme")) @@ -340,7 +362,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (let ((option (gnc:lookup-option odb "foo" "bar")) (test-template test-literal-output-template)) (gnc-option-set-value option 42.0) - (test-equal (test-template (GncOption-get-scm-value option)) + (test-equal (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) (test-end "test-gnc-number-range-option-to-scheme")) @@ -361,7 +383,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (let ((option (gnc:lookup-option odb "foo" "bar")) (test-template test-literal-output-template)) (gnc-option-set-value option 420) - (test-equal (test-template (GncOption-get-scm-value option)) + (test-equal (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) (test-end "test-gnc-number-plot-size-option-to-scheme")) @@ -384,7 +406,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (let ((option (gnc:lookup-option odb "__reg" "query")) (test-template test-literal-output-template)) (gnc-option-set-value option (gnc-scm2query query-scm)) - (test-equal (test-template (GncOption-get-scm-value option)) + (test-equal (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) (test-end "test-gnc-number-plot-size-option-to-scheme")) @@ -420,7 +442,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (let ((option (gnc:lookup-option odb "foo" "bar")) (test-template test-literal-output-template)) (gnc-option-set-value option '"13b305236443451a86c5366b7f890ecb") - (test-equal (test-template (GncOption-get-scm-value option)) + (test-equal (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) (test-end "test-gnc-owner-option-to-scheme")) From 269249378faf679c98d9ab42e52cc82dfb0bc479 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 2 Dec 2021 16:05:14 -0800 Subject: [PATCH 271/298] c++options fix setting account-selection widget from option. --- gnucash/gnome-utils/dialog-options.cpp | 4 ++-- libgnucash/app-utils/gnc-option.cpp | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 2f6b4ac322..2759da03dd 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1767,9 +1767,9 @@ public: void set_ui_item_from_option(GncOption& option) noexcept override { auto widget{GNC_ACCOUNT_SEL(get_widget())}; - auto instance{option.get_value()}; + auto instance{option.get_value()}; if (instance) - gnc_account_sel_set_account(widget, GNC_ACCOUNT(instance), FALSE); + gnc_account_sel_set_account(widget, const_cast(instance), FALSE); } void set_option_from_ui_item(GncOption& option) noexcept override { diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 47248390f7..8c156c25ed 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -479,6 +479,7 @@ 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 const Account* GncOption::get_value() const; template RelativeDatePeriod GncOption::get_value() const; template GncOptionAccountList GncOption::get_value() const; template GncMultichoiceOptionIndexVec GncOption::get_value() const; @@ -491,6 +492,7 @@ 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 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; @@ -504,6 +506,7 @@ 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(const Account*); template void GncOption::set_value(RelativeDatePeriod); template void GncOption::set_value(size_t); template void GncOption::set_value(GncOptionAccountList); @@ -518,6 +521,7 @@ 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); @@ -533,6 +537,7 @@ 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(const Account*) const; template bool GncOption::validate(const QofQuery*) const; template bool GncOption::validate(RelativeDatePeriod) const; template bool GncOption::validate(GncMultichoiceOptionIndexVec) const; From 51cc2668a11933607a2b502c34c5ba2c590e8242 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Dec 2021 14:39:19 -0800 Subject: [PATCH 272/298] c++options Ensure signals are inactive for initial setting of option entries. Ensures that the initial setting of the entries doesn't trigger gnc_option_changed_widget_cb. The entry widget hasn't been parented yet and besides we don't want to activate the apply and ok buttons yet. --- gnucash/gnome-utils/dialog-options.cpp | 139 +++++++++++++++++-------- 1 file changed, 98 insertions(+), 41 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 2759da03dd..7abf645031 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -878,12 +878,11 @@ create_option_widget (GncOption& option, auto ui_item{std::make_unique(widget)}; - g_signal_connect(G_OBJECT(widget), "toggled", - G_CALLBACK(gnc_option_changed_widget_cb), &option); - 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); @@ -924,11 +923,11 @@ create_option_widget (GncOption& option, gtk_entry_set_alignment (GTK_ENTRY(widget), 1.0); auto ui_item{std::make_unique(widget)}; - g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), &option); 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; @@ -979,11 +978,11 @@ create_option_widget (GncOption& option, GtkGrid *page_bo auto ui_item{std::make_unique(widget)}; auto text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); - g_signal_connect(G_OBJECT(text_buffer), "changed", - G_CALLBACK(gnc_option_changed_option_cb), &option); 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); @@ -1020,11 +1019,11 @@ create_option_widget (GncOption& option, GtkGrid *pag gtk_box_set_homogeneous (GTK_BOX (*enclosing), FALSE); auto widget = gnc_currency_edit_new(); auto ui_item{std::make_unique(widget)}; - g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), &option); 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; @@ -1065,8 +1064,6 @@ create_option_widget (GncOption& option, GtkGrid *pa auto ui_item{std::make_unique(widget)}; - g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), &option); option.set_ui_item(std::move(ui_item)); option.set_ui_item_from_option(); @@ -1106,9 +1103,6 @@ create_multichoice_widget(GncOption& option) renderer, "text", 0); g_object_unref(store); - g_signal_connect(G_OBJECT(widget), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), &option); - return widget; } @@ -1144,6 +1138,9 @@ create_option_widget (GncOption& option, GtkGrid * 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; @@ -1162,6 +1159,7 @@ public: // 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; }; @@ -1176,16 +1174,29 @@ public: 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; - g_signal_connect(G_OBJECT(entry), "changed", - G_CALLBACK(gnc_option_changed_option_cb), &option); + 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 @@ -1209,8 +1220,10 @@ public: 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; }; @@ -1238,8 +1251,9 @@ RelativeDateEntry::RelativeDateEntry(GncOption& option) g_object_unref(store); - g_signal_connect(G_OBJECT(m_entry), "changed", - G_CALLBACK(gnc_option_changed_widget_cb), &option); + m_handler_id = g_signal_connect(G_OBJECT(m_entry), "changed", + G_CALLBACK(gnc_option_changed_widget_cb), + &option); } void @@ -1254,6 +1268,16 @@ 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; @@ -1267,6 +1291,7 @@ public: 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; @@ -1274,6 +1299,8 @@ private: 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); @@ -1288,10 +1315,10 @@ BothDateEntry::BothDateEntry(GncOption& option) : m_rel_entry{std::make_unique(option)} { gtk_box_set_homogeneous (GTK_BOX(m_widget), FALSE); - g_signal_connect(G_OBJECT(m_abs_button), "toggled", - G_CALLBACK(date_set_absolute_cb), &option); - g_signal_connect(G_OBJECT(m_rel_button), "toggled", - G_CALLBACK(date_set_relative_cb), &option); + 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); @@ -1319,10 +1346,16 @@ BothDateEntry::toggle_relative(bool 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 @@ -1334,6 +1367,22 @@ BothDateEntry::set_option_from_entry(GncOption& option) 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 { @@ -1412,7 +1461,6 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box, break; } - option.set_ui_item_from_option(); auto widget{option_get_gtk_widget(&option)}; if (type == GncOptionUIType::DATE_RELATIVE) { @@ -1441,6 +1489,14 @@ create_date_option_widget(GncOption& option, GtkGrid *page_box, 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; } @@ -1660,6 +1716,9 @@ create_account_widget(GncOption& option, char *name) 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")); @@ -1710,9 +1769,6 @@ create_account_widget(GncOption& option, char *name) g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(show_hidden_toggled_cb), &option); - option.set_ui_item(std::make_unique(tree)); - option.set_ui_item_from_option(); - gtk_container_add(GTK_CONTAINER(scroll_win), tree); return frame; } @@ -2275,6 +2331,10 @@ create_option_widget (GncOption& option, "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", @@ -2284,9 +2344,6 @@ create_option_widget (GncOption& option, g_signal_connect_swapped(G_OBJECT (button), "clicked", G_CALLBACK(gtk_file_chooser_unselect_all), widget); - option.set_ui_item(std::make_unique(widget)); - option.set_ui_item_from_option(); - gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(*enclosing), button, FALSE, FALSE, 0); @@ -2377,6 +2434,9 @@ create_radiobutton_widget(char *name, GncOption& option) 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++) { @@ -2410,9 +2470,6 @@ create_option_widget (GncOption& option, GtkGrid * auto widget = create_radiobutton_widget(NULL, option); - option.set_ui_item(std::make_unique(widget)); - option.set_ui_item_from_option(); - gtk_box_pack_start(GTK_BOX(*enclosing), widget, FALSE, FALSE, 0); gtk_widget_show_all(*enclosing); return widget; @@ -2566,9 +2623,6 @@ create_option_widget (GncOption& option, auto value_px = create_range_spinner(option); - 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); @@ -2576,18 +2630,12 @@ create_option_widget (GncOption& option, 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), GTK_WIDGET(px_butt), FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(value_px), FALSE, FALSE, 0); @@ -2598,6 +2646,15 @@ create_option_widget (GncOption& option, 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; From 24d2999aecfb589639aba8768dc7c1bfe17255f6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 4 Dec 2021 15:29:01 -0800 Subject: [PATCH 273/298] c++options: Set the dialog entries only once at dialog creation. --- gnucash/gnome-utils/dialog-options.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index 7abf645031..f1da64f697 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -211,7 +211,6 @@ dialog_changed_internal (GtkWidget *widget, bool sensitive) return; g_assert(toplevel && GTK_IS_WINDOW(toplevel)); - /* Can't static cast, no inheritance relationship. */ auto option_win = static_cast(g_object_get_data(G_OBJECT(toplevel), "optionwin")); @@ -482,21 +481,6 @@ GncOptionsDialog::build_contents(GncOptionDB *odb, bool show_dialog) default_page = page; }); - /* call each option widget changed callbacks once at this point, now that - * all options widgets exist. - * - * Note that this may be superfluous as each create_option_widget - * specialization calls option.set_ui_item_from_option after creating the UI - * item. - */ - odb->foreach_section( - [](GncOptionSectionPtr& section) { - section->foreach_option( - [](GncOption& option) { - option.set_ui_item_from_option(); - }); - }); - gtk_notebook_popup_enable(GTK_NOTEBOOK(m_notebook)); if (default_page >= 0) { From f26014a04e26278827fa9f67f487bd7936e93eee Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 11 Dec 2021 15:11:26 -0800 Subject: [PATCH 274/298] c++options: QofInstanceValue: Protect against crashes when m_value is nullptr. --- libgnucash/app-utils/gnc-option-impl.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index e36d9b1a5a..5131aef8cc 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -435,8 +435,9 @@ GncOptionValue::reset_default_value() template std::string GncOptionValue::serialize() const noexcept { + static const std::string no_value{"No Value"}; if constexpr(std::is_same_v) - return qof_instance_to_string(m_value); + return m_value ? qof_instance_to_string(m_value) : no_value; else if constexpr(is_same_decayed_v) return m_value; else if constexpr(is_same_decayed_v) @@ -500,8 +501,9 @@ GncOptionValue::reset_default_value() template std::string GncOptionValidatedValue::serialize() const noexcept { + static const std::string no_value{"No Value"}; if constexpr(std::is_same_v) - return qof_instance_to_string(m_value); + return m_value ? qof_instance_to_string(m_value) : no_value; else if constexpr(is_same_decayed_v) return m_value; else if constexpr(is_same_decayed_v) @@ -535,8 +537,11 @@ GncOptionValidatedValue::deserialize(const std::string& str) noexcept 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) @@ -571,7 +576,8 @@ GncOptionAccountListValue::deserialize(const std::string& str) noexcept std::string GncOptionAccountSelValue::serialize() const noexcept { - return qof_instance_to_string(QOF_INSTANCE(m_value)); + static const std::string no_value{"No Value"}; + return m_value ?qof_instance_to_string(QOF_INSTANCE(m_value)) : no_value; } bool @@ -584,8 +590,11 @@ GncOptionAccountSelValue::deserialize(const std::string& str) noexcept 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) From 947c061989fdc8cefce6802044e88d9d8dce05cf Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 11 Dec 2021 15:12:22 -0800 Subject: [PATCH 275/298] C++options: Create QofQueryValue trait. --- libgnucash/app-utils/gnc-option-impl.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index fa92008a0d..702d62fbe8 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -230,6 +230,18 @@ struct is_QofInstanceValue template inline constexpr bool is_QofInstanceValue_v = is_QofInstanceValue::value; +template +struct is_QofQueryValue +{ + static constexpr bool value = + (std::is_same_v, GncOptionValue> || + std::is_same_v, + GncOptionValidatedValue>); +}; + +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 * >>. From 216b483e265f05e118fba053451f1380e93fdb9a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 13 Dec 2021 11:05:15 -0800 Subject: [PATCH 276/298] c++options SCM bindings Rewrite save_scm_value to branch on type. Instead of GncOptionUIType, mostly. We still need to do that to tell apart commodities and currencies from other QofInstances. Allows compile-time dispatch for most types. --- libgnucash/app-utils/gnc-optiondb.i | 138 +++++++++++++++++++--------- 1 file changed, 94 insertions(+), 44 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 6e200e62b7..3cb568f40a 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -1006,36 +1006,37 @@ inline SCM return_scm_value(ValueType value) SCM save_scm_value() { - const SCM plain_format_str{scm_from_utf8_string("~s")}; - const SCM ticked_format_str{scm_from_utf8_string("'~a")}; + 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. - auto serial{$self->serialize()}; - SCM value = serial.empty() ? SCM_BOOL_F : - scm_list_1(scm_from_utf8_string(serial.c_str())); - auto uitype{$self->get_ui_type()}; - if (uitype == GncOptionUIType::STRING || - uitype == GncOptionUIType::TEXT || - uitype == GncOptionUIType::FONT || - uitype == GncOptionUIType::BOOLEAN || - uitype == GncOptionUIType::PIXMAP - ) + return std::visit([$self] (auto &option) -> SCM { + static const auto no_value{scm_from_utf8_string("No Value")}; + if constexpr (is_same_decayed_v) { - return scm_simple_format(SCM_BOOL_F, plain_format_str, value); - } - else if (uitype == GncOptionUIType::ACCOUNT_LIST || - uitype == GncOptionUIType::ACCOUNT_SEL || - uitype == GncOptionUIType::INVOICE || - uitype == GncOptionUIType::TAX_TABLE || - uitype == GncOptionUIType::OWNER) + static const SCM list_format_str{scm_from_utf8_string("'~s")}; + auto acct_list{option.get_value()}; + if (acct_list.empty()) + return no_value; + SCM guid_list{scm_c_eval_string("'()")};//Empty list + for(auto acct : acct_list) { - if (value && scm_is_true(value)) - return scm_simple_format(SCM_BOOL_F, plain_format_str, value); - else - return scm_simple_format(SCM_BOOL_F, plain_format_str, - scm_list_1(SCM_BOOL_F)); + auto acct_str{qof_instance_to_string(QOF_INSTANCE(acct))}; + auto acct_scm{scm_from_utf8_string(acct_str.c_str())}; + guid_list = scm_cons(acct_scm, guid_list); } - else if (uitype == GncOptionUIType::CURRENCY) + return scm_simple_format(SCM_BOOL_F, list_format_str, scm_list_1(guid_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()))}; + if (uitype == GncOptionUIType::CURRENCY) { const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")}; return scm_simple_format(SCM_BOOL_F, quoted_format_str, value); @@ -1043,39 +1044,88 @@ inline SCM return_scm_value(ValueType value) else if (uitype == GncOptionUIType::COMMODITY) { const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")}; - auto sep{serial.find(':')}; - if (sep == std::string::npos) - sep = serial.find(' '); - if (sep == std::string::npos) + auto comm{GNC_COMMODITY(option.get_value())}; + auto name_space{gnc_commodity_get_namespace(comm)}; + auto mnemonic{gnc_commodity_get_mnemonic(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); + } + else { - const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")}; - return scm_simple_format(SCM_BOOL_F, quoted_format_str, value); + return scm_simple_format(SCM_BOOL_F, plain_format_str, value); } - auto name_space{serial.substr(0, sep)}; - auto symbol{serial.substr(sep + 1, std::string::npos)}; - auto commodity_val{scm_list_2(scm_from_utf8_string(name_space.c_str()), - scm_from_utf8_string(symbol.c_str()))}; - return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val); } - else if (uitype == GncOptionUIType::DATE_ABSOLUTE || - uitype == GncOptionUIType::DATE_RELATIVE || - uitype == GncOptionUIType::DATE_BOTH) + 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); } - else + + if constexpr (is_same_decayed_v>) + { + return scm_from_utf8_string("Not Yet Implemented"); + } + 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>) { - if (value && scm_is_true(value)) + auto serial{option.serialize()}; + if (serial.empty()) { - return scm_simple_format(SCM_BOOL_F, ticked_format_str, value); + return no_value; } else { - return scm_simple_format(SCM_BOOL_F, plain_format_str, - scm_list_1(SCM_BOOL_F)); + 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); + } + } + 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) From 0ce841d4ce650135b8d9c293e06b1cb667e175e9 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 13 Dec 2021 11:06:33 -0800 Subject: [PATCH 277/298] c++options Fix Scheme bindings for QofQuery options. --- libgnucash/app-utils/gnc-option-impl.cpp | 8 ++- libgnucash/app-utils/gnc-optiondb.i | 71 ++++++++++++++---------- libgnucash/app-utils/options.scm | 2 +- 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 5131aef8cc..92f60c29cd 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -334,9 +334,6 @@ qof_instance_from_guid(GncGUID* guid, GncOptionUIType type) case GncOptionUIType::TAX_TABLE: qof_type = "gncTaxtable"; break; - case GncOptionUIType::QUERY: - qof_type = "gncQuery"; - break; case GncOptionUIType::ACCOUNT_LIST: case GncOptionUIType::ACCOUNT_SEL: default: @@ -432,6 +429,11 @@ 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 { diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 3cb568f40a..334bb5d112 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -219,16 +219,15 @@ scm_from_value(const Account* value) } template <> inline SCM -scm_from_value(const QofQuery* value) +scm_from_value(QofQuery* value) { - auto ptr{static_cast(const_cast(value))}; - return SWIG_NewPointerObj(ptr, SWIGTYPE_p__QofQuery, FALSE); + return gnc_query2scm(value); } template <> inline SCM -scm_from_value(QofQuery* value) +scm_from_value(const QofQuery* value) { - return scm_from_value(value); + return scm_from_value(const_cast(value)); } template <> inline SCM @@ -1014,18 +1013,18 @@ inline SCM return_scm_value(ValueType value) 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 acct_list{option.get_value()}; if (acct_list.empty()) return no_value; SCM guid_list{scm_c_eval_string("'()")};//Empty list for(auto acct : acct_list) - { + { auto acct_str{qof_instance_to_string(QOF_INSTANCE(acct))}; auto acct_scm{scm_from_utf8_string(acct_str.c_str())}; guid_list = scm_cons(acct_scm, guid_list); - } + } return scm_simple_format(SCM_BOOL_F, list_format_str, scm_list_1(guid_list)); } @@ -1037,13 +1036,13 @@ inline SCM return_scm_value(ValueType value) return no_value; auto value{scm_list_1(scm_from_utf8_string(serial.c_str()))}; if (uitype == GncOptionUIType::CURRENCY) - { - const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")}; - return scm_simple_format(SCM_BOOL_F, quoted_format_str, value); - } - else if (uitype == GncOptionUIType::COMMODITY) - { - const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")}; + { + const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")}; + return scm_simple_format(SCM_BOOL_F, quoted_format_str, value); + } + else if (uitype == GncOptionUIType::COMMODITY) + { + const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")}; auto comm{GNC_COMMODITY(option.get_value())}; auto name_space{gnc_commodity_get_namespace(comm)}; auto mnemonic{gnc_commodity_get_mnemonic(comm)}; @@ -1052,20 +1051,20 @@ inline SCM return_scm_value(ValueType value) return scm_simple_format(SCM_BOOL_F, commodity_fmt, commodity_val); } else - { + { return scm_simple_format(SCM_BOOL_F, plain_format_str, value); - } - } + } + } 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); - } + 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>) @@ -1084,14 +1083,14 @@ inline SCM return_scm_value(ValueType value) GncOptionRangeValue> || is_same_decayed_v>) - { + { auto serial{option.serialize()}; if (serial.empty()) - { + { return no_value; - } - else - { + } + 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); } @@ -1100,7 +1099,7 @@ inline SCM return_scm_value(ValueType value) if (serial.empty()) { return no_value; - } + } else if ($self->get_ui_type() == GncOptionUIType::COLOR) { auto red{static_cast(std::stoi(serial.substr(0, 2), @@ -1124,7 +1123,7 @@ inline SCM return_scm_value(ValueType value) { 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)); } @@ -1175,6 +1174,20 @@ inline SCM return_scm_value(ValueType value) } 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) { diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 52e55c2e00..f0aaf2077b 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -274,7 +274,7 @@ (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-SCM-option section name "" "query" defv (GncOptionUIType-INTERNAL)))) + (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)) From 66f9fc81c70d2c767bb9befb548b0a8b5656d34f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 14 Dec 2021 10:57:41 -0800 Subject: [PATCH 278/298] c++options: Implement serialization for GncOwner. --- libgnucash/app-utils/gnc-option-impl.cpp | 20 +++++++++++++++++++- libgnucash/app-utils/gnc-optiondb.i | 6 +++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 92f60c29cd..f339eabf35 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -27,7 +27,7 @@ #include #include #include - +#include extern "C" { #include "gnc-accounting-period.h" @@ -440,6 +440,16 @@ 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(); + } else if constexpr(is_same_decayed_v) return m_value; else if constexpr(is_same_decayed_v) @@ -455,6 +465,14 @@ 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); + } else if constexpr(is_same_decayed_v) set_value(str); else if constexpr(is_same_decayed_v) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 334bb5d112..769e1d6ed7 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -1069,7 +1069,11 @@ inline SCM return_scm_value(ValueType value) if constexpr (is_same_decayed_v>) { - return scm_from_utf8_string("Not Yet Implemented"); + 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) { From e9b850cca5a6be742919668886572e9e07744aa7 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 14 Dec 2021 10:58:48 -0800 Subject: [PATCH 279/298] c++options: Correct Scheme serialization of type bool. --- libgnucash/app-utils/gnc-optiondb.i | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 769e1d6ed7..9ab5d4bbe0 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -1099,6 +1099,13 @@ inline SCM return_scm_value(ValueType value) 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); + } auto serial{option.serialize()}; if (serial.empty()) { From 6cd88c230c1032b6e7427a04ae2220e0e72e7100 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 14 Dec 2021 11:03:01 -0800 Subject: [PATCH 280/298] c++options: Create bool or string internal options when possible. There's no need to save internal options as SCM when we support the underlying type in C++. --- libgnucash/app-utils/options.scm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index f0aaf2077b..7cfa911ad2 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -280,7 +280,13 @@ (let ((type (GncOptionUIType-INTERNAL)) (key "_") (desc "internal")) - (gnc-make-SCM-option section name key desc default type))) + (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))) + (else + (gnc-make-SCM-option section name key desc default type))))) (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 From a3f50586dfaf76c0801bf9ff62561d1492f5d8db Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 14 Dec 2021 11:03:37 -0800 Subject: [PATCH 281/298] c++options: More thorough testing of scheme serialization. --- libgnucash/app-utils/gnc-option-impl.cpp | 2 +- .../test/test-gnc-option-scheme-output.scm | 309 +++++++++++++----- 2 files changed, 223 insertions(+), 88 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index f339eabf35..e8aaa6ea47 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -457,7 +457,7 @@ GncOptionValue::serialize() const noexcept else if constexpr(std::is_arithmetic_v) return std::to_string(m_value); else - return ""; + return "Serialization not implemented"; } template bool diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm index 34d1389a62..cd8facb9e7 100644 --- a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm +++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm @@ -32,13 +32,23 @@ (test-begin "test-gnc-option-scheme-io") (test-gnc-string-option-to-scheme) (test-gnc-text-option-to-scheme) - (test-gnc-pixmap-option-to-scheme) + (test-gnc-font-option-to-scheme) (test-gnc-currency-option-to-scheme) (test-gnc-budget-option-to-scheme) - (test-gnc-font-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 @@ -82,7 +92,8 @@ " value)) (define (test-commodity-output-template value) - (format #f " + (let ((value-parts (string-split value #\:))) + (format #f " ; Section: foo (let ((option (gnc:lookup-option options @@ -90,7 +101,7 @@ \"bar\"))) ((lambda (o) (if o (gnc:option-set-value o \"~a\" \"~a\"))) option)) -" (string-split value #\:))) +" (car value-parts) (cadr value-parts)))) (define (test-budget-output-template value) (format #f " @@ -105,25 +116,31 @@ (gncBudgetGetGUID value))) -(define (test-option-scheme-output make-option-func test-template default 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 test-unchanged-section-output-template + (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 (test-template (GncOption-serialize (gnc:lookup-option odb "foo" "bar"))) + (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 gnc:make-string-option test-string-output-template + (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 gnc:make-string-option test-string-output-template + (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 @@ -132,7 +149,9 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (define (test-gnc-font-option-to-scheme) (test-begin "test-gnc-font-option-to-scheme") - (test-option-scheme-output gnc:make-font-option test-string-output-template + (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")) @@ -147,8 +166,10 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (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 gnc:make-currency-option test-currency-output-template - USD 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) @@ -157,8 +178,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (define (test-gnc-budget-option-to-scheme) (test-begin "test-gnc-budget-option-to-scheme") - (let* ((session (gnc-get-current-session)) - (book (gnc-get-current-book)) + (let* ((book (gnc-get-current-book)) (budget2 (gnc-budget-new book)) (budget1 (gnc-budget-new book)) (guid1 (gncBudgetGetGUID budget1)) @@ -171,32 +191,43 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (let ((odb (gnc:new-options)) (option (gnc:make-budget-option "foo" "bar" "baz" "Test Option"))) (gnc:register-option odb option) - (test-equal test-unchanged-section-output-template + (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 (gnc-budget-get-default book) budget1) - (test-equal (test-budget-output-template 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* ((book (gnc-option-test-book-new)) + (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))) - (test-option-scheme-output gnc:make-commodity-option test-currency-output-template - AAPL 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 gnc:make-simple-boolean-option test-string-output-template #f #t) + (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 gnc:make-pixmap-option test-string-output-template "" "~/mybusiness/mylogo.png") + (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) @@ -252,48 +283,61 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") ;; Destroying the book destroys the account tree too (gnc-option-test-book-destroy book)) - (define (test-gnc-account-list-option-to-scheme) + (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:register-option odb (gnc:make-account-list-option - "foo" "bar" "a" "baz" acctlist + "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 test-unchanged-section-output-template + (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-literal-output-template) + (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 (test-template (GncOption-serialize option)) + (gnc:option-set-value option new-acclist) + (test-equal "account list form" + (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) + (define (test-gnc-account-sel-option-to-scheme book) (test-begin "test-gnc-account-sel-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" acctlist + (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)))) #t)) - (test-equal test-unchanged-section-output-template + (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-literal-output-template) - (new-acclist (gnc-account-list-from-types book (list ACCT-TYPE-BANK)))) - (gnc-option-set-value option new-acclist) - (test-equal (test-template (GncOption-serialize option)) + (test-template test-string-output-template)) + (gnc:option-set-value option bank) + (test-equal "account sel form" + (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) (test-end "test-gnc-account-sel-option-to-scheme")) @@ -302,8 +346,8 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (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) - (test-gnc-account-sel-option-to-scheme) + (test-gnc-account-list-option-to-scheme book) + (test-gnc-account-sel-option-to-scheme book) (cleanup book root-account)))) @@ -319,11 +363,11 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (list (vector 'all "All") (vector 1 "1") (vector 2 "2") (vector 3 "3") (vector 4 "4") (vector 5 "5") (vector 6 "6")))) - (test-equal test-unchanged-section-output-template + (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 (test-template (GncOption-serialize option)) + (test-equal "multichoice form" (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")))) (test-end "test-gnc-multichoice-option-to-scheme")) @@ -333,15 +377,15 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (choices (list (vector 'good "The Good") (vector 'bad "The Bad") (vector 'ugly "The Ugly")))) - (gnc-register-option odb + (gnc:register-option odb (gnc:make-list-option "foo" "bar" "a" "baz" '(bad) choices)) - (test-equal test-unchanged-section-output-template + (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 (test-template (GncOption-serialize option)) + (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")) @@ -353,16 +397,17 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (max-value 100.0) (dec-places 2.0) (step 0.10)) - (gnc-register-option odb + (gnc:register-option odb (gnc:make-number-range-option "foo" "bar" "a" "baz" 49.0 min-value max-value dec-places step)) - (test-equal test-unchanged-section-output-template + (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 (test-template (GncOption-serialize option)) + (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")) @@ -370,26 +415,45 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (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 100) - (max-value 10000) + (min-value 10) + (max-value 100) (dec-places 0) (step 5)) - (gnc-register-option odb + (gnc:register-option odb (gnc:make-number-plot-size-option - "foo" "bar" "a" "baz" 490 min-value + "foo" "bar" "a" "baz" 49 min-value max-value dec-places step)) - (test-equal test-unchanged-section-output-template + (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 420) - (test-equal (test-template (GncOption-serialize option)) + (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) - (test-begin "test-gnc-number-plot-size-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")) @@ -399,56 +463,127 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (secondary-sort #f) (tertiary-sort #f) (max-results -1)))) - (gnc-register-option odb + (gnc:register-option odb (gnc:make-query-option "__reg" "query" '())) - (test-equal test-unchanged-section-output-template + (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 test-literal-output-template)) - (gnc-option-set-value option (gnc-scm2query query-scm)) - (test-equal (test-template (GncOption-serialize option)) + (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-number-plot-size-option-to-scheme")) + (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)) + (default-color (list #xb2 #x22 #x22 #xff)) (new-color (list #x00 #xca #x3b #xff))) - (test-option-scheme-output gnc:make-color-option - test-literal-output-template - default-color new-color)) + (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)) - (invoice '"13b305236443451a86c5366b7f890ecb")) - (test-option-scheme-output gnc:make-color-option - test-literal-output-template - (lambda () '()) invoice)) + (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") + (test-begin "test-owner-option-to-scheme") (let ((odb (gnc:new-options))) - (gnc-register-option odb + (gnc:register-option odb (gnc:make-owner-option "foo" "bar" "a" "baz" - (lambda () '()) #f - 'GNC-OWNER-CUSTOMER)) - (test-equal test-unchanged-section-output-template + (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)) - (gnc-option-set-value option '"13b305236443451a86c5366b7f890ecb") - (test-equal (test-template (GncOption-serialize option)) + (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-taxtable-option-to-scheme) ;;(define (test-gnc-counter-option-to-scheme) ;;(define (test-gnc-counter-format-option-to-scheme) From eb0bd4f998ad4a39fd0c890ee258f98fbf28972e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 14 Dec 2021 11:18:15 -0800 Subject: [PATCH 282/298] c++options: Remove unneeded C++20 header and resolve ambiguous call. --- libgnucash/app-utils/gnc-option-impl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index e8aaa6ea47..6bd896595c 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include extern "C" { @@ -697,7 +696,8 @@ GncOptionDateValue::deserialize(const std::string& str) noexcept auto period_str{str.substr(date_value_pos)}; if (type_str == "absolute") { - set_value(std::stoll(period_str)); + // Need a cast to disambiguate from time64. + set_value(static_cast(std::stoll(period_str))); return true; } else if (type_str == "relative ") From a700701cd3dcb9f37aa17b85d8cf7f2d0147f87d Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 14 Dec 2021 11:11:51 -0800 Subject: [PATCH 283/298] c++options: Remove three unused test functions. Scheme serialization is now tested in test-option-gnc-scheme-output.scm. --- libgnucash/app-utils/gnc-option-impl.hpp | 1 - .../app-utils/test/gtest-gnc-option.cpp | 41 ------------------- 2 files changed, 42 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 702d62fbe8..3adb3f634e 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -56,7 +56,6 @@ extern "C" #include "gnc-option-uitype.hpp" -static const char* commodity_scm_intro{"'(commodity-scm "}; #ifndef SWIG size_t constexpr classifier_size_max{50}; size_t constexpr sort_tag_size_max{10}; diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index e2e01e132d..0582979c64 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -306,25 +306,6 @@ static inline std::string make_commodity_str(gnc_commodity* com) return com_str; } -static inline std::string make_currency_SCM_str(gnc_commodity* cur) -{ - std::string cur_str{gnc_commodity_get_mnemonic(cur)}; - cur_str.insert(0, "\""); - cur_str += "\""; - return cur_str; -} - -static inline std::string make_commodity_SCM_str(gnc_commodity* com) -{ - std::string com_str{commodity_scm_intro}; - com_str += "\""; - com_str += gnc_commodity_get_namespace(com); - com_str += "\" \""; - com_str += gnc_commodity_get_mnemonic(com); - com_str += "\")"; - return com_str; -} - TEST_F(GncOptionCommodityTest, test_currency_out) { auto option = make_currency_option("foo", "bar", "baz", "Phony Option", @@ -740,28 +721,6 @@ TEST_F(GncOptionAccountTest, test_account_list_in) EXPECT_EQ(acclist[1], sel_option.get_value()[0]); } -static inline std::string -make_account_list_SCM_str(const GncOptionAccountList& acclist) -{ - std::string retval{"'("}; - bool first = true; - for (auto acc : acclist) - { - if (first) - { - first = false; - retval += '"'; - } - else - retval += " \""; - retval += gnc::GUID{*qof_instance_get_guid(acc)}.to_string(); - retval += '"'; - } - retval += ')'; - return retval; -} - - using KT = GncOptionMultichoiceKeyType; class GncOptionMultichoiceTest : public ::testing::Test { From b5d0c42505a12e341d4c4fa6a7ebf48ddc38bd88 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 14 Dec 2021 12:06:39 -0800 Subject: [PATCH 284/298] c++options: Put copyright and FSF header comment on gnc-optiondb.i. --- libgnucash/app-utils/gnc-optiondb.i | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 9ab5d4bbe0..a74fdc91b8 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -1,7 +1,28 @@ /* - * Temporary swig interface file while developing C++ options. + * gnc-optiondb.i -- Swig Guile interface for the options system. * - * unique_ptr SWIG wrapper from https://stackoverflow.com/questions/27693812/how-to-handle-unique-ptrs-with-swig + * 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) From 18edc175419534f578d0d365308eabc54a866325 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 14 Dec 2021 14:20:20 -0800 Subject: [PATCH 285/298] c++options remove stray debugging comment. --- libgnucash/app-utils/gnc-optiondb.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 133949c6aa..7120fafe42 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1259,7 +1259,6 @@ SCM gnc_option_db_lookup_scm_value(GncOptionDB* odb, const char* section, const char* name) { - std::cerr << "Use gnc_option_db_lookup_value." << std::endl; return SCM_BOOL_F; } From 759376eb1323f21d908a820d2b81bbc82316a584 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 15 Dec 2021 14:13:47 -0800 Subject: [PATCH 286/298] c++options: Fix multicolumn report --- gnucash/gnome/dialog-report-column-view.cpp | 8 ++++++++ libgnucash/app-utils/gnc-optiondb.cpp | 9 ++++++--- libgnucash/app-utils/gnc-optiondb.i | 9 ++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp index 5c45dcea6b..3c7c7c9fcb 100644 --- a/gnucash/gnome/dialog-report-column-view.cpp +++ b/gnucash/gnome/dialog-report-column-view.cpp @@ -79,11 +79,19 @@ struct gncp_column_view_edit 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, diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 7120fafe42..1e561dc945 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -1259,13 +1259,16 @@ SCM gnc_option_db_lookup_scm_value(GncOptionDB* odb, const char* section, const char* name) { - return SCM_BOOL_F; + auto option{odb->find_option(section, name)}; + return option->get_value(); } void -gnc_option_db_set_scm_value(GncOptionDB*, const char*, const char*, SCM) +gnc_option_db_set_scm_value(GncOptionDB* odb, const char* section, + const char* name, SCM value) { - std::cerr << "Use gnc_set_option." << std::endl; + auto option{odb->find_option(section, name)}; + option->set_value(value); } // Force creation of templates diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index a74fdc91b8..8a23eb1c31 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -1039,7 +1039,7 @@ inline SCM return_scm_value(ValueType value) auto acct_list{option.get_value()}; if (acct_list.empty()) return no_value; - SCM guid_list{scm_c_eval_string("'()")};//Empty list + SCM guid_list{SCM_EOL}; for(auto acct : acct_list) { auto acct_str{qof_instance_to_string(QOF_INSTANCE(acct))}; @@ -1127,6 +1127,13 @@ inline SCM return_scm_value(ValueType 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()) { From 16dc15964c0eaa8727b8bd2d0d0940bd8451e97e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 15 Dec 2021 14:32:39 -0800 Subject: [PATCH 287/298] c++options: Remove the callback registration functions. --- gnucash/report/report-core.scm | 9 +------- .../report/reports/standard/view-column.scm | 23 ------------------- libgnucash/app-utils/options.scm | 6 ----- 3 files changed, 1 insertion(+), 37 deletions(-) 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/view-column.scm b/gnucash/report/reports/standard/view-column.scm index b84d536b9d..7ef37994e4 100644 --- a/gnucash/report/reports/standard/view-column.scm +++ b/gnucash/report/reports/standard/view-column.scm @@ -53,18 +53,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 +68,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 diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 7cfa911ad2..c26663dd6a 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -178,12 +178,6 @@ (with-output-to-string generate-forms)) -;; FIXME: Fake callback functions for boolean-complex and multichoice-callback - -(define-public (gnc:options-register-callback section name callback options) (options 'register-callback) 1) -(define-public (gnc:options-register-c-callback section name callback data options) (options 'register-c-callback) 1) -(define-public (gnc:options-unregister-callback-id id) 0 (options 'unregister-callback-id)) - ;; 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.") From 11225d974172d93c134d3eb2beb002955a0fa08f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Wed, 15 Dec 2021 14:54:41 -0800 Subject: [PATCH 288/298] c++options: Remove gnc:options-data --- .../report/reports/standard/test/test-stress-options.scm | 9 ++++++--- libgnucash/app-utils/options.scm | 9 --------- 2 files changed, 6 insertions(+), 12 deletions(-) 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/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index c26663dd6a..702fca981d 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -85,15 +85,6 @@ (define-public (gnc:option-type option) (GncOption-get-type option)) -;; Used only by test-stress-options.scm -(define-public (gnc:option-data option) -; (define num-values (GncOption-num-permissible-values option)) -; (let loop ((i 0) (retval '())) -; (if (>= i num-values) (reverse retval) -; (let ((value (GncOption-permissible-value option i)) -; (name (GncOption-permissible-value-name option i))) -; (loop (1+ i) (cons (vector value name) retval)))))) - (list (vector 1 2))) ;; Create the database and return a dispatch function. (define-public (gnc:new-options) (let ((optiondb (new-gnc-optiondb))) From 6841e5b5c8ac57d204c1bbf73ad232a4e5ed4ad8 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 30 Dec 2021 12:21:56 -0800 Subject: [PATCH 289/298] Replace GncOptionValue with GncOptionQofInstanceValue. That stores its values as a pair of so that it won't have dangling ptrs if the instance gets deleted. --- libgnucash/app-utils/gnc-option-impl.cpp | 172 +++++++++++++++++- libgnucash/app-utils/gnc-option-impl.hpp | 35 +++- libgnucash/app-utils/gnc-option.cpp | 13 +- libgnucash/app-utils/gnc-option.hpp | 3 +- libgnucash/app-utils/gnc-optiondb.cpp | 20 +- libgnucash/app-utils/gnc-optiondb.i | 28 ++- .../app-utils/test/gtest-gnc-option.cpp | 24 +-- 7 files changed, 250 insertions(+), 45 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 6bd896595c..f8905edaab 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -38,6 +38,168 @@ 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 + if (m_ui_type == GncOptionUIType::CURRENCY || + m_ui_type == GncOptionUIType::COMMODITY) + { + auto book{gnc_get_current_book()}; + auto table = gnc_commodity_table_get_table(book); + auto sep{str.find(":")}; + if (sep != std::string::npos) + { + auto name_space{str.substr(0, sep)}; + auto mnemonic{str.substr(sep + 1, -1)}; + inst = QOF_INSTANCE(gnc_commodity_table_lookup(table, + name_space.c_str(), + mnemonic.c_str())); + } + if (!inst && m_ui_type == GncOptionUIType::CURRENCY) + inst = QOF_INSTANCE(gnc_commodity_table_lookup(table, + "CURRENCY", + str.c_str())); + if (inst) + { + m_value = make_gnc_item(inst); + return true; + } + } + + if (!inst) + { + try { + auto guid{static_cast(gnc::GUID::from_string(str))}; + inst = qof_instance_from_guid(&guid, m_ui_type); + if (inst) + { + auto coll{qof_instance_get_collection(inst)}; + m_value = std::make_pair(qof_collection_get_type(coll), guid); + 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; +} + bool GncOptionAccountListValue::validate(const GncOptionAccountList& values) const { @@ -536,9 +698,7 @@ GncOptionValidatedValue::serialize() const noexcept template bool GncOptionValidatedValue::deserialize(const std::string& str) noexcept { - if constexpr(std::is_same_v) - set_value(qof_instance_from_string(str, get_ui_type())); - else if constexpr(is_same_decayed_v) + if constexpr(is_same_decayed_v) set_value(str); else if constexpr(is_same_decayed_v) set_value(str == "True"); @@ -728,7 +888,6 @@ 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&); @@ -743,7 +902,6 @@ 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 QofInstance*); template void GncOptionValue::set_value(const QofQuery*); template void GncOptionValue::set_value(const GncOwner*); template void GncOptionValue::set_value(RelativeDatePeriod); @@ -757,7 +915,6 @@ 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 QofInstance*); template void GncOptionValue::set_default_value(const QofQuery*); template void GncOptionValue::set_default_value(const GncOwner*); template void GncOptionValue::set_default_value(RelativeDatePeriod); @@ -771,7 +928,6 @@ 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(); @@ -785,7 +941,6 @@ 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; @@ -808,7 +963,6 @@ 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; diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 3adb3f634e..253bc4d75a 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -141,6 +141,37 @@ private: 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 GncOptionValidatedValue * Validated values have an additional member function, provided as a * constructor argument, that checks value parameters for some property before @@ -221,7 +252,7 @@ template struct is_QofInstanceValue { static constexpr bool value = - (std::is_same_v, GncOptionValue> || + (std::is_same_v, GncOptionQofInstanceValue> || std::is_same_v, GncOptionValidatedValue>); }; @@ -276,7 +307,7 @@ operator<< (std::ostream& oss, const OptType& opt) if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) { - if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY) + if (type == GncOptionUIType::COMMODITY) { oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " "; } diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 8c156c25ed..aa7ae040b0 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -35,7 +35,7 @@ extern "C" template , - int>> + int>> GncOption::GncOption(const char* section, const char* name, const char* key, const char* doc_string, ValueType value, GncOptionUIType ui_type) : @@ -343,7 +343,7 @@ GncOption::permissible_value_index(const char* value) const GncOptionDateValue>) return option.permissible_value_index(value); else - return size_t_max;; + return size_t_max; }, *m_option); } @@ -462,8 +462,6 @@ 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 QofInstance*, GncOptionUIType); template GncOption::GncOption(const char*, const char*, const char*, const char*, SCM, GncOptionUIType); template GncOption::GncOption(const char*, const char*, const char*, @@ -553,9 +551,4 @@ template GncOption* gnc_make_option(const char*, const char*, const char*, template GncOption* gnc_make_option(const char*, const char*, const char*, const char*, int64_t, GncOptionUIType); -template GncOption* gnc_make_option(const char*, - const char*, - const char*, - const char*, - const QofInstance*, - GncOptionUIType); + diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index 2c93c9283f..e0bdf61fa4 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -55,6 +55,7 @@ using QofQuery = _QofQuery; struct QofInstance_s; using QofInstance = QofInstance_s; template class GncOptionValue; +class GncOptionQofInstanceValue; class GncOptionAccountListValue; class GncOptionAccountSelValue; class GncOptionMultichoiceValue; @@ -97,7 +98,7 @@ is_RangeValue_v = is_RangeValue::value; using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, - GncOptionValue, + GncOptionQofInstanceValue, GncOptionValue, GncOptionValue, GncOptionValue, diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 1e561dc945..5da83348b4 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -630,8 +630,9 @@ gnc_register_budget_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncBudget *value) { - GncOption option{section, name, key, doc_string, (const QofInstance*)value, - GncOptionUIType::BUDGET}; + GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string, + (const QofInstance*)value, + GncOptionUIType::BUDGET}}; db->register_option(section, std::move(option)); } @@ -650,8 +651,9 @@ gnc_register_commodity_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { - GncOption option{section, name, key, doc_string, (const QofInstance*)value, - GncOptionUIType::COMMODITY}; + GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string, + (const QofInstance*)value, + GncOptionUIType::COMMODITY}}; db->register_option(section, std::move(option)); } @@ -862,8 +864,9 @@ gnc_register_invoice_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncInvoice* value) { - GncOption option{section, name, key, doc_string, (const QofInstance*)value, - GncOptionUIType::INVOICE}; + GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string, + (const QofInstance*)value, + GncOptionUIType::INVOICE}}; db->register_option(section, std::move(option)); } @@ -872,8 +875,9 @@ gnc_register_taxtable_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, GncTaxTable* value) { - GncOption option{section, name, key, doc_string, (const QofInstance*)value, - GncOptionUIType::TAX_TABLE}; + GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string, + (const QofInstance*)value, + GncOptionUIType::TAX_TABLE}}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 8a23eb1c31..f9c77abf72 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -140,9 +140,10 @@ SCM scm_init_sw_gnc_optiondb_module(void); $descriptor(_gncJob*), $descriptor(_gncVendor*) }; void* ptr{}; + SCM instance{$input}; auto pos = std::find_if(types.begin(), types.end(), - [&$input, &ptr](auto type){ - SWIG_ConvertPtr($input, &ptr, type, 0); + [&instance, &ptr](auto type){ + SWIG_ConvertPtr(instance, &ptr, type, 0); return ptr != nullptr; }); if (pos == types.end()) $1 = nullptr; @@ -990,7 +991,6 @@ inline SCM return_scm_value(ValueType value) %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_qofinstance_option) gnc_make_option; %template(gnc_make_query_option) gnc_make_option; %template(gnc_make_owner_option) gnc_make_option; @@ -1383,6 +1383,26 @@ inline SCM return_scm_value(ValueType value) 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, @@ -1569,7 +1589,7 @@ inline SCM return_scm_value(ValueType value) const char* key, const char* doc_string, gnc_commodity *value) { - return new GncOption{GncOptionValue{ + return new GncOption{GncOptionQofInstanceValue{ section, name, key, doc_string, (const QofInstance*)value, GncOptionUIType::COMMODITY}}; } diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 0582979c64..8f2e3f8631 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -154,8 +154,9 @@ TEST_F(GncOptionTest, test_budget_ctor) { auto budget = gnc_budget_new(m_book); EXPECT_NO_THROW({ - GncOption option("foo", "bar", "baz", "Phony Option", - (const QofInstance*)budget); + GncOption option(GncOptionQofInstanceValue{"foo", "bar", "baz", + "Phony Option", + (const QofInstance*)budget}); }); gnc_budget_destroy(budget); } @@ -163,7 +164,7 @@ TEST_F(GncOptionTest, test_budget_ctor) TEST_F(GncOptionTest, test_budget_out) { auto budget = gnc_budget_new(m_book); - GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)budget}; + 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; @@ -177,7 +178,7 @@ 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{GncOptionValue{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}}; + 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); @@ -188,9 +189,10 @@ 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("foo", "bar", "baz", "Phony Option", - (const QofInstance*)hpe); - }); + GncOption option(GncOptionQofInstanceValue{"foo", "bar", "baz", + "Phony Option", + (const QofInstance*)hpe}); + }); gnc_commodity_destroy(hpe); } @@ -319,8 +321,8 @@ TEST_F(GncOptionCommodityTest, test_currency_out) TEST_F(GncOptionCommodityTest, test_commodity_out) { - GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe, - GncOptionUIType::COMMODITY}; + 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; @@ -348,8 +350,8 @@ TEST_F(GncOptionCommodityTest, test_currency_in) TEST_F(GncOptionCommodityTest, test_commodity_in) { - GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl, - GncOptionUIType::COMMODITY}; + GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl, + GncOptionUIType::COMMODITY}}; std::string hpe_str{make_commodity_str(m_hpe)}; std::istringstream iss{hpe_str}; From 65bd860249b3fc284558144eb81df413d92b32ca Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 1 Jan 2022 16:05:06 -0800 Subject: [PATCH 290/298] Fix relative_date_to_time64 calculations. Where the date requested extends beyond the start or end of the year. --- libgnucash/app-utils/gnc-option-date.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-date.cpp b/libgnucash/app-utils/gnc-option-date.cpp index 30847b2a53..3d2a3e141f 100644 --- a/libgnucash/app-utils/gnc-option-date.cpp +++ b/libgnucash/app-utils/gnc-option-date.cpp @@ -434,13 +434,23 @@ 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; + 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_in_month[--now.tm_mon]; + now.tm_mday += days(--now.tm_mon, now.tm_year); - while (now.tm_mday > days_in_month[now.tm_mon]) - now.tm_mday -= days_in_month[now.tm_mon++]; + 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) { From a7a643f7f2f364228d57eda935e9b9d2c8fa2f84 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 3 Jan 2022 14:47:18 -0800 Subject: [PATCH 291/298] Store option commodities and namespace and mnemonic instead of pointer. Protects against crashes caused by the user deleting the commodity and allows the option to work if a deleted commodity is recreated. --- libgnucash/app-utils/gnc-option-impl.cpp | 261 ++++++++---------- libgnucash/app-utils/gnc-option-impl.hpp | 146 ++++------ libgnucash/app-utils/gnc-option.cpp | 20 +- libgnucash/app-utils/gnc-option.hpp | 5 +- libgnucash/app-utils/gnc-optiondb.cpp | 16 +- libgnucash/app-utils/gnc-optiondb.i | 147 +++++++--- .../app-utils/test/gtest-gnc-option.cpp | 30 +- .../test/test-gnc-option-scheme-output.scm | 8 +- 8 files changed, 313 insertions(+), 320 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index f8905edaab..9c1cb89cdc 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -127,47 +127,18 @@ GncOptionQofInstanceValue::deserialize(const std::string& str) noexcept { QofInstance* inst{}; // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic - if (m_ui_type == GncOptionUIType::CURRENCY || - m_ui_type == GncOptionUIType::COMMODITY) - { - auto book{gnc_get_current_book()}; - auto table = gnc_commodity_table_get_table(book); - auto sep{str.find(":")}; - if (sep != std::string::npos) - { - auto name_space{str.substr(0, sep)}; - auto mnemonic{str.substr(sep + 1, -1)}; - inst = QOF_INSTANCE(gnc_commodity_table_lookup(table, - name_space.c_str(), - mnemonic.c_str())); - } - if (!inst && m_ui_type == GncOptionUIType::CURRENCY) - inst = QOF_INSTANCE(gnc_commodity_table_lookup(table, - "CURRENCY", - str.c_str())); + 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; } } - - if (!inst) + catch (const gnc::guid_syntax_exception& err) { - try { - auto guid{static_cast(gnc::GUID::from_string(str))}; - inst = qof_instance_from_guid(&guid, m_ui_type); - if (inst) - { - auto coll{qof_instance_get_collection(inst)}; - m_value = std::make_pair(qof_collection_get_type(coll), guid); - return true; - } - } - catch (const gnc::guid_syntax_exception& err) - { - PWARN("Failed to convert %s to a GUID", str.c_str()); - } + PWARN("Failed to convert %s to a GUID", str.c_str()); } return false; } @@ -200,6 +171,103 @@ GncOptionQofInstanceValue::serialize() const noexcept 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 { @@ -470,10 +538,6 @@ qof_instance_from_guid(GncGUID* guid, GncOptionUIType type) QofIdType qof_type; switch(type) { - case GncOptionUIType::CURRENCY: - case GncOptionUIType::COMMODITY: - qof_type = "Commodity"; - break; case GncOptionUIType::BUDGET: qof_type = "Budget"; break; @@ -510,37 +574,13 @@ QofInstance* qof_instance_from_string(const std::string& str, GncOptionUIType type) { QofInstance* retval{nullptr}; - // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic - if (type == GncOptionUIType::CURRENCY || - type == GncOptionUIType::COMMODITY) - { - auto book{gnc_get_current_book()}; - auto table = gnc_commodity_table_get_table(book); - auto sep{str.find(":")}; - if (sep != std::string::npos) - { - auto name_space{str.substr(0, sep)}; - auto mnemonic{str.substr(sep + 1, -1)}; - retval = QOF_INSTANCE(gnc_commodity_table_lookup(table, - name_space.c_str(), - mnemonic.c_str())); - } - if (!retval && type == GncOptionUIType::CURRENCY) - retval = QOF_INSTANCE(gnc_commodity_table_lookup(table, - "CURRENCY", - str.c_str())); + try { + auto guid{static_cast(gnc::GUID::from_string(str))}; + retval = qof_instance_from_guid(&guid, type); } - - if (!retval) + catch (const gnc::guid_syntax_exception& err) { - 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()); - } + PWARN("Failed to convert %s to a GUID", str.c_str()); } return retval; } @@ -549,26 +589,8 @@ std::string qof_instance_to_string(const QofInstance* inst) { 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{*qof_instance_get_guid(inst)}; - retval = guid.to_string(); - } + gnc::GUID guid{*qof_instance_get_guid(inst)}; + retval = guid.to_string(); return retval; } @@ -678,41 +700,6 @@ GncOptionValue::reset_default_value() m_value = m_default_value; scm_gc_protect_object(m_value); } - -template std::string -GncOptionValidatedValue::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; - 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 "Invalid Value Type"; -} - -template bool -GncOptionValidatedValue::deserialize(const std::string& str) noexcept -{ - 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 { @@ -881,6 +868,16 @@ GncOptionDateValue::deserialize(const std::string& str) noexcept } } +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&); @@ -944,16 +941,6 @@ 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 GncOptionValidatedValue::serialize() const noexcept; -template std::string GncOptionValidatedValue::serialize() const noexcept; -template std::string GncOptionValidatedValue::serialize() const noexcept; -template std::string GncOptionValidatedValue::serialize() const noexcept; -template std::string GncOptionValidatedValue::serialize() const noexcept; -template std::string GncOptionValidatedValue::serialize() const noexcept; -template std::string GncOptionValidatedValue::serialize() const noexcept; -template std::string GncOptionValidatedValue::serialize() const noexcept; -template std::string GncOptionValidatedValue::serialize() const noexcept; -template std::string GncOptionValidatedValue::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; @@ -966,15 +953,5 @@ template bool GncOptionValue::deserialize(const std::string&) noexc 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 GncOptionValidatedValue::deserialize(const std::string&) noexcept; -template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; -template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; -template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; -template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; -template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; -template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; -template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; -template bool GncOptionValidatedValue::deserialize(const std::string&) noexcept; -template bool GncOptionValidatedValue::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 index 253bc4d75a..9e74b1f81d 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -172,75 +172,54 @@ private: GncItem m_default_value; }; -/** class GncOptionValidatedValue - * Validated values have an additional member function, provided as a - * constructor argument, that checks value parameters for some property before - * setting the object's value member. If the function returns false a - * std::invalid_argument exception is thrown. +/** 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. */ -template -class GncOptionValidatedValue : public OptionClassifier + +class GncOptionCommodityValue : public OptionClassifier { public: - GncOptionValidatedValue() = delete; - GncOptionValidatedValue(const char* section, const char* name, + GncOptionCommodityValue() = delete; + GncOptionCommodityValue(const char* section, const char* name, const char* key, const char* doc_string, - ValueType value, - std::functionvalidator, - GncOptionUIType ui_type = GncOptionUIType::INTERNAL - ) : + gnc_commodity* value, + GncOptionUIType ui_type = GncOptionUIType::COMMODITY) : OptionClassifier{section, name, key, doc_string}, - m_ui_type{ui_type}, m_value{value}, m_default_value{value}, - m_validator{validator} - { - if (!this->validate(value)) - throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); - } - GncOptionValidatedValue(const char* section, const char* name, - const char* key, const char* doc_string, - ValueType value, - std::functionvalidator, - ValueType val_data) : - OptionClassifier{section, name, key, doc_string}, - m_ui_type{GncOptionUIType::INTERNAL}, m_value{value}, - m_default_value{value}, m_validator{validator}, m_validation_data{val_data} + 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 (!this->validate(value)) - throw std::invalid_argument("Attempt to create GncValidatedOption with bad value."); + if (!validate(value)) + throw std::invalid_argument("Attempt to create GncOptionCommodityValue with currency UIType and non-currency value."); } - GncOptionValidatedValue(const GncOptionValidatedValue&) = default; - GncOptionValidatedValue(GncOptionValidatedValue&&) = default; - GncOptionValidatedValue& operator=(const GncOptionValidatedValue&) = default; - GncOptionValidatedValue& operator=(GncOptionValidatedValue&&) = default; - ValueType get_value() const { return m_value; } - ValueType get_default_value() const { return m_default_value; } - bool validate(ValueType value) const { return m_validator(value); } - 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 reset_default_value() { m_value = m_default_value; } - bool is_changed() const noexcept { return m_value != m_default_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; - ValueType m_value; - ValueType m_default_value; - std::function m_validator; //11 - ValueType m_validation_data; + 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, @@ -252,9 +231,7 @@ template struct is_QofInstanceValue { static constexpr bool value = - (std::is_same_v, GncOptionQofInstanceValue> || - std::is_same_v, - GncOptionValidatedValue>); + std::is_same_v, GncOptionQofInstanceValue>; }; template inline constexpr bool @@ -264,9 +241,7 @@ template struct is_QofQueryValue { static constexpr bool value = - (std::is_same_v, GncOptionValue> || - std::is_same_v, - GncOptionValidatedValue>); + std::is_same_v, GncOptionValue>; }; template inline constexpr bool @@ -298,25 +273,20 @@ operator<< >(std::ostream& oss, 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(); - if (auto type = opt.get_ui_type(); type == GncOptionUIType::COMMODITY || - type == GncOptionUIType::CURRENCY) - { - if (type == GncOptionUIType::COMMODITY) - { - oss << gnc_commodity_get_namespace(GNC_COMMODITY(value)) << " "; - } - oss << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)); - } - else - { - oss << qof_instance_to_string(value); - } + oss << qof_instance_to_string(value); return oss; } @@ -339,35 +309,15 @@ std::istream& operator>>(std::istream& iss, OptType& opt) } } +std::istream& operator>> (std::istream& iss, GncOptionCommodityValue& opt); + template, int> = 0> std::istream& operator>> (std::istream& iss, OptType& opt) { std::string instr; - auto type = opt.get_ui_type(); - if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY) - { - std::string name_space, mnemonic; - if (type == GncOptionUIType::COMMODITY) - iss >> name_space; - else - name_space = GNC_COMMODITY_NS_CURRENCY; - if (name_space.find(":") == std::string::npos) - { - iss >> mnemonic; - instr = name_space + ":"; - instr += mnemonic; - } - else - { - instr = name_space; - } - } - else - { - iss >> instr; - } + iss >> instr; opt.set_value(qof_instance_from_string(instr, opt.get_ui_type())); return iss; } diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index aa7ae040b0..5b2b940d19 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -302,15 +302,15 @@ GncOption::validate(ValueType value) const return std::visit( [value] (const auto& option) -> bool { if constexpr ((is_same_decayed_v && - is_same_decayed_v) || + GncOptionMultichoiceValue> && + is_same_decayed_v) || (is_same_decayed_v && + GncOptionMultichoiceValue> && is_same_decayed_v) || - is_same_decayed_v>) + GncMultichoiceOptionIndexVec>) || + (is_same_decayed_v && + is_same_decayed_v)) return option.validate(value); else return false; @@ -448,8 +448,6 @@ gnc_make_SCM_option(const char* section, const char* name, */ -template class GncOptionValidatedValue; - template GncOption::GncOption(const char*, const char*, const char*, const char*, bool, GncOptionUIType); //template GncOption::GncOption(const char*, const char*, const char*, @@ -477,6 +475,7 @@ 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; @@ -490,6 +489,7 @@ 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; @@ -504,6 +504,7 @@ 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); @@ -535,6 +536,7 @@ 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; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index e0bdf61fa4..72022d9f42 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -60,7 +60,7 @@ class GncOptionAccountListValue; class GncOptionAccountSelValue; class GncOptionMultichoiceValue; template class GncOptionRangeValue; -template class GncOptionValidatedValue; +class GncOptionCommodityValue; class GncOptionDateValue; template @@ -107,8 +107,7 @@ using GncOptionVariant = std::variant, GncOptionMultichoiceValue, GncOptionRangeValue, GncOptionRangeValue, - GncOptionValidatedValue, - GncOptionValidatedValue, + GncOptionCommodityValue, GncOptionDateValue>; using GncOptionVariantPtr = std::unique_ptr; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 5da83348b4..510b58a86d 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -444,8 +444,6 @@ is_qofinstance_ui_type(GncOptionUIType type) { switch (type) { - case GncOptionUIType::CURRENCY: - case GncOptionUIType::COMMODITY: case GncOptionUIType::ACCOUNT_SEL: case GncOptionUIType::BUDGET: case GncOptionUIType::OWNER: @@ -651,8 +649,8 @@ gnc_register_commodity_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { - GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string, - (const QofInstance*)value, + GncOption option{GncOptionCommodityValue{section, name, key, doc_string, + value, GncOptionUIType::COMMODITY}}; db->register_option(section, std::move(option)); } @@ -917,14 +915,8 @@ gnc_register_currency_option(GncOptionDB* db, const char* section, const char* name, const char* key, const char* doc_string, gnc_commodity *value) { - GncOption option{GncOptionValidatedValue{ - section, name, key, doc_string, (const QofInstance*)value, - [](const QofInstance* new_value) -> bool - { - return GNC_IS_COMMODITY (new_value) && - gnc_commodity_is_currency(GNC_COMMODITY(new_value)); - }, - GncOptionUIType::CURRENCY + GncOption option{GncOptionCommodityValue{ + section, name, key, doc_string, value,GncOptionUIType::CURRENCY }}; db->register_option(section, std::move(option)); } diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index f9c77abf72..cebe269232 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -240,6 +240,14 @@ 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) { @@ -334,7 +342,7 @@ scm_to_value(SCM new_value) auto info = SWIG_PointerType(new_value); - static const std::array types{ + 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, @@ -352,6 +360,36 @@ scm_to_value(SCM new_value) 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) { @@ -431,6 +469,12 @@ gnc_option_test_book_destroy(QofBook* book) %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&&); @@ -1056,25 +1100,27 @@ inline SCM return_scm_value(ValueType value) if (serial.empty()) return no_value; auto value{scm_list_1(scm_from_utf8_string(serial.c_str()))}; - if (uitype == GncOptionUIType::CURRENCY) - { - const SCM quoted_format_str{scm_from_utf8_string("\"~a\"")}; - return scm_simple_format(SCM_BOOL_F, quoted_format_str, value); - } - else if (uitype == GncOptionUIType::COMMODITY) - { - const SCM commodity_fmt{scm_from_utf8_string("\"~a\" \"~a\"")}; - auto comm{GNC_COMMODITY(option.get_value())}; - auto name_space{gnc_commodity_get_namespace(comm)}; - auto mnemonic{gnc_commodity_get_mnemonic(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); - } - else - { 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) @@ -1198,6 +1244,34 @@ inline SCM return_scm_value(ValueType value) 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)) @@ -1288,6 +1362,12 @@ inline SCM return_scm_value(ValueType value) 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)) @@ -1589,8 +1669,8 @@ inline SCM return_scm_value(ValueType value) const char* key, const char* doc_string, gnc_commodity *value) { - return new GncOption{GncOptionQofInstanceValue{ - section, name, key, doc_string, (const QofInstance*)value, + return new GncOption{GncOptionCommodityValue{ + section, name, key, doc_string, value, GncOptionUIType::COMMODITY}}; } @@ -1603,15 +1683,17 @@ inline SCM return_scm_value(ValueType value) 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)) + 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); - + if (commodity) + return gnc_make_commodity_option(section, name, key, doc_string, + commodity); + } return nullptr; } @@ -1622,16 +1704,9 @@ inline SCM return_scm_value(ValueType value) { try { - return new GncOption{GncOptionValidatedValue{ - section, name, key, doc_string, (const QofInstance*)value, - [](const QofInstance* new_value) -> bool - { - return GNC_IS_COMMODITY (new_value) && - gnc_commodity_is_currency(GNC_COMMODITY(new_value)); - }, - GncOptionUIType::CURRENCY - } - }; + return new GncOption{GncOptionCommodityValue{ + section, name, key, doc_string, value, + GncOptionUIType::CURRENCY}}; } catch (const std::exception& err) { diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 8f2e3f8631..94df08f7a7 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -243,13 +243,9 @@ 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{GncOptionValidatedValue{ - section, name, key, doc_string, (const QofInstance*)value, - [](const QofInstance* new_value) -> bool - { - return GNC_IS_COMMODITY (new_value) && - gnc_commodity_is_currency(GNC_COMMODITY(new_value)); - }, is_currency ? GncOptionUIType::CURRENCY : GncOptionUIType::COMMODITY} + GncOption option{GncOptionCommodityValue{ + section, name, key, doc_string, value, + is_currency ? GncOptionUIType::CURRENCY : GncOptionUIType::COMMODITY} }; return option; } @@ -258,7 +254,7 @@ TEST_F(GncOptionCommodityTest, test_currency_ctor) { EXPECT_THROW({ auto option = make_currency_option("foo", "bar", "baz", - "Phony Option", m_hpe, false); + "Phony Option", m_hpe, true); }, std::invalid_argument); EXPECT_NO_THROW({ auto option = make_currency_option("foo", "bar", "baz", @@ -275,23 +271,23 @@ 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((const QofInstance*)m_usd); + option.set_value(m_usd); }); EXPECT_PRED2(gnc_commodity_equal, m_usd, - GNC_COMMODITY(option.get_value())); + GNC_COMMODITY(option.get_value())); EXPECT_THROW({ - option.set_value((const QofInstance*)m_hpe); + option.set_value(m_hpe); }, std::invalid_argument); EXPECT_PRED2(gnc_commodity_equal, m_usd, - GNC_COMMODITY(option.get_value())); + 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((const QofInstance*)m_usd)); - EXPECT_FALSE(option.validate((const QofInstance*)m_aapl)); + EXPECT_TRUE(option.validate(m_usd)); + EXPECT_FALSE(option.validate(m_aapl)); } static inline std::string make_currency_str(gnc_commodity* cur) @@ -344,19 +340,19 @@ TEST_F(GncOptionCommodityTest, test_currency_in) std::string usd_str{make_currency_str(m_usd)}; std::istringstream iss{usd_str}; iss >> option; - EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value()); + EXPECT_EQ(m_usd, option.get_value()); }); } TEST_F(GncOptionCommodityTest, test_commodity_in) { - GncOption option{GncOptionQofInstanceValue{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl, + 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(QOF_INSTANCE(m_hpe), option.get_value()); + EXPECT_EQ(m_hpe, option.get_value()); } class GncUIType diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm index cd8facb9e7..c582be5a41 100644 --- a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm +++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm @@ -87,19 +87,19 @@ (let ((option (gnc:lookup-option options \"foo\" \"bar\"))) - ((lambda (o) (if o (gnc:option-set-value o \"~a\"))) option)) + ((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 " + (format #f " ; Section: foo (let ((option (gnc:lookup-option options \"foo\" \"bar\"))) - ((lambda (o) (if o (gnc:option-set-value o \"~a\" \"~a\"))) option)) + ((lambda (o) (if o (gnc:option-set-value o ~s ~s))) option)) " (car value-parts) (cadr value-parts)))) @@ -208,6 +208,8 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (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 From d4c3c30b1a0e27a7d9f97b62b1ef447b104c3cf6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 12 Mar 2022 17:58:23 -0800 Subject: [PATCH 292/298] Use GUIDs to represent QofInstances instead of pointers. Converting them to pointers for Scheme to use. Prevents dangling pointers when the user deletes a QofInstance as long as the Scheme report code re-fetches its pointers from the options when it's regenerated. --- gnucash/gnome-utils/dialog-options.cpp | 15 +++-- libgnucash/app-utils/gnc-option-impl.cpp | 51 +++++++++++++---- libgnucash/app-utils/gnc-option-impl.hpp | 45 +++++++++------ libgnucash/app-utils/gnc-optiondb.cpp | 8 ++- libgnucash/app-utils/gnc-optiondb.hpp | 2 +- libgnucash/app-utils/gnc-optiondb.i | 57 ++++++++++++------- .../app-utils/test/gtest-gnc-option.cpp | 22 ++++--- .../app-utils/test/gtest-gnc-optiondb.cpp | 43 ++++++++------ .../test/test-gnc-option-scheme-output.scm | 4 +- .../app-utils/test/test-gnc-optiondb.scm | 8 +-- 10 files changed, 169 insertions(+), 86 deletions(-) diff --git a/gnucash/gnome-utils/dialog-options.cpp b/gnucash/gnome-utils/dialog-options.cpp index f1da64f697..71ee5f3df5 100644 --- a/gnucash/gnome-utils/dialog-options.cpp +++ b/gnucash/gnome-utils/dialog-options.cpp @@ -1524,7 +1524,7 @@ create_option_widget(GncOption& option, documentation, enclosing, packed); } -using GncOptionAccountList = std::vector; +using GncOptionAccountList = std::vector; static void account_select_all_cb(GtkWidget *widget, gpointer data) @@ -1604,8 +1604,12 @@ public: GList *acc_list = nullptr; const GncOptionAccountList& accounts = option.get_value(); - for (auto account : accounts) - acc_list = g_list_prepend(acc_list, static_cast(const_cast(account))); + 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); @@ -1617,7 +1621,10 @@ public: GncOptionAccountList acc_vec; acc_vec.reserve(g_list_length(acc_list)); for (auto node = acc_list; node; node = g_list_next(node)) - acc_vec.push_back(static_cast(node->data)); + { + auto guid{qof_entity_get_guid(node->data)}; + acc_vec.push_back(*guid); + } g_list_free(acc_list); option.set_value(acc_vec); } diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 9c1cb89cdc..470be01f95 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -275,13 +275,20 @@ GncOptionAccountListValue::validate(const GncOptionAccountList& values) const 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; - for(auto account : values) { + auto book{gnc_get_current_book()}; + for(auto& guid : values) + { if (std::find(m_allowed.begin(), m_allowed.end(), - xaccAccountGetType(account)) == m_allowed.end()) - return false; + 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; } @@ -310,17 +317,33 @@ GncOptionAccountListValue::get_default_value() const 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(GNC_ACCOUNT(node->data)); + 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. @@ -354,15 +377,20 @@ GncOptionAccountSelValue::validate(const Account* value) const const Account* GncOptionAccountSelValue::get_value() const { - return m_value ? m_value : get_default_value(); + 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 (m_default_value) - return m_default_value; + 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. @@ -713,7 +741,7 @@ GncOptionAccountListValue::serialize() const noexcept if (!first) retval += " "; first = false; - retval += qof_instance_to_string(QOF_INSTANCE(val)); + retval += guid_to_string(&val); } return retval; } @@ -732,8 +760,9 @@ GncOptionAccountListValue::deserialize(const std::string& str) noexcept if (!first) ++pos; first = false; - auto ptr = qof_instance_from_string(str.substr(pos, pos + GUID_ENCODING_LENGTH), get_ui_type()); - m_value.push_back(reinterpret_cast(ptr)); + 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; @@ -743,7 +772,7 @@ std::string GncOptionAccountSelValue::serialize() const noexcept { static const std::string no_value{"No Value"}; - return m_value ?qof_instance_to_string(QOF_INSTANCE(m_value)) : no_value; + return guid_equal(guid_null(), &m_value) ? no_value : guid_to_string(&m_value); } bool diff --git a/libgnucash/app-utils/gnc-option-impl.hpp b/libgnucash/app-utils/gnc-option-impl.hpp index 9e74b1f81d..a784aa7cf7 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -717,7 +717,7 @@ operator>> (std::istream& iss, } -using GncOptionAccountList = std::vector; +using GncOptionAccountList = std::vector; using GncOptionAccountTypeList = std::vector; @@ -795,7 +795,7 @@ public: } GList* account_type_list() const noexcept; void reset_default_value() { m_value = m_default_value; } - bool is_changed() const noexcept { return 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; } @@ -821,7 +821,7 @@ operator<< (std::ostream& oss, first = false; else oss << " "; - oss << qof_instance_to_string(QOF_INSTANCE(value)); + oss << guid_to_string(&value); } return oss; } @@ -836,7 +836,10 @@ operator>> (std::istream& iss, std::string str; std::getline(iss, str, ' '); if (!str.empty()) - values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type())); + { + auto guid{qof_entity_get_guid(qof_instance_from_string(str, opt.get_ui_type()))}; + values.push_back(*guid); + } else break; } @@ -856,32 +859,32 @@ public: const char* key, const char* doc_string, GncOptionUIType ui_type) : OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type}, - m_value{}, m_default_value{}, m_allowed{} {} + 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{const_cast(value)}, - m_default_value{const_cast(value)}, m_allowed{} {} + 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{}, m_default_value{}, m_allowed{std::move(allowed)} {} + 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{}, m_default_value{}, m_allowed{std::move(allowed)} { + 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 = const_cast(value); - m_default_value = const_cast(value); + m_value = m_default_value = *qof_entity_get_guid(value); } const Account* get_value() const; @@ -889,25 +892,31 @@ public: bool validate (const Account* value) const; void set_value (const Account* value) { if (validate(value)) - //throw! - m_value = const_cast(value); + { + auto guid{qof_entity_get_guid(value)}; + m_value = *guid; + } + //else throw } void set_default_value (const Account* value) { if (validate(value)) - //throw! - m_value = m_default_value = const_cast(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 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; - Account* m_value; - Account* m_default_value; + GncGUID m_value; + GncGUID m_default_value; GncOptionAccountTypeList m_allowed; }; diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 510b58a86d..0de184906f 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "gnc-optiondb.h" #include "gnc-optiondb.hpp" #include "gnc-optiondb-impl.hpp" @@ -708,6 +709,11 @@ gnc_register_account_list_limited_option(GncOptionDB* db, { 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)); @@ -729,7 +735,7 @@ find_children(Account* account, void* data) const GncOptionAccountTypeList& types = datapair->second; if (std::find(types.begin(), types.end(), xaccAccountGetType(account)) != types.end()) - list.push_back(account); + list.push_back(*qof_entity_get_guid(account)); } GncOptionAccountList diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index b274155ad6..86a6aedbe7 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -54,7 +54,7 @@ extern "C" class GncOptionDB; using GncOptionDBPtr = std::unique_ptr; -using GncOptionAccountList = std::vector; +using GncOptionAccountList = std::vector; using GncOptionAccountTypeList = std::vector; using GncMultichoiceOptionEntry = std::tuple(SCM new_value) void* account{}; SWIG_ConvertPtr(node, &account, SWIGTYPE_p_Account, 0); if (account) - retval.push_back(static_cast(account)); + { + auto guid{qof_entity_get_guid(static_cast(account))}; + retval.push_back(*guid); + } next = scm_cdr(next); if (scm_is_null(next)) break; @@ -440,9 +443,13 @@ template <>inline SCM scm_from_value(GncOptionAccountList value) { SCM s_list = SCM_EOL; - for (auto acct : value) + 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); } @@ -452,13 +459,14 @@ void gnc_option_test_book_destroy(QofBook*); QofBook* gnc_option_test_book_new() { - return static_cast(g_object_new(QOF_TYPE_BOOK, nullptr)); + auto session = gnc_get_current_session(); + return gnc_get_current_book(); } void gnc_option_test_book_destroy(QofBook* book) { - g_object_unref(book); + gnc_clear_current_session(); } %} @@ -574,7 +582,8 @@ gnc_option_test_book_destroy(QofBook* book) SCM s_account = scm_list_ref($input, scm_from_size_t(i)); Account* acct = (Account*)SWIG_MustGetPtr(s_account, SWIGTYPE_p_Account, 1, 0); - $1.push_back(acct); + if (acct) + $1.push_back(*qof_entity_get_guid(acct)); } } @@ -602,7 +611,7 @@ gnc_option_test_book_destroy(QofBook* book) $1 = &types; } -%typemap(in) GncOptionAccountList +%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) @@ -610,8 +619,10 @@ gnc_option_test_book_destroy(QofBook* book) SCM s_account = scm_list_ref($input, scm_from_size_t(i)); Account* acct = (Account*)SWIG_MustGetPtr(s_account, SWIGTYPE_p_Account, 1, 0); - $1.push_back(acct); + if (acct) + alist.push_back(*qof_entity_get_guid(acct)); } + $1 = &alist; } %typemap(in) GncOptionAccountList& (GncOptionAccountList acclist) @@ -623,7 +634,7 @@ gnc_option_test_book_destroy(QofBook* book) 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(acct); + acclist.push_back(*qof_entity_get_guid(acct)); } $1 = &acclist; } @@ -631,18 +642,26 @@ gnc_option_test_book_destroy(QofBook* book) %typemap(out) GncOptionAccountList { $result = SCM_EOL; - for (auto acct : $1) + 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) GncOptionAccountList& +%typemap(out) const GncOptionAccountList& { $result = SCM_EOL; - for (auto acct : *$1) + 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) } @@ -1080,17 +1099,17 @@ inline SCM return_scm_value(ValueType value) GncOptionAccountListValue>) { static const SCM list_format_str{scm_from_utf8_string("'~s")}; - auto acct_list{option.get_value()}; - if (acct_list.empty()) + auto guid_list{option.get_value()}; + if (guid_list.empty()) return no_value; - SCM guid_list{SCM_EOL}; - for(auto acct : acct_list) + SCM string_list{SCM_EOL}; + for(auto guid : guid_list) { - auto acct_str{qof_instance_to_string(QOF_INSTANCE(acct))}; - auto acct_scm{scm_from_utf8_string(acct_str.c_str())}; - guid_list = scm_cons(acct_scm, 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(guid_list)); + return scm_simple_format(SCM_BOOL_F, list_format_str, scm_list_1(string_list)); } if constexpr (is_QofInstanceValue_v) diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 94df08f7a7..49959de90a 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -515,7 +515,7 @@ find_children(Account* account, void* data) const GncOptionAccountTypeList& types = datapair->second; if (std::find(types.begin(), types.end(), xaccAccountGetType(account)) != types.end()) - list.push_back(account); + list.push_back(*qof_entity_get_guid(account)); } class GncOptionAccountTest : public ::testing::Test @@ -571,6 +571,12 @@ protected: 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); @@ -662,9 +668,9 @@ TEST_F(GncOptionAccountTest, test_account_list_out) GncOptionUIType::ACCOUNT_LIST, acclist}}; std::ostringstream oss; - std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()}; + std::string acc_guids{gnc::GUID{acclist[0]}.to_string()}; acc_guids += " "; - acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string(); + acc_guids += gnc::GUID{acclist[1]}.to_string(); oss << option; EXPECT_EQ(acc_guids, oss.str()); @@ -675,7 +681,7 @@ TEST_F(GncOptionAccountTest, test_account_list_out) GncOptionUIType::ACCOUNT_LIST, accsel, GncOptionAccountTypeList{ACCT_TYPE_BANK}}}; - acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string(); + acc_guids = gnc::GUID{accsel[0]}.to_string(); oss.str(""); oss << sel_option; @@ -688,9 +694,9 @@ TEST_F(GncOptionAccountTest, test_account_list_in) GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option", GncOptionUIType::ACCOUNT_LIST, acclist}}; - std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()}; + std::string acc_guids{gnc::GUID{acclist[0]}.to_string()}; acc_guids += " "; - acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string(); + acc_guids += gnc::GUID{acclist[1]}.to_string(); std::istringstream iss{acc_guids}; iss >> option; @@ -703,7 +709,7 @@ TEST_F(GncOptionAccountTest, test_account_list_in) accsel, GncOptionAccountTypeList{ACCT_TYPE_BANK}}}; GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})}; - acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string(); + acc_guids = gnc::GUID{acclistbad[1]}.to_string(); acc_guids += " "; iss.str(acc_guids); @@ -711,7 +717,7 @@ TEST_F(GncOptionAccountTest, test_account_list_in) EXPECT_EQ(accsel, sel_option.get_value()); iss.clear(); //Reset the failedbit from the invalid selection type. - acc_guids = gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string(); + acc_guids = gnc::GUID{acclist[1]}.to_string(); EXPECT_NO_THROW({ iss.str(acc_guids); iss >> sel_option; diff --git a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp index 15a3cca4ea..74d56b33b1 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -86,10 +86,12 @@ TEST_F(GncOptionDBTest, test_register_string_option) */ -struct AccountTestBook +struct GncOptionDBAccountTest : public ::testing::Test { - AccountTestBook() : - m_book{qof_book_new()}, m_root{gnc_account_create_root(m_book)} + 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* { @@ -117,31 +119,31 @@ struct AccountTestBook create_account(expenses, ACCT_TYPE_EXPENSE, "Gas"); create_account(expenses, ACCT_TYPE_EXPENSE, "Rent"); } - ~AccountTestBook() + ~GncOptionDBAccountTest() { xaccAccountBeginEdit(m_root); xaccAccountDestroy(m_root); //It does the commit - qof_book_destroy(m_book); + gnc_clear_current_session(); } + QofSession* m_sess; QofBook* m_book; Account* m_root; + GncOptionDBPtr m_db; }; -TEST_F(GncOptionDBTest, test_register_account_list_option) +TEST_F(GncOptionDBAccountTest, test_register_account_list_option) { - AccountTestBook book; - auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; + 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(GncOptionDBTest, test_register_account_list_limited_option) +TEST_F(GncOptionDBAccountTest, test_register_account_list_limited_option) { - AccountTestBook book; - auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; + 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}); @@ -149,10 +151,9 @@ TEST_F(GncOptionDBTest, test_register_account_list_limited_option) EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get_value().at(3)); } -TEST_F(GncOptionDBTest, test_register_account_sel_limited_option) +TEST_F(GncOptionDBAccountTest, test_register_account_sel_limited_option) { - AccountTestBook book; - auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; + 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, @@ -161,10 +162,9 @@ TEST_F(GncOptionDBTest, test_register_account_sel_limited_option) EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get_value().at(0)); } -TEST_F(GncOptionDBTest, test_register_account_sel_limited_option_fail_construct) +TEST_F(GncOptionDBAccountTest, test_register_account_sel_limited_option_fail_construct) { - AccountTestBook book; - auto acclist{gnc_account_list_from_types(book.m_book, {ACCT_TYPE_STOCK})}; + 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}); @@ -276,6 +276,12 @@ TEST_F(GncOptionDBTest, test_register_start_date_option) } +static bool +operator==(const GncGUID& l, const GncGUID& r) +{ + return guid_equal(&l, &r); +} + class GncOptionDBIOTest : public ::testing::Test { protected: @@ -322,7 +328,8 @@ protected: RelativeDatePeriod::START_CURRENT_QUARTER); gnc_register_account_list_option(m_db, "quux", "xyzzy", "second", "Phony AccountList Option", - {aapl, hpe}); + {*qof_entity_get_guid(aapl), + *qof_entity_get_guid(hpe)}); } ~GncOptionDBIOTest() diff --git a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm index c582be5a41..8b070433fb 100644 --- a/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm +++ b/libgnucash/app-utils/test/test-gnc-option-scheme-output.scm @@ -315,7 +315,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (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" + (test-equal "account list form" ;;fails (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) @@ -338,7 +338,7 @@ veritatis et quasi architecto beatae vitae dicta sunt, explicabo.") (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" + (test-equal "account sel form" ;; fails (test-template (GncOption-serialize option)) (gnc:generate-restore-forms odb "options")) )) diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index 30b5da1d03..a1296b885e 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -112,16 +112,16 @@ (test-equal (car acctlist) (car acct-list))) ))) (define (test-make-account-list-limited-option book) - (test-group "test-make-account-list-option" + (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 + (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)) - (test-equal (cadr acctlist) (cadr acct-list))) + (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)) From 572cb6b1d1da4aa1a971b16201c4048c5cfdb3a5 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 13 Mar 2022 15:51:16 -0700 Subject: [PATCH 293/298] Banish gnc_get_optiondb_from_dispatcher to gnc-report.cpp. Puts it closer to its points of use and removes it with its Scheme dependency from libgnucash. --- gnucash/gnome/dialog-report-column-view.cpp | 2 +- gnucash/gnome/dialog-report-style-sheet.cpp | 2 +- gnucash/gnome/gnc-plugin-page-report.cpp | 2 +- gnucash/gnome/window-report.cpp | 2 +- gnucash/gnucash-commands.cpp | 2 +- gnucash/gnucash-core-app.cpp | 2 +- gnucash/gnucash.cpp | 2 +- gnucash/report/CMakeLists.txt | 2 +- .../report/{gnc-report.c => gnc-report.cpp} | 44 +++++++++++++---- gnucash/report/gnc-report.h | 47 ++++++++++++++----- libgnucash/app-utils/gnc-optiondb.cpp | 23 --------- libgnucash/app-utils/gnc-optiondb.h | 14 ------ po/POTFILES.in | 2 +- 13 files changed, 79 insertions(+), 67 deletions(-) rename gnucash/report/{gnc-report.c => gnc-report.cpp} (91%) diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp index 3c7c7c9fcb..504b6c28f7 100644 --- a/gnucash/gnome/dialog-report-column-view.cpp +++ b/gnucash/gnome/dialog-report-column-view.cpp @@ -35,12 +35,12 @@ extern "C" #include "window-report.h" #include "guile-mappings.h" #include "gnc-guile-utils.h" -#include "gnc-report.h" #include "gnc-ui.h" } #include "dialog-report-column-view.hpp" #include +#include #include enum available_cols diff --git a/gnucash/gnome/dialog-report-style-sheet.cpp b/gnucash/gnome/dialog-report-style-sheet.cpp index a1a8d651bc..a873541545 100644 --- a/gnucash/gnome/dialog-report-style-sheet.cpp +++ b/gnucash/gnome/dialog-report-style-sheet.cpp @@ -37,10 +37,10 @@ extern "C" #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 diff --git a/gnucash/gnome/gnc-plugin-page-report.cpp b/gnucash/gnome/gnc-plugin-page-report.cpp index ab5888207b..23e98f52dc 100644 --- a/gnucash/gnome/gnc-plugin-page-report.cpp +++ b/gnucash/gnome/gnc-plugin-page-report.cpp @@ -65,7 +65,6 @@ extern "C" #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" @@ -79,6 +78,7 @@ extern "C" #include +#include #include /* NW: you can add GNC_MOD_REPORT to gnc-engine.h diff --git a/gnucash/gnome/window-report.cpp b/gnucash/gnome/window-report.cpp index 5a50e22016..5580bad58b 100644 --- a/gnucash/gnome/window-report.cpp +++ b/gnucash/gnome/window-report.cpp @@ -37,13 +37,13 @@ extern "C" #include "swig-runtime.h" #include "gnc-guile-utils.h" -#include "gnc-report.h" #include "gnc-ui.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" 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 fbb223dbe8..70ed0c269c 100644 --- a/gnucash/gnucash.cpp +++ b/gnucash/gnucash.cpp @@ -54,7 +54,6 @@ extern "C" { #include #include #include -#include #include #include #include @@ -68,6 +67,7 @@ extern "C" { #include #endif #include +#include #include namespace bl = boost::locale; 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/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 0de184906f..8b060e01b7 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -570,29 +570,6 @@ GncOptionDB::load_from_kvp(QofBook* book) noexcept }); } -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(); -} - void gnc_register_string_option(GncOptionDB* db, const char* section, const char* name, const char* key, diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h index f828536e98..8078459d5d 100644 --- a/libgnucash/app-utils/gnc-optiondb.h +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -89,20 +89,6 @@ GncOptionDB* gnc_option_db_new(void); */ void gnc_option_db_destroy(GncOptionDB* odb); -/** - * 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); - /** * Write all changed ui_item values to their options. * @param odb The GncOptionDB. diff --git a/po/POTFILES.in b/po/POTFILES.in index f1b1f1e593..9a0b099f10 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -439,7 +439,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 From 30e6c8ab11be8f7014ddcd5bda82cf9d0ffe409c Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 15 Mar 2022 16:16:50 -0700 Subject: [PATCH 294/298] [gnc-optiondb.i]Explicitly include array. Xcode-13.3 errors without it. --- libgnucash/app-utils/gnc-optiondb.i | 1 + 1 file changed, 1 insertion(+) diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 7415e3176f..5f78cabdf0 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -67,6 +67,7 @@ namespace std { #include "gnc-optiondb.hpp" #include "gnc-optiondb-impl.hpp" #include "gnc-option-date.hpp" +#include #include #include From 5457d4a9e38bebd38f5d6e5e5a6e829477b9de46 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 21 Mar 2022 12:44:52 -0700 Subject: [PATCH 295/298] [C++options] Convert account-summary.scm to gnc-register-option. Just to make sure that it works. --- .../reports/standard/account-summary.scm | 109 ++++++++---------- 1 file changed, 45 insertions(+), 64 deletions(-) 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) From 278aa484d72f2168d9217b43ef0c0e171300432b Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 21 Mar 2022 12:46:02 -0700 Subject: [PATCH 296/298] [C++options] Implement new GncOptionValue type GncOptionReportPlacement. For multicolumn reports so that we don't have to pass Scheme values. --- gnucash/gnome/dialog-report-column-view.cpp | 346 ++++++------------ .../report/reports/standard/view-column.scm | 4 +- libgnucash/app-utils/gnc-option-impl.cpp | 32 ++ libgnucash/app-utils/gnc-option-impl.hpp | 10 + libgnucash/app-utils/gnc-option-uitype.hpp | 2 +- libgnucash/app-utils/gnc-option.cpp | 6 + libgnucash/app-utils/gnc-option.hpp | 6 +- libgnucash/app-utils/gnc-optiondb.cpp | 15 + libgnucash/app-utils/gnc-optiondb.hpp | 15 + libgnucash/app-utils/gnc-optiondb.i | 81 +++- .../app-utils/test/gtest-gnc-option.cpp | 16 + .../app-utils/test/gtest-gnc-optiondb.cpp | 15 + .../app-utils/test/test-gnc-optiondb.scm | 12 + 13 files changed, 303 insertions(+), 257 deletions(-) diff --git a/gnucash/gnome/dialog-report-column-view.cpp b/gnucash/gnome/dialog-report-column-view.cpp index 504b6c28f7..3a189219b1 100644 --- a/gnucash/gnome/dialog-report-column-view.cpp +++ b/gnucash/gnome/dialog-report-column-view.cpp @@ -24,6 +24,7 @@ #include #include #include +#include extern "C" { @@ -41,7 +42,7 @@ extern "C" #include "dialog-report-column-view.hpp" #include #include -#include +#include enum available_cols { @@ -59,6 +60,8 @@ enum contents_cols NUM_CONTENTS_COLS }; +using StrVec = std::vector; + struct gncp_column_view_edit { GncOptionsDialog * optwin; @@ -68,8 +71,8 @@ struct gncp_column_view_edit SCM view; GncOptionDB * odb; - SCM available_list; - SCM contents_list; + StrVec available_list; + GncOptionReportPlacementVec contents_list; int contents_selected; GtkWidget *add_button; @@ -95,9 +98,9 @@ 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, SCM new_value) + const char* name, const GncOptionReportPlacementVec& new_value) { - gnc_option_db_set_scm_value(odb, section, name, new_value); + odb->find_option(section, name)->set_value(new_value); } static void @@ -109,25 +112,28 @@ gnc_column_view_edit_destroy(gnc_column_view_edit * view) 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 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; + std::string 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)); + 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)) { @@ -135,41 +141,30 @@ update_available_lists(gnc_column_view_edit * view) gtk_tree_model_get(model, &iter, AVAILABLE_COL_GUID, &guid_str, -1); - selection = scm_from_utf8_string(guid_str); + selection = std::string(guid_str); g_free (guid_str); } - else - selection = SCM_UNDEFINED; + view->available_list = get_available_reports(); - scm_gc_unprotect_object(view->available_list); - view->available_list = rpt_guids; - scm_gc_protect_object(view->available_list); - - store = GTK_LIST_STORE(model); + auto store = GTK_LIST_STORE(model); gtk_list_store_clear(store); - if (scm_is_list(rpt_guids)) + for (auto guid : view->available_list) { - for (int i = 0; !scm_is_null(rpt_guids); rpt_guids = SCM_CDR(rpt_guids), i++) - { - SCM rpt_guids_temp = SCM_CAR(rpt_guids); + 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)); - 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.c_str(), + -1); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - AVAILABLE_COL_NAME, _(name), - AVAILABLE_COL_GUID, guid_str, - -1); + if (guid == selection) + gtk_tree_selection_select_iter (tree_selection, &iter); - if (scm_is_equal (rpt_guids_temp, selection)) - gtk_tree_selection_select_iter (tree_selection, &iter); - - g_free (name); - g_free (guid_str); - } + g_free (name); } } @@ -177,43 +172,25 @@ 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_scm_value(view->odb, "__general", - "report-list"); - SCM this_report; - SCM selection = SCM_UNDEFINED; - - GtkListStore *store; + auto contents{view->odb->find_option("__general", "report-list")->get_value()}; GtkTreeIter iter; - GtkTreeSelection *tree_selection; + GncOptionReportPlacement selection{0, 0, 0}; /* Update the list of selected reports (right selection box). */ - tree_selection = gtk_tree_view_get_selection(view->contents); + auto 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)); - } - - 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)); + 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); - if (!scm_is_list(contents)) - return; - - for (int i = 0; !scm_is_null(contents); - contents = SCM_CDR(contents), ++i) + for (size_t i = 0; i < contents.size(); ++i) { - SCM contents_temp = SCM_CAR(contents); - - int id = scm_to_int(SCM_CAAR(contents)); - - this_report = gnc_report_find(id); + 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); @@ -221,11 +198,11 @@ update_contents_lists(gnc_column_view_edit * view) (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)), + CONTENTS_COL_REPORT_COLS, wide, + CONTENTS_COL_REPORT_ROWS, high, -1); - if (scm_is_equal (contents_temp, selection)) + if (id == std::get<0>(selection)) gtk_tree_selection_select_iter (tree_selection, &iter); g_free (name); @@ -256,7 +233,7 @@ gnc_column_view_update_buttons_cb (GtkTreeSelection *selection, if (is_selected) { - int len = scm_ilength (reinterpret_cast(r->contents_list)); + int len = r->contents_list.size(); gtk_tree_model_get (model, &iter, CONTENTS_COL_ROW, &r->contents_selected, -1); @@ -366,9 +343,9 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view) r->size_button = GTK_WIDGET(gtk_builder_get_object (builder, "size_button1")); r->view = view; - r->available_list = SCM_EOL; + r->available_list.clear(); r->contents_selected = 0; - r->contents_list = SCM_EOL; + r->contents_list.clear(); r->odb = odb; r->optwin->build_contents(r->odb); @@ -378,8 +355,6 @@ gnc_column_view_edit_options(GncOptionDB* odb, SCM view) gtk_label_new(_("Contents"))); 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); @@ -449,12 +424,6 @@ 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?!"); - 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; @@ -467,55 +436,26 @@ gnc_column_view_edit_add_cb(GtkButton * button, gpointer user_data) else return; - if (scm_is_list(r->available_list)) + 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 { - 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); - r->optwin->changed (); + 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); } @@ -523,41 +463,34 @@ void gnc_column_view_edit_remove_cb(GtkButton * button, gpointer user_data) { auto r = static_cast(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)); - } - } + 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); - if (r->contents_selected > 0 && oldlength == r->contents_selected + 1) - { - r->contents_selected --; - } + r->optwin->changed(); + update_contents_lists(r); +} - scm_gc_unprotect_object(r->contents_list); - r->contents_list = newlist; - scm_gc_protect_object(r->contents_list); +static void +move_selected_item(gnc_column_view_edit* r, int increment) +{ + if (!r || !increment) + return; - gnc_column_view_set_option(r->odb, "__general", "report-list", - r->contents_list); + 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; - r->optwin->changed(); - } + gnc_column_view_set_option(r->odb, "__general", "report-list", + r->contents_list); + r->optwin->changed(); update_contents_lists(r); } @@ -565,76 +498,14 @@ void gnc_edit_column_view_move_up_cb(GtkButton * button, gpointer user_data) { auto r = static_cast(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); - - r->optwin->changed(); - - update_contents_lists(r); - } + move_selected_item(r, -1); } void gnc_edit_column_view_move_down_cb(GtkButton * button, gpointer user_data) { auto r = static_cast(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); - - r->optwin->changed(); - - update_contents_lists(r); - } + move_selected_item(r, 1); } void @@ -662,32 +533,27 @@ gnc_column_view_edit_size_cb(GtkButton * button, gpointer user_data) 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) + if (r->contents_list.size() > static_cast(r->contents_selected)) { - current = scm_list_ref(r->contents_list, - scm_from_int (r->contents_selected)); + auto [id, wide, high] = r->contents_list[r->contents_selected]; + gtk_spin_button_set_value(GTK_SPIN_BUTTON(colspin), - (float)scm_to_int(SCM_CADR(current))); + static_cast(wide)); gtk_spin_button_set_value(GTK_SPIN_BUTTON(rowspin), - (float)scm_to_int(SCM_CADDR(current))); + static_cast(high)); 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); + 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); } diff --git a/gnucash/report/reports/standard/view-column.scm b/gnucash/report/reports/standard/view-column.scm index 7ef37994e4..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 @@ -83,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/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 470be01f95..7c4e795fc5 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -27,6 +27,8 @@ #include #include #include +#include + extern "C" { #include "gnc-accounting-period.h" @@ -661,6 +663,18 @@ GncOptionValue::serialize() const noexcept 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) @@ -684,6 +698,18 @@ GncOptionValue::deserialize(const std::string& str) noexcept 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) @@ -920,6 +946,7 @@ 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); @@ -934,6 +961,7 @@ 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); @@ -947,6 +975,7 @@ template void GncOptionValue::set_default_value(RelativeDate 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(); @@ -960,6 +989,7 @@ 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; @@ -970,6 +1000,7 @@ 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; @@ -982,5 +1013,6 @@ template bool GncOptionValue::deserialize(const std::string&) noexc 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 index a784aa7cf7..6bd82dc546 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -331,6 +331,16 @@ operator>> >(std::istream& iss, 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 diff --git a/libgnucash/app-utils/gnc-option-uitype.hpp b/libgnucash/app-utils/gnc-option-uitype.hpp index 76c6e48e03..83b0f93477 100644 --- a/libgnucash/app-utils/gnc-option-uitype.hpp +++ b/libgnucash/app-utils/gnc-option-uitype.hpp @@ -67,7 +67,7 @@ enum class GncOptionUIType : unsigned int JOB, TAX_TABLE, QUERY, - REPORT_LIST, + REPORT_PLACEMENT, MAX_VALUE, //Nake sure this one is always last }; diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index 5b2b940d19..e69bc0c021 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -481,6 +481,7 @@ template RelativeDatePeriod GncOption::get_value() const; template GncOptionAccountList GncOption::get_value() const; template GncMultichoiceOptionIndexVec GncOption::get_value() const; template SCM GncOption::get_value() const; +template GncOptionReportPlacementVec GncOption::get_value() const; template bool GncOption::get_default_value() const; template int GncOption::get_default_value() const; @@ -495,6 +496,7 @@ template RelativeDatePeriod GncOption::get_default_value() c template GncOptionAccountList GncOption::get_default_value() const; template GncMultichoiceOptionIndexVec GncOption::get_default_value() const; template SCM GncOption::get_default_value() const; +template GncOptionReportPlacementVec GncOption::get_default_value() const; template void GncOption::set_value(bool); template void GncOption::set_value(int); @@ -511,6 +513,7 @@ template void GncOption::set_value(size_t); template void GncOption::set_value(GncOptionAccountList); template void GncOption::set_value(GncMultichoiceOptionIndexVec); template void GncOption::set_value(SCM); +template void GncOption::set_value(GncOptionReportPlacementVec); template void GncOption::set_default_value(bool); template void GncOption::set_default_value(int); @@ -526,6 +529,7 @@ 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(SCM); +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; @@ -541,6 +545,7 @@ 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*, @@ -554,3 +559,4 @@ 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 index 72022d9f42..e46fa92164 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -40,8 +40,10 @@ #include #include #include +#include #include "gnc-option-ui.hpp" #include "gnc-option-date.hpp" +#include struct OptionClassifier; class GncOptionUIItem; @@ -62,7 +64,8 @@ class GncOptionMultichoiceValue; template class GncOptionRangeValue; class GncOptionCommodityValue; class GncOptionDateValue; - +using GncOptionReportPlacement = std::tuple; +using GncOptionReportPlacementVec = std::vector; template struct is_OptionClassifier { @@ -102,6 +105,7 @@ using GncOptionVariant = std::variant, GncOptionValue, GncOptionValue, GncOptionValue, + GncOptionValue, GncOptionAccountListValue, GncOptionAccountSelValue, GncOptionMultichoiceValue, diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 8b060e01b7..9d06fb951a 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -840,6 +840,7 @@ gnc_register_internal_option(GncOptionDB* db, const char* section, 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, @@ -995,6 +996,20 @@ gnc_register_end_date_option(GncOptionDB* db, const char* section, 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) { diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index 86a6aedbe7..d92f145053 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -600,6 +600,21 @@ inline void gnc_register_internal_option(GncOptionDBPtr& db, 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. * diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index 5f78cabdf0..bff5ee3d41 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -274,6 +274,39 @@ 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) { @@ -440,18 +473,25 @@ scm_to_value(SCM new_value) return retval; } -template <>inline SCM -scm_from_value(GncOptionAccountList value) +template <>inline GncOptionReportPlacementVec +scm_to_value(SCM new_value) { - SCM s_list = SCM_EOL; - auto book{gnc_get_current_book()}; - for (auto guid : 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 acct{xaccAccountLookup(&guid, book)}; - s_list = scm_cons(SWIG_NewPointerObj(acct, SWIGTYPE_p_Account, 0), - s_list); + 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 scm_reverse(s_list); + return rp; } QofBook* gnc_option_test_book_new(); @@ -640,6 +680,12 @@ gnc_option_test_book_destroy(QofBook* book) $1 = &acclist; } +%typemap (in) GncOptionReportPlacementVec& (GncOptionReportPlacementVec rp) +{ + rp = scm_to_value($input); + $1 = &rp; +} + %typemap(out) GncOptionAccountList { $result = SCM_EOL; @@ -666,6 +712,11 @@ gnc_option_test_book_destroy(QofBook* book) $result = scm_reverse ($result) } +%typemap(out) const GncOptionReportPlacementVec& +{ + $result = scm_from_value($1); +} + wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %ignore swig_get_option(GncOption&); @@ -709,7 +760,6 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %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_internal_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*); @@ -721,7 +771,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB); %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;" @@ -1057,7 +1108,6 @@ inline SCM return_scm_value(ValueType value) %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() { @@ -1200,6 +1250,13 @@ inline SCM return_scm_value(ValueType value) return scm_simple_format(SCM_BOOL_F, ticked_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()) { diff --git a/libgnucash/app-utils/test/gtest-gnc-option.cpp b/libgnucash/app-utils/test/gtest-gnc-option.cpp index 49959de90a..e235b2c46d 100644 --- a/libgnucash/app-utils/test/gtest-gnc-option.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-option.cpp @@ -1311,3 +1311,19 @@ TEST_F(GncDateOption, test_stream_in_prev_year_end) 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 index 74d56b33b1..9804aa77ff 100644 --- a/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp +++ b/libgnucash/app-utils/test/gtest-gnc-optiondb.cpp @@ -81,6 +81,21 @@ TEST_F(GncOptionDBTest, test_register_string_option) 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. */ diff --git a/libgnucash/app-utils/test/test-gnc-optiondb.scm b/libgnucash/app-utils/test/test-gnc-optiondb.scm index a1296b885e..7174a4f537 100644 --- a/libgnucash/app-utils/test/test-gnc-optiondb.scm +++ b/libgnucash/app-utils/test/test-gnc-optiondb.scm @@ -54,6 +54,7 @@ (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) @@ -235,3 +236,14 @@ (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 From f4bfd9df3e43fb331287a76fd74049bf4989ee11 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Tue, 14 Dec 2021 12:54:13 +0800 Subject: [PATCH 297/298] [account-piecharts] don't use gnc:make-internal-option --- .../reports/standard/account-piecharts.scm | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/gnucash/report/reports/standard/account-piecharts.scm b/gnucash/report/reports/standard/account-piecharts.scm index 75e8f32639..aa21538b1c 100644 --- a/gnucash/report/reports/standard/account-piecharts.scm +++ b/gnucash/report/reports/standard/account-piecharts.scm @@ -84,15 +84,12 @@ balance at a given time")) ;; The option-generator. The only dependence on the type of piechart ;; is the list of account types that the account selection option ;; accepts. -(define (options-generator account-types reverse-balance? do-intervals? depth-based?) +(define (options-generator account-types do-intervals? depth-based?) (let* ((options (gnc:new-options)) (add-option (lambda (new-option) (gnc:register-option options new-option)))) - (add-option - (gnc:make-internal-option "__report" "reverse-balance?" reverse-balance?)) - (if do-intervals? (gnc:options-add-date-interval! options gnc:pagename-general @@ -305,7 +302,7 @@ balance at a given time")) ;; account-types to work on and whether this report works on ;; intervals as arguments. (define (piechart-renderer report-obj reportname report-guid - account-types do-intervals? depth-based? + account-types do-intervals? depth-based? reverse-balance? display-name sort-comparator get-data) ;; This is a helper function for looking up option values. @@ -350,7 +347,6 @@ balance at a given time")) (height (get-option gnc:pagename-display optname-plot-height)) (width (get-option gnc:pagename-display optname-plot-width)) (sort-method (get-option gnc:pagename-display optname-sort-method)) - (reverse-balance? (get-option "__report" "reverse-balance?")) (document (gnc:make-html-document)) (chart (gnc:make-html-chart)) @@ -451,7 +447,7 @@ balance at a given time")) (define (fix-signs combined) (map (lambda (pair) - (if (reverse-balance? (cadr pair)) + (if reverse-balance? (cons (- (car pair)) (cdr pair)) pair)) combined)) @@ -595,8 +591,8 @@ balance at a given time")) document))) (define (build-report! - name acct-types income-expense? depth-based? menuname menutip - reverse-balance? uuid) + name acct-types income-expense? depth-based? reverse-balance? + menuname menutip uuid) (gnc:define-report 'version 1 'name name @@ -607,12 +603,12 @@ balance at a given time")) 'menu-name menuname 'menu-tip menutip 'options-generator (lambda () (options-generator acct-types - reverse-balance? income-expense? depth-based?)) 'renderer (lambda (report-obj) (piechart-renderer report-obj name uuid acct-types income-expense? depth-based? + reverse-balance? (if depth-based? display-name-accounts display-name-security) @@ -626,17 +622,15 @@ balance at a given time")) (build-report! reportname-income (list ACCT-TYPE-INCOME) - #t #t + #t #t #t menuname-income menutip-income - (lambda (x) #t) "e1bd09b8a1dd49dd85760db9d82b045c") (build-report! reportname-expense (list ACCT-TYPE-EXPENSE) - #t #t + #t #t #f menuname-expense menutip-expense - (lambda (x) #f) "9bf1892805cb4336be6320fe48ce5446") (build-report! @@ -645,9 +639,8 @@ balance at a given time")) ACCT-TYPE-SAVINGS ACCT-TYPE-MONEYMRKT ACCT-TYPE-RECEIVABLE ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY) - #f #t + #f #t #f menuname-assets menutip-assets - (lambda (x) #f) "5c7fd8a1fe9a4cd38884ff54214aa88a") (build-report! @@ -656,16 +649,14 @@ balance at a given time")) ACCT-TYPE-SAVINGS ACCT-TYPE-MONEYMRKT ACCT-TYPE-RECEIVABLE ACCT-TYPE-STOCK ACCT-TYPE-MUTUAL ACCT-TYPE-CURRENCY) - #f #f + #f #f #f menuname-securities menutip-securities - (lambda (x) #f) "e9418ff64f2c11e5b61d1c7508d793ed") (build-report! reportname-liabilities (list ACCT-TYPE-LIABILITY ACCT-TYPE-PAYABLE ACCT-TYPE-CREDIT ACCT-TYPE-CREDITLINE) - #f #t + #f #t #t menuname-liabilities menutip-liabilities - (lambda (x) #t) "3fe6dce77da24c66bdc8f8efdea7f9ac") From 4fe83c3b32884ab82dcccd24a221246316c817cb Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 21 Mar 2022 14:08:06 -0700 Subject: [PATCH 298/298] [C++options] Remove GncOptionValue. No longer needed. --- libgnucash/app-utils/gnc-option-impl.cpp | 32 ------------------------ libgnucash/app-utils/gnc-option-impl.hpp | 31 +++-------------------- libgnucash/app-utils/gnc-option.cpp | 16 ------------ libgnucash/app-utils/gnc-option.hpp | 9 ------- libgnucash/app-utils/gnc-optiondb.cpp | 26 ------------------- libgnucash/app-utils/gnc-optiondb.h | 25 ------------------ libgnucash/app-utils/gnc-optiondb.hpp | 29 --------------------- libgnucash/app-utils/gnc-optiondb.i | 7 ------ libgnucash/app-utils/options.scm | 7 +++++- 9 files changed, 10 insertions(+), 172 deletions(-) diff --git a/libgnucash/app-utils/gnc-option-impl.cpp b/libgnucash/app-utils/gnc-option-impl.cpp index 7c4e795fc5..5a6f0e9c74 100644 --- a/libgnucash/app-utils/gnc-option-impl.cpp +++ b/libgnucash/app-utils/gnc-option-impl.cpp @@ -725,35 +725,6 @@ GncOptionValue::deserialize(const std::string& str) noexcept return true; } -template <> void -GncOptionValue::set_value(SCM new_value) -{ - if (m_value) - scm_gc_unprotect_object(m_value); - m_value = new_value; - scm_gc_protect_object(m_value); -} - -template <> void -GncOptionValue::set_default_value(SCM new_value) -{ - if (m_value) - scm_gc_unprotect_object(m_value); - if (m_default_value) - scm_gc_unprotect_object(m_default_value); - m_value = m_default_value = new_value; - scm_gc_protect_object(m_value); - scm_gc_protect_object(m_default_value); -} - -template <> void -GncOptionValue::reset_default_value() -{ - if (m_value) - scm_gc_unprotect_object(m_value); - m_value = m_default_value; - scm_gc_protect_object(m_value); -} std::string GncOptionAccountListValue::serialize() const noexcept { @@ -947,7 +918,6 @@ 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); @@ -999,7 +969,6 @@ 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; @@ -1012,7 +981,6 @@ template bool GncOptionValue::deserialize(const std::string&) noexc 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 index 6bd82dc546..d126e3ea7a 100644 --- a/libgnucash/app-utils/gnc-option-impl.hpp +++ b/libgnucash/app-utils/gnc-option-impl.hpp @@ -93,38 +93,16 @@ public: 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} { - if constexpr(std::is_same_v, SCM>) { - if (value) { - scm_gc_protect_object(m_value); - scm_gc_protect_object(m_default_value); - } - } - } + 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()} - { - if constexpr(std::is_same_v, SCM>) { - if (m_value) - scm_gc_protect_object(m_value); - if (m_default_value) - scm_gc_protect_object(m_default_value); - } - } + m_default_value{from.get_default_value()}{} GncOptionValue(GncOptionValue&&) = default; GncOptionValue& operator=(const GncOptionValue&) = default; GncOptionValue& operator=(GncOptionValue&&) = default; - ~GncOptionValue() { - if constexpr(std::is_same_v, SCM>) { - if (m_value) - scm_gc_unprotect_object(m_value); - if (m_default_value) - scm_gc_unprotect_object(m_default_value); - } - } + ~GncOptionValue() = default; ValueType get_value() const { return m_value; } ValueType get_default_value() const { return m_default_value; } void set_value(ValueType new_value); @@ -297,8 +275,7 @@ template>(std::istream& iss, OptType& opt) { std::decay_t value; - if constexpr (std::is_same_v, SCM> || - std::is_same_v, const _gncOwner*> || + if constexpr (std::is_same_v, const _gncOwner*> || std::is_same_v, const _QofQuery*>) return iss; else diff --git a/libgnucash/app-utils/gnc-option.cpp b/libgnucash/app-utils/gnc-option.cpp index e69bc0c021..c113a6db6f 100644 --- a/libgnucash/app-utils/gnc-option.cpp +++ b/libgnucash/app-utils/gnc-option.cpp @@ -433,16 +433,6 @@ GncOption::in_stream(std::istream& iss) }, *m_option); } -GncOption* -gnc_make_SCM_option(const char* section, const char* name, - const char* key, const char* doc_string, - SCM value, GncOptionUIType ui_type) -{ - return new GncOption(section, name, key, doc_string, - reinterpret_cast(value), ui_type); -} - - /* We must instantiate all of the templates we need here because we don't expose * the template implementation in the public header. */ @@ -460,8 +450,6 @@ 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*, SCM, GncOptionUIType); template GncOption::GncOption(const char*, const char*, const char*, const char*, const QofQuery*, GncOptionUIType); template GncOption::GncOption(const char*, const char*, const char*, @@ -480,7 +468,6 @@ 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 SCM GncOption::get_value() const; template GncOptionReportPlacementVec GncOption::get_value() const; template bool GncOption::get_default_value() const; @@ -495,7 +482,6 @@ 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 SCM GncOption::get_default_value() const; template GncOptionReportPlacementVec GncOption::get_default_value() const; template void GncOption::set_value(bool); @@ -512,7 +498,6 @@ 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(SCM); template void GncOption::set_value(GncOptionReportPlacementVec); template void GncOption::set_default_value(bool); @@ -528,7 +513,6 @@ 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(SCM); template void GncOption::set_default_value(GncOptionReportPlacementVec); template void GncOption::get_limits(double&, double&, double&) const noexcept; diff --git a/libgnucash/app-utils/gnc-option.hpp b/libgnucash/app-utils/gnc-option.hpp index e46fa92164..9cb97acff3 100644 --- a/libgnucash/app-utils/gnc-option.hpp +++ b/libgnucash/app-utils/gnc-option.hpp @@ -104,7 +104,6 @@ using GncOptionVariant = std::variant, GncOptionQofInstanceValue, GncOptionValue, GncOptionValue, - GncOptionValue, GncOptionValue, GncOptionAccountListValue, GncOptionAccountSelValue, @@ -256,14 +255,6 @@ gnc_make_option(const char* section, const char* name, return new GncOption(section, name, key, doc_string, value, ui_type); } -/** - * Free function wrapping GncOption's constructor using an SCM value. - * To work around SWIG_Guile's typedef of SCM to unsigned long - */ -GncOption* gnc_make_SCM_option(const char* section, const char* name, - const char* key, const char* doc_string, - SCM value, GncOptionUIType ui_type); - #endif //GNC_OPTION_HPP_ /** @} diff --git a/libgnucash/app-utils/gnc-optiondb.cpp b/libgnucash/app-utils/gnc-optiondb.cpp index 9d06fb951a..b36a1ba605 100644 --- a/libgnucash/app-utils/gnc-optiondb.cpp +++ b/libgnucash/app-utils/gnc-optiondb.cpp @@ -831,16 +831,6 @@ gnc_register_owner_option(GncOptionDB* db, const char* section, db->register_option(section, std::move(option)); } -void -gnc_register_internal_option(GncOptionDB* db, const char* section, - const char* name, const char* key, - const char* doc_string, SCM 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, @@ -1249,22 +1239,6 @@ gnc_option_db_lookup_qofinstance_value(GncOptionDB* odb, const char* section, return option->get_value(); } -SCM -gnc_option_db_lookup_scm_value(GncOptionDB* odb, const char* section, - const char* name) -{ - auto option{odb->find_option(section, name)}; - return option->get_value(); -} - -void -gnc_option_db_set_scm_value(GncOptionDB* odb, const char* section, - const char* name, SCM value) -{ - auto option{odb->find_option(section, name)}; - option->set_value(value); -} - // Force creation of templates template void gnc_register_number_range_option(GncOptionDBPtr& db, const char* section, const char* name, diff --git a/libgnucash/app-utils/gnc-optiondb.h b/libgnucash/app-utils/gnc-optiondb.h index 8078459d5d..88d12300d5 100644 --- a/libgnucash/app-utils/gnc-optiondb.h +++ b/libgnucash/app-utils/gnc-optiondb.h @@ -166,31 +166,6 @@ const QofInstance* gnc_option_db_lookup_qofinstance_value(GncOptionDB*, const char*, const char*); -/** - * Retrieve the GList* 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 GList* of the value or nullptr if the option isn't found - * or if its value isn't a string. - */ -SCM gnc_option_db_lookup_scm_value(GncOptionDB*, const char*, const char*); - -/** - * Set the GList* 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_scm_value(GncOptionDB*, const char*, - const char*, SCM); - #ifdef __cplusplus } #endif diff --git a/libgnucash/app-utils/gnc-optiondb.hpp b/libgnucash/app-utils/gnc-optiondb.hpp index d92f145053..25920f4c2a 100644 --- a/libgnucash/app-utils/gnc-optiondb.hpp +++ b/libgnucash/app-utils/gnc-optiondb.hpp @@ -571,35 +571,6 @@ inline void gnc_register_color_option(GncOptionDBPtr& db, const char* section, gnc_register_color_option(db.get(), section, name, key, doc_string, value); } -/** - * Create a new internal string option and register it in the options database. - * - * Internal options are used for passing state data in some reports. As the name - * suggests they do not create UI elements in options dialogs. - * @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_internal_option(GncOptionDB* db, const char* section, - const char* name, const char* key, - const char* doc_string, SCM value); - - -/** - * As above but takes a const GncOptionDBPtr& (const std::unique_ptr&) for calling from C++. - */ -inline void gnc_register_internal_option(GncOptionDBPtr& db, - const char* section, const char* name, - const char* key, - const char* doc_string, - SCM value) -{ - gnc_register_internal_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, diff --git a/libgnucash/app-utils/gnc-optiondb.i b/libgnucash/app-utils/gnc-optiondb.i index bff5ee3d41..8f696e0f08 100644 --- a/libgnucash/app-utils/gnc-optiondb.i +++ b/libgnucash/app-utils/gnc-optiondb.i @@ -1243,13 +1243,6 @@ inline SCM return_scm_value(ValueType 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); - } if constexpr (is_same_decayed_v>) { diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index 702fca981d..51086d6b27 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -270,8 +270,13 @@ (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 - (gnc-make-SCM-option section name key desc default type))))) + (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