mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-29 12:14:31 -06:00
b041a76690
Report options need to be saved when they're different from the defaults, book options need to be saved when their value changes regardless of whether it's the default value. That's dirty. Implement it.
1319 lines
52 KiB
C++
1319 lines
52 KiB
C++
/********************************************************************\
|
|
* gnc-optiondb.cpp -- 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 *
|
|
* *
|
|
\********************************************************************/
|
|
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <string>
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include "gnc-option-uitype.hpp"
|
|
#include "kvp-value.hpp"
|
|
#include "qofbookslots.h"
|
|
#include "guid.hpp"
|
|
#include "gnc-optiondb.h"
|
|
#include "gnc-optiondb.hpp"
|
|
#include "gnc-optiondb-impl.hpp"
|
|
#include "gnc-option-ui.hpp"
|
|
|
|
#include "gnc-session.h"
|
|
constexpr const char* log_module{G_LOG_DOMAIN};
|
|
|
|
constexpr auto stream_max = std::numeric_limits<std::streamsize>::max();
|
|
using AliasedOption = std::pair<const char*, const char*>;
|
|
using OptionAlias = std::pair<const char*, AliasedOption>;
|
|
using OptionAliases = std::vector<OptionAlias>;
|
|
class Aliases
|
|
{
|
|
static const OptionAliases c_option_aliases;
|
|
public:
|
|
static const AliasedOption* find_alias (const char* old_name)
|
|
{
|
|
if (!old_name) return nullptr;
|
|
const auto alias =
|
|
std::find_if(c_option_aliases.begin(), c_option_aliases.end(),
|
|
[old_name](auto alias){
|
|
return std::strcmp(old_name, alias.first) == 0;
|
|
});
|
|
if (alias == c_option_aliases.end())
|
|
return nullptr;
|
|
|
|
return &alias->second;
|
|
}
|
|
};
|
|
|
|
const OptionAliases Aliases::c_option_aliases
|
|
{
|
|
{"Accounts to include", {nullptr, "Accounts"}},
|
|
{"Exclude transactions between selected accounts?",
|
|
{nullptr, "Exclude transactions between selected accounts"}},
|
|
{"Filter Accounts", {nullptr, "Filter By…"}},
|
|
{"Flatten list to depth limit?",
|
|
{nullptr, "Flatten list to depth limit"}},
|
|
{"From", {nullptr, "Start Date"}},
|
|
{"Report Accounts", {nullptr, "Accounts"}},
|
|
{"Report Currency", {nullptr, "Report's currency"}},
|
|
{"Show Account Code?", {nullptr, "Show Account Code"}},
|
|
{"Show Full Account Name?", {nullptr, "Show Full Account Name"}},
|
|
{"Show Multi-currency Totals?",
|
|
{nullptr, "Show Multi-currency Totals"}},
|
|
{"Show zero balance items?", {nullptr, "Show zero balance items"}},
|
|
{"Sign Reverses?", {nullptr, "Sign Reverses"}},
|
|
{"To", {nullptr, "End Date"}},
|
|
{"Charge Type", {nullptr, "Action"}}, // easy-invoice.scm, renamed June 2018
|
|
// the following 4 options in income-gst-statement.scm renamed Dec 2018
|
|
{"Individual income columns", {nullptr, "Individual sales columns"}},
|
|
{"Individual expense columns",
|
|
{nullptr, "Individual purchases columns"}},
|
|
{"Remittance amount", {nullptr, "Gross Balance"}},
|
|
{"Net Income", {nullptr, "Net Balance"}},
|
|
// transaction.scm:
|
|
{"Use Full Account Name?", {nullptr, "Use Full Account Name"}},
|
|
{"Use Full Other Account Name?",
|
|
{nullptr, "Use Full Other Account Name"}},
|
|
{"Void Transactions?", {"Filter", "Void Transactions"}},
|
|
{"Void Transactions", {"Filter", "Void Transactions"}},
|
|
{"Account Substring", {"Filter", "Account Name Filter"}},
|
|
{"Enable links", {nullptr, "Enable Links"}},
|
|
// trep-engine: moved currency options to own tab
|
|
{"Common Currency", {"Currency", "Common Currency"}},
|
|
{"Show original currency amount",
|
|
{"Currency", "Show original currency amount"}},
|
|
{"Report's currency", {"Currency", "Report's currency"}},
|
|
{"Reconcile Status", {nullptr, "Reconciled Status"}},
|
|
// new-owner-report.scm, renamed Oct 2020 to differentiate with
|
|
// Document Links:
|
|
{"Links", {nullptr, "Transaction Links"}},
|
|
// invoice.scm, renamed November 2018
|
|
{"Individual Taxes", {nullptr, "Use Detailed Tax Summary"}},
|
|
{"Show Accounts until level", {nullptr, "Levels of Subaccounts"}},
|
|
{"Invoice number", {nullptr, "Invoice Number"}},
|
|
{"Report title", {nullptr, "Report Title"}},
|
|
{"Extra notes", {nullptr, "Extra Notes"}},
|
|
// income-gst-statement.scm
|
|
{"default format", {nullptr, "Default Format"}},
|
|
{"Report format", {nullptr, "Report Format"}},
|
|
// ... replaced to …, Dec 2022
|
|
{"Filter By...", {nullptr, "Filter By…"}},
|
|
{"Specify date to filter by...", {nullptr, "Specify date to filter by…"}},
|
|
// trep-engine:
|
|
{"Running Balance", {nullptr, "Account Balance"}},
|
|
{"Totals", {nullptr, "Grand Total"}},
|
|
};
|
|
|
|
static bool
|
|
operator==(const std::string& str, const char* cstr)
|
|
{
|
|
return strcmp(str.c_str(), cstr) == 0;
|
|
}
|
|
|
|
void
|
|
GncOptionSection::foreach_option(std::function<void(GncOption&)> func)
|
|
{
|
|
std::for_each(m_options.begin(), m_options.end(), func);
|
|
}
|
|
|
|
void
|
|
GncOptionSection::foreach_option(std::function<void(const GncOption&)> func) const
|
|
{
|
|
std::for_each(m_options.begin(), m_options.end(), func);
|
|
}
|
|
|
|
void
|
|
GncOptionSection::add_option(GncOption&& option)
|
|
{
|
|
m_options.push_back(std::move(option));
|
|
if (!std::is_sorted(m_options.begin(), m_options.end()))
|
|
std::sort(m_options.begin(), m_options.end());
|
|
}
|
|
|
|
void
|
|
GncOptionSection::remove_option(const char* name)
|
|
{
|
|
m_options.erase(std::remove_if(m_options.begin(), m_options.end(),
|
|
[name](const auto& option) -> bool
|
|
{
|
|
return option.get_name() == name;
|
|
}), m_options.end());
|
|
}
|
|
|
|
const GncOption*
|
|
GncOptionSection::find_option(const char* name) const
|
|
{
|
|
auto option = std::find_if(m_options.begin(), m_options.end(),
|
|
[name](auto& option) -> bool {
|
|
return option.get_name() == name;
|
|
});
|
|
if (option != m_options.end())
|
|
return &*option;
|
|
|
|
auto alias = Aliases::find_alias(name);
|
|
if (!alias || alias->first) // No alias or the alias
|
|
return nullptr; // is in a different section.
|
|
return find_option(alias->second);
|
|
}
|
|
|
|
GncOptionDB::GncOptionDB() : m_default_section{} {}
|
|
|
|
GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
|
|
|
|
void
|
|
GncOptionDB::register_option(const char* sectname, GncOption&& option)
|
|
{
|
|
auto section = find_section(sectname);
|
|
|
|
if (section)
|
|
{
|
|
section->add_option(std::move(option));
|
|
return;
|
|
}
|
|
|
|
m_sections.push_back(std::make_shared<GncOptionSection>(sectname));
|
|
m_sections.back()->add_option(std::move(option));
|
|
if (!std::is_sorted(m_sections.begin(), m_sections.end()))
|
|
std::sort(m_sections.begin(), m_sections.end());
|
|
}
|
|
|
|
void
|
|
GncOptionDB::register_option(const char* sectname, GncOption* option)
|
|
{
|
|
register_option(sectname, std::move(*option));
|
|
delete option;
|
|
}
|
|
|
|
void
|
|
GncOptionDB::unregister_option(const char* sectname, const char* name)
|
|
{
|
|
auto section = find_section(sectname);
|
|
if (section)
|
|
section->remove_option(name);
|
|
}
|
|
|
|
void
|
|
GncOptionDB::set_default_section(const char* sectname)
|
|
{
|
|
m_default_section = find_section(sectname);
|
|
}
|
|
|
|
const GncOptionSection* const
|
|
GncOptionDB::get_default_section() const noexcept
|
|
{
|
|
return m_default_section;
|
|
}
|
|
|
|
const GncOptionSection*
|
|
GncOptionDB::find_section(const std::string& section) const
|
|
{
|
|
auto db_section = std::find_if(m_sections.begin(), m_sections.end(),
|
|
[§ion](auto& sect) -> bool
|
|
{
|
|
return section == sect->get_name();
|
|
});
|
|
return db_section == m_sections.end() ? nullptr : db_section->get();
|
|
}
|
|
|
|
const GncOption*
|
|
GncOptionDB::find_option(const std::string& section, const char* name) const
|
|
{
|
|
auto db_section = const_cast<GncOptionDB*>(this)->find_section(section);
|
|
const GncOption* option = nullptr;
|
|
if (db_section)
|
|
option = db_section->find_option(name);
|
|
if (option)
|
|
return option;
|
|
auto alias = Aliases::find_alias(name);
|
|
/* Only try again if alias.first isn't
|
|
* nullptr. GncOptionSection::find_option already checked if the alias
|
|
* should have been in the same section.
|
|
*/
|
|
if (alias && alias->first && section != alias->first)
|
|
return find_option(alias->first, alias->second);
|
|
return nullptr;
|
|
}
|
|
|
|
std::string
|
|
GncOptionDB::lookup_string_option(const char* section, const char* name)
|
|
{
|
|
static const std::string empty_string{};
|
|
|
|
auto db_opt = find_option(section, name);
|
|
if (!db_opt)
|
|
return empty_string;
|
|
return db_opt->get_value<std::string>();
|
|
}
|
|
|
|
void
|
|
GncOptionDB::make_internal(const char* section, const char* name)
|
|
{
|
|
|
|
auto db_opt = find_option(section, name);
|
|
if (db_opt)
|
|
db_opt->make_internal();
|
|
}
|
|
|
|
std::ostream&
|
|
GncOptionDB::save_option_key_value(std::ostream& oss,
|
|
const std::string& section,
|
|
const std::string& name) const noexcept
|
|
{
|
|
|
|
auto db_opt = find_option(section, name.c_str());
|
|
if (!db_opt || !db_opt->is_changed())
|
|
return oss;
|
|
oss << section.substr(0, classifier_size_max) << ":" <<
|
|
name.substr(0, classifier_size_max) << "=" << *db_opt << ";";
|
|
return oss;
|
|
}
|
|
|
|
std::istream&
|
|
GncOptionDB::load_option_key_value(std::istream& iss)
|
|
{
|
|
|
|
char section[classifier_size_max], name[classifier_size_max];
|
|
iss.getline(section, classifier_size_max, ':');
|
|
iss.getline(name, classifier_size_max, '=');
|
|
if (!iss)
|
|
throw std::invalid_argument("Section or name delimiter not found or values too long");
|
|
auto option = find_option(section, name);
|
|
if (!option)
|
|
iss.ignore(stream_max, ';');
|
|
else
|
|
{
|
|
std::string value;
|
|
std::getline(iss, value, ';');
|
|
std::istringstream item_iss{value};
|
|
item_iss >> *option;
|
|
}
|
|
return iss;
|
|
}
|
|
|
|
std::ostream&
|
|
GncOptionDB::save_to_key_value(std::ostream& oss) const noexcept
|
|
{
|
|
|
|
foreach_section(
|
|
[&oss](const GncOptionSectionPtr& section)
|
|
{
|
|
oss << "[Options]\n";
|
|
section->foreach_option(
|
|
[&oss, §ion](auto& option)
|
|
{
|
|
if (option.is_changed())
|
|
oss << section->get_name().substr(0, classifier_size_max) <<
|
|
':' << option.get_name().substr(0, classifier_size_max) <<
|
|
'=' << option << '\n';
|
|
});
|
|
});
|
|
return oss;
|
|
}
|
|
|
|
std::istream&
|
|
GncOptionDB::load_from_key_value(std::istream& iss)
|
|
{
|
|
if (iss.peek() == '[')
|
|
{
|
|
char buf[classifier_size_max];
|
|
iss.getline(buf, classifier_size_max);
|
|
if (strcmp(buf, "[Options]") != 0) // safe
|
|
throw std::runtime_error("Wrong secion header for options.");
|
|
}
|
|
// Otherwise assume we were sent here correctly:
|
|
while (iss.peek() != '[') //Indicates the start of the next file section
|
|
{
|
|
load_option_key_value(iss);
|
|
}
|
|
return iss;
|
|
}
|
|
|
|
size_t
|
|
GncOptionDB::register_callback(GncOptionDBChangeCallback cb, void* data)
|
|
{
|
|
constexpr std::hash<GncOptionDBChangeCallback> cb_hash;
|
|
auto id{cb_hash(cb)};
|
|
if (std::find_if(m_callbacks.begin(), m_callbacks.end(),
|
|
[id](auto&cb)->bool{ return cb.m_id == id; }) == m_callbacks.end())
|
|
m_callbacks.emplace_back(id, cb, data);
|
|
return id;
|
|
}
|
|
|
|
void
|
|
GncOptionDB::unregister_callback(size_t id)
|
|
{
|
|
m_callbacks.erase(std::remove_if(m_callbacks.begin(), m_callbacks.end(),
|
|
[id](auto& cb)->bool { return cb.m_id == id; }),
|
|
m_callbacks.end());
|
|
}
|
|
|
|
void
|
|
GncOptionDB::run_callbacks()
|
|
{
|
|
std::for_each(m_callbacks.begin(), m_callbacks.end(),
|
|
[](auto& cb)->void { cb.m_func(cb.m_data); });
|
|
}
|
|
|
|
static inline void
|
|
counter_option_path(const GncOption& option, GSList* list, std::string& name)
|
|
{
|
|
constexpr const char* counters{"counters"};
|
|
constexpr const char* formats{"counter_formats"};
|
|
auto key = option.get_key();
|
|
name = key.substr(0, key.size() - 1);
|
|
list->next->data = (void*)name.c_str();
|
|
if (option.get_name().rfind("format")
|
|
!= std::string::npos)
|
|
list->data = (void*)formats;
|
|
else
|
|
list->data = (void*)counters;
|
|
}
|
|
|
|
static inline void
|
|
option_path(const GncOption& option, GSList* list)
|
|
{
|
|
list->next->data = (void*)option.get_name().c_str();
|
|
list->data = (void*)option.get_section().c_str();
|
|
}
|
|
|
|
/* The usage "option.template get_value<bool>()" looks weird, but it's required
|
|
* by the C++ standard: "When the name of a member template specialization
|
|
* appears after . or -> in a postfix-expression, or after nested-name-specifier
|
|
* in a qualified-id, and the postfix-expression or qualified-id explicitly
|
|
* depends on a template-parameter (14.6.2), the member template name must be
|
|
* prefixed by the keyword template. Otherwise the name is assumed to name a
|
|
* non-template."
|
|
*/
|
|
static inline KvpValue*
|
|
kvp_value_from_bool_option(const GncOption& option)
|
|
{
|
|
auto val{option.template get_value<bool>()};
|
|
// ~KvpValue will g_free the value.
|
|
return new KvpValue(val ? g_strdup("t") : g_strdup("f"));
|
|
}
|
|
|
|
static bool
|
|
is_qofinstance_ui_type(GncOptionUIType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case GncOptionUIType::ACCOUNT_SEL:
|
|
case GncOptionUIType::BUDGET:
|
|
case GncOptionUIType::OWNER:
|
|
case GncOptionUIType::CUSTOMER:
|
|
case GncOptionUIType::VENDOR:
|
|
case GncOptionUIType::EMPLOYEE:
|
|
case GncOptionUIType::INVOICE:
|
|
case GncOptionUIType::TAX_TABLE:
|
|
case GncOptionUIType::QUERY:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline KvpValue*
|
|
kvp_value_from_qof_instance_option(const GncOption& option)
|
|
{
|
|
const QofInstance* inst{QOF_INSTANCE(option.template get_value<const QofInstance*>())};
|
|
auto guid = guid_copy(qof_instance_get_guid(inst));
|
|
return new KvpValue(guid);
|
|
}
|
|
|
|
void
|
|
GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
|
|
{
|
|
if (clear_options)
|
|
qof_book_options_delete(book, nullptr);
|
|
const_cast<GncOptionDB*>(this)->foreach_section(
|
|
[book](GncOptionSectionPtr& section)
|
|
{
|
|
section->foreach_option(
|
|
[book, §ion](GncOption& option) {
|
|
if (option.is_dirty())
|
|
{
|
|
/* We need the string name out here so that it stays in
|
|
* scope long enough to pass its c_str to
|
|
* gnc_book_set_option. */
|
|
std::string name;
|
|
/* qof_book_set_option wants a GSList path. Let's avoid
|
|
* allocating and make one here. */
|
|
GSList list_tail{}, list_head{nullptr, &list_tail};
|
|
if (strcmp(section->get_name().c_str(), "Counters") == 0)
|
|
counter_option_path(option, &list_head, name);
|
|
else
|
|
option_path(option, &list_head);
|
|
auto type{option.get_ui_type()};
|
|
KvpValue* kvp{};
|
|
if (type == GncOptionUIType::BOOLEAN)
|
|
kvp = kvp_value_from_bool_option(option);
|
|
else if (is_qofinstance_ui_type(type))
|
|
kvp = kvp_value_from_qof_instance_option(option);
|
|
else if (type == GncOptionUIType::NUMBER_RANGE)
|
|
{
|
|
if (option.is_alternate())
|
|
{
|
|
kvp = new KvpValue(static_cast<int64_t>(option.template get_value<int>()));
|
|
}
|
|
else
|
|
{
|
|
kvp = new KvpValue(option.template get_value<double>());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto str{option.template get_value<std::string>()};
|
|
kvp = new KvpValue{g_strdup(str.c_str())};
|
|
}
|
|
qof_book_set_option(book, kvp, &list_head);
|
|
option.mark_saved();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
static inline void
|
|
fill_option_from_string_kvp(GncOption& option, KvpValue* kvp)
|
|
{
|
|
auto str{kvp->get<const char*>()};
|
|
if (option.get_ui_type() == GncOptionUIType::BOOLEAN)
|
|
option.set_value(*str == 't' ? true : false);
|
|
else
|
|
option.set_value(std::string{str});
|
|
}
|
|
|
|
static inline void
|
|
fill_option_from_guid_kvp(GncOption& option, KvpValue* kvp)
|
|
{
|
|
auto guid{kvp->get<GncGUID*>()};
|
|
option.set_value(
|
|
(const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type()));
|
|
}
|
|
|
|
void
|
|
GncOptionDB::load_from_kvp(QofBook* book) noexcept
|
|
{
|
|
foreach_section(
|
|
[book](GncOptionSectionPtr& section)
|
|
{
|
|
section->foreach_option(
|
|
[book, §ion](GncOption& option)
|
|
{
|
|
// Make path list as above.
|
|
std::string name;
|
|
/* qof_book_set_option wants a GSList path. Let's avoid
|
|
* allocating and make one here. */
|
|
GSList list_tail{}, list_head{nullptr, &list_tail};
|
|
if (strcmp(section->get_name().c_str(), "Counters") == 0)
|
|
counter_option_path(option, &list_head, name);
|
|
else
|
|
option_path(option, &list_head);
|
|
auto kvp = qof_book_get_option(book, &list_head);
|
|
if (!kvp)
|
|
return;
|
|
|
|
auto set_double = [&option, kvp, &list_head]() {
|
|
/*counters might have been set as doubles
|
|
* because of
|
|
* https://bugs.gnucash.org/show_bug.cgi?id=798930. They
|
|
* should be int.
|
|
*/
|
|
constexpr const char *counters{"counters"};
|
|
auto value{kvp->get<double>()};
|
|
if (strcmp(static_cast<char*>(list_head.data), counters) == 0)
|
|
option.set_value(static_cast<int>(value));
|
|
else
|
|
option.set_value(value);
|
|
};
|
|
|
|
switch (kvp->get_type())
|
|
{
|
|
case KvpValue::Type::DOUBLE:
|
|
set_double();
|
|
break;
|
|
case KvpValue::Type::INT64:
|
|
option.set_value(static_cast<int>(kvp->get<int64_t>()));
|
|
break;
|
|
case KvpValue::Type::STRING:
|
|
fill_option_from_string_kvp(option, kvp);
|
|
break;
|
|
case KvpValue::Type::GUID:
|
|
fill_option_from_guid_kvp(option, kvp);
|
|
break;
|
|
default:
|
|
return;
|
|
break;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
void
|
|
gnc_register_string_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, std::string value)
|
|
{
|
|
GncOption option{section, name, key, doc_string, value,
|
|
GncOptionUIType::STRING};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_text_option(GncOptionDB* db, const char* section, const char* name,
|
|
const char* key, const char* doc_string,
|
|
std::string value)
|
|
{
|
|
GncOption option{section, name, key, doc_string, value,
|
|
GncOptionUIType::TEXT};
|
|
db->register_option(section, std::move(option));
|
|
|
|
}
|
|
|
|
void
|
|
gnc_register_font_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, std::string value)
|
|
{
|
|
GncOption option{section, name, key, doc_string, value,
|
|
GncOptionUIType::FONT};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_budget_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, GncBudget *value)
|
|
{
|
|
GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
|
|
(const QofInstance*)value,
|
|
GncOptionUIType::BUDGET}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_color_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, std::string value)
|
|
{
|
|
GncOption option{section, name, key, doc_string, value,
|
|
GncOptionUIType::COLOR};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_commodity_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, gnc_commodity *value)
|
|
{
|
|
GncOption option{GncOptionCommodityValue{section, name, key, doc_string,
|
|
value,
|
|
GncOptionUIType::COMMODITY}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_commodity_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, const char* value)
|
|
{
|
|
gnc_commodity* commodity{};
|
|
const auto book{qof_session_get_book(gnc_get_current_session())};
|
|
const auto commodity_table{gnc_commodity_table_get_table(book)};
|
|
const auto namespaces{gnc_commodity_table_get_namespaces(commodity_table)};
|
|
for (auto node = namespaces; node && commodity == nullptr;
|
|
node = g_list_next(node))
|
|
{
|
|
commodity = gnc_commodity_table_lookup(commodity_table,
|
|
(const char*)(node->data),
|
|
value);
|
|
if (commodity)
|
|
break;
|
|
}
|
|
GncOption option{GncOptionCommodityValue{section, name, key, doc_string,
|
|
commodity,
|
|
GncOptionUIType::COMMODITY}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_simple_boolean_option(GncOptionDB* db,
|
|
const char* section, const char* name,
|
|
const char* key, const char* doc_string,
|
|
bool value)
|
|
{
|
|
GncOption option{section, name, key, doc_string, value,
|
|
GncOptionUIType::BOOLEAN};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_pixmap_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, std::string value)
|
|
{
|
|
GncOption option{section, name, key, doc_string, value,
|
|
GncOptionUIType::PIXMAP};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_account_list_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string,
|
|
const GncOptionAccountList& value)
|
|
{
|
|
GncOption option{GncOptionAccountListValue{section, name, key, doc_string,
|
|
GncOptionUIType::ACCOUNT_LIST, value}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_account_list_limited_option(GncOptionDB* db,
|
|
const char* section, const char* name,
|
|
const char* key,
|
|
const char* doc_string,
|
|
const GncOptionAccountList& value,
|
|
GncOptionAccountTypeList&& allowed)
|
|
{
|
|
try
|
|
{
|
|
GncOption option{GncOptionAccountListValue{section, name, key, doc_string,
|
|
GncOptionUIType::ACCOUNT_LIST, value, std::move(allowed)}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
catch (const std::invalid_argument& err)
|
|
{
|
|
PWARN("Account List Limited Option, value failed validation, option not registered.");
|
|
}
|
|
}
|
|
|
|
using AccountPair = std::pair<GncOptionAccountList&,
|
|
const GncOptionAccountTypeList&>;
|
|
static void
|
|
find_children(Account* account, void* data)
|
|
{
|
|
auto datapair =
|
|
(AccountPair*)data;
|
|
GncOptionAccountList& list = datapair->first;
|
|
const GncOptionAccountTypeList& types = datapair->second;
|
|
if (std::find(types.begin(), types.end(),
|
|
xaccAccountGetType(account)) != types.end())
|
|
list.push_back(*qof_entity_get_guid(account));
|
|
}
|
|
|
|
GncOptionAccountList
|
|
gnc_account_list_from_types(QofBook *book,
|
|
const GncOptionAccountTypeList& types)
|
|
{
|
|
GncOptionAccountList list;
|
|
AccountPair funcdata{list, types};
|
|
Account* base_acct = gnc_book_get_root_account(book);
|
|
gnc_account_foreach_descendant(base_acct, (AccountCb)find_children,
|
|
&funcdata);
|
|
return list;
|
|
}
|
|
|
|
|
|
void
|
|
gnc_register_account_sel_limited_option(GncOptionDB* db,
|
|
const char* section, const char* name,
|
|
const char* key, const char* doc_string,
|
|
const Account* value,
|
|
GncOptionAccountTypeList&& allowed)
|
|
{
|
|
try
|
|
{
|
|
GncOption option{GncOptionAccountSelValue{section, name, key, doc_string,
|
|
GncOptionUIType::ACCOUNT_SEL, value, std::move(allowed)}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
catch (const std::invalid_argument& err)
|
|
{
|
|
PWARN("Account Sel Limited Option, value failed validation, option not registerd.");
|
|
}
|
|
}
|
|
|
|
void
|
|
gnc_register_multichoice_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, const char* default_val,
|
|
GncMultichoiceOptionChoices&& choices)
|
|
{
|
|
std::string defval{default_val};
|
|
auto found{std::find_if(choices.begin(), choices.end(),
|
|
[&defval](auto& choice)->bool {
|
|
return defval == std::get<0>(choice);
|
|
})};
|
|
if (found == choices.end())
|
|
defval = (choices.empty() ? std::string{"None"} :
|
|
std::get<0>(choices.at(0)));
|
|
GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
|
|
defval.c_str(), std::move(choices)}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_list_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, const char* value,
|
|
GncMultichoiceOptionChoices&& list)
|
|
{
|
|
GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
|
|
value, std::move(list), GncOptionUIType::LIST}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
/* Only balance-forecast.scm, sample-report.scm, and net-charts.scm
|
|
* use decimals and fractional steps and they can be worked around.
|
|
*/
|
|
template <typename ValueType> void
|
|
gnc_register_number_range_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, ValueType value,
|
|
ValueType min, ValueType max, ValueType step)
|
|
{
|
|
try
|
|
{
|
|
GncOption option{GncOptionRangeValue<ValueType>{section, name, key,
|
|
doc_string, value, min,
|
|
max, step}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
catch(const std::invalid_argument& err)
|
|
{
|
|
PWARN("Number Range Option %s, option not registerd.",
|
|
err.what());
|
|
}
|
|
}
|
|
|
|
void
|
|
gnc_register_number_plot_size_option(GncOptionDB* db,
|
|
const char* section, const char* name,
|
|
const char* key, const char* doc_string,
|
|
int value)
|
|
{
|
|
//65K is 10x reasonable, but it's a convenient constant.
|
|
GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
|
|
value, 10, UINT16_MAX, 1, GncOptionUIType::PLOT_SIZE}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_query_option(GncOptionDB* db, const char* section,
|
|
const char* name, const QofQuery* value)
|
|
{
|
|
GncOption option{section, name, "", "", value,
|
|
GncOptionUIType::INTERNAL};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_owner_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, const GncOwner* value,
|
|
GncOwnerType type)
|
|
{
|
|
GncOptionUIType uitype;
|
|
switch (type)
|
|
{
|
|
case GNC_OWNER_CUSTOMER:
|
|
uitype = GncOptionUIType::CUSTOMER;
|
|
break;
|
|
case GNC_OWNER_EMPLOYEE:
|
|
uitype = GncOptionUIType::EMPLOYEE;
|
|
break;
|
|
case GNC_OWNER_JOB:
|
|
uitype = GncOptionUIType::JOB;
|
|
break;
|
|
case GNC_OWNER_VENDOR:
|
|
uitype = GncOptionUIType::VENDOR;
|
|
break;
|
|
default:
|
|
uitype = GncOptionUIType::INTERNAL;
|
|
};
|
|
GncOption option{GncOptionGncOwnerValue{section, name, key, doc_string,
|
|
value, uitype}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_invoice_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, GncInvoice* value)
|
|
{
|
|
GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
|
|
(const QofInstance*)value,
|
|
GncOptionUIType::INVOICE}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_taxtable_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, GncTaxTable* value)
|
|
{
|
|
GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
|
|
(const QofInstance*)value,
|
|
GncOptionUIType::TAX_TABLE}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_invoice_print_report_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, std::string value)
|
|
{
|
|
GncOption option{section, name, key, doc_string,
|
|
value, GncOptionUIType::INV_REPORT};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_counter_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, int value)
|
|
{
|
|
GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
|
|
value, 0, 999999999, 1}};
|
|
option.set_alternate(true);
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_counter_format_option(GncOptionDB* db,
|
|
const char* section, const char* name,
|
|
const char* key, const char* doc_string,
|
|
std::string value)
|
|
{
|
|
GncOption option{section, name, key, doc_string, value,
|
|
GncOptionUIType::STRING};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_dateformat_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, std::string value)
|
|
{
|
|
GncOption option{section, name, key, doc_string, value,
|
|
GncOptionUIType::DATE_FORMAT};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_currency_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, gnc_commodity *value)
|
|
{
|
|
GncOption option{GncOptionCommodityValue{
|
|
section, name, key, doc_string, value, GncOptionUIType::CURRENCY
|
|
}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_currency_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, const char* value)
|
|
{
|
|
const auto book{qof_session_get_book(gnc_get_current_session())};
|
|
const auto commodity_table{gnc_commodity_table_get_table(book)};
|
|
const auto commodity = gnc_commodity_table_lookup(commodity_table,
|
|
"CURRENCY",
|
|
value);
|
|
GncOption option{GncOptionCommodityValue{
|
|
section, name, key, doc_string, commodity, GncOptionUIType::CURRENCY
|
|
}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_date_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, time64 time,
|
|
RelativeDateUI ui)
|
|
{
|
|
auto ui_type = ui == RelativeDateUI::BOTH ? GncOptionUIType::DATE_BOTH :
|
|
ui == RelativeDateUI::RELATIVE ? GncOptionUIType::DATE_RELATIVE :
|
|
GncOptionUIType::DATE_ABSOLUTE;
|
|
GncOption option{GncOptionDateValue(section, name, key, doc_string,
|
|
ui_type, time)};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_date_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, RelativeDatePeriod period,
|
|
RelativeDateUI ui)
|
|
{
|
|
auto ui_type = ui == RelativeDateUI::BOTH ? GncOptionUIType::DATE_BOTH :
|
|
ui == RelativeDateUI::RELATIVE ? GncOptionUIType::DATE_RELATIVE :
|
|
GncOptionUIType::DATE_ABSOLUTE;
|
|
GncOption option{GncOptionDateValue(section, name, key, doc_string,
|
|
ui_type, period)};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_date_option(GncOptionDB* db,
|
|
const char* section, const char* name,
|
|
const char* key, const char* doc_string,
|
|
RelativeDatePeriodVec& period_set,
|
|
bool both)
|
|
{
|
|
auto is_absolute = period_set.size() == 1 &&
|
|
period_set.front() == RelativeDatePeriod::ABSOLUTE;
|
|
auto ui_type = both ? GncOptionUIType::DATE_BOTH :
|
|
is_absolute ? GncOptionUIType::DATE_ABSOLUTE : GncOptionUIType::DATE_RELATIVE;
|
|
GncOption option{GncOptionDateValue(section, name, key, doc_string,
|
|
ui_type, period_set)};
|
|
if (is_absolute)
|
|
option.set_default_value(gnc_time(nullptr));
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
|
|
static const RelativeDatePeriodVec begin_dates
|
|
{
|
|
RelativeDatePeriod::TODAY,
|
|
RelativeDatePeriod::START_THIS_MONTH,
|
|
RelativeDatePeriod::START_PREV_MONTH,
|
|
RelativeDatePeriod::START_CURRENT_QUARTER,
|
|
RelativeDatePeriod::START_PREV_QUARTER,
|
|
RelativeDatePeriod::START_CAL_YEAR,
|
|
RelativeDatePeriod::START_PREV_YEAR,
|
|
RelativeDatePeriod::START_ACCOUNTING_PERIOD
|
|
};
|
|
|
|
void
|
|
gnc_register_start_date_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, bool both)
|
|
{
|
|
auto ui_type = both ? GncOptionUIType::DATE_BOTH :
|
|
GncOptionUIType::DATE_RELATIVE;
|
|
GncOption option{GncOptionDateValue(section, name, key, doc_string,
|
|
ui_type, begin_dates)};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
static const RelativeDatePeriodVec end_dates
|
|
{
|
|
RelativeDatePeriod::TODAY,
|
|
RelativeDatePeriod::END_THIS_MONTH,
|
|
RelativeDatePeriod::END_PREV_MONTH,
|
|
RelativeDatePeriod::END_CURRENT_QUARTER,
|
|
RelativeDatePeriod::END_PREV_QUARTER,
|
|
RelativeDatePeriod::END_CAL_YEAR,
|
|
RelativeDatePeriod::END_PREV_YEAR,
|
|
RelativeDatePeriod::END_ACCOUNTING_PERIOD
|
|
};
|
|
|
|
void
|
|
gnc_register_end_date_option(GncOptionDB* db, const char* section,
|
|
const char* name, const char* key,
|
|
const char* doc_string, bool both)
|
|
{
|
|
auto ui_type = both ? GncOptionUIType::DATE_BOTH :
|
|
GncOptionUIType::DATE_RELATIVE;
|
|
GncOption option{GncOptionDateValue(section, name, key, doc_string,
|
|
ui_type, end_dates)};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_report_placement_option(GncOptionDBPtr& db,
|
|
const char* section, const char* name)
|
|
{
|
|
/* This is a special option with it's own UI file so we have fake values to pass
|
|
* to the template creation function.
|
|
*/
|
|
GncOptionReportPlacementVec value;
|
|
GncOption option{GncOptionValue<GncOptionReportPlacementVec>{section, name,
|
|
"no_key", "nodoc_string",
|
|
value,GncOptionUIType::REPORT_PLACEMENT}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_internal_option(GncOptionDBPtr& db,
|
|
const char* section, const char* name,
|
|
const std::string& value)
|
|
{
|
|
GncOption option{
|
|
GncOptionValue<std::string>{section, name, "", "", value,
|
|
GncOptionUIType::INTERNAL}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
void
|
|
gnc_register_internal_option(GncOptionDBPtr& db,
|
|
const char* section, const char* name,
|
|
bool value)
|
|
{
|
|
GncOption option{
|
|
GncOptionValue<bool>{section, name, "", "", value,
|
|
GncOptionUIType::INTERNAL}};
|
|
db->register_option(section, std::move(option));
|
|
}
|
|
|
|
GncOptionDB*
|
|
gnc_option_db_new(void)
|
|
{
|
|
return new GncOptionDB;
|
|
}
|
|
|
|
void
|
|
gnc_option_db_destroy(GncOptionDB* odb)
|
|
{
|
|
PWARN("Direct Destroy called on GncOptionDB %" G_GUINT64_FORMAT, (uint64_t)odb);
|
|
}
|
|
|
|
GList*
|
|
gnc_option_db_commit(GncOptionDB* odb)
|
|
{
|
|
GList* errors{};
|
|
odb->foreach_section(
|
|
[&errors](GncOptionSectionPtr& section){
|
|
section->foreach_option(
|
|
[&errors](GncOption& option) {
|
|
try
|
|
{
|
|
option.set_option_from_ui_item();
|
|
}
|
|
catch (const std::invalid_argument& err)
|
|
{
|
|
PWARN("Option %s:%s failed to set its value %s",
|
|
option.get_section().c_str(),
|
|
option.get_name().c_str(), err.what());
|
|
errors = g_list_prepend(errors,
|
|
(void*)option.get_name().c_str());
|
|
} });
|
|
});
|
|
if (!errors)
|
|
odb->run_callbacks();
|
|
return errors;
|
|
}
|
|
|
|
void
|
|
gnc_option_db_clean(GncOptionDB* odb)
|
|
{
|
|
odb->foreach_section(
|
|
[](GncOptionSectionPtr& section){
|
|
section->foreach_option(
|
|
[](GncOption& option) {
|
|
option.set_ui_item_from_option();
|
|
});
|
|
});
|
|
}
|
|
|
|
void gnc_option_db_load(GncOptionDB* odb, QofBook* book)
|
|
{
|
|
odb->load_from_kvp(book);
|
|
}
|
|
|
|
void
|
|
gnc_option_db_save(GncOptionDB* odb, QofBook* book,
|
|
gboolean clear_options)
|
|
{
|
|
odb->save_to_kvp(book, static_cast<bool>(clear_options));
|
|
}
|
|
|
|
void
|
|
gnc_option_db_book_options(GncOptionDB* odb)
|
|
{
|
|
constexpr const char* business_section{N_("Business")};
|
|
constexpr const char* counter_section{N_("Counters")};
|
|
static const std::string empty_string{""};
|
|
|
|
//Accounts Tab
|
|
|
|
gnc_register_number_range_option<double>(odb, OPTION_SECTION_ACCOUNTS,
|
|
OPTION_NAME_AUTO_READONLY_DAYS, "a",
|
|
N_("Choose the number of days after which transactions will be read-only and cannot be edited anymore. This threshold is marked by a red line in the account register windows. If zero, all transactions can be edited and none are read-only."),
|
|
0.0, 0.0, 3650.0, 1.0);
|
|
|
|
gnc_register_simple_boolean_option(odb, OPTION_SECTION_ACCOUNTS,
|
|
OPTION_NAME_NUM_FIELD_SOURCE, "b",
|
|
N_("Check to have split action field used in registers for 'Num' field in place of transaction number; transaction number shown as 'T-Num' on second line of register. Has corresponding effect on business features, reporting and imports/exports."),
|
|
false);
|
|
gnc_register_simple_boolean_option(odb, OPTION_SECTION_ACCOUNTS,
|
|
OPTION_NAME_TRADING_ACCOUNTS, "a",
|
|
N_("Check to have trading accounts used for transactions involving more than one currency or commodity."),
|
|
false);
|
|
|
|
//Budgeting Tab
|
|
|
|
gnc_register_budget_option(odb, OPTION_SECTION_BUDGETING,
|
|
OPTION_NAME_DEFAULT_BUDGET, "a",
|
|
N_("Budget to be used when none has been otherwise specified."),
|
|
nullptr);
|
|
|
|
//Counters Tab
|
|
|
|
gnc_register_counter_option(odb, counter_section,
|
|
N_("Customer number"), "gncCustomera",
|
|
N_("The previous customer number generated. This number will be incremented to generate the next customer number."),
|
|
0);
|
|
gnc_register_counter_format_option(odb, counter_section,
|
|
N_("Customer number format"),
|
|
"gncCustomerb",
|
|
N_("The format string to use for generating customer numbers. This is a printf-style format string."),
|
|
empty_string);
|
|
gnc_register_counter_option(odb, counter_section,
|
|
N_("Employee number"), "gncEmployeea",
|
|
N_("The previous employee number generated. This number will be incremented to generate the next employee number."),
|
|
0);
|
|
gnc_register_counter_format_option(odb, counter_section,
|
|
N_("Employee number format"),
|
|
"gncEmployeeb",
|
|
N_("The format string to use for generating employee numbers. This is a printf-style format string."),
|
|
empty_string);
|
|
gnc_register_counter_option(odb, counter_section,
|
|
N_("Invoice number"), "gncInvoicea",
|
|
N_("The previous invoice number generated. This number will be incremented to generate the next invoice number."),
|
|
0);
|
|
gnc_register_counter_format_option(odb, counter_section,
|
|
N_("Invoice number format"),
|
|
"gncInvoiceb",
|
|
N_("The format string to use for generating invoice numbers. This is a printf-style format string."),
|
|
empty_string);
|
|
gnc_register_counter_option(odb, counter_section,
|
|
N_("Bill number"), "gncBilla",
|
|
N_("The previous bill number generated. This number will be incremented to generate the next bill number."),
|
|
0);
|
|
gnc_register_counter_format_option(odb, counter_section,
|
|
N_("Bill number format"), "gncBillb",
|
|
N_("The format string to use for generating bill numbers. This is a printf-style format string."),
|
|
empty_string);
|
|
gnc_register_counter_option(odb, counter_section,
|
|
N_("Expense voucher number"), "gncExpVouchera",
|
|
N_("The previous expense voucher number generated. This number will be incremented to generate the next voucher number."),
|
|
0);
|
|
gnc_register_counter_format_option(odb, counter_section,
|
|
N_("Expense voucher number format"),
|
|
"gncExpVoucherb",
|
|
N_("The format string to use for generating expense voucher numbers. This is a printf-style format string."),
|
|
empty_string);
|
|
gnc_register_counter_option(odb, counter_section,
|
|
N_("Job number"), "gncJoba",
|
|
N_("The previous job number generated. This number will be incremented to generate the next job number."),
|
|
0);
|
|
gnc_register_counter_format_option(odb, counter_section,
|
|
N_("Job number format"), "gncJobb",
|
|
N_("The format string to use for generating job numbers. This is a printf-style format string."),
|
|
empty_string);
|
|
gnc_register_counter_option(odb, counter_section,
|
|
N_("Order number"), "gncOrdera",
|
|
N_("The previous order number generated. This number will be incremented to generate the next order number."),
|
|
0);
|
|
gnc_register_counter_format_option(odb, counter_section,
|
|
N_("Order number format"), "gncOrderb",
|
|
N_("The format string to use for generating order numbers. This is a printf-style format string."),
|
|
empty_string);
|
|
gnc_register_counter_option(odb, counter_section,
|
|
N_("Vendor number"), "gncVendora",
|
|
N_("The previous vendor number generated. This number will be incremented to generate the next vendor number."),
|
|
0);
|
|
gnc_register_counter_format_option(odb, counter_section,
|
|
N_("Vendor number format"), "gncVendorb",
|
|
N_("The format string to use for generating vendor numbers. This is a printf-style format string."),
|
|
empty_string);
|
|
|
|
//Business Tab
|
|
|
|
gnc_register_string_option(odb, business_section, N_("Company Name"), "a",
|
|
N_("The name of your business."),
|
|
empty_string);
|
|
gnc_register_text_option(odb, business_section, N_("Company Address"), "b1",
|
|
N_("The address of your business."),
|
|
empty_string);
|
|
gnc_register_string_option(odb, business_section,
|
|
N_("Company Contact Person"), "b2",
|
|
N_("The contact person to print on invoices."),
|
|
empty_string);
|
|
gnc_register_string_option(odb, business_section,
|
|
N_("Company Phone Number"), "c1",
|
|
N_("The contact person to print on invoices."),
|
|
empty_string);
|
|
gnc_register_string_option(odb, business_section,
|
|
N_("Company Fax Number"), "c2",
|
|
N_("The fax number of your business."),
|
|
empty_string);
|
|
gnc_register_string_option(odb, business_section,
|
|
N_("Company Email Address"), "c3",
|
|
N_ ("The email address of your business."),
|
|
empty_string);
|
|
gnc_register_string_option(odb, business_section,
|
|
N_("Company Website URL"), "c4",
|
|
N_("The URL address of your website."),
|
|
empty_string);
|
|
gnc_register_string_option(odb, business_section, N_("Company ID"), "c5",
|
|
N_("The ID for your company (eg 'Tax-ID: 00-000000)."),
|
|
empty_string);
|
|
gnc_register_invoice_print_report_option(odb, business_section,
|
|
OPTION_NAME_DEFAULT_INVOICE_REPORT, "e1",
|
|
N_("The invoice report to be used for printing."),
|
|
empty_string);
|
|
gnc_register_number_range_option<double>(odb, business_section,
|
|
OPTION_NAME_DEFAULT_INVOICE_REPORT_TIMEOUT, "e2",
|
|
N_("Length of time to change the used invoice report. A value of 0 means disabled."),
|
|
0.0, 0.0, 20.0, 1.0);
|
|
gnc_register_taxtable_option(odb, business_section,
|
|
N_("Default Customer TaxTable"), "f1",
|
|
N_("The default tax table to apply to customers."),
|
|
nullptr);
|
|
gnc_register_taxtable_option(odb, business_section,
|
|
N_("Default Vendor TaxTable"), "f2",
|
|
N_("The default tax table to apply to vendors."),
|
|
nullptr);
|
|
gnc_register_dateformat_option(odb, business_section,
|
|
N_("Fancy Date Format"), "g",
|
|
N_("The default date format used for fancy printed dates."),
|
|
empty_string);
|
|
|
|
//Tax Tab
|
|
|
|
gnc_register_string_option(odb, N_("Tax"), N_("Tax Number"), "a",
|
|
N_("The electronic tax number of your business"),
|
|
empty_string);
|
|
}
|
|
const QofInstance*
|
|
gnc_option_db_lookup_qofinstance_value(GncOptionDB* odb, const char* section,
|
|
const char* name)
|
|
{
|
|
auto option{odb->find_option(section, name)};
|
|
if (option)
|
|
return option->get_value<const QofInstance*>();
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
// Force creation of templates
|
|
template void gnc_register_number_range_option(GncOptionDB* db,
|
|
const char* section, const char* name,
|
|
const char* key, const char* doc_string,
|
|
int value, int min, int max, int step);
|
|
template void gnc_register_number_range_option(GncOptionDB* db,
|
|
const char* section, const char* name,
|
|
const char* key, const char* doc_string,
|
|
double value, double min,
|
|
double max, double step);
|