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())); +}