mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-29 20:24:25 -06:00
ee7ed89b68
With these changes the currency works but the commodity allows you to
set the commodity but will crash if you save config or leave report
open, this was tested on the 'Price scatter plot' report. Fixed with
John's commit a8e6a59
987 lines
32 KiB
C++
987 lines
32 KiB
C++
/********************************************************************\
|
|
* 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>
|
|
#include <cassert>
|
|
#include <sstream>
|
|
#include <numeric>
|
|
|
|
extern "C"
|
|
{
|
|
#include "gnc-accounting-period.h"
|
|
#include "gnc-ui-util.h"
|
|
}
|
|
|
|
static const QofLogModule log_module{"gnc.options"};
|
|
|
|
const std::string GncOptionMultichoiceValue::c_empty_string{""};
|
|
const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"};
|
|
|
|
using GncItem = std::pair<QofIdTypeConst, GncGUID>;
|
|
|
|
static GncItem
|
|
make_gnc_item(const QofInstance* inst)
|
|
{
|
|
if (!inst)
|
|
return std::make_pair<QofIdTypeConst, GncGUID>("", guid_new_return());
|
|
auto type{qof_collection_get_type(qof_instance_get_collection(inst))};
|
|
auto guid{qof_instance_get_guid(inst)};
|
|
return std::make_pair(std::move(type), std::move(*const_cast<GncGUID*>(guid)));
|
|
}
|
|
|
|
static const QofInstance*
|
|
qof_instance_from_gnc_item(const GncItem& item)
|
|
{
|
|
auto [type, guid] = item;
|
|
auto book{gnc_get_current_book()};
|
|
auto coll{qof_book_get_collection(book, type)};
|
|
return static_cast<QofInstance*>(qof_collection_lookup_entity(coll, &guid));
|
|
}
|
|
|
|
static bool
|
|
operator!=(const GncItem& left, const GncItem& right)
|
|
{
|
|
auto [ltype, lguid]{left};
|
|
auto [rtype, rguid]{right};
|
|
return strcmp(rtype, ltype) && !guid_equal(&rguid, &lguid);
|
|
}
|
|
|
|
GncOptionQofInstanceValue::GncOptionQofInstanceValue(
|
|
const char* section, const char* name,
|
|
const char* key, const char* doc_string,
|
|
const QofInstance* value, GncOptionUIType ui_type) :
|
|
OptionClassifier{section, name, key, doc_string},
|
|
m_ui_type(ui_type), m_value{},
|
|
m_default_value{} {
|
|
m_value = make_gnc_item(value);
|
|
m_default_value = make_gnc_item(value);
|
|
}
|
|
|
|
GncOptionQofInstanceValue::GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from) :
|
|
OptionClassifier{from.m_section, from.m_name, from.m_sort_tag,
|
|
from.m_doc_string},
|
|
m_ui_type(from.get_ui_type()), m_value{from.get_item()},
|
|
m_default_value{from.get_default_item()}
|
|
{
|
|
}
|
|
void
|
|
GncOptionQofInstanceValue::set_value(const QofInstance* new_value)
|
|
{
|
|
m_value = make_gnc_item(new_value);
|
|
}
|
|
|
|
void
|
|
GncOptionQofInstanceValue::set_default_value(const QofInstance *new_value)
|
|
{
|
|
m_value = m_default_value = make_gnc_item(new_value);
|
|
|
|
}
|
|
|
|
const QofInstance*
|
|
GncOptionQofInstanceValue::get_value() const
|
|
{
|
|
return qof_instance_from_gnc_item(m_value);
|
|
}
|
|
|
|
const QofInstance*
|
|
GncOptionQofInstanceValue::get_default_value() const
|
|
{
|
|
return qof_instance_from_gnc_item(m_default_value);
|
|
}
|
|
|
|
void
|
|
GncOptionQofInstanceValue::reset_default_value()
|
|
{
|
|
m_value = m_default_value;
|
|
}
|
|
|
|
bool
|
|
GncOptionQofInstanceValue::is_changed() const noexcept
|
|
{
|
|
return m_value != m_default_value;
|
|
}
|
|
|
|
bool
|
|
GncOptionQofInstanceValue::deserialize(const std::string& str) noexcept
|
|
{
|
|
QofInstance* inst{};
|
|
// Commodities are often serialized as Namespace::Mnemonic or just Mnemonic
|
|
try {
|
|
auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
|
|
inst = qof_instance_from_guid(&guid, m_ui_type);
|
|
if (inst)
|
|
{
|
|
m_value = make_gnc_item(inst);
|
|
return true;
|
|
}
|
|
}
|
|
catch (const gnc::guid_syntax_exception& err)
|
|
{
|
|
PWARN("Failed to convert %s to a GUID", str.c_str());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string
|
|
GncOptionQofInstanceValue::serialize() const noexcept
|
|
{
|
|
auto inst{get_value()};
|
|
std::string retval;
|
|
if (GNC_IS_COMMODITY(inst))
|
|
{
|
|
auto commodity{GNC_COMMODITY(inst)};
|
|
if (!gnc_commodity_is_currency(commodity))
|
|
{
|
|
auto name_space{gnc_commodity_get_namespace(GNC_COMMODITY(inst))};
|
|
if (name_space && *name_space != '\0')
|
|
{
|
|
retval = name_space;
|
|
retval += ":";
|
|
}
|
|
}
|
|
retval += gnc_commodity_get_mnemonic(GNC_COMMODITY(inst));
|
|
return retval;
|
|
}
|
|
else
|
|
{
|
|
gnc::GUID guid{m_value.second};
|
|
retval = guid.to_string();
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static gnc_commodity*
|
|
gnc_commodity_from_namespace_and_mnemonic(std::string_view name_space,
|
|
std::string_view mnemonic)
|
|
{
|
|
auto book{gnc_get_current_book()};
|
|
auto table = gnc_commodity_table_get_table(book);
|
|
return gnc_commodity_table_lookup(table, name_space.data(),
|
|
mnemonic.data());
|
|
}
|
|
|
|
gnc_commodity*
|
|
GncOptionCommodityValue::get_value() const
|
|
{
|
|
return gnc_commodity_from_namespace_and_mnemonic(m_namespace, m_mnemonic);
|
|
}
|
|
|
|
gnc_commodity*
|
|
GncOptionCommodityValue::get_default_value() const
|
|
{
|
|
return gnc_commodity_from_namespace_and_mnemonic(m_default_namespace,
|
|
m_default_mnemonic);
|
|
}
|
|
|
|
void
|
|
GncOptionCommodityValue::set_value(gnc_commodity* value)
|
|
{
|
|
if (!validate(value))
|
|
throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
|
|
m_mnemonic = gnc_commodity_get_mnemonic(value);
|
|
m_namespace = gnc_commodity_get_namespace(value);
|
|
}
|
|
|
|
void
|
|
GncOptionCommodityValue::set_default_value(gnc_commodity* value)
|
|
{
|
|
if (!validate(value))
|
|
throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
|
|
m_mnemonic = m_default_mnemonic = gnc_commodity_get_mnemonic(value);
|
|
m_namespace = m_default_namespace = gnc_commodity_get_namespace(value);
|
|
}
|
|
|
|
void
|
|
GncOptionCommodityValue::reset_default_value()
|
|
{
|
|
m_mnemonic = m_default_mnemonic;
|
|
m_namespace = m_default_namespace;
|
|
}
|
|
|
|
bool
|
|
GncOptionCommodityValue::is_changed() const noexcept
|
|
{
|
|
return m_namespace != m_default_namespace || m_mnemonic != m_default_mnemonic;
|
|
}
|
|
|
|
bool
|
|
GncOptionCommodityValue::validate(gnc_commodity* comm) const noexcept
|
|
{
|
|
if (!GNC_IS_COMMODITY(comm))
|
|
return false;
|
|
if (m_is_currency && !gnc_commodity_is_currency(comm))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
std::string
|
|
GncOptionCommodityValue::serialize() const noexcept
|
|
{
|
|
if (m_is_currency)
|
|
return m_mnemonic;
|
|
else
|
|
return m_namespace + ":" + m_mnemonic;
|
|
}
|
|
|
|
bool
|
|
GncOptionCommodityValue::deserialize(const std::string& str) noexcept
|
|
{
|
|
auto sep{str.find(":")};
|
|
gnc_commodity* comm{};
|
|
std::string mnemonic, name_space;
|
|
if (sep != std::string::npos)
|
|
{
|
|
name_space = str.substr(0, sep);
|
|
mnemonic = str.substr(sep + 1, -1);
|
|
}
|
|
else
|
|
{
|
|
name_space = "CURRENCY";
|
|
mnemonic = str;
|
|
}
|
|
comm = gnc_commodity_from_namespace_and_mnemonic(name_space, mnemonic);
|
|
if (!validate(comm))
|
|
return false;
|
|
m_namespace = std::move(name_space);
|
|
m_mnemonic = std::move(mnemonic);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
GncOptionAccountListValue::validate(const GncOptionAccountList& values) const
|
|
{
|
|
if (values.empty())
|
|
return true;
|
|
if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) &&
|
|
values.size() != 1)
|
|
{
|
|
std::cerr << "GncOptionAccountListValue::validate: Multiple values for a non-multiselect option." << std::endl;
|
|
return false;
|
|
}
|
|
if (m_allowed.empty())
|
|
return true;
|
|
auto book{gnc_get_current_book()};
|
|
for(auto& guid : values)
|
|
{
|
|
if (std::find(m_allowed.begin(), m_allowed.end(),
|
|
xaccAccountGetType(xaccAccountLookup(&guid, book))) == m_allowed.end())
|
|
{
|
|
std::cerr << "GncOptionAccountListValue::validate: Account " << gnc::GUID(guid).to_string() << " is not of an allowed type" << std::endl;
|
|
return false; }
|
|
}
|
|
return true;
|
|
}
|
|
|
|
GncOptionAccountList
|
|
GncOptionAccountListValue::get_value() const
|
|
{
|
|
return !m_value.empty() ? m_value : get_default_value();
|
|
}
|
|
|
|
GncOptionAccountList
|
|
GncOptionAccountListValue::get_default_value() const
|
|
{
|
|
if (!m_default_value.empty())
|
|
return m_default_value;
|
|
|
|
/* If no default has been set and there's an allowed set then find the first
|
|
* account that matches one of the allowed account types.
|
|
*/
|
|
GncOptionAccountList retval{};
|
|
if (m_allowed.empty())
|
|
return retval;
|
|
|
|
auto root{gnc_get_current_root_account()};
|
|
auto account_list{gnc_account_get_descendants_sorted(root)};
|
|
if (!account_list)
|
|
return retval;
|
|
|
|
auto book{gnc_get_current_book()};
|
|
for (auto node = account_list; node; node = g_list_next (node))
|
|
{
|
|
if (std::find(m_allowed.begin(), m_allowed.end(),
|
|
xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
|
|
{
|
|
retval.push_back(*qof_entity_get_guid(GNC_ACCOUNT(node->data)));
|
|
break;
|
|
}
|
|
}
|
|
g_list_free(account_list);
|
|
return retval;
|
|
}
|
|
|
|
static bool
|
|
operator==(const GncGUID& l, const GncGUID& r)
|
|
{
|
|
return guid_equal(&l, &r);
|
|
}
|
|
|
|
bool
|
|
GncOptionAccountListValue::is_changed() const noexcept
|
|
{
|
|
return m_value != m_default_value;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
|
|
* gnc_account_sel_set_acct_filters copies the list so the intermediary caller
|
|
* is responsible for freeing the list.
|
|
*
|
|
* @return an allocated GList* or nullptr if the list is empty.
|
|
*/
|
|
GList*
|
|
GncOptionAccountListValue::account_type_list() const noexcept
|
|
{
|
|
if (m_allowed.empty())
|
|
return nullptr;
|
|
GList* retval{nullptr};
|
|
for (auto type : m_allowed)
|
|
retval = g_list_prepend(retval, GINT_TO_POINTER(type));
|
|
return g_list_reverse(retval);
|
|
}
|
|
|
|
bool
|
|
GncOptionAccountSelValue::validate(const Account* value) const
|
|
{
|
|
if (m_allowed.empty() || !value)
|
|
return true;
|
|
if (std::find(m_allowed.begin(), m_allowed.end(),
|
|
xaccAccountGetType(value)) == m_allowed.end())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
const Account*
|
|
GncOptionAccountSelValue::get_value() const
|
|
{
|
|
auto book{gnc_get_current_book()};
|
|
return guid_equal(guid_null(), &m_value) ? get_default_value() :
|
|
xaccAccountLookup(&m_value, book);
|
|
}
|
|
|
|
const Account*
|
|
GncOptionAccountSelValue::get_default_value() const
|
|
{
|
|
|
|
if (!guid_equal(guid_null(), &m_default_value))
|
|
{
|
|
auto book{gnc_get_current_book()};
|
|
return xaccAccountLookup(&m_default_value, book);
|
|
}
|
|
|
|
/* If no default has been set and there's an allowed set then find the first
|
|
* account that matches one of the allowed account types.
|
|
*/
|
|
if (m_allowed.empty())
|
|
return nullptr;
|
|
|
|
const Account* retval{nullptr};
|
|
auto root{gnc_get_current_root_account()};
|
|
auto account_list{gnc_account_get_descendants_sorted(root)};
|
|
if (!account_list)
|
|
return nullptr;
|
|
|
|
for (auto node = account_list; node; node = g_list_next (node))
|
|
if (std::find(m_allowed.begin(), m_allowed.end(),
|
|
xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
|
|
{
|
|
retval = GNC_ACCOUNT(node->data);
|
|
break;
|
|
}
|
|
g_list_free(account_list);
|
|
return retval;
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
|
|
* gnc_account_sel_set_acct_filters copies the list so the intermediary caller
|
|
* is responsible for freeing the list.
|
|
*
|
|
* @return an allocated GList* or nullptr if the list is empty.
|
|
*/
|
|
GList*
|
|
GncOptionAccountSelValue::account_type_list() const noexcept
|
|
{
|
|
if (m_allowed.empty())
|
|
return nullptr;
|
|
GList* retval{nullptr};
|
|
for (auto type : m_allowed)
|
|
retval = g_list_prepend(retval, GINT_TO_POINTER(type));
|
|
return g_list_reverse(retval);
|
|
}
|
|
|
|
bool
|
|
GncOptionDateValue::validate(RelativeDatePeriod value) {
|
|
if (m_period_set.empty())
|
|
return true; // No restrictions
|
|
if (std::find(m_period_set.begin(), m_period_set.end(),
|
|
value) != m_period_set.end())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
time64
|
|
GncOptionDateValue::get_value() const noexcept
|
|
{
|
|
if (m_period == RelativeDatePeriod::ABSOLUTE)
|
|
return m_date;
|
|
return gnc_relative_date_to_time64(m_period);
|
|
}
|
|
|
|
time64
|
|
GncOptionDateValue::get_default_value() const noexcept
|
|
{
|
|
if (m_default_period == RelativeDatePeriod::ABSOLUTE)
|
|
return m_default_date;
|
|
return gnc_relative_date_to_time64(m_default_period);
|
|
}
|
|
|
|
/* Use asserts for pre- and post-conditions to deliberately crash if they're not
|
|
* met as the program design should prevent that from happening.
|
|
*/
|
|
size_t
|
|
GncOptionDateValue::get_period_index() const noexcept
|
|
{
|
|
assert (m_period != RelativeDatePeriod::ABSOLUTE);
|
|
assert(!m_period_set.empty());
|
|
auto item{std::find(m_period_set.begin(), m_period_set.end(), m_period)};
|
|
assert(item != m_period_set.end());
|
|
return item - m_period_set.begin();
|
|
}
|
|
|
|
size_t
|
|
GncOptionDateValue::get_default_period_index() const noexcept
|
|
{
|
|
assert(m_period != RelativeDatePeriod::ABSOLUTE);
|
|
assert(!m_period_set.empty());
|
|
auto item{std::find(m_period_set.begin(), m_period_set.end(),
|
|
m_default_period)};
|
|
assert (item != m_period_set.end());
|
|
return item - m_period_set.begin();
|
|
}
|
|
|
|
void
|
|
GncOptionDateValue::set_value(size_t index) noexcept
|
|
{
|
|
assert(!m_period_set.empty());
|
|
assert(index < m_period_set.size());
|
|
m_date = INT64_MAX;
|
|
m_period = m_period_set[index];
|
|
}
|
|
|
|
size_t
|
|
GncOptionDateValue::permissible_value_index(const char* key) const noexcept
|
|
{
|
|
auto index = std::find_if(m_period_set.begin(), m_period_set.end(),
|
|
[key](auto period) -> bool {
|
|
return strcmp(gnc_relative_date_display_string(period),
|
|
key) == 0;
|
|
});
|
|
return index != m_period_set.end() ? index - m_period_set.begin() : 0;
|
|
}
|
|
|
|
static const char* date_type_str[] {"absolute", "relative"};
|
|
|
|
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] << " . " <<
|
|
gnc_relative_date_storage_string(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 = gnc_relative_date_from_storage_string(period_str.c_str());
|
|
if (period == RelativeDatePeriod::ABSOLUTE)
|
|
{
|
|
std::string err{"Unknown period string in date option: '"};
|
|
err += period_str;
|
|
err += "'";
|
|
throw std::invalid_argument(err);
|
|
}
|
|
|
|
set_value(period);
|
|
}
|
|
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::BUDGET:
|
|
qof_type = "Budget";
|
|
break;
|
|
case GncOptionUIType::JOB:
|
|
qof_type = "gncJob";
|
|
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::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)
|
|
{
|
|
QofInstance* retval{nullptr};
|
|
try {
|
|
auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
|
|
retval = qof_instance_from_guid(&guid, type);
|
|
}
|
|
catch (const gnc::guid_syntax_exception& err)
|
|
{
|
|
PWARN("Failed to convert %s to a GUID", str.c_str());
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
std::string
|
|
qof_instance_to_string(const QofInstance* inst)
|
|
{
|
|
std::string retval;
|
|
gnc::GUID guid{*qof_instance_get_guid(inst)};
|
|
retval = guid.to_string();
|
|
return retval;
|
|
}
|
|
|
|
template <typename ValueType> void
|
|
GncOptionValue<ValueType>::set_value(ValueType new_value)
|
|
{
|
|
m_value = new_value;
|
|
}
|
|
|
|
template <typename ValueType> void
|
|
GncOptionValue<ValueType>::set_default_value(ValueType new_value)
|
|
{
|
|
m_value = m_default_value = new_value;
|
|
}
|
|
|
|
template <typename ValueType> void
|
|
GncOptionValue<ValueType>::reset_default_value()
|
|
{
|
|
m_value = m_default_value;
|
|
}
|
|
|
|
/* Missing on purpose: QofQuery because for current usage it's serialized with
|
|
* gnc_query2scm. The future is to replace QofQuery with SQL queries so there's
|
|
* not much point to spending the time to create a std::string serialization for
|
|
* them.
|
|
*/
|
|
template <typename ValueType> std::string
|
|
GncOptionValue<ValueType>::serialize() const noexcept
|
|
{
|
|
static const std::string no_value{"No Value"};
|
|
if constexpr(std::is_same_v<ValueType, const QofInstance*>)
|
|
return m_value ? qof_instance_to_string(m_value) : no_value;
|
|
if constexpr(std::is_same_v<ValueType, const GncOwner*>)
|
|
{
|
|
if (!m_value)
|
|
return no_value;
|
|
auto guid{qof_instance_to_string(qofOwnerGetOwner(m_value))};
|
|
auto type{qofOwnerGetType(m_value)};
|
|
std::ostringstream ostr{};
|
|
ostr << type << " " << guid;
|
|
return ostr.str();
|
|
}
|
|
if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
|
|
{
|
|
std::ostringstream ostr{};
|
|
ostr << "'(";
|
|
std::for_each(m_value.begin(), m_value.end(),
|
|
[&ostr](auto rp){
|
|
auto [id, wide, high] = rp;
|
|
ostr << "(" << id << " " << wide << " " << high << " #f) ";
|
|
});
|
|
ostr << ")";
|
|
return ostr.str();
|
|
}
|
|
else if constexpr(is_same_decayed_v<ValueType, std::string>)
|
|
return m_value;
|
|
else if constexpr(is_same_decayed_v<ValueType, bool>)
|
|
return m_value ? "True" : "False";
|
|
else if constexpr(std::is_arithmetic_v<ValueType>)
|
|
return std::to_string(m_value);
|
|
else
|
|
return "Serialization not implemented";
|
|
}
|
|
|
|
template <typename ValueType> bool
|
|
GncOptionValue<ValueType>::deserialize(const std::string& str) noexcept
|
|
{
|
|
if constexpr(std::is_same_v<ValueType, const QofInstance*>)
|
|
set_value(qof_instance_from_string(str, get_ui_type()));
|
|
if constexpr(std::is_same_v<ValueType, const GncOwner*>)
|
|
{
|
|
std::istringstream istr{str};
|
|
std::string type, guid;
|
|
istr >> type >> guid;
|
|
auto inst{qof_instance_from_string(guid, get_ui_type())};
|
|
qofOwnerSetEntity(const_cast<GncOwner*>(m_value), inst);
|
|
}
|
|
if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
|
|
{
|
|
std::istringstream istr{str};
|
|
GncOptionReportPlacementVec rpv;
|
|
while (istr)
|
|
{
|
|
uint32_t id, wide, high;
|
|
istr >> id >> wide >> high;
|
|
rpv.emplace_back(id, wide, high);
|
|
}
|
|
set_value(rpv);
|
|
}
|
|
else if constexpr(is_same_decayed_v<ValueType, std::string>)
|
|
set_value(str);
|
|
else if constexpr(is_same_decayed_v<ValueType, bool>)
|
|
set_value(str == "True");
|
|
else if constexpr(is_same_decayed_v<ValueType, int>)
|
|
set_value(stoi(str));
|
|
else if constexpr(is_same_decayed_v<ValueType, int64_t>)
|
|
set_value(stoll(str));
|
|
else if constexpr(is_same_decayed_v<ValueType, double>)
|
|
set_value(stod(str));
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
std::string
|
|
GncOptionAccountListValue::serialize() const noexcept
|
|
{
|
|
static const std::string no_value{"No Value"};
|
|
std::string retval;
|
|
bool first = true;
|
|
if (m_value.empty())
|
|
return no_value;
|
|
for (auto val : m_value)
|
|
{
|
|
if (!first)
|
|
retval += " ";
|
|
first = false;
|
|
retval += guid_to_string(&val);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool
|
|
GncOptionAccountListValue::deserialize(const std::string& str) noexcept
|
|
{
|
|
if (str.empty() || str.size() < GUID_ENCODING_LENGTH)
|
|
return false;
|
|
m_value.clear();
|
|
m_value.reserve(str.size() / GUID_ENCODING_LENGTH);
|
|
bool first = true;
|
|
size_t pos{};
|
|
while (pos + GUID_ENCODING_LENGTH < str.size())
|
|
{
|
|
if (!first)
|
|
++pos;
|
|
first = false;
|
|
GncGUID guid{};
|
|
string_to_guid(str.substr(pos, pos + GUID_ENCODING_LENGTH).c_str(), &guid);
|
|
m_value.push_back(guid);
|
|
pos += GUID_ENCODING_LENGTH;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string
|
|
GncOptionAccountSelValue::serialize() const noexcept
|
|
{
|
|
static const std::string no_value{"No Value"};
|
|
return guid_equal(guid_null(), &m_value) ? no_value : guid_to_string(&m_value);
|
|
}
|
|
|
|
bool
|
|
GncOptionAccountSelValue::deserialize(const std::string& str) noexcept
|
|
{
|
|
set_value(reinterpret_cast<Account*>(qof_instance_from_string(str, get_ui_type())));
|
|
return true;
|
|
}
|
|
|
|
std::string
|
|
GncOptionMultichoiceValue::serialize() const noexcept
|
|
{
|
|
static const std::string no_value{"No Value"};
|
|
std::string retval;
|
|
bool first = true;
|
|
if (m_value.empty())
|
|
return no_value;
|
|
for (auto index : m_value)
|
|
{
|
|
if (!first)
|
|
retval += " ";
|
|
first = false;
|
|
retval += std::get<0>(m_choices[index]);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool
|
|
GncOptionMultichoiceValue::deserialize(const std::string& str) noexcept
|
|
{
|
|
static const auto size_t_max = std::numeric_limits<std::size_t>::max();
|
|
if (str.empty())
|
|
|
|
return false;
|
|
size_t pos{};
|
|
while (pos < str.size())
|
|
{
|
|
auto endpos{str.find(' ', pos)};
|
|
if (endpos == std::string::npos)
|
|
endpos = str.size();
|
|
//need a null-terminated char* to pass to permissible_value_index
|
|
auto index{permissible_value_index(str.substr(pos, endpos).c_str())};
|
|
if (index == size_t_max)
|
|
return false;
|
|
m_value.push_back(index);
|
|
pos = endpos + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename ValueType> std::string
|
|
GncOptionRangeValue<ValueType>::serialize() const noexcept
|
|
{
|
|
if constexpr (std::is_arithmetic_v<ValueType>)
|
|
return std::to_string(m_value);
|
|
return "";
|
|
}
|
|
|
|
template <typename ValueType> bool
|
|
GncOptionRangeValue<ValueType>::deserialize(const std::string& str) noexcept
|
|
{
|
|
if constexpr(is_same_decayed_v<ValueType, int>)
|
|
set_value(stoi(str));
|
|
else if constexpr(is_same_decayed_v<ValueType, double>)
|
|
set_value(stod(str));
|
|
return true;
|
|
}
|
|
|
|
std::string
|
|
GncOptionDateValue::serialize() const noexcept
|
|
{
|
|
std::string retval{"("};
|
|
if (m_period == RelativeDatePeriod::ABSOLUTE)
|
|
{
|
|
retval += date_type_str[0];
|
|
retval += " . ";
|
|
retval += std::to_string(m_date);
|
|
}
|
|
else
|
|
{
|
|
retval += date_type_str[1];
|
|
retval += " . ";
|
|
retval += gnc_relative_date_storage_string(m_period);
|
|
}
|
|
retval += ")";
|
|
return retval;
|
|
}
|
|
|
|
bool
|
|
GncOptionDateValue::deserialize(const std::string& str) noexcept
|
|
{
|
|
//The length of both "absolute" and "relative".
|
|
static constexpr size_t date_type_len{9};
|
|
// date_type_len plus the length of " . ".
|
|
static constexpr size_t date_value_pos{12};
|
|
auto type_str{str.substr(0, date_type_len)};
|
|
auto period_str{str.substr(date_value_pos)};
|
|
if (type_str == "absolute")
|
|
{
|
|
// Need a cast to disambiguate from time64.
|
|
set_value(static_cast<size_t>(std::stoll(period_str)));
|
|
return true;
|
|
}
|
|
else if (type_str == "relative ")
|
|
{
|
|
auto period = gnc_relative_date_from_storage_string(period_str.c_str());
|
|
if (period == RelativeDatePeriod::ABSOLUTE)
|
|
{
|
|
PWARN("Unknown period string in date option: '%s'",
|
|
period_str.c_str());
|
|
return false;
|
|
}
|
|
|
|
set_value(period);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
PWARN("Unknown date type string in date option: '%s'",
|
|
type_str.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::istream&
|
|
operator>> (std::istream& iss, GncOptionCommodityValue& opt)
|
|
{
|
|
std::string instr;
|
|
iss >> instr;
|
|
if (!opt.deserialize(instr))
|
|
throw std::invalid_argument("Invalid commodity string in stream.");
|
|
return iss;
|
|
}
|
|
|
|
template GncOptionValue<bool>::GncOptionValue(const GncOptionValue<bool>&);
|
|
template GncOptionValue<int>::GncOptionValue(const GncOptionValue<int>&);
|
|
template GncOptionValue<int64_t>::GncOptionValue(const GncOptionValue<int64_t>&);
|
|
template GncOptionValue<double>::GncOptionValue(const GncOptionValue<double>&);
|
|
template GncOptionValue<char*>::GncOptionValue(const GncOptionValue<char*>&);
|
|
template GncOptionValue<const char*>::GncOptionValue(const GncOptionValue<const char*>&);
|
|
template GncOptionValue<std::string>::GncOptionValue(const GncOptionValue<std::string>&);
|
|
template GncOptionValue<const QofQuery*>::GncOptionValue(const GncOptionValue<const QofQuery*>&);
|
|
template GncOptionValue<const GncOwner*>::GncOptionValue(const GncOptionValue<const GncOwner*>&);
|
|
template GncOptionValue<RelativeDatePeriod>::GncOptionValue(const GncOptionValue<RelativeDatePeriod>&);
|
|
template GncOptionValue<size_t>::GncOptionValue(const GncOptionValue<size_t>&);
|
|
template GncOptionValue<GncOptionAccountList>::GncOptionValue(const GncOptionValue<GncOptionAccountList>&);
|
|
template GncOptionValue<GncMultichoiceOptionIndexVec>::GncOptionValue(const GncOptionValue<GncMultichoiceOptionIndexVec>&);
|
|
template GncOptionValue<GncOptionReportPlacementVec>::GncOptionValue(const GncOptionValue<GncOptionReportPlacementVec>&);
|
|
template void GncOptionValue<bool>::set_value(bool);
|
|
template void GncOptionValue<int>::set_value(int);
|
|
template void GncOptionValue<int64_t>::set_value(int64_t);
|
|
template void GncOptionValue<double>::set_value(double);
|
|
template void GncOptionValue<char*>::set_value(char*);
|
|
template void GncOptionValue<const char*>::set_value(const char*);
|
|
template void GncOptionValue<std::string>::set_value(std::string);
|
|
template void GncOptionValue<const QofQuery*>::set_value(const QofQuery*);
|
|
template void GncOptionValue<const GncOwner*>::set_value(const GncOwner*);
|
|
template void GncOptionValue<RelativeDatePeriod>::set_value(RelativeDatePeriod);
|
|
template void GncOptionValue<size_t>::set_value(size_t);
|
|
template void GncOptionValue<GncOptionAccountList>::set_value(GncOptionAccountList);
|
|
template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_value(GncMultichoiceOptionIndexVec);
|
|
template void GncOptionValue<GncOptionReportPlacementVec>::set_value(GncOptionReportPlacementVec);
|
|
template void GncOptionValue<bool>::set_default_value(bool);
|
|
template void GncOptionValue<int>::set_default_value(int);
|
|
template void GncOptionValue<int64_t>::set_default_value(int64_t);
|
|
template void GncOptionValue<double>::set_default_value(double);
|
|
template void GncOptionValue<char*>::set_default_value(char*);
|
|
template void GncOptionValue<const char*>::set_default_value(const char*);
|
|
template void GncOptionValue<std::string>::set_default_value(std::string);
|
|
template void GncOptionValue<const QofQuery*>::set_default_value(const QofQuery*);
|
|
template void GncOptionValue<const GncOwner*>::set_default_value(const GncOwner*);
|
|
template void GncOptionValue<RelativeDatePeriod>::set_default_value(RelativeDatePeriod);
|
|
template void GncOptionValue<size_t>::set_default_value(size_t);
|
|
template void GncOptionValue<GncOptionAccountList>::set_default_value(GncOptionAccountList);
|
|
template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_default_value(GncMultichoiceOptionIndexVec);
|
|
template void GncOptionValue<GncOptionReportPlacementVec>::set_default_value(GncOptionReportPlacementVec);
|
|
template void GncOptionValue<bool>::reset_default_value();
|
|
template void GncOptionValue<int>::reset_default_value();
|
|
template void GncOptionValue<int64_t>::reset_default_value();
|
|
template void GncOptionValue<double>::reset_default_value();
|
|
template void GncOptionValue<char*>::reset_default_value();
|
|
template void GncOptionValue<const char*>::reset_default_value();
|
|
template void GncOptionValue<std::string>::reset_default_value();
|
|
template void GncOptionValue<const QofQuery*>::reset_default_value();
|
|
template void GncOptionValue<const GncOwner*>::reset_default_value();
|
|
template void GncOptionValue<RelativeDatePeriod>::reset_default_value();
|
|
template void GncOptionValue<size_t>::reset_default_value();
|
|
template void GncOptionValue<GncOptionAccountList>::reset_default_value();
|
|
template void GncOptionValue<GncMultichoiceOptionIndexVec>::reset_default_value();
|
|
template void GncOptionValue<GncOptionReportPlacementVec>::reset_default_value();
|
|
template std::string GncOptionValue<bool>::serialize() const noexcept;
|
|
template std::string GncOptionValue<int>::serialize() const noexcept;
|
|
template std::string GncOptionValue<int64_t>::serialize() const noexcept;
|
|
template std::string GncOptionValue<double>::serialize() const noexcept;
|
|
template std::string GncOptionValue<char*>::serialize() const noexcept;
|
|
template std::string GncOptionValue<const char*>::serialize() const noexcept;
|
|
template std::string GncOptionValue<std::string>::serialize() const noexcept;
|
|
template std::string GncOptionValue<const QofQuery*>::serialize() const noexcept;
|
|
template std::string GncOptionValue<const GncOwner*>::serialize() const noexcept;
|
|
template std::string GncOptionValue<GncOptionReportPlacementVec>::serialize() const noexcept;
|
|
template std::string GncOptionRangeValue<int>::serialize() const noexcept;
|
|
template std::string GncOptionRangeValue<double>::serialize() const noexcept;
|
|
template bool GncOptionValue<bool>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionValue<int>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionValue<int64_t>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionValue<double>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionValue<char*>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionValue<const char*>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionValue<std::string>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionValue<const QofQuery*>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionValue<const GncOwner*>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionValue<GncOptionReportPlacementVec>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionRangeValue<int>::deserialize(const std::string&) noexcept;
|
|
template bool GncOptionRangeValue<double>::deserialize(const std::string&) noexcept;
|