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.
This commit is contained in:
John Ralls 2020-01-27 13:40:39 -08:00
parent 20b3ef8a89
commit aa246d3096
13 changed files with 1761 additions and 1446 deletions

View File

@ -0,0 +1,293 @@
/********************************************************************\
* gnc-option-impl.cpp -- Application options system *
* Copyright (C) 2019 John Ralls <jralls@ceridwen.us> *
* *
* 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 <gnc-datetime.hpp>
#include <guid.hpp>
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<time64>(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<time64>(now_t);
struct tm now{static_cast<tm>(now_t)};
struct tm period{static_cast<tm>(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<time64>(GncDateTime(now));
}
static const char* date_type_str[] {"absolute", "relative"};
static const std::array<const char*, 15> 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<int>(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<RelativeDatePeriod>(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<GncGUID>(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();
}

View File

@ -0,0 +1,794 @@
/********************************************************************\
* gnc-option-impl.hpp -- Application options system *
* Copyright (C) 2019 John Ralls <jralls@ceridwen.us> *
* *
* 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 <config.h>
#include <qof.h>
#include <Account.h>
#include <gnc-budget.h>
#include <gnc-commodity.h>
}
#include <gnc-datetime.hpp>
#include <libguile.h>
#include <string>
#include <utility>
#include <vector>
#include <exception>
#include <functional>
#include <variant>
#include <iostream>
#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 <typename ValueType>
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<std::string> get_option_strings(); //15
virtual set_changed_callback(std::function<void(void*)>); //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<void(void*)> m_changed_callback; //Part of the make-option closure
std::function<void(void*)>m_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<std::size_t>::max();
#endif
template <typename ValueType>
class GncOptionValue : public OptionClassifier, public OptionUIItem
{
public:
GncOptionValue<ValueType>(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<ValueType>(const GncOptionValue<ValueType>&) = default;
GncOptionValue<ValueType>(GncOptionValue<ValueType>&&) = default;
GncOptionValue<ValueType>& operator=(const GncOptionValue<ValueType>&) = default;
GncOptionValue<ValueType>& operator=(GncOptionValue<ValueType>&&) = 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 <typename ValueType>
class GncOptionValidatedValue : public OptionClassifier, public OptionUIItem
{
public:
GncOptionValidatedValue<ValueType>() = delete;
GncOptionValidatedValue<ValueType>(const char* section, const char* name,
const char* key, const char* doc_string,
ValueType value,
std::function<bool(ValueType)>validator,
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<ValueType>(const char* section, const char* name,
const char* key, const char* doc_string,
ValueType value,
std::function<bool(ValueType)>validator,
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<ValueType>(const GncOptionValidatedValue<ValueType>&) = default;
GncOptionValidatedValue<ValueType>(GncOptionValidatedValue<ValueType>&&) = default;
GncOptionValidatedValue<ValueType>& operator=(const GncOptionValidatedValue<ValueType>&) = default;
GncOptionValidatedValue<ValueType>& operator=(GncOptionValidatedValue<ValueType>&&) = 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<bool(ValueType)> 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<class OptionValueClass,
typename std::enable_if_t<std::is_base_of_v<OptionClassifier,
std::decay_t<OptionValueClass>> &&
!(std::is_same_v<std::decay_t<OptionValueClass>,
GncOptionValue<const QofInstance*>> ||
std::is_same_v<std::decay_t<OptionValueClass>,
GncOptionValidatedValue<const QofInstance*>>), int> = 0>
std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
{
oss << opt.get_value();
return oss;
}
template<> inline std::ostream&
operator<< <GncOptionValue<bool>>(std::ostream& oss,
const GncOptionValue<bool>& opt)
{
oss << (opt.get_value() ? "#t" : "#f");
return oss;
}
template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*> >, 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<class OptionValueClass,
typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>> &&
!(std::is_same_v<std::decay_t<OptionValueClass>,
GncOptionValue<const QofInstance*>> ||
std::is_same_v<std::decay_t<OptionValueClass>,
GncOptionValidatedValue<const QofInstance*>>), int> = 0>
std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
{
std::decay_t<decltype(opt.get_value())> value;
iss >> value;
opt.set_value(value);
return iss;
}
template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*> >, 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>> <GncOptionValue<bool>>(std::istream& iss,
GncOptionValue<bool>& opt)
{
std::string instr;
iss >> instr;
opt.set_value(instr == "#t" ? true : false);
return iss;
}
template<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*>>, 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<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, GncOptionValidatedValue<const QofInstance*>> || std::is_same_v<std::decay_t<OptType>, GncOptionValue<const QofInstance*>>, 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 <typename ValueType>
class GncOptionRangeValue :
public OptionClassifier, public OptionUIItem
{
public:
GncOptionRangeValue<ValueType>(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<ValueType>(const GncOptionRangeValue<ValueType>&) = default;
GncOptionRangeValue<ValueType>(GncOptionRangeValue<ValueType>&&) = default;
GncOptionRangeValue<ValueType>& operator=(const GncOptionRangeValue<ValueType>&) = default;
GncOptionRangeValue<ValueType>& operator=(GncOptionRangeValue<ValueType>&&) = 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<const std::string,
const std::string,
const std::string>;
using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
/** 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<const Account*>;
using GncOptionAccountTypeList = std::vector<GNCAccountType>;
/** 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<< <GncOptionAccountValue>(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>> <GncOptionAccountValue>(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<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, 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<class OptType, typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>, 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<time64>(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<< <GncOptionDateValue>(std::ostream& oss,
const GncOptionDateValue& opt)
{
return opt.out_stream(oss);
}
template<> inline std::istream&
operator>> <GncOptionDateValue>(std::istream& iss,
GncOptionDateValue& opt)
{
return opt.in_stream(iss);
}
#endif //GNC_OPTION_IMPL_HPP_

View File

@ -0,0 +1,55 @@
/********************************************************************\
* gnc-option-uitype.hpp -- UI Control Enum for GncOption *
* Copyright (C) 2020 John Ralls <jralls@ceridwen.us> *
* *
* 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__

View File

@ -1,6 +1,6 @@
/********************************************************************\
* gnc-option.cpp -- Application options system *
* Copyright (C) 2019 John Ralls <jralls@ceridwen.us> *
* Copyright (C) 2020 John Ralls <jralls@ceridwen.us> *
* *
* 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 <gnc-datetime.hpp>
#include <guid.hpp>
extern "C"
#include "gnc-option-impl.hpp"
#include "gnc-option-uitype.hpp"
template <typename ValueType>
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<GncOptionVariant>(GncOptionValue<ValueType> {
section, name, key, doc_string, value, ui_type})}
{
#include "gnc-accounting-period.h"
#include "gnc-ui-util.h"
}
template <typename ValueType> ValueType
GncOption::get_value() const
{
return std::visit([](const auto option)->ValueType {
if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
return option.get_value();
return ValueType {};
}, *m_option);
}
template <typename ValueType> ValueType
GncOption::get_default_value() const
{
return std::visit([](const auto option)->ValueType {
if constexpr (std::is_same_v<std::decay_t<decltype(option.get_value())>, std::decay_t<ValueType>>)
return option.get_default_value();
return ValueType {};
}, *m_option);
}
template <typename ValueType> void
GncOption::set_value(ValueType value)
{
std::visit([value](auto& option) {
if constexpr
(std::is_same_v<std::decay_t<decltype(option.get_value())>,
std::decay_t<ValueType>> ||
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionDateValue> &&
std::is_same_v<std::decay_t<ValueType>,
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<typename ValueType> 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<std::decay_t<decltype(option)>,
GncOptionMultichoiceValue> &&
std::is_same_v<std::decay_t<ValueType>,
std::string>) ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValidatedValue<ValueType>>)
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<time64>(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<time64>(now_t);
struct tm now{static_cast<tm>(now_t)};
struct tm period{static_cast<tm>(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<time64>(GncDateTime(now));
return std::visit([] (const auto& option) -> size_t {
if constexpr (std::is_same_v<std::decay_t<decltype(option)>,
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<const char*, 15> 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<std::decay_t<decltype(option)>,
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<std::decay_t<decltype(option)>,
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<std::decay_t<decltype(option)>,
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<std::decay_t<decltype(option)>,
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<int>(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<RelativeDatePeriod>(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<std::decay_t<decltype(option)>,
GncOptionAccountValue>)
gnc_option_to_scheme(oss, option);
else if constexpr
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionMultichoiceValue>)
oss << "'" << option;
else if constexpr
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValue<const QofInstance*>> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValidatedValue<const QofInstance*>>)
gnc_option_to_scheme(oss, option);
else if constexpr
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionDateValue>)
oss << "'(" << option << ")";
else if constexpr
(std::is_same_v<std::decay_t<decltype(option.get_value())>,
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<GncGUID>(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<std::decay_t<decltype(option)>,
GncOptionAccountValue>)
gnc_option_from_scheme(iss, option);
else if constexpr
((std::is_same_v<std::decay_t<decltype(option)>,
GncOptionMultichoiceValue>))
{
iss.ignore(1, '\'');
iss >> option;
}
else if constexpr
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValue<const QofInstance*>> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValidatedValue<const QofInstance*>>)
gnc_option_from_scheme(iss, option);
else if constexpr
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionDateValue>)
{
iss.ignore(2, '(');
iss >> option;
//operator >> clears the trailing ')'
}
else if constexpr
(std::is_same_v<std::decay_t<decltype(option.get_value())>,
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<const Account*>;
template class GncOptionValidatedValue<const QofInstance*>;
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<bool>() const;
template int GncOption::get_value<int>() const;
template int64_t GncOption::get_value<int64_t>() const;
template double GncOption::get_value<double>() const;
template const char* GncOption::get_value<const char*>() const;
template std::string GncOption::get_value<std::string>() const;
template const QofInstance* GncOption::get_value<const QofInstance*>() const;
template RelativeDatePeriod GncOption::get_value<RelativeDatePeriod>() const;
template GncOptionAccountList GncOption::get_value<GncOptionAccountList>() const;
template bool GncOption::get_default_value<bool>() const;
template int GncOption::get_default_value<int>() const;
template int64_t GncOption::get_default_value<int64_t>() const;
template double GncOption::get_default_value<double>() const;
template const char* GncOption::get_default_value<const char*>() const;
template std::string GncOption::get_default_value<std::string>() const;
template const QofInstance* GncOption::get_default_value<const QofInstance*>() const;
template RelativeDatePeriod GncOption::get_default_value<RelativeDatePeriod>() 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;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,127 @@
/********************************************************************\
* gnc-optiondb.hpp -- Collection of GncOption objects *
* Copyright (C) 2019 John Ralls <jralls@ceridwen.us> *
* *
* 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 <functional>
#include <exception>
#include <optional>
#include <iostream>
extern "C"
{
#include <config.h>
#include <qof.h>
#include <gncInvoice.h>
#include <gncOwner.h>
#include <gncTaxTable.h>
}
using GncOptionVec = std::vector<GncOption>;
using GncOptionSection = std::pair<std::string, GncOptionVec>;
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<void(GncOption&)> func);
void set_option_from_ui(const char* section, const char* name,
std::function<void(GncOption&)> func);
std::string lookup_string_option(const char* section,
const char* name);
template <typename ValueType>
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<std::reference_wrapper<GncOptionSection>> find_section(const std::string& section);
std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
return static_cast<const GncOptionDB&>(*this).find_option(section, name);
}
std::optional<std::reference_wrapper<GncOption>> 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<std::reference_wrapper<GncOptionSection>> m_default_section;
std::vector<GncOptionSection> m_sections;
bool m_dirty = false;
std::function<GncOptionUIItem*()> m_get_ui_value;
std::function<void(GncOptionUIItem*)> 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_

View File

@ -21,10 +21,12 @@
* *
\********************************************************************/
#include "gnc-optiondb.hpp"
#include <string>
#include <limits>
#include <sstream>
#include <kvp-value.hpp>
#include "gnc-optiondb.hpp"
#include "gnc-optiondb-impl.hpp"
auto constexpr stream_max = std::numeric_limits<std::streamsize>::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(),
[&section](GncOptionSection sect) -> bool
[&section](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<GncOptionDB*>(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<QofInstance*>())};
const QofInstance* inst{QOF_INSTANCE(option.get_value<const QofInstance*>())};
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<GncGUID*>()};
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<QofInstance*>{
section, name, key, doc_string, QOF_INSTANCE(value),
[](QofInstance* new_value) -> bool
GncOption option{GncOptionValidatedValue<const QofInstance*>{
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));

View File

@ -24,102 +24,33 @@
#ifndef GNC_OPTIONDB_HPP_
#define GNC_OPTIONDB_HPP_
#include "gnc-option.hpp"
#include <string>
#include <functional>
#include <exception>
#include <optional>
#include <iostream>
extern "C"
{
#include <config.h>
#include <Account.h>
#include <gnc-budget.h>
#include <gnc-commodity.h>
#include <gncInvoice.h>
#include <gncOwner.h>
#include <gncTaxTable.h>
}
#include "gnc-option.hpp"
#include <gnc-datetime.hpp>
class GncOptionDB;
using GncOptionAccountList = std::vector<const Account*>;
using GncOptionVec = std::vector<GncOption>;
using GncOptionSection = std::pair<std::string, GncOptionVec>;
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<void(GncOption&)> func);
void set_option_from_ui(const char* section, const char* name,
std::function<void(GncOption&)> func);
std::string lookup_string_option(const char* section,
const char* name);
template <typename ValueType>
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<std::reference_wrapper<GncOptionSection>> find_section(const std::string& section);
std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
return static_cast<const GncOptionDB&>(*this).find_option(section, name);
}
std::optional<std::reference_wrapper<GncOption>> 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<std::reference_wrapper<GncOptionSection>> m_default_section;
std::vector<GncOptionSection> m_sections;
bool m_dirty = false;
std::function<GncOptionUIItem*()> m_get_ui_value;
std::function<void(GncOptionUIItem*)> 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<GNCAccountType>;
using GncMultiChoiceOptionEntry = std::tuple<const std::string,
const std::string,
const std::string>;
using GncMultiChoiceOptionChoices = std::vector<GncMultiChoiceOptionEntry>;
/**
* Extract a list of accounts in the book having one of the GNCAccountTypes in

View File

@ -46,6 +46,7 @@ extern "C"
#include <gnc-engine-guile.h>
}
#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<decltype(value)>(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<decltype(value)>(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<std::decay_t<decltype(option.get_value())>>(new_value));
}, $self->_get_option());
}, *($self->_get_option()));
}
};
@ -331,6 +334,8 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
};
%inline %{
using GncOptionDBPtr = std::unique_ptr<GncOptionDB>;
static SCM
gnc_option_value(const GncOptionDBPtr& optiondb, const char* section,
const char* name)

View File

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

View File

@ -23,9 +23,15 @@
#include <gtest/gtest.h>
#include <gnc-option.hpp>
#include <gnc-option-impl.hpp>
#include <guid.hpp>
extern "C"
{
#include <config.h>
#include <qof.h>
#include <Account.h>
#include <gnc-budget.h>
#include <gnc-commodity.h>
#include <gnc-date.h>
#include <time.h>
#include <gnc-ui-util.h>
@ -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<QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
GncOption option{GncOptionValue<const QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
iss >> option;
EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<QofInstance*>());
EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<const QofInstance*>());
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<QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
GncOption option{GncOptionValue<const QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
option.from_scheme(iss);
EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<QofInstance*>());
EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<const QofInstance*>());
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<QofInstance*>{
section, name, key, doc_string, QOF_INSTANCE(value),
[](QofInstance* new_value) -> bool
GncOption option{GncOptionValidatedValue<const QofInstance*>{
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<QofInstance*>()));
GNC_COMMODITY(option.get_value<const QofInstance*>()));
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<QofInstance*>()));
GNC_COMMODITY(option.get_value<const QofInstance*>()));
}
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<QofInstance*>());
EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<const QofInstance*>());
});
}
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<QofInstance*>());
EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
}
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<QofInstance*>());
EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<const QofInstance*>());
}
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<QofInstance*>());
EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
}
class GncUIItem

View File

@ -23,6 +23,7 @@
#include <gtest/gtest.h>
#include <gnc-optiondb.hpp>
#include <gnc-optiondb-impl.hpp>
#include <kvp-value.hpp>
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<GncOptionAccountList>().size());
EXPECT_EQ(4, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
EXPECT_EQ(acclist[3], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().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<GncOptionAccountList>().size());
EXPECT_EQ(1, m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().size());
EXPECT_EQ(accsel[0], m_db->find_option("foo", "bar")->get().get_value<GncOptionAccountList>().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<GncOptionAccountList>()[0]);
m_db->load_option_scheme(iss);
EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().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<time64>());
EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>()[1]);
EXPECT_EQ(2U, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
EXPECT_EQ(2, m_db->find_option("quux", "xyzzy")->get().get_value<GncOptionAccountList>().size());
}

View File

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