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.
This commit is contained in:
John Ralls 2021-02-25 10:34:43 -08:00
parent c62b526ba0
commit 534a7c2893
7 changed files with 113 additions and 44 deletions

View File

@ -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

View File

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

View File

@ -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));
}
/**

View File

@ -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<int>& 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<int>& 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<std::decay_t<decltype(option)>,
GncOptionMultichoiceValue> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionRangeValue<int>>)
return get_scm_default_value(option);
auto value{option.get_default_value()};
if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
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)
{

View File

@ -949,18 +949,20 @@ TEST_F(GncOptionAccountTest, test_account_list_from_scheme)
EXPECT_EQ(acclist[1], sel_option.get_value<GncOptionAccountList>()[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;
};

View File

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

View File

@ -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)