mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-20 11:48:30 -06:00
Implement GncOptionMultichoiceValue
Replaces GncOptionValue<GncMultiChoiceOptionChoices> because having the vector as the value obviously wouldn't work and besides it needs additional functions.
This commit is contained in:
parent
8eedcb6d6d
commit
435667e8fe
@ -125,9 +125,6 @@ struct OptionClassifier
|
||||
std::string m_doc_string;
|
||||
};
|
||||
|
||||
using GncMultiChoiceOptionEntry = std::pair<std::string, std::string>;
|
||||
using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
|
||||
|
||||
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<const std::string,
|
||||
const std::string,
|
||||
const std::string>;
|
||||
using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
|
||||
|
||||
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<std::size_t>::max();
|
||||
|
||||
}
|
||||
void set_value(const std::string& value)
|
||||
{
|
||||
auto index = find_key(value);
|
||||
if (index != std::numeric_limits<std::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));
|
||||
}
|
||||
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<std::size_t>::max();
|
||||
|
||||
}
|
||||
std::size_t m_value;
|
||||
std::size_t m_default_value;
|
||||
GncMultiChoiceOptionChoices m_choices;
|
||||
};
|
||||
|
||||
using GncOptionVariant = std::variant<GncOptionValue<std::string>,
|
||||
GncOptionValue<bool>,
|
||||
GncOptionValue<int64_t>,
|
||||
GncOptionValue<QofInstance*>,
|
||||
GncOptionValue<QofQuery*>,
|
||||
GncOptionValue<std::vector<GncGUID>>,
|
||||
GncOptionValue<GncMultiChoiceOptionChoices>,
|
||||
GncOptionMultichoiceValue,
|
||||
GncOptionRangeValue<int>,
|
||||
GncOptionRangeValue<GncNumeric>,
|
||||
GncOptionValidatedValue<QofInstance*>>;
|
||||
|
||||
class GncOption
|
||||
{
|
||||
public:
|
||||
@ -316,9 +406,8 @@ public:
|
||||
template <typename ValueType> ValueType get_value() const
|
||||
{
|
||||
return std::visit([](const auto& option)->ValueType {
|
||||
if constexpr (std::is_same_v<decltype(option.get_value()), ValueType>)
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
|
||||
return option.get_value();
|
||||
else
|
||||
return ValueType {};
|
||||
}, m_option);
|
||||
}
|
||||
@ -326,9 +415,8 @@ public:
|
||||
template <typename ValueType> ValueType get_default_value() const
|
||||
{
|
||||
return std::visit([](const auto& option)->ValueType {
|
||||
if constexpr (std::is_same_v<decltype(option.get_value()), ValueType>)
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
|
||||
return option.get_default_value();
|
||||
else
|
||||
return ValueType {};
|
||||
}, m_option);
|
||||
|
||||
@ -337,7 +425,7 @@ public:
|
||||
template <typename ValueType> void set_value(ValueType value)
|
||||
{
|
||||
std::visit([value](auto& option) {
|
||||
if constexpr (std::is_same_v<decltype(option.get_value()), ValueType>)
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
|
||||
option.set_value(value);
|
||||
}, m_option);
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ extern "C" SCM scm_init_sw_gnc_optiondb_module(void);
|
||||
%}
|
||||
|
||||
%include <std_string.i>
|
||||
%import <base-typemaps.i>
|
||||
|
||||
|
||||
%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<int>;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
TEST(GncOption, test_string_scm_functions)
|
||||
{
|
||||
|
@ -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<std::size_t>::max(),
|
||||
m_option.permissible_value_index("xyzzy"));
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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")))
|
||||
|
Loading…
Reference in New Issue
Block a user