Implement multichoice selection options.

This commit is contained in:
John Ralls 2021-05-03 09:46:57 -07:00
parent 7239eb809a
commit 52ef53a405
2 changed files with 80 additions and 46 deletions

View File

@ -283,7 +283,8 @@ template<class OptType,
std::istream& operator>>(std::istream& iss, OptType& opt) std::istream& operator>>(std::istream& iss, OptType& opt)
{ {
std::decay_t<decltype(opt.get_value())> value; std::decay_t<decltype(opt.get_value())> value;
if constexpr (std::is_same_v<std::decay_t<decltype(opt.get_value())>, SCM>) if constexpr (std::is_same_v<std::decay_t<decltype(opt.get_value())>, SCM> ||
std::is_same_v<std::decay_t<decltype(opt.get_value())>, const _QofQuery*>)
return iss; return iss;
else else
{ {
@ -809,10 +810,10 @@ inline std::ostream&
gnc_option_to_scheme(std::ostream& oss, const OptType& opt) gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
{ {
auto indexes{opt.get_multiple()}; auto indexes{opt.get_multiple()};
if (indexes.m_vec.size() > 1) if (indexes.size() > 1)
oss << "'("; oss << "'(";
bool first = true; bool first = true;
for (auto index : indexes.m_vec) for (auto index : indexes)
{ {
if (first) if (first)
first = false; first = false;

View File

@ -376,6 +376,21 @@ gnc_option_test_book_destroy(QofBook* book)
$1 = &period_set; $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) %typemap(in) GncMultichoiceOptionChoices&& (GncMultichoiceOptionChoices choices)
{ {
using KeyType = GncOptionMultichoiceKeyType; 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 scm_to_int64(scm_is_pair(date) ? scm_cdr(date) : date);
return gnc_relative_date_to_time64(scm_relative_date_get_period(date)); return gnc_relative_date_to_time64(scm_relative_date_get_period(date));
} }
%} %}
%ignore GncOptionMultichoiceKeyType; %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 %{
inline SCM scm_from_multichoices (const GncMultichoiceOptionIndexVec& indexes, inline GncMultichoiceOptionIndexVec
const GncOptionMultichoiceValue& option) scm_to_multichoices(const SCM new_value,
const GncOptionMultichoiceValue& option)
{
static const auto size_t_max = std::numeric_limits<std::size_t>::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; using KeyType = GncOptionMultichoiceKeyType;
auto scm_value = [](const char* value, KeyType keytype) -> SCM { auto scm_value = [](const char* value, KeyType keytype) -> SCM {
@ -651,20 +698,22 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
return SCM_BOOL_F; 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]), return scm_value(option.permissible_value(indexes[0]),
option.get_keytype(indexes[0])); option.get_keytype(indexes[0]));
auto values{scm_list_1(SCM_UNDEFINED)}; auto values{SCM_BOOL_F};
for(auto index : indexes) for(auto index : indexes)
{ {
auto val{scm_list_1(scm_value(option.permissible_value(index), auto val{scm_list_1(scm_value(option.permissible_value(index),
option.get_keytype(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); return scm_reverse(values);
} }
SCM get_scm_value(const GncOptionMultichoiceValue& option) SCM get_scm_value(const GncOptionMultichoiceValue& option)
{ {
@ -688,14 +737,14 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
SCM get_scm_value(const GncOptionRangeValue<int>& option) SCM get_scm_value(const GncOptionRangeValue<int>& option)
{ {
auto val{option.get_value()}; 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)); return scm_cons(desig, scm_from_int(val));
} }
SCM get_scm_default_value(const GncOptionRangeValue<int>& option) SCM get_scm_default_value(const GncOptionRangeValue<int>& option)
{ {
auto val{option.get_default_value()}; 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)); 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)); option.set_value(scm_relative_date_get_period(new_value));
return; return;
} }
if constexpr (std::is_same_v<std::decay_t<decltype(option)>, if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
GncOptionMultichoiceValue>) GncOptionMultichoiceValue>)
{ {
if (scm_is_integer(new_value)) option.set_multiple(scm_to_multichoices(new_value, option));
{
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; return;
} }
if constexpr (std::is_same_v<std::decay_t<decltype(option)>, if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
GncOptionRangeValue<int>>) GncOptionRangeValue<int>>)
{ {
@ -790,6 +830,7 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
option.set_value(scm_to_int(new_value)); option.set_value(scm_to_int(new_value));
return; return;
} }
auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)}; //Can't inline, set_value takes arg by reference. auto value{scm_to_value<std::decay_t<decltype(option.get_value())>>(new_value)}; //Can't inline, set_value takes arg by reference.
option.set_value(value); option.set_value(value);
}, swig_get_option($self)); }, swig_get_option($self));
@ -812,18 +853,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
if constexpr (std::is_same_v<std::decay_t<decltype(option)>, if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
GncOptionMultichoiceValue>) GncOptionMultichoiceValue>)
{ {
if (scm_is_integer(new_value)) option.set_default_multiple(scm_to_multichoices(new_value,
{ option));
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; return;
} }
if constexpr (std::is_same_v<std::decay_t<decltype(option)>, if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
@ -984,7 +1015,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
defval = (choices.empty() ? std::string{"None"} : defval = (choices.empty() ? std::string{"None"} :
std::get<0>(choices.at(0))); std::get<0>(choices.at(0)));
return new GncOption{GncOptionMultichoiceValue{section, name, key, 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) catch (const std::exception& err)
{ {
@ -995,12 +1027,13 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
GncOption* gnc_make_list_option(const char* section, GncOption* gnc_make_list_option(const char* section,
const char* name, const char* key, const char* name, const char* key,
const char* doc_string, const char* value, const char* doc_string,
GncMultichoiceOptionIndexVec indexes,
GncMultichoiceOptionChoices&& list) GncMultichoiceOptionChoices&& list)
{ {
try { try {
return new GncOption{GncOptionMultichoiceValue{section, name, key, return new GncOption{GncOptionMultichoiceValue{section, name, key,
doc_string, value, std::move(list), doc_string, std::move(indexes), std::move(list),
GncOptionUIType::LIST}}; GncOptionUIType::LIST}};
} }
catch (const std::exception& err) catch (const std::exception& err)