Branch data Line data Source code
1 : : /********************************************************************\
2 : : * gnc-optiondb.cpp -- Collection of GncOption objects *
3 : : * Copyright (C) 2019 John Ralls <jralls@ceridwen.us> *
4 : : * *
5 : : * This program is free software; you can redistribute it and/or *
6 : : * modify it under the terms of the GNU General Public License as *
7 : : * published by the Free Software Foundation; either version 2 of *
8 : : * the License, or (at your option) any later version. *
9 : : * *
10 : : * This program is distributed in the hope that it will be useful, *
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 : : * GNU General Public License for more details. *
14 : : * *
15 : : * You should have received a copy of the GNU General Public License*
16 : : * along with this program; if not, contact: *
17 : : * *
18 : : * Free Software Foundation Voice: +1-617-542-5942 *
19 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
21 : : * *
22 : : \********************************************************************/
23 : :
24 : : #include <cstdint>
25 : : #include <functional>
26 : : #include <string>
27 : : #include <limits>
28 : : #include <sstream>
29 : : #include "gnc-option-uitype.hpp"
30 : : #include "kvp-value.hpp"
31 : : #include "kvp-frame.hpp"
32 : : #include "qofbookslots.h"
33 : : #include "guid.hpp"
34 : : #include "gnc-optiondb.h"
35 : : #include "gnc-optiondb.hpp"
36 : : #include "gnc-optiondb-impl.hpp"
37 : : #include "gnc-option-ui.hpp"
38 : :
39 : : #include "gnc-session.h"
40 : : constexpr const char* log_module{G_LOG_DOMAIN};
41 : :
42 : : constexpr auto stream_max = std::numeric_limits<std::streamsize>::max();
43 : : using AliasedOption = std::pair<const char*, const char*>;
44 : : using OptionAlias = std::pair<const char*, AliasedOption>;
45 : : using OptionAliases = std::vector<OptionAlias>;
46 : : class Aliases
47 : : {
48 : : static const OptionAliases c_option_aliases;
49 : : public:
50 : 56489 : static const AliasedOption* find_alias (const char* old_name)
51 : : {
52 : 56489 : if (!old_name) return nullptr;
53 : : const auto alias =
54 : 56489 : std::find_if(c_option_aliases.begin(), c_option_aliases.end(),
55 : 2227906 : [old_name](auto alias){
56 : 2227906 : return std::strcmp(old_name, alias.first) == 0;
57 : : });
58 : 56489 : if (alias == c_option_aliases.end())
59 : 54211 : return nullptr;
60 : :
61 : 2278 : return &alias->second;
62 : : }
63 : : };
64 : :
65 : : const OptionAliases Aliases::c_option_aliases
66 : : {
67 : : {"Accounts to include", {nullptr, "Accounts"}},
68 : : {"Exclude transactions between selected accounts?",
69 : : {nullptr, "Exclude transactions between selected accounts"}},
70 : : {"Filter Accounts", {nullptr, "Filter By…"}},
71 : : {"Flatten list to depth limit?",
72 : : {nullptr, "Flatten list to depth limit"}},
73 : : {"From", {nullptr, "Start Date"}},
74 : : {"Report Accounts", {nullptr, "Accounts"}},
75 : : {"Report Currency", {nullptr, "Report's currency"}},
76 : : {"Show Account Code?", {nullptr, "Show Account Code"}},
77 : : {"Show Full Account Name?", {nullptr, "Show Full Account Name"}},
78 : : {"Show Multi-currency Totals?",
79 : : {nullptr, "Show Multi-currency Totals"}},
80 : : {"Show zero balance items?", {nullptr, "Show zero balance items"}},
81 : : {"Sign Reverses?", {nullptr, "Sign Reverses"}},
82 : : {"To", {nullptr, "End Date"}},
83 : : {"Charge Type", {nullptr, "Action"}}, // easy-invoice.scm, renamed June 2018
84 : : // the following 4 options in income-gst-statement.scm renamed Dec 2018
85 : : {"Individual income columns", {nullptr, "Individual sales columns"}},
86 : : {"Individual expense columns",
87 : : {nullptr, "Individual purchases columns"}},
88 : : {"Remittance amount", {nullptr, "Gross Balance"}},
89 : : {"Net Income", {nullptr, "Net Balance"}},
90 : : // transaction.scm:
91 : : {"Use Full Account Name?", {nullptr, "Use Full Account Name"}},
92 : : {"Use Full Other Account Name?",
93 : : {nullptr, "Use Full Other Account Name"}},
94 : : {"Void Transactions?", {"Filter", "Void Transactions"}},
95 : : {"Void Transactions", {"Filter", "Void Transactions"}},
96 : : {"Account Substring", {"Filter", "Account Name Filter"}},
97 : : {"Enable links", {nullptr, "Enable Links"}},
98 : : // trep-engine: moved currency options to own tab
99 : : {"Common Currency", {"Currency", "Common Currency"}},
100 : : {"Show original currency amount",
101 : : {"Currency", "Show original currency amount"}},
102 : : {"Report's currency", {"Currency", "Report's currency"}},
103 : : {"Reconcile Status", {nullptr, "Reconciled Status"}},
104 : : // new-owner-report.scm, renamed Oct 2020 to differentiate with
105 : : // Document Links:
106 : : {"Links", {nullptr, "Transaction Links"}},
107 : : // invoice.scm, renamed November 2018
108 : : {"Individual Taxes", {nullptr, "Use Detailed Tax Summary"}},
109 : : {"Show Accounts until level", {nullptr, "Levels of Subaccounts"}},
110 : : {"Invoice number", {nullptr, "Invoice Number"}},
111 : : {"Report title", {nullptr, "Report Title"}},
112 : : {"Extra notes", {nullptr, "Extra Notes"}},
113 : : // income-gst-statement.scm
114 : : {"default format", {nullptr, "Default Format"}},
115 : : {"Report format", {nullptr, "Report Format"}},
116 : : // ... replaced to …, Dec 2022
117 : : {"Filter By...", {nullptr, "Filter By…"}},
118 : : {"Specify date to filter by...", {nullptr, "Specify date to filter by…"}},
119 : : // trep-engine:
120 : : {"Running Balance", {nullptr, "Account Balance"}},
121 : : {"Totals", {nullptr, "Grand Total"}},
122 : : };
123 : :
124 : : static bool
125 : 832020 : operator==(const std::string& str, const char* cstr)
126 : : {
127 : 832020 : return strcmp(str.c_str(), cstr) == 0;
128 : : }
129 : :
130 : : void
131 : 8311 : GncOptionSection::foreach_option(std::function<void(GncOption&)> func)
132 : : {
133 : 8311 : std::for_each(m_options.begin(), m_options.end(), func);
134 : 8311 : }
135 : :
136 : : void
137 : 0 : GncOptionSection::foreach_option(std::function<void(const GncOption&)> func) const
138 : : {
139 : 0 : std::for_each(m_options.begin(), m_options.end(), func);
140 : 0 : }
141 : :
142 : : void
143 : 83284 : GncOptionSection::add_option(GncOption&& option)
144 : : {
145 : 83284 : m_options.push_back(std::move(option));
146 : 83284 : if (!std::is_sorted(m_options.begin(), m_options.end()))
147 : 22623 : std::sort(m_options.begin(), m_options.end());
148 : 83284 : }
149 : :
150 : : void
151 : 7 : GncOptionSection::remove_option(const char* name)
152 : : {
153 : 7 : m_options.erase(std::remove_if(m_options.begin(), m_options.end(),
154 : 19 : [name](const auto& option) -> bool
155 : : {
156 : 19 : return option.get_name() == name;
157 : 7 : }), m_options.end());
158 : 7 : }
159 : :
160 : : const GncOption*
161 : 130583 : GncOptionSection::find_option(const char* name) const
162 : : {
163 : 130583 : auto option = std::find_if(m_options.begin(), m_options.end(),
164 : 832001 : [name](auto& option) -> bool {
165 : 832001 : return option.get_name() == name;
166 : : });
167 : 130583 : if (option != m_options.end())
168 : 107239 : return &*option;
169 : :
170 : 23344 : auto alias = Aliases::find_alias(name);
171 : 23344 : if (!alias || alias->first) // No alias or the alias
172 : 23321 : return nullptr; // is in a different section.
173 : 23 : return find_option(alias->second);
174 : : }
175 : :
176 : 2446 : GncOptionDB::GncOptionDB() : m_default_section{} {}
177 : :
178 : 0 : GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
179 : :
180 : : void
181 : 83284 : GncOptionDB::register_option(const char* sectname, GncOption&& option)
182 : : {
183 : 83284 : auto section = find_section(sectname);
184 : :
185 : 83284 : if (section)
186 : : {
187 : 72877 : section->add_option(std::move(option));
188 : 72877 : return;
189 : : }
190 : :
191 : 10407 : m_sections.push_back(std::make_shared<GncOptionSection>(sectname));
192 : 10407 : m_sections.back()->add_option(std::move(option));
193 : 10407 : if (!std::is_sorted(m_sections.begin(), m_sections.end()))
194 : 5825 : std::sort(m_sections.begin(), m_sections.end());
195 : : }
196 : :
197 : : void
198 : 20 : GncOptionDB::register_option(const char* sectname, GncOption* option)
199 : : {
200 : 20 : register_option(sectname, std::move(*option));
201 : 20 : delete option;
202 : 20 : }
203 : :
204 : : void
205 : 7 : GncOptionDB::unregister_option(const char* sectname, const char* name)
206 : : {
207 : 7 : auto section = find_section(sectname);
208 : 7 : if (section)
209 : 7 : section->remove_option(name);
210 : 7 : }
211 : :
212 : : void
213 : 2354 : GncOptionDB::set_default_section(const char* sectname)
214 : : {
215 : 2354 : m_default_section = find_section(sectname);
216 : 2354 : }
217 : :
218 : : const GncOptionSection* const
219 : 0 : GncOptionDB::get_default_section() const noexcept
220 : : {
221 : 0 : return m_default_section;
222 : : }
223 : :
224 : : const GncOptionSection*
225 : 226029 : GncOptionDB::find_section(const std::string& section) const
226 : : {
227 : 226029 : auto db_section = std::find_if(m_sections.begin(), m_sections.end(),
228 : 658899 : [§ion](auto& sect) -> bool
229 : : {
230 : 658899 : return section == sect->get_name();
231 : : });
232 : 226029 : return db_section == m_sections.end() ? nullptr : db_section->get();
233 : : }
234 : :
235 : : const GncOption*
236 : 140384 : GncOptionDB::find_option(const std::string& section, const char* name) const
237 : : {
238 : 140384 : auto db_section = const_cast<GncOptionDB*>(this)->find_section(section);
239 : 140384 : const GncOption* option = nullptr;
240 : 140384 : if (db_section)
241 : 130560 : option = db_section->find_option(name);
242 : 140384 : if (option)
243 : 107239 : return option;
244 : 33145 : auto alias = Aliases::find_alias(name);
245 : : /* Only try again if alias.first isn't
246 : : * nullptr. GncOptionSection::find_option already checked if the alias
247 : : * should have been in the same section.
248 : : */
249 : 33145 : if (alias && alias->first && section != alias->first)
250 : 6699 : return find_option(alias->first, alias->second);
251 : 30912 : return nullptr;
252 : : }
253 : :
254 : : std::string
255 : 7 : GncOptionDB::lookup_string_option(const char* section, const char* name)
256 : : {
257 : 7 : static const std::string empty_string{};
258 : :
259 : 7 : auto db_opt = find_option(section, name);
260 : 7 : if (!db_opt)
261 : 1 : return empty_string;
262 : 6 : return db_opt->get_value<std::string>();
263 : : }
264 : :
265 : : void
266 : 82 : GncOptionDB::make_internal(const char* section, const char* name)
267 : : {
268 : :
269 : 82 : auto db_opt = find_option(section, name);
270 : 82 : if (db_opt)
271 : 82 : db_opt->make_internal();
272 : 82 : }
273 : :
274 : : std::ostream&
275 : 2 : GncOptionDB::save_option_key_value(std::ostream& oss,
276 : : const std::string& section,
277 : : const std::string& name) const noexcept
278 : : {
279 : :
280 : 2 : auto db_opt = find_option(section, name.c_str());
281 : 2 : if (!db_opt || !db_opt->is_changed())
282 : 1 : return oss;
283 : 1 : oss << section.substr(0, classifier_size_max) << ":" <<
284 : 1 : name.substr(0, classifier_size_max) << "=" << *db_opt << ";";
285 : 1 : return oss;
286 : : }
287 : :
288 : : std::istream&
289 : 1 : GncOptionDB::load_option_key_value(std::istream& iss)
290 : : {
291 : :
292 : : char section[classifier_size_max], name[classifier_size_max];
293 : 1 : iss.getline(section, classifier_size_max, ':');
294 : 1 : iss.getline(name, classifier_size_max, '=');
295 : 1 : if (!iss)
296 : 0 : throw std::invalid_argument("Section or name delimiter not found or values too long");
297 : 1 : auto option = find_option(section, name);
298 : 1 : if (!option)
299 : 0 : iss.ignore(stream_max, ';');
300 : : else
301 : : {
302 : 1 : std::string value;
303 : 1 : std::getline(iss, value, ';');
304 : 1 : std::istringstream item_iss{value};
305 : 1 : item_iss >> *option;
306 : 1 : }
307 : 1 : return iss;
308 : : }
309 : :
310 : : std::ostream&
311 : 0 : GncOptionDB::save_to_key_value(std::ostream& oss) const noexcept
312 : : {
313 : :
314 : 0 : foreach_section(
315 : 0 : [&oss](const GncOptionSectionPtr& section)
316 : : {
317 : 0 : oss << "[Options]\n";
318 : 0 : section->foreach_option(
319 : 0 : [&oss, §ion](auto& option)
320 : : {
321 : 0 : if (option.is_changed())
322 : 0 : oss << section->get_name().substr(0, classifier_size_max) <<
323 : 0 : ':' << option.get_name().substr(0, classifier_size_max) <<
324 : 0 : '=' << option << '\n';
325 : 0 : });
326 : 0 : });
327 : 0 : return oss;
328 : : }
329 : :
330 : : std::istream&
331 : 0 : GncOptionDB::load_from_key_value(std::istream& iss)
332 : : {
333 : 0 : if (iss.peek() == '[')
334 : : {
335 : : char buf[classifier_size_max];
336 : 0 : iss.getline(buf, classifier_size_max);
337 : 0 : if (strcmp(buf, "[Options]") != 0) // safe
338 : 0 : throw std::runtime_error("Wrong secion header for options.");
339 : : }
340 : : // Otherwise assume we were sent here correctly:
341 : 0 : while (iss.peek() != '[') //Indicates the start of the next file section
342 : : {
343 : 0 : load_option_key_value(iss);
344 : : }
345 : 0 : return iss;
346 : : }
347 : :
348 : : size_t
349 : 0 : GncOptionDB::register_callback(GncOptionDBChangeCallback cb, void* data)
350 : : {
351 : : constexpr std::hash<GncOptionDBChangeCallback> cb_hash;
352 : 0 : auto id{cb_hash(cb)};
353 : 0 : if (std::find_if(m_callbacks.begin(), m_callbacks.end(),
354 : 0 : [id](auto&cb)->bool{ return cb.m_id == id; }) == m_callbacks.end())
355 : 0 : m_callbacks.emplace_back(id, cb, data);
356 : 0 : return id;
357 : : }
358 : :
359 : : void
360 : 0 : GncOptionDB::unregister_callback(size_t id)
361 : : {
362 : 0 : m_callbacks.erase(std::remove_if(m_callbacks.begin(), m_callbacks.end(),
363 : 0 : [id](auto& cb)->bool { return cb.m_id == id; }),
364 : 0 : m_callbacks.end());
365 : 0 : }
366 : :
367 : : void
368 : 0 : GncOptionDB::run_callbacks()
369 : : {
370 : 0 : std::for_each(m_callbacks.begin(), m_callbacks.end(),
371 : 0 : [](auto& cb)->void { cb.m_func(cb.m_data); });
372 : 0 : }
373 : :
374 : : static inline void
375 : 0 : counter_option_path(const GncOption& option, GSList* list, std::string& name)
376 : : {
377 : 0 : constexpr const char* counters{"counters"};
378 : 0 : constexpr const char* formats{"counter_formats"};
379 : 0 : auto key = option.get_key();
380 : 0 : name = key.substr(0, key.size() - 1);
381 : 0 : list->next->data = (void*)name.c_str();
382 : 0 : if (option.get_name().rfind("format")
383 : 0 : != std::string::npos)
384 : 0 : list->data = (void*)formats;
385 : : else
386 : 0 : list->data = (void*)counters;
387 : 0 : }
388 : :
389 : : static inline void
390 : 1 : option_path(const GncOption& option, GSList* list)
391 : : {
392 : 1 : list->next->data = (void*)option.get_name().c_str();
393 : 1 : list->data = (void*)option.get_section().c_str();
394 : 1 : }
395 : :
396 : : /* The usage "option.template get_value<bool>()" looks weird, but it's required
397 : : * by the C++ standard: "When the name of a member template specialization
398 : : * appears after . or -> in a postfix-expression, or after nested-name-specifier
399 : : * in a qualified-id, and the postfix-expression or qualified-id explicitly
400 : : * depends on a template-parameter (14.6.2), the member template name must be
401 : : * prefixed by the keyword template. Otherwise the name is assumed to name a
402 : : * non-template."
403 : : */
404 : : static inline KvpValue*
405 : 0 : kvp_value_from_bool_option(const GncOption& option)
406 : : {
407 : 0 : auto val{option.template get_value<bool>()};
408 : : // ~KvpValue will g_free the value.
409 : 0 : return new KvpValue(val ? g_strdup("t") : g_strdup("f"));
410 : : }
411 : :
412 : : static bool
413 : 1 : is_qofinstance_ui_type(GncOptionUIType type)
414 : : {
415 : 1 : switch (type)
416 : : {
417 : 0 : case GncOptionUIType::ACCOUNT_SEL:
418 : : case GncOptionUIType::BUDGET:
419 : : case GncOptionUIType::OWNER:
420 : : case GncOptionUIType::CUSTOMER:
421 : : case GncOptionUIType::VENDOR:
422 : : case GncOptionUIType::EMPLOYEE:
423 : : case GncOptionUIType::INVOICE:
424 : : case GncOptionUIType::TAX_TABLE:
425 : : case GncOptionUIType::QUERY:
426 : 0 : return true;
427 : 1 : default:
428 : 1 : return false;
429 : : }
430 : : }
431 : :
432 : : static inline KvpValue*
433 : 0 : kvp_value_from_qof_instance_option(const GncOption& option)
434 : : {
435 : 0 : const QofInstance* inst{QOF_INSTANCE(option.template get_value<const QofInstance*>())};
436 : 0 : auto guid = guid_copy(qof_instance_get_guid(inst));
437 : 0 : return new KvpValue(guid);
438 : : }
439 : :
440 : : /* GncOptionDateFormat Constants and support functions. These are frozen for backwards compatibility. */
441 : :
442 : : static const char* date_format_frame_key = "Fancy Date Format";
443 : : static const char* date_format_custom_key ="custom";
444 : : static const char* date_format_months_key = "month";
445 : : static const char* date_format_years_key = "years";
446 : : static const char *date_format_format_key = "fmt";
447 : :
448 : : static inline KvpValue *
449 : 0 : kvp_frame_from_date_format_option(const GncOption& option)
450 : : {
451 : 0 : auto [format, months, years, custom] = option.get_value<GncOptionDateFormat>();
452 : :
453 : 0 : if (format == QOF_DATE_FORMAT_UNSET)
454 : 0 : return nullptr;
455 : :
456 : 0 : auto frame{new KvpFrame};
457 : 0 : frame->set({date_format_format_key}, new KvpValue(g_strdup(gnc_date_dateformat_to_string(format))));
458 : 0 : frame->set({date_format_months_key}, new KvpValue(g_strdup(gnc_date_monthformat_to_string(months))));
459 : 0 : frame->set({date_format_years_key}, new KvpValue(static_cast<int64_t>(years)));
460 : 0 : frame->set({date_format_custom_key}, new KvpValue(g_strdup(custom.c_str())));
461 : 0 : return new KvpValue(frame);
462 : 0 : };
463 : :
464 : : void
465 : 2 : GncOptionDB::save_to_kvp(QofBook* book, bool clear_options) const noexcept
466 : : {
467 : 2 : if (clear_options)
468 : 1 : qof_book_options_delete(book, nullptr);
469 : 2 : const_cast<GncOptionDB*>(this)->foreach_section(
470 : 2 : [book](GncOptionSectionPtr& section)
471 : : {
472 : 16 : section->foreach_option(
473 : 8 : [book, §ion](GncOption& option) {
474 : 12 : if (option.is_dirty())
475 : : {
476 : : /* We need the string name out here so that it stays in
477 : : * scope long enough to pass its c_str to
478 : : * gnc_book_set_option. */
479 : 1 : std::string name;
480 : : /* qof_book_set_option wants a GSList path. Let's avoid
481 : : * allocating and make one here. */
482 : 1 : GSList list_tail{}, list_head{nullptr, &list_tail};
483 : 1 : if (strcmp(section->get_name().c_str(), "Counters") == 0)
484 : 0 : counter_option_path(option, &list_head, name);
485 : : else
486 : 1 : option_path(option, &list_head);
487 : 1 : auto type{option.get_ui_type()};
488 : 1 : KvpValue* kvp{};
489 : 1 : if (type == GncOptionUIType::BOOLEAN)
490 : 0 : kvp = kvp_value_from_bool_option(option);
491 : 1 : else if (is_qofinstance_ui_type(type))
492 : 0 : kvp = kvp_value_from_qof_instance_option(option);
493 : 1 : else if (type == GncOptionUIType::NUMBER_RANGE)
494 : : {
495 : 0 : if (option.is_alternate())
496 : : {
497 : 0 : kvp = new KvpValue(static_cast<int64_t>(option.template get_value<int>()));
498 : : }
499 : : else
500 : : {
501 : 0 : kvp = new KvpValue(option.template get_value<double>());
502 : : }
503 : : }
504 : 1 : else if (type == GncOptionUIType::DATE_FORMAT)
505 : 0 : kvp = kvp_frame_from_date_format_option(option);
506 : : else
507 : : {
508 : 1 : auto str{option.template get_value<std::string>()};
509 : 2 : kvp = new KvpValue{g_strdup(str.c_str())};
510 : 1 : }
511 : 1 : qof_book_set_option(book, kvp, &list_head);
512 : 1 : option.mark_saved();
513 : 1 : }
514 : 12 : });
515 : 8 : });
516 : 2 : }
517 : :
518 : : static inline void
519 : 0 : fill_option_from_string_kvp(GncOption& option, KvpValue* kvp)
520 : : {
521 : 0 : auto str{kvp->get<const char*>()};
522 : 0 : if (option.get_ui_type() == GncOptionUIType::BOOLEAN)
523 : 0 : option.set_value(*str == 't' ? true : false);
524 : : else
525 : 0 : option.set_value(std::string{str});
526 : 0 : }
527 : :
528 : : static inline void
529 : 0 : fill_option_from_guid_kvp(GncOption& option, KvpValue* kvp)
530 : : {
531 : 0 : auto guid{kvp->get<GncGUID*>()};
532 : 0 : option.set_value(
533 : 0 : (const QofInstance*)qof_instance_from_guid(guid, option.get_ui_type()));
534 : 0 : }
535 : :
536 : : static inline void
537 : 0 : fill_option_from_date_format_kvp(GncOption& option, KvpValue* kvp)
538 : : {
539 : 0 : GncOptionDateFormat default_fmt{QOF_DATE_FORMAT_UNSET, GNCDATE_MONTH_NUMBER, true, ""};
540 : 0 : auto frame{kvp->get<KvpFrame*>()};
541 : 0 : if (!frame)
542 : : {
543 : 0 : option.set_value(default_fmt);
544 : 0 : return;
545 : : }
546 : 0 : auto format_str{frame->get_slot({date_format_format_key})->get<const char*>()};
547 : : QofDateFormat format;
548 : 0 : if (!format_str || gnc_date_string_to_dateformat(format_str, &format))
549 : : {
550 : 0 : option.set_value(default_fmt);
551 : 0 : return;
552 : : }
553 : 0 : GNCDateMonthFormat months = GNCDATE_MONTH_NUMBER;
554 : 0 : auto months_str{frame->get_slot({date_format_months_key})->get<const char*>()};
555 : 0 : if (months_str)
556 : 0 : gnc_date_string_to_monthformat(months_str, &months);
557 : 0 : auto years_num{frame->get_slot({date_format_years_key})->get<int64_t>()};
558 : 0 : bool years = static_cast<bool>(years_num);
559 : 0 : auto custom_str{frame->get_slot({date_format_custom_key})->get<const char*>()};
560 : 0 : option.set_value<GncOptionDateFormat>({format, months, years, custom_str ? custom_str : ""});
561 : 0 : }
562 : :
563 : : void
564 : 0 : GncOptionDB::load_from_kvp(QofBook* book) noexcept
565 : : {
566 : 0 : foreach_section(
567 : 0 : [book](GncOptionSectionPtr& section)
568 : : {
569 : 0 : section->foreach_option(
570 : 0 : [book, §ion](GncOption& option)
571 : : {
572 : : // Make path list as above.
573 : 0 : std::string name;
574 : : /* qof_book_set_option wants a GSList path. Let's avoid
575 : : * allocating and make one here. */
576 : 0 : GSList list_tail{}, list_head{nullptr, &list_tail};
577 : 0 : if (strcmp(section->get_name().c_str(), "Counters") == 0)
578 : 0 : counter_option_path(option, &list_head, name);
579 : : else
580 : 0 : option_path(option, &list_head);
581 : 0 : auto kvp = qof_book_get_option(book, &list_head);
582 : 0 : if (!kvp)
583 : 0 : return;
584 : :
585 : 0 : auto set_double = [&option, kvp, &list_head]() {
586 : : /*counters might have been set as doubles
587 : : * because of
588 : : * https://bugs.gnucash.org/show_bug.cgi?id=798930. They
589 : : * should be int.
590 : : */
591 : 0 : constexpr const char *counters{"counters"};
592 : 0 : auto value{kvp->get<double>()};
593 : 0 : if (strcmp(static_cast<char*>(list_head.data), counters) == 0)
594 : 0 : option.set_value(static_cast<int>(value));
595 : : else
596 : 0 : option.set_value(value);
597 : 0 : };
598 : :
599 : 0 : switch (kvp->get_type())
600 : : {
601 : 0 : case KvpValue::Type::DOUBLE:
602 : 0 : set_double();
603 : 0 : break;
604 : 0 : case KvpValue::Type::INT64:
605 : 0 : option.set_value(static_cast<int>(kvp->get<int64_t>()));
606 : 0 : break;
607 : 0 : case KvpValue::Type::STRING:
608 : 0 : fill_option_from_string_kvp(option, kvp);
609 : 0 : break;
610 : 0 : case KvpValue::Type::GUID:
611 : 0 : fill_option_from_guid_kvp(option, kvp);
612 : 0 : break;
613 : 0 : case KvpValue::Type::FRAME:
614 : 0 : if (g_strcmp0(option.get_name().c_str(), date_format_frame_key) == 0)
615 : 0 : fill_option_from_date_format_kvp(option, kvp);
616 : 0 : break;
617 : 0 : default:
618 : 0 : return;
619 : : break;
620 : : }
621 : 0 : });
622 : 0 : });
623 : 0 : }
624 : :
625 : : void
626 : 4542 : gnc_register_string_option(GncOptionDB* db, const char* section,
627 : : const char* name, const char* key,
628 : : const char* doc_string, std::string value)
629 : : {
630 : : GncOption option{section, name, key, doc_string, value,
631 : 4542 : GncOptionUIType::STRING};
632 : 4542 : db->register_option(section, std::move(option));
633 : 4542 : }
634 : :
635 : : void
636 : 156 : gnc_register_text_option(GncOptionDB* db, const char* section, const char* name,
637 : : const char* key, const char* doc_string,
638 : : std::string value)
639 : : {
640 : : GncOption option{section, name, key, doc_string, value,
641 : 156 : GncOptionUIType::TEXT};
642 : 156 : db->register_option(section, std::move(option));
643 : :
644 : 156 : }
645 : :
646 : : void
647 : 174 : gnc_register_font_option(GncOptionDB* db, const char* section,
648 : : const char* name, const char* key,
649 : : const char* doc_string, std::string value)
650 : : {
651 : : GncOption option{section, name, key, doc_string, value,
652 : 174 : GncOptionUIType::FONT};
653 : 174 : db->register_option(section, std::move(option));
654 : 174 : }
655 : :
656 : : void
657 : 21 : gnc_register_budget_option(GncOptionDB* db, const char* section,
658 : : const char* name, const char* key,
659 : : const char* doc_string, GncBudget *value)
660 : : {
661 : 42 : GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
662 : : (const QofInstance*)value,
663 : 21 : GncOptionUIType::BUDGET}};
664 : 21 : db->register_option(section, std::move(option));
665 : 21 : }
666 : :
667 : : void
668 : 50 : gnc_register_color_option(GncOptionDB* db, const char* section,
669 : : const char* name, const char* key,
670 : : const char* doc_string, std::string value)
671 : : {
672 : : GncOption option{section, name, key, doc_string, value,
673 : 50 : GncOptionUIType::COLOR};
674 : 50 : db->register_option(section, std::move(option));
675 : 50 : }
676 : :
677 : : void
678 : 0 : gnc_register_commodity_option(GncOptionDB* db, const char* section,
679 : : const char* name, const char* key,
680 : : const char* doc_string, gnc_commodity *value)
681 : : {
682 : 0 : GncOption option{GncOptionCommodityValue{section, name, key, doc_string,
683 : : value,
684 : 0 : GncOptionUIType::COMMODITY}};
685 : 0 : db->register_option(section, std::move(option));
686 : 0 : }
687 : :
688 : : void
689 : 5 : gnc_register_commodity_option(GncOptionDB* db, const char* section,
690 : : const char* name, const char* key,
691 : : const char* doc_string, const char* value)
692 : : {
693 : 5 : gnc_commodity* commodity{};
694 : 5 : const auto book{qof_session_get_book(gnc_get_current_session())};
695 : 5 : const auto commodity_table{gnc_commodity_table_get_table(book)};
696 : 5 : const auto namespaces{gnc_commodity_table_get_namespaces(commodity_table)};
697 : 5 : for (auto node = namespaces; node && commodity == nullptr;
698 : 0 : node = g_list_next(node))
699 : : {
700 : 10 : commodity = gnc_commodity_table_lookup(commodity_table,
701 : 5 : (const char*)(node->data),
702 : : value);
703 : 5 : if (commodity)
704 : 5 : break;
705 : : }
706 : 10 : GncOption option{GncOptionCommodityValue{section, name, key, doc_string,
707 : : commodity,
708 : 5 : GncOptionUIType::COMMODITY}};
709 : 5 : db->register_option(section, std::move(option));
710 : 5 : g_list_free (namespaces);
711 : 5 : }
712 : :
713 : : void
714 : 37112 : gnc_register_simple_boolean_option(GncOptionDB* db,
715 : : const char* section, const char* name,
716 : : const char* key, const char* doc_string,
717 : : bool value)
718 : : {
719 : : GncOption option{section, name, key, doc_string, value,
720 : 37112 : GncOptionUIType::BOOLEAN};
721 : 37112 : db->register_option(section, std::move(option));
722 : 37112 : }
723 : :
724 : : void
725 : 61 : gnc_register_pixmap_option(GncOptionDB* db, const char* section,
726 : : const char* name, const char* key,
727 : : const char* doc_string, std::string value)
728 : : {
729 : : GncOption option{section, name, key, doc_string, value,
730 : 61 : GncOptionUIType::PIXMAP};
731 : 61 : db->register_option(section, std::move(option));
732 : 61 : }
733 : :
734 : : void
735 : 3175 : gnc_register_account_list_option(GncOptionDB* db, const char* section,
736 : : const char* name, const char* key,
737 : : const char* doc_string,
738 : : const GncOptionAccountList& value)
739 : : {
740 : 6350 : GncOption option{GncOptionAccountListValue{section, name, key, doc_string,
741 : 3175 : GncOptionUIType::ACCOUNT_LIST, value}};
742 : 3175 : db->register_option(section, std::move(option));
743 : 3175 : }
744 : :
745 : : void
746 : 158 : gnc_register_account_list_limited_option(GncOptionDB* db,
747 : : const char* section, const char* name,
748 : : const char* key,
749 : : const char* doc_string,
750 : : const GncOptionAccountList& value,
751 : : GncOptionAccountTypeList&& allowed)
752 : : {
753 : : try
754 : : {
755 : 313 : GncOption option{GncOptionAccountListValue{section, name, key, doc_string,
756 : 313 : GncOptionUIType::ACCOUNT_LIST, value, std::move(allowed)}};
757 : 155 : db->register_option(section, std::move(option));
758 : 155 : }
759 : 3 : catch (const std::invalid_argument& err)
760 : : {
761 : 3 : PWARN("Account List Limited Option, value failed validation, option not registered.");
762 : 3 : }
763 : 158 : }
764 : :
765 : : using AccountPair = std::pair<GncOptionAccountList&,
766 : : const GncOptionAccountTypeList&>;
767 : : static void
768 : 179 : find_children(Account* account, void* data)
769 : : {
770 : 179 : auto datapair =
771 : : (AccountPair*)data;
772 : 179 : GncOptionAccountList& list = datapair->first;
773 : 179 : const GncOptionAccountTypeList& types = datapair->second;
774 : 179 : if (std::find(types.begin(), types.end(),
775 : 358 : xaccAccountGetType(account)) != types.end())
776 : 73 : list.push_back(*qof_entity_get_guid(account));
777 : 179 : }
778 : :
779 : : GncOptionAccountList
780 : 19 : gnc_account_list_from_types(QofBook *book,
781 : : const GncOptionAccountTypeList& types)
782 : : {
783 : 19 : GncOptionAccountList list;
784 : 19 : AccountPair funcdata{list, types};
785 : 19 : Account* base_acct = gnc_book_get_root_account(book);
786 : 19 : gnc_account_foreach_descendant(base_acct, (AccountCb)find_children,
787 : : &funcdata);
788 : 38 : return list;
789 : 0 : }
790 : :
791 : :
792 : : void
793 : 32 : gnc_register_account_sel_limited_option(GncOptionDB* db,
794 : : const char* section, const char* name,
795 : : const char* key, const char* doc_string,
796 : : const Account* value,
797 : : GncOptionAccountTypeList&& allowed)
798 : : {
799 : : try
800 : : {
801 : 64 : GncOption option{GncOptionAccountSelValue{section, name, key, doc_string,
802 : 64 : GncOptionUIType::ACCOUNT_SEL, value, std::move(allowed)}};
803 : 32 : db->register_option(section, std::move(option));
804 : 32 : }
805 : 0 : catch (const std::invalid_argument& err)
806 : : {
807 : 0 : PWARN("Account Sel Limited Option, value failed validation, option not registerd.");
808 : 0 : }
809 : 32 : }
810 : :
811 : : void
812 : 15738 : gnc_register_multichoice_option(GncOptionDB* db, const char* section,
813 : : const char* name, const char* key,
814 : : const char* doc_string, const char* default_val,
815 : : GncMultichoiceOptionChoices&& choices)
816 : : {
817 : 31476 : std::string defval{default_val};
818 : 15738 : auto found{std::find_if(choices.begin(), choices.end(),
819 : 25952 : [&defval](auto& choice)->bool {
820 : 25952 : return defval == std::get<0>(choice);
821 : : })};
822 : 15738 : if (found == choices.end())
823 : 20 : defval = (choices.empty() ? std::string{"None"} :
824 : 5 : std::get<0>(choices.at(0)));
825 : 47214 : GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
826 : 31476 : defval.c_str(), std::move(choices)}};
827 : 15738 : db->register_option(section, std::move(option));
828 : 15738 : }
829 : :
830 : : void
831 : 11 : gnc_register_list_option(GncOptionDB* db, const char* section,
832 : : const char* name, const char* key,
833 : : const char* doc_string, const char* value,
834 : : GncMultichoiceOptionChoices&& list)
835 : : {
836 : 22 : GncOption option{GncOptionMultichoiceValue{section, name, key, doc_string,
837 : 22 : value, std::move(list), GncOptionUIType::LIST}};
838 : 11 : db->register_option(section, std::move(option));
839 : 11 : }
840 : :
841 : : /* Only balance-forecast.scm, sample-report.scm, and net-charts.scm
842 : : * use decimals and fractional steps and they can be worked around.
843 : : */
844 : : template <typename ValueType> void
845 : 298 : gnc_register_number_range_option(GncOptionDB* db, const char* section,
846 : : const char* name, const char* key,
847 : : const char* doc_string, ValueType value,
848 : : ValueType min, ValueType max, ValueType step)
849 : : {
850 : : try
851 : : {
852 : 298 : GncOption option{GncOptionRangeValue<ValueType>{section, name, key,
853 : : doc_string, value, min,
854 : : max, step}};
855 : 298 : db->register_option(section, std::move(option));
856 : 298 : }
857 : 0 : catch(const std::invalid_argument& err)
858 : : {
859 : 0 : PWARN("Number Range Option %s, option not registerd.",
860 : : err.what());
861 : : }
862 : 298 : }
863 : :
864 : : void
865 : 412 : gnc_register_number_plot_size_option(GncOptionDB* db,
866 : : const char* section, const char* name,
867 : : const char* key, const char* doc_string,
868 : : int value)
869 : : {
870 : : //65K is 10x reasonable, but it's a convenient constant.
871 : 824 : GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
872 : 412 : value, 10, UINT16_MAX, 1, GncOptionUIType::PLOT_SIZE}};
873 : 412 : db->register_option(section, std::move(option));
874 : 412 : }
875 : :
876 : : void
877 : 7 : gnc_register_query_option(GncOptionDB* db, const char* section,
878 : : const char* name, const QofQuery* value)
879 : : {
880 : : GncOption option{section, name, "", "", value,
881 : 7 : GncOptionUIType::INTERNAL};
882 : 7 : db->register_option(section, std::move(option));
883 : 7 : }
884 : :
885 : : void
886 : 20 : gnc_register_owner_option(GncOptionDB* db, const char* section,
887 : : const char* name, const char* key,
888 : : const char* doc_string, const GncOwner* value,
889 : : GncOwnerType type)
890 : : {
891 : : GncOptionUIType uitype;
892 : 20 : switch (type)
893 : : {
894 : 11 : case GNC_OWNER_CUSTOMER:
895 : 11 : uitype = GncOptionUIType::CUSTOMER;
896 : 11 : break;
897 : 3 : case GNC_OWNER_EMPLOYEE:
898 : 3 : uitype = GncOptionUIType::EMPLOYEE;
899 : 3 : break;
900 : 3 : case GNC_OWNER_JOB:
901 : 3 : uitype = GncOptionUIType::JOB;
902 : 3 : break;
903 : 3 : case GNC_OWNER_VENDOR:
904 : 3 : uitype = GncOptionUIType::VENDOR;
905 : 3 : break;
906 : 0 : default:
907 : 0 : uitype = GncOptionUIType::INTERNAL;
908 : : };
909 : 40 : GncOption option{GncOptionGncOwnerValue{section, name, key, doc_string,
910 : 40 : value, uitype}};
911 : 20 : db->register_option(section, std::move(option));
912 : 20 : }
913 : :
914 : : void
915 : 39 : gnc_register_invoice_option(GncOptionDB* db, const char* section,
916 : : const char* name, const char* key,
917 : : const char* doc_string, GncInvoice* value)
918 : : {
919 : 78 : GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
920 : : (const QofInstance*)value,
921 : 39 : GncOptionUIType::INVOICE}};
922 : 39 : db->register_option(section, std::move(option));
923 : 39 : }
924 : :
925 : : void
926 : 0 : gnc_register_taxtable_option(GncOptionDB* db, const char* section,
927 : : const char* name, const char* key,
928 : : const char* doc_string, GncTaxTable* value)
929 : : {
930 : 0 : GncOption option{GncOptionQofInstanceValue{section, name, key, doc_string,
931 : : (const QofInstance*)value,
932 : 0 : GncOptionUIType::TAX_TABLE}};
933 : 0 : db->register_option(section, std::move(option));
934 : 0 : }
935 : :
936 : : void
937 : 0 : gnc_register_invoice_print_report_option(GncOptionDB* db, const char* section,
938 : : const char* name, const char* key,
939 : : const char* doc_string, std::string value)
940 : : {
941 : : GncOption option{section, name, key, doc_string,
942 : 0 : value, GncOptionUIType::INV_REPORT};
943 : 0 : db->register_option(section, std::move(option));
944 : 0 : }
945 : :
946 : : void
947 : 0 : gnc_register_counter_option(GncOptionDB* db, const char* section,
948 : : const char* name, const char* key,
949 : : const char* doc_string, int value)
950 : : {
951 : 0 : GncOption option{GncOptionRangeValue<int>{section, name, key, doc_string,
952 : 0 : value, 0, 999999999, 1}};
953 : 0 : option.set_alternate(true);
954 : 0 : db->register_option(section, std::move(option));
955 : 0 : }
956 : :
957 : : void
958 : 0 : gnc_register_counter_format_option(GncOptionDB* db,
959 : : const char* section, const char* name,
960 : : const char* key, const char* doc_string,
961 : : std::string value)
962 : : {
963 : : GncOption option{section, name, key, doc_string, value,
964 : 0 : GncOptionUIType::STRING};
965 : 0 : db->register_option(section, std::move(option));
966 : 0 : }
967 : :
968 : : void
969 : 0 : gnc_register_dateformat_option(GncOptionDB* db, const char* section,
970 : : const char* name, const char* key,
971 : : const char* doc_string, GncOptionDateFormat&& value)
972 : : {
973 : 0 : GncOption option{section, name, key, doc_string, std::move(value),
974 : 0 : GncOptionUIType::DATE_FORMAT};
975 : 0 : db->register_option(section, std::move(option));
976 : 0 : }
977 : :
978 : : void
979 : 2271 : gnc_register_currency_option(GncOptionDB* db, const char* section,
980 : : const char* name, const char* key,
981 : : const char* doc_string, gnc_commodity *value)
982 : : {
983 : 4542 : GncOption option{GncOptionCommodityValue{
984 : : section, name, key, doc_string, value, GncOptionUIType::CURRENCY
985 : 2271 : }};
986 : 2271 : db->register_option(section, std::move(option));
987 : 2271 : }
988 : :
989 : : void
990 : 0 : gnc_register_currency_option(GncOptionDB* db, const char* section,
991 : : const char* name, const char* key,
992 : : const char* doc_string, const char* value)
993 : : {
994 : 0 : const auto book{qof_session_get_book(gnc_get_current_session())};
995 : 0 : const auto commodity_table{gnc_commodity_table_get_table(book)};
996 : 0 : const auto commodity = gnc_commodity_table_lookup(commodity_table,
997 : : "CURRENCY",
998 : : value);
999 : 0 : GncOption option{GncOptionCommodityValue{
1000 : : section, name, key, doc_string, commodity, GncOptionUIType::CURRENCY
1001 : 0 : }};
1002 : 0 : db->register_option(section, std::move(option));
1003 : 0 : }
1004 : :
1005 : : void
1006 : 1 : gnc_register_date_option(GncOptionDB* db, const char* section,
1007 : : const char* name, const char* key,
1008 : : const char* doc_string, time64 time,
1009 : : RelativeDateUI ui)
1010 : : {
1011 : 1 : auto ui_type = ui == RelativeDateUI::BOTH ? GncOptionUIType::DATE_BOTH :
1012 : 0 : ui == RelativeDateUI::RELATIVE ? GncOptionUIType::DATE_RELATIVE :
1013 : : GncOptionUIType::DATE_ABSOLUTE;
1014 : 2 : GncOption option{GncOptionDateValue(section, name, key, doc_string,
1015 : 1 : ui_type, time)};
1016 : 1 : db->register_option(section, std::move(option));
1017 : 1 : }
1018 : :
1019 : : void
1020 : 5 : gnc_register_date_option(GncOptionDB* db, const char* section,
1021 : : const char* name, const char* key,
1022 : : const char* doc_string, RelativeDatePeriod period,
1023 : : RelativeDateUI ui)
1024 : : {
1025 : 5 : auto ui_type = ui == RelativeDateUI::BOTH ? GncOptionUIType::DATE_BOTH :
1026 : 0 : ui == RelativeDateUI::RELATIVE ? GncOptionUIType::DATE_RELATIVE :
1027 : : GncOptionUIType::DATE_ABSOLUTE;
1028 : 10 : GncOption option{GncOptionDateValue(section, name, key, doc_string,
1029 : 5 : ui_type, period)};
1030 : 5 : db->register_option(section, std::move(option));
1031 : 5 : }
1032 : :
1033 : : void
1034 : 10 : gnc_register_date_option(GncOptionDB* db,
1035 : : const char* section, const char* name,
1036 : : const char* key, const char* doc_string,
1037 : : RelativeDatePeriodVec& period_set,
1038 : : bool both)
1039 : : {
1040 : 13 : auto is_absolute = period_set.size() == 1 &&
1041 : 3 : period_set.front() == RelativeDatePeriod::ABSOLUTE;
1042 : 16 : auto ui_type = both ? GncOptionUIType::DATE_BOTH :
1043 : 6 : is_absolute ? GncOptionUIType::DATE_ABSOLUTE : GncOptionUIType::DATE_RELATIVE;
1044 : 20 : GncOption option{GncOptionDateValue(section, name, key, doc_string,
1045 : 10 : ui_type, period_set)};
1046 : 10 : if (is_absolute)
1047 : 3 : option.set_default_value(gnc_time(nullptr));
1048 : 10 : db->register_option(section, std::move(option));
1049 : 10 : }
1050 : :
1051 : :
1052 : : static const RelativeDatePeriodVec begin_dates
1053 : : {
1054 : : RelativeDatePeriod::TODAY,
1055 : : RelativeDatePeriod::START_THIS_MONTH,
1056 : : RelativeDatePeriod::START_PREV_MONTH,
1057 : : RelativeDatePeriod::START_CURRENT_QUARTER,
1058 : : RelativeDatePeriod::START_PREV_QUARTER,
1059 : : RelativeDatePeriod::START_CAL_YEAR,
1060 : : RelativeDatePeriod::START_PREV_YEAR,
1061 : : RelativeDatePeriod::START_ACCOUNTING_PERIOD
1062 : : };
1063 : :
1064 : : void
1065 : 2253 : gnc_register_start_date_option(GncOptionDB* db, const char* section,
1066 : : const char* name, const char* key,
1067 : : const char* doc_string, bool both)
1068 : : {
1069 : 2253 : auto ui_type = both ? GncOptionUIType::DATE_BOTH :
1070 : : GncOptionUIType::DATE_RELATIVE;
1071 : 4506 : GncOption option{GncOptionDateValue(section, name, key, doc_string,
1072 : 2253 : ui_type, begin_dates)};
1073 : 2253 : db->register_option(section, std::move(option));
1074 : 2253 : }
1075 : :
1076 : : static const RelativeDatePeriodVec end_dates
1077 : : {
1078 : : RelativeDatePeriod::TODAY,
1079 : : RelativeDatePeriod::END_THIS_MONTH,
1080 : : RelativeDatePeriod::END_PREV_MONTH,
1081 : : RelativeDatePeriod::END_CURRENT_QUARTER,
1082 : : RelativeDatePeriod::END_PREV_QUARTER,
1083 : : RelativeDatePeriod::END_CAL_YEAR,
1084 : : RelativeDatePeriod::END_PREV_YEAR,
1085 : : RelativeDatePeriod::END_ACCOUNTING_PERIOD
1086 : : };
1087 : :
1088 : : void
1089 : 2296 : gnc_register_end_date_option(GncOptionDB* db, const char* section,
1090 : : const char* name, const char* key,
1091 : : const char* doc_string, bool both)
1092 : : {
1093 : 2296 : auto ui_type = both ? GncOptionUIType::DATE_BOTH :
1094 : : GncOptionUIType::DATE_RELATIVE;
1095 : 4592 : GncOption option{GncOptionDateValue(section, name, key, doc_string,
1096 : 2296 : ui_type, end_dates)};
1097 : 2296 : db->register_option(section, std::move(option));
1098 : 2296 : }
1099 : :
1100 : : void
1101 : 5 : gnc_register_report_placement_option(GncOptionDBPtr& db,
1102 : : const char* section, const char* name)
1103 : : {
1104 : : /* This is a special option with it's own UI file so we have fake values to pass
1105 : : * to the template creation function.
1106 : : */
1107 : 5 : GncOptionReportPlacementVec value;
1108 : 10 : GncOption option{GncOptionValue<GncOptionReportPlacementVec>{section, name,
1109 : : "no_key", "nodoc_string",
1110 : 5 : value,GncOptionUIType::REPORT_PLACEMENT}};
1111 : 5 : db->register_option(section, std::move(option));
1112 : 5 : }
1113 : :
1114 : : void
1115 : 14 : gnc_register_internal_option(GncOptionDBPtr& db,
1116 : : const char* section, const char* name,
1117 : : const std::string& value)
1118 : : {
1119 : : GncOption option{
1120 : 28 : GncOptionValue<std::string>{section, name, "", "", value,
1121 : 14 : GncOptionUIType::INTERNAL}};
1122 : 14 : db->register_option(section, std::move(option));
1123 : 14 : }
1124 : :
1125 : : void
1126 : 1038 : gnc_register_internal_option(GncOptionDBPtr& db,
1127 : : const char* section, const char* name,
1128 : : bool value)
1129 : : {
1130 : : GncOption option{
1131 : 2076 : GncOptionValue<bool>{section, name, "", "", value,
1132 : 1038 : GncOptionUIType::INTERNAL}};
1133 : 1038 : db->register_option(section, std::move(option));
1134 : 1038 : }
1135 : :
1136 : : GncOptionDB*
1137 : 0 : gnc_option_db_new(void)
1138 : : {
1139 : 0 : return new GncOptionDB;
1140 : : }
1141 : :
1142 : : void
1143 : 0 : gnc_option_db_destroy(GncOptionDB* odb)
1144 : : {
1145 : 0 : PWARN("Direct Destroy called on GncOptionDB %" G_GUINT64_FORMAT, (uint64_t)odb);
1146 : 0 : }
1147 : :
1148 : : GList*
1149 : 0 : gnc_option_db_commit(GncOptionDB* odb)
1150 : : {
1151 : 0 : GList* errors{};
1152 : 0 : odb->foreach_section(
1153 : 0 : [&errors](GncOptionSectionPtr& section){
1154 : 0 : section->foreach_option(
1155 : 0 : [&errors](GncOption& option) {
1156 : : try
1157 : : {
1158 : 0 : option.set_option_from_ui_item();
1159 : : }
1160 : 0 : catch (const std::invalid_argument& err)
1161 : : {
1162 : 0 : PWARN("Option %s:%s failed to set its value %s",
1163 : : option.get_section().c_str(),
1164 : : option.get_name().c_str(), err.what());
1165 : 0 : errors = g_list_prepend(errors,
1166 : 0 : (void*)option.get_name().c_str());
1167 : 0 : } });
1168 : 0 : });
1169 : 0 : if (!errors)
1170 : 0 : odb->run_callbacks();
1171 : 0 : return errors;
1172 : : }
1173 : :
1174 : : void
1175 : 0 : gnc_option_db_clean(GncOptionDB* odb)
1176 : : {
1177 : 0 : odb->foreach_section(
1178 : 0 : [](GncOptionSectionPtr& section){
1179 : 0 : section->foreach_option(
1180 : 0 : [](GncOption& option) {
1181 : 0 : option.set_ui_item_from_option();
1182 : 0 : });
1183 : 0 : });
1184 : 0 : }
1185 : :
1186 : 0 : void gnc_option_db_load(GncOptionDB* odb, QofBook* book)
1187 : : {
1188 : 0 : odb->load_from_kvp(book);
1189 : 0 : }
1190 : :
1191 : : void
1192 : 0 : gnc_option_db_save(GncOptionDB* odb, QofBook* book,
1193 : : gboolean clear_options)
1194 : : {
1195 : 0 : odb->save_to_kvp(book, static_cast<bool>(clear_options));
1196 : 0 : }
1197 : :
1198 : : void
1199 : 0 : gnc_option_db_book_options(GncOptionDB* odb)
1200 : : {
1201 : 0 : constexpr const char* business_section{N_("Business")};
1202 : 0 : constexpr const char* counter_section{N_("Counters")};
1203 : 0 : static const std::string empty_string{""};
1204 : :
1205 : : //Accounts Tab
1206 : :
1207 : 0 : gnc_register_number_range_option<double>(odb, OPTION_SECTION_ACCOUNTS,
1208 : : OPTION_NAME_AUTO_READONLY_DAYS, "a",
1209 : : 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."),
1210 : : 0.0, 0.0, 3650.0, 1.0);
1211 : :
1212 : 0 : gnc_register_simple_boolean_option(odb, OPTION_SECTION_ACCOUNTS,
1213 : : OPTION_NAME_NUM_FIELD_SOURCE, "b",
1214 : : 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."),
1215 : : false);
1216 : 0 : gnc_register_simple_boolean_option(odb, OPTION_SECTION_ACCOUNTS,
1217 : : OPTION_NAME_TRADING_ACCOUNTS, "a",
1218 : : N_("Check to have trading accounts used for transactions involving more than one currency or commodity."),
1219 : : false);
1220 : :
1221 : : //Budgeting Tab
1222 : :
1223 : 0 : gnc_register_budget_option(odb, OPTION_SECTION_BUDGETING,
1224 : : OPTION_NAME_DEFAULT_BUDGET, "a",
1225 : : N_("Budget to be used when none has been otherwise specified."),
1226 : : nullptr);
1227 : :
1228 : : //Counters Tab
1229 : :
1230 : 0 : gnc_register_counter_option(odb, counter_section,
1231 : : N_("Customer number"), "gncCustomera",
1232 : : N_("The previous customer number generated. This number will be incremented to generate the next customer number."),
1233 : : 0);
1234 : 0 : gnc_register_counter_format_option(odb, counter_section,
1235 : : N_("Customer number format"),
1236 : : "gncCustomerb",
1237 : : N_("The format string to use for generating customer numbers. This is a printf-style format string."),
1238 : : empty_string);
1239 : 0 : gnc_register_counter_option(odb, counter_section,
1240 : : N_("Employee number"), "gncEmployeea",
1241 : : N_("The previous employee number generated. This number will be incremented to generate the next employee number."),
1242 : : 0);
1243 : 0 : gnc_register_counter_format_option(odb, counter_section,
1244 : : N_("Employee number format"),
1245 : : "gncEmployeeb",
1246 : : N_("The format string to use for generating employee numbers. This is a printf-style format string."),
1247 : : empty_string);
1248 : 0 : gnc_register_counter_option(odb, counter_section,
1249 : : N_("Invoice number"), "gncInvoicea",
1250 : : N_("The previous invoice number generated. This number will be incremented to generate the next invoice number."),
1251 : : 0);
1252 : 0 : gnc_register_counter_format_option(odb, counter_section,
1253 : : N_("Invoice number format"),
1254 : : "gncInvoiceb",
1255 : : N_("The format string to use for generating invoice numbers. This is a printf-style format string."),
1256 : : empty_string);
1257 : 0 : gnc_register_counter_option(odb, counter_section,
1258 : : N_("Bill number"), "gncBilla",
1259 : : N_("The previous bill number generated. This number will be incremented to generate the next bill number."),
1260 : : 0);
1261 : 0 : gnc_register_counter_format_option(odb, counter_section,
1262 : : N_("Bill number format"), "gncBillb",
1263 : : N_("The format string to use for generating bill numbers. This is a printf-style format string."),
1264 : : empty_string);
1265 : 0 : gnc_register_counter_option(odb, counter_section,
1266 : : N_("Expense voucher number"), "gncExpVouchera",
1267 : : N_("The previous expense voucher number generated. This number will be incremented to generate the next voucher number."),
1268 : : 0);
1269 : 0 : gnc_register_counter_format_option(odb, counter_section,
1270 : : N_("Expense voucher number format"),
1271 : : "gncExpVoucherb",
1272 : : N_("The format string to use for generating expense voucher numbers. This is a printf-style format string."),
1273 : : empty_string);
1274 : 0 : gnc_register_counter_option(odb, counter_section,
1275 : : N_("Job number"), "gncJoba",
1276 : : N_("The previous job number generated. This number will be incremented to generate the next job number."),
1277 : : 0);
1278 : 0 : gnc_register_counter_format_option(odb, counter_section,
1279 : : N_("Job number format"), "gncJobb",
1280 : : N_("The format string to use for generating job numbers. This is a printf-style format string."),
1281 : : empty_string);
1282 : 0 : gnc_register_counter_option(odb, counter_section,
1283 : : N_("Order number"), "gncOrdera",
1284 : : N_("The previous order number generated. This number will be incremented to generate the next order number."),
1285 : : 0);
1286 : 0 : gnc_register_counter_format_option(odb, counter_section,
1287 : : N_("Order number format"), "gncOrderb",
1288 : : N_("The format string to use for generating order numbers. This is a printf-style format string."),
1289 : : empty_string);
1290 : 0 : gnc_register_counter_option(odb, counter_section,
1291 : : N_("Vendor number"), "gncVendora",
1292 : : N_("The previous vendor number generated. This number will be incremented to generate the next vendor number."),
1293 : : 0);
1294 : 0 : gnc_register_counter_format_option(odb, counter_section,
1295 : : N_("Vendor number format"), "gncVendorb",
1296 : : N_("The format string to use for generating vendor numbers. This is a printf-style format string."),
1297 : : empty_string);
1298 : :
1299 : : //Business Tab
1300 : :
1301 : 0 : gnc_register_string_option(odb, business_section, N_("Company Name"), "a",
1302 : : N_("The name of your business."),
1303 : : empty_string);
1304 : 0 : gnc_register_text_option(odb, business_section, N_("Company Address"), "b1",
1305 : : N_("The address of your business."),
1306 : : empty_string);
1307 : 0 : gnc_register_string_option(odb, business_section,
1308 : : N_("Company Contact Person"), "b2",
1309 : : N_("The contact person to print on invoices."),
1310 : : empty_string);
1311 : 0 : gnc_register_string_option(odb, business_section,
1312 : : N_("Company Phone Number"), "c1",
1313 : : N_("The contact person to print on invoices."),
1314 : : empty_string);
1315 : 0 : gnc_register_string_option(odb, business_section,
1316 : : N_("Company Fax Number"), "c2",
1317 : : N_("The fax number of your business."),
1318 : : empty_string);
1319 : 0 : gnc_register_string_option(odb, business_section,
1320 : : N_("Company Email Address"), "c3",
1321 : : N_ ("The email address of your business."),
1322 : : empty_string);
1323 : 0 : gnc_register_string_option(odb, business_section,
1324 : : N_("Company Website URL"), "c4",
1325 : : N_("The URL address of your website."),
1326 : : empty_string);
1327 : 0 : gnc_register_string_option(odb, business_section, N_("Company ID"), "c5",
1328 : : N_("The ID for your company (eg 'Tax-ID: 00-000000)."),
1329 : : empty_string);
1330 : 0 : gnc_register_invoice_print_report_option(odb, business_section,
1331 : : OPTION_NAME_DEFAULT_INVOICE_REPORT, "e1",
1332 : : N_("The invoice report to be used for printing."),
1333 : : empty_string);
1334 : 0 : gnc_register_number_range_option<double>(odb, business_section,
1335 : : OPTION_NAME_DEFAULT_INVOICE_REPORT_TIMEOUT, "e2",
1336 : : N_("Length of time to change the used invoice report. A value of 0 means disabled."),
1337 : : 0.0, 0.0, 20.0, 1.0);
1338 : 0 : gnc_register_taxtable_option(odb, business_section,
1339 : : N_("Default Customer TaxTable"), "f1",
1340 : : N_("The default tax table to apply to customers."),
1341 : : nullptr);
1342 : 0 : gnc_register_taxtable_option(odb, business_section,
1343 : : N_("Default Vendor TaxTable"), "f2",
1344 : : N_("The default tax table to apply to vendors."),
1345 : : nullptr);
1346 : :
1347 : 0 : gnc_register_dateformat_option(odb, business_section,
1348 : : N_("Fancy Date Format"), "g",
1349 : : N_("The default date format used for fancy printed dates."),
1350 : 0 : {QOF_DATE_FORMAT_UNSET, GNCDATE_MONTH_NUMBER, true, ""});
1351 : :
1352 : : //Tax Tab
1353 : :
1354 : 0 : gnc_register_string_option(odb, N_("Tax"), N_("Tax Number"), "a",
1355 : : N_("The electronic tax number of your business"),
1356 : : empty_string);
1357 : 0 : }
1358 : : const QofInstance*
1359 : 0 : gnc_option_db_lookup_qofinstance_value(GncOptionDB* odb, const char* section,
1360 : : const char* name)
1361 : : {
1362 : 0 : auto option{odb->find_option(section, name)};
1363 : 0 : if (option)
1364 : 0 : return option->get_value<const QofInstance*>();
1365 : : else
1366 : 0 : return nullptr;
1367 : : }
1368 : :
1369 : : // Force creation of templates
1370 : : template void gnc_register_number_range_option(GncOptionDB* db,
1371 : : const char* section, const char* name,
1372 : : const char* key, const char* doc_string,
1373 : : int value, int min, int max, int step);
1374 : : template void gnc_register_number_range_option(GncOptionDB* db,
1375 : : const char* section, const char* name,
1376 : : const char* key, const char* doc_string,
1377 : : double value, double min,
1378 : : double max, double step);
|