Branch data Line data Source code
1 : : /********************************************************************\
2 : : * gnc-option-impl.cpp -- Application options system *
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 "gnc-option-impl.hpp"
25 : : #include "gnc-datetime.hpp"
26 : : #include "gnc-option-uitype.hpp"
27 : : #include "guid.hpp"
28 : : #include <cassert>
29 : : #include <sstream>
30 : : #include <numeric>
31 : :
32 : : #include "gnc-accounting-period.h"
33 : : #include "gnc-session.h"
34 : : #include "gncOwner.h"
35 : :
36 : : static const QofLogModule log_module{"gnc.options"};
37 : :
38 : : const std::string GncOptionMultichoiceValue::c_empty_string{""};
39 : : const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"};
40 : :
41 : : static inline GncOwnerType
42 : 0 : ui_type_to_owner_type(GncOptionUIType ui_type)
43 : : {
44 : 0 : if (ui_type == GncOptionUIType::CUSTOMER)
45 : 0 : return GNC_OWNER_CUSTOMER;
46 : 0 : if (ui_type == GncOptionUIType::VENDOR)
47 : 0 : return GNC_OWNER_VENDOR;
48 : 0 : if (ui_type == GncOptionUIType::EMPLOYEE)
49 : 0 : return GNC_OWNER_EMPLOYEE;
50 : 0 : if (ui_type == GncOptionUIType::JOB)
51 : 0 : return GNC_OWNER_JOB;
52 : 0 : return GNC_OWNER_NONE;
53 : : }
54 : :
55 : : static GncOwner*
56 : 93 : make_owner_ptr(const GncOwner* owner)
57 : : {
58 : 93 : if (!owner)
59 : 84 : return nullptr;
60 : 9 : auto rv{gncOwnerNew()};
61 : 9 : gncOwnerCopy(owner, rv);
62 : 9 : return rv;
63 : : }
64 : :
65 : 21 : GncOptionGncOwnerValue::GncOptionGncOwnerValue(
66 : : const char* section, const char* name,
67 : : const char* key, const char* doc_string,
68 : 21 : const GncOwner* value, GncOptionUIType ui_type) :
69 : : OptionClassifier{section, name, key, doc_string},
70 : 21 : m_ui_type(ui_type), m_value{make_owner_ptr(value)},
71 : 126 : m_default_value{make_owner_ptr(value)} {}
72 : :
73 : 21 : GncOptionGncOwnerValue::GncOptionGncOwnerValue(const GncOptionGncOwnerValue& from) :
74 : 63 : OptionClassifier{from.m_section, from.m_name, from.m_sort_tag,
75 : 21 : from.m_doc_string},
76 : 21 : m_ui_type(from.get_ui_type()), m_value{make_owner_ptr(from.get_value())},
77 : 42 : m_default_value{make_owner_ptr(from.get_default_value())} {}
78 : :
79 : : void
80 : 9 : GncOptionGncOwnerValue::set_value(const GncOwner* new_value)
81 : : {
82 : 9 : m_value.reset(make_owner_ptr(new_value));
83 : 9 : m_dirty = true;
84 : 9 : }
85 : :
86 : : void
87 : 0 : GncOptionGncOwnerValue::set_default_value(const GncOwner *new_value)
88 : : {
89 : 0 : m_value.reset(make_owner_ptr(new_value));
90 : 0 : m_default_value.reset(make_owner_ptr(new_value));
91 : 0 : }
92 : :
93 : : const GncOwner*
94 : 58 : GncOptionGncOwnerValue::get_value() const
95 : : {
96 : 58 : return m_value.get();
97 : : }
98 : :
99 : : const GncOwner*
100 : 23 : GncOptionGncOwnerValue::get_default_value() const
101 : : {
102 : 23 : return m_default_value.get();
103 : : }
104 : :
105 : : void
106 : 0 : GncOptionGncOwnerValue::reset_default_value()
107 : : {
108 : 0 : gncOwnerCopy(m_default_value.get(), m_value.get());
109 : 0 : }
110 : :
111 : : bool
112 : 0 : GncOptionGncOwnerValue::is_changed() const noexcept
113 : : {
114 : 0 : return gncOwnerEqual(m_value.get(), m_default_value.get());
115 : : }
116 : :
117 : : bool
118 : 0 : GncOptionGncOwnerValue::deserialize(const std::string& str) noexcept
119 : : {
120 : : try {
121 : 0 : auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
122 : 0 : auto inst = qof_instance_from_guid(&guid, m_ui_type);
123 : 0 : if (inst)
124 : : {
125 : 0 : GncOwner owner{};
126 : 0 : owner.type = ui_type_to_owner_type(m_ui_type);
127 : 0 : owner.owner.undefined = inst;
128 : 0 : set_default_value(&owner);
129 : 0 : return true;
130 : : }
131 : : }
132 : 0 : catch (const gnc::guid_syntax_exception& err)
133 : : {
134 : 0 : PWARN("Failed to convert %s to a GUID", str.c_str());
135 : 0 : }
136 : 0 : return false;
137 : : }
138 : :
139 : : std::string
140 : 0 : GncOptionGncOwnerValue::serialize() const noexcept
141 : : {
142 : :
143 : 0 : auto owner{m_value.get()};
144 : 0 : gnc::GUID guid{*qof_instance_get_guid(static_cast<QofInstance*>(owner->owner.undefined))};
145 : 0 : std::string retval{guid.to_string()};
146 : :
147 : 0 : return retval;
148 : : }
149 : :
150 : : static GncItem
151 : 165 : make_gnc_item(const QofInstance* inst)
152 : : {
153 : 165 : if (!inst)
154 : 108 : return std::make_pair<QofIdTypeConst, GncGUID>("", guid_new_return());
155 : 57 : auto type{qof_collection_get_type(qof_instance_get_collection(inst))};
156 : 57 : auto guid{qof_instance_get_guid(inst)};
157 : 57 : return std::make_pair(std::move(type), std::move(*const_cast<GncGUID*>(guid)));
158 : : }
159 : :
160 : : static inline QofBook*
161 : 3574 : get_current_book(void)
162 : : {
163 : 3574 : return qof_session_get_book(gnc_get_current_session());
164 : : }
165 : :
166 : : static inline Account*
167 : 261 : get_current_root_account(void)
168 : : {
169 : 261 : return gnc_book_get_root_account(get_current_book());
170 : : }
171 : :
172 : : static const QofInstance*
173 : 264 : qof_instance_from_gnc_item(const GncItem& item)
174 : : {
175 : 264 : auto [type, guid] = item;
176 : 264 : auto book{get_current_book()};
177 : 264 : auto coll{qof_book_get_collection(book, type)};
178 : 528 : return static_cast<QofInstance*>(qof_collection_lookup_entity(coll, &guid));
179 : : }
180 : :
181 : 67 : GncOptionQofInstanceValue::GncOptionQofInstanceValue(
182 : : const char* section, const char* name,
183 : : const char* key, const char* doc_string,
184 : 67 : const QofInstance* value, GncOptionUIType ui_type) :
185 : : OptionClassifier{section, name, key, doc_string},
186 : 67 : m_ui_type(ui_type), m_value{},
187 : 335 : m_default_value{} {
188 : 67 : m_value = make_gnc_item(value);
189 : 67 : m_default_value = make_gnc_item(value);
190 : 67 : }
191 : :
192 : 67 : GncOptionQofInstanceValue::GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from) :
193 : 201 : OptionClassifier{from.m_section, from.m_name, from.m_sort_tag,
194 : 67 : from.m_doc_string},
195 : 67 : m_ui_type(from.get_ui_type()), m_value{from.get_item()},
196 : 134 : m_default_value{from.get_default_item()}
197 : : {
198 : 67 : }
199 : : void
200 : 30 : GncOptionQofInstanceValue::set_value(const QofInstance* new_value)
201 : : {
202 : 30 : m_value = make_gnc_item(new_value);
203 : 30 : m_dirty = true;
204 : 30 : }
205 : :
206 : : void
207 : 1 : GncOptionQofInstanceValue::set_default_value(const QofInstance *new_value)
208 : : {
209 : 1 : m_value = m_default_value = make_gnc_item(new_value);
210 : :
211 : 1 : }
212 : :
213 : : const QofInstance*
214 : 260 : GncOptionQofInstanceValue::get_value() const
215 : : {
216 : 260 : return qof_instance_from_gnc_item(m_value);
217 : : }
218 : :
219 : : const QofInstance*
220 : 4 : GncOptionQofInstanceValue::get_default_value() const
221 : : {
222 : 4 : return qof_instance_from_gnc_item(m_default_value);
223 : : }
224 : :
225 : : void
226 : 0 : GncOptionQofInstanceValue::reset_default_value()
227 : : {
228 : 0 : m_value = m_default_value;
229 : 0 : }
230 : :
231 : : bool
232 : 0 : GncOptionQofInstanceValue::is_changed() const noexcept
233 : : {
234 : 0 : return m_value != m_default_value;
235 : : }
236 : :
237 : : bool
238 : 0 : GncOptionQofInstanceValue::deserialize(const std::string& str) noexcept
239 : : {
240 : 0 : QofInstance* inst{};
241 : : // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic
242 : : try {
243 : 0 : auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
244 : 0 : inst = qof_instance_from_guid(&guid, m_ui_type);
245 : 0 : if (inst)
246 : : {
247 : 0 : m_value = make_gnc_item(inst);
248 : 0 : return true;
249 : : }
250 : : }
251 : 0 : catch (const gnc::guid_syntax_exception& err)
252 : : {
253 : 0 : PWARN("Failed to convert %s to a GUID", str.c_str());
254 : 0 : }
255 : 0 : return false;
256 : : }
257 : :
258 : : std::string
259 : 4 : GncOptionQofInstanceValue::serialize() const noexcept
260 : : {
261 : 4 : auto inst{get_value()};
262 : 4 : std::string retval;
263 : 4 : if (GNC_IS_COMMODITY(inst))
264 : : {
265 : 1 : auto commodity{GNC_COMMODITY(inst)};
266 : 1 : if (!gnc_commodity_is_currency(commodity))
267 : : {
268 : 1 : auto name_space{gnc_commodity_get_namespace(GNC_COMMODITY(inst))};
269 : 1 : if (name_space && *name_space != '\0')
270 : : {
271 : 1 : retval = name_space;
272 : 1 : retval += ":";
273 : : }
274 : : }
275 : 1 : retval += gnc_commodity_get_mnemonic(GNC_COMMODITY(inst));
276 : 1 : return retval;
277 : : }
278 : : else
279 : : {
280 : 3 : gnc::GUID guid{m_value.second};
281 : 3 : retval = guid.to_string();
282 : : }
283 : 3 : return retval;
284 : : }
285 : :
286 : : static gnc_commodity*
287 : 2748 : gnc_commodity_from_namespace_and_mnemonic(std::string_view name_space,
288 : : std::string_view mnemonic)
289 : : {
290 : 2748 : auto book{get_current_book()};
291 : 2748 : auto table = gnc_commodity_table_get_table(book);
292 : 2748 : return gnc_commodity_table_lookup(table, name_space.data(),
293 : 2748 : mnemonic.data());
294 : : }
295 : :
296 : : gnc_commodity*
297 : 2741 : GncOptionCommodityValue::get_value() const
298 : : {
299 : 2741 : return gnc_commodity_from_namespace_and_mnemonic(m_namespace, m_mnemonic);
300 : : }
301 : :
302 : : gnc_commodity*
303 : 4 : GncOptionCommodityValue::get_default_value() const
304 : : {
305 : 4 : return gnc_commodity_from_namespace_and_mnemonic(m_default_namespace,
306 : 4 : m_default_mnemonic);
307 : : }
308 : :
309 : : void
310 : 2796 : GncOptionCommodityValue::set_value(gnc_commodity* value)
311 : : {
312 : 2796 : if (!validate(value))
313 : 1 : throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
314 : 2795 : m_mnemonic = gnc_commodity_get_mnemonic(value);
315 : 2795 : m_namespace = gnc_commodity_get_namespace(value);
316 : 2795 : m_dirty = true;
317 : 2795 : }
318 : :
319 : : void
320 : 0 : GncOptionCommodityValue::set_default_value(gnc_commodity* value)
321 : : {
322 : 0 : if (!validate(value))
323 : 0 : throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
324 : 0 : m_mnemonic = m_default_mnemonic = gnc_commodity_get_mnemonic(value);
325 : 0 : m_namespace = m_default_namespace = gnc_commodity_get_namespace(value);
326 : 0 : }
327 : :
328 : : void
329 : 0 : GncOptionCommodityValue::reset_default_value()
330 : : {
331 : 0 : m_mnemonic = m_default_mnemonic;
332 : 0 : m_namespace = m_default_namespace;
333 : 0 : }
334 : :
335 : : bool
336 : 179 : GncOptionCommodityValue::is_changed() const noexcept
337 : : {
338 : 179 : return m_namespace != m_default_namespace || m_mnemonic != m_default_mnemonic;
339 : : }
340 : :
341 : : bool
342 : 5087 : GncOptionCommodityValue::validate(gnc_commodity* comm) const noexcept
343 : : {
344 : 5087 : if (!GNC_IS_COMMODITY(comm))
345 : 0 : return false;
346 : 5087 : if (m_is_currency && !gnc_commodity_is_currency(comm))
347 : 4 : return false;
348 : 5083 : return true;
349 : : }
350 : :
351 : : std::string
352 : 3 : GncOptionCommodityValue::serialize() const noexcept
353 : : {
354 : 3 : if (m_is_currency)
355 : 2 : return m_mnemonic;
356 : : else
357 : 1 : return m_namespace + ":" + m_mnemonic;
358 : : }
359 : :
360 : : bool
361 : 3 : GncOptionCommodityValue::deserialize(const std::string& str) noexcept
362 : : {
363 : 3 : auto sep{str.find(":")};
364 : 3 : gnc_commodity* comm{};
365 : 3 : std::string mnemonic, name_space;
366 : 3 : if (sep != std::string::npos)
367 : : {
368 : 2 : name_space = str.substr(0, sep);
369 : 2 : mnemonic = str.substr(sep + 1, -1);
370 : : }
371 : : else
372 : : {
373 : 1 : name_space = "CURRENCY";
374 : 1 : mnemonic = str;
375 : : }
376 : 3 : comm = gnc_commodity_from_namespace_and_mnemonic(name_space, mnemonic);
377 : 3 : if (!validate(comm))
378 : 1 : return false;
379 : 2 : m_namespace = std::move(name_space);
380 : 2 : m_mnemonic = std::move(mnemonic);
381 : 2 : return true;
382 : 3 : }
383 : :
384 : : bool
385 : 3045 : GncOptionAccountListValue::validate(const GncOptionAccountList& values) const
386 : : {
387 : 3045 : if (values.empty())
388 : 57 : return true;
389 : 2989 : if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) &&
390 : 1 : values.size() != 1)
391 : : {
392 : 1 : PWARN("GncOptionAccountListValue::validate: Multiple values for a non-multiselect option.");
393 : 1 : return false;
394 : : }
395 : 2987 : if (m_allowed.empty())
396 : 2778 : return true;
397 : 209 : auto book{get_current_book()};
398 : 855 : for(auto& guid : values)
399 : : {
400 : 725 : if (std::find(m_allowed.begin(), m_allowed.end(),
401 : 1450 : xaccAccountGetType(xaccAccountLookup(&guid, book))) == m_allowed.end())
402 : : {
403 : 79 : PWARN("GncOptionAccountListValue::validate: Account %s is not of an allowed type", gnc::GUID(guid).to_string().c_str());
404 : 79 : return false; }
405 : : }
406 : 130 : return true;
407 : : }
408 : :
409 : : GncOptionAccountList
410 : 2492 : GncOptionAccountListValue::get_value() const
411 : : {
412 : 2492 : return !m_value.empty() ? m_value : get_default_value();
413 : : }
414 : :
415 : : GncOptionAccountList
416 : 1049 : GncOptionAccountListValue::get_default_value() const
417 : : {
418 : 1049 : if (!m_default_value.empty())
419 : 4 : return m_default_value;
420 : :
421 : : /* If no default has been set and there's an allowed set then find the first
422 : : * account that matches one of the allowed account types.
423 : : */
424 : 1045 : GncOptionAccountList retval{};
425 : 1045 : if (m_allowed.empty())
426 : 860 : return retval;
427 : :
428 : 185 : auto root{get_current_root_account()};
429 : 185 : auto account_list{gnc_account_get_descendants_sorted(root)};
430 : 185 : if (!account_list)
431 : 140 : return retval;
432 : :
433 : 66 : for (auto node = account_list; node; node = g_list_next (node))
434 : : {
435 : 65 : if (std::find(m_allowed.begin(), m_allowed.end(),
436 : 130 : xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
437 : : {
438 : 44 : retval.push_back(*qof_entity_get_guid(GNC_ACCOUNT(node->data)));
439 : 44 : break;
440 : : }
441 : : }
442 : 45 : g_list_free(account_list);
443 : 45 : return retval;
444 : 1045 : }
445 : :
446 : : bool
447 : 374 : GncOptionAccountListValue::is_changed() const noexcept
448 : : {
449 : 374 : return m_value != m_default_value;
450 : : }
451 : :
452 : :
453 : :
454 : : /**
455 : : * Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
456 : : * gnc_account_sel_set_acct_filters copies the list so the intermediary caller
457 : : * is responsible for freeing the list.
458 : : *
459 : : * @return an allocated GList* or nullptr if the list is empty.
460 : : */
461 : : GList*
462 : 0 : GncOptionAccountListValue::account_type_list() const noexcept
463 : : {
464 : 0 : if (m_allowed.empty())
465 : 0 : return nullptr;
466 : 0 : GList* retval{nullptr};
467 : 0 : for (auto type : m_allowed)
468 : 0 : retval = g_list_prepend(retval, GINT_TO_POINTER(type));
469 : 0 : return g_list_reverse(retval);
470 : : }
471 : :
472 : : bool
473 : 36 : GncOptionAccountSelValue::validate(const Account* value) const
474 : : {
475 : 36 : if (m_allowed.empty() || !value)
476 : 33 : return true;
477 : 3 : if (std::find(m_allowed.begin(), m_allowed.end(),
478 : 6 : xaccAccountGetType(value)) == m_allowed.end())
479 : 0 : return false;
480 : 3 : return true;
481 : : }
482 : :
483 : : const Account*
484 : 87 : GncOptionAccountSelValue::get_value() const
485 : : {
486 : 87 : auto book{get_current_book()};
487 : 87 : return guid_equal(guid_null(), &m_value) ? get_default_value() :
488 : 87 : xaccAccountLookup(&m_value, book);
489 : : }
490 : :
491 : : const Account*
492 : 83 : GncOptionAccountSelValue::get_default_value() const
493 : : {
494 : :
495 : 83 : if (!guid_equal(guid_null(), &m_default_value))
496 : : {
497 : 0 : auto book{get_current_book()};
498 : 0 : return xaccAccountLookup(&m_default_value, book);
499 : : }
500 : :
501 : : /* If no default has been set and there's an allowed set then find the first
502 : : * account that matches one of the allowed account types.
503 : : */
504 : 83 : if (m_allowed.empty())
505 : 7 : return nullptr;
506 : :
507 : 76 : const Account* retval{nullptr};
508 : 76 : auto root{get_current_root_account()};
509 : 76 : auto account_list{gnc_account_get_descendants_sorted(root)};
510 : 76 : if (!account_list)
511 : 40 : return nullptr;
512 : :
513 : 344 : for (auto node = account_list; node; node = g_list_next (node))
514 : 338 : if (std::find(m_allowed.begin(), m_allowed.end(),
515 : 676 : xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
516 : : {
517 : 30 : retval = GNC_ACCOUNT(node->data);
518 : 30 : break;
519 : : }
520 : 36 : g_list_free(account_list);
521 : 36 : return retval;
522 : : }
523 : :
524 : :
525 : : /**
526 : : * Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
527 : : * gnc_account_sel_set_acct_filters copies the list so the intermediary caller
528 : : * is responsible for freeing the list.
529 : : *
530 : : * @return an allocated GList* or nullptr if the list is empty.
531 : : */
532 : : GList*
533 : 0 : GncOptionAccountSelValue::account_type_list() const noexcept
534 : : {
535 : 0 : if (m_allowed.empty())
536 : 0 : return nullptr;
537 : 0 : GList* retval{nullptr};
538 : 0 : for (auto type : m_allowed)
539 : 0 : retval = g_list_prepend(retval, GINT_TO_POINTER(type));
540 : 0 : return g_list_reverse(retval);
541 : : }
542 : :
543 : : bool
544 : 255 : GncOptionDateValue::validate(RelativeDatePeriod value) {
545 : 255 : if (m_period_set.empty())
546 : 27 : return true; // No restrictions
547 : 228 : if (std::find(m_period_set.begin(), m_period_set.end(),
548 : 456 : value) != m_period_set.end())
549 : 226 : return true;
550 : 2 : return false;
551 : : }
552 : :
553 : : time64
554 : 4964 : GncOptionDateValue::get_value() const noexcept
555 : : {
556 : 4964 : if (m_period == RelativeDatePeriod::ABSOLUTE)
557 : 4951 : return m_date;
558 : 13 : return gnc_relative_date_to_time64(m_period);
559 : : }
560 : :
561 : : time64
562 : 0 : GncOptionDateValue::get_default_value() const noexcept
563 : : {
564 : 0 : if (m_default_period == RelativeDatePeriod::ABSOLUTE)
565 : 0 : return m_default_date;
566 : 0 : return gnc_relative_date_to_time64(m_default_period);
567 : : }
568 : :
569 : : /* Use asserts for pre- and post-conditions to deliberately crash if they're not
570 : : * met as the program design should prevent that from happening.
571 : : */
572 : : uint16_t
573 : 4 : GncOptionDateValue::get_period_index() const noexcept
574 : : {
575 : 4 : assert (m_period != RelativeDatePeriod::ABSOLUTE);
576 : 4 : assert(!m_period_set.empty());
577 : 4 : auto item{std::find(m_period_set.begin(), m_period_set.end(), m_period)};
578 : 4 : assert(item != m_period_set.end());
579 : 4 : return item - m_period_set.begin();
580 : : }
581 : :
582 : : uint16_t
583 : 0 : GncOptionDateValue::get_default_period_index() const noexcept
584 : : {
585 : 0 : assert(m_period != RelativeDatePeriod::ABSOLUTE);
586 : 0 : assert(!m_period_set.empty());
587 : 0 : auto item{std::find(m_period_set.begin(), m_period_set.end(),
588 : 0 : m_default_period)};
589 : 0 : assert (item != m_period_set.end());
590 : 0 : return item - m_period_set.begin();
591 : : }
592 : :
593 : : void
594 : 2 : GncOptionDateValue::set_value(uint16_t index) noexcept
595 : : {
596 : 2 : assert(!m_period_set.empty());
597 : 2 : assert(index < m_period_set.size());
598 : 2 : m_date = INT64_MAX;
599 : 2 : m_period = m_period_set[index];
600 : 2 : m_dirty = true;
601 : 2 : }
602 : :
603 : : uint16_t
604 : 0 : GncOptionDateValue::permissible_value_index(const char* key) const noexcept
605 : : {
606 : 0 : auto index = std::find_if(m_period_set.begin(), m_period_set.end(),
607 : 0 : [key](auto period) -> bool {
608 : 0 : return strcmp(gnc_relative_date_display_string(period),
609 : 0 : key) == 0;
610 : : });
611 : 0 : return index != m_period_set.end() ? index - m_period_set.begin() : 0;
612 : : }
613 : :
614 : : static const char* date_type_str[] {"absolute", "relative"};
615 : :
616 : : std::ostream&
617 : 0 : GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
618 : : {
619 : 0 : if (m_period == RelativeDatePeriod::ABSOLUTE)
620 : 0 : oss << date_type_str[0] << " . " << m_date;
621 : : else
622 : : oss << date_type_str[1] << " . " <<
623 : 0 : gnc_relative_date_storage_string(m_period);
624 : 0 : return oss;
625 : : }
626 : :
627 : : std::istream&
628 : 13 : GncOptionDateValue::in_stream(std::istream& iss)
629 : : {
630 : : char type_str[10]; //The length of both "absolute" and "relative" plus 1.
631 : 13 : iss.getline(type_str, sizeof(type_str), '.');
632 : 13 : if(!iss)
633 : 0 : throw std::invalid_argument("Date Type separator missing");
634 : : /* strcmp is safe, istream::getline null terminates the buffer. */
635 : 13 : if (strcmp(type_str, "absolute ") == 0)
636 : : {
637 : : time64 time;
638 : 1 : iss >> time;
639 : 1 : set_value(time);
640 : 1 : if (iss.get() != ')')
641 : 1 : iss.unget();
642 : : }
643 : 12 : else if (strcmp(type_str, "relative ") == 0)
644 : : {
645 : 12 : std::string period_str;
646 : 12 : iss >> period_str;
647 : 12 : if (period_str.back() == ')')
648 : 0 : period_str.pop_back();
649 : 12 : auto period = gnc_relative_date_from_storage_string(period_str.c_str());
650 : 12 : if (period == RelativeDatePeriod::ABSOLUTE)
651 : : {
652 : 0 : std::string err{"Unknown period string in date option: '"};
653 : 0 : err += period_str;
654 : 0 : err += "'";
655 : 0 : throw std::invalid_argument(err);
656 : 0 : }
657 : :
658 : 12 : set_value(period);
659 : 12 : }
660 : : else
661 : : {
662 : 0 : std::string err{"Unknown date type string in date option: '"};
663 : 0 : err += type_str;
664 : 0 : err += "'";
665 : 0 : throw std::invalid_argument{err};
666 : 0 : }
667 : 13 : return iss;
668 : : }
669 : :
670 : : QofInstance*
671 : 5 : qof_instance_from_guid(GncGUID* guid, GncOptionUIType type)
672 : : {
673 : : QofIdType qof_type;
674 : 5 : switch(type)
675 : : {
676 : 1 : case GncOptionUIType::BUDGET:
677 : 1 : qof_type = "Budget";
678 : 1 : break;
679 : 0 : case GncOptionUIType::JOB:
680 : 0 : qof_type = "gncJob";
681 : 0 : break;
682 : 0 : case GncOptionUIType::CUSTOMER:
683 : 0 : qof_type = "gncCustomer";
684 : 0 : break;
685 : 0 : case GncOptionUIType::VENDOR:
686 : 0 : qof_type = "gncVendor";
687 : 0 : break;
688 : 0 : case GncOptionUIType::EMPLOYEE:
689 : 0 : qof_type = "gncEmployee";
690 : 0 : break;
691 : 0 : case GncOptionUIType::INVOICE:
692 : 0 : qof_type = "gncInvoice";
693 : 0 : break;
694 : 0 : case GncOptionUIType::TAX_TABLE:
695 : 0 : qof_type = "gncTaxTable";
696 : 0 : break;
697 : 4 : case GncOptionUIType::ACCOUNT_LIST:
698 : : case GncOptionUIType::ACCOUNT_SEL:
699 : : default:
700 : 4 : qof_type = "Account";
701 : 4 : break;
702 : : }
703 : 5 : auto book{get_current_book()};
704 : 5 : auto col{qof_book_get_collection(book, qof_type)};
705 : 5 : return QOF_INSTANCE(qof_collection_lookup_entity(col, guid));
706 : : }
707 : :
708 : : QofInstance*
709 : 5 : qof_instance_from_string(const std::string& str, GncOptionUIType type)
710 : : {
711 : 5 : QofInstance* retval{nullptr};
712 : : try {
713 : 5 : auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
714 : 5 : retval = qof_instance_from_guid(&guid, type);
715 : : }
716 : 0 : catch (const gnc::guid_syntax_exception& err)
717 : : {
718 : 0 : PWARN("Failed to convert %s to a GUID", str.c_str());
719 : 0 : }
720 : 5 : return retval;
721 : : }
722 : :
723 : : std::string
724 : 1 : qof_instance_to_string(const QofInstance* inst)
725 : : {
726 : 1 : std::string retval;
727 : 1 : gnc::GUID guid{*qof_instance_get_guid(inst)};
728 : 1 : retval = guid.to_string();
729 : 2 : return retval;
730 : 0 : }
731 : :
732 : : template <typename ValueType> void
733 : 16802 : GncOptionValue<ValueType>::set_value(ValueType new_value)
734 : : {
735 : 16802 : m_value = new_value;
736 : 16802 : m_dirty = true;
737 : 16802 : }
738 : :
739 : : template <typename ValueType> void
740 : 126 : GncOptionValue<ValueType>::set_default_value(ValueType new_value)
741 : : {
742 : 126 : m_value = m_default_value = new_value;
743 : 126 : }
744 : :
745 : : template <typename ValueType> void
746 : 0 : GncOptionValue<ValueType>::reset_default_value()
747 : : {
748 : 0 : m_value = m_default_value;
749 : 0 : }
750 : :
751 : : /* Missing on purpose: QofQuery because for current usage it's serialized with
752 : : * gnc_query2scm. The future is to replace QofQuery with SQL queries so there's
753 : : * not much point to spending the time to create a std::string serialization for
754 : : * them.
755 : : */
756 : : template <typename ValueType> std::string
757 : 12 : GncOptionValue<ValueType>::serialize() const noexcept
758 : : {
759 : 20 : static const std::string no_value{"No Value"};
760 : : if constexpr(std::is_same_v<ValueType, const QofInstance*>)
761 : : return m_value ? qof_instance_to_string(m_value) : no_value;
762 : : if constexpr(std::is_same_v<ValueType, const GncOwner*>)
763 : : {
764 : 0 : if (!m_value)
765 : 0 : return no_value;
766 : 0 : auto guid{qof_instance_to_string(qofOwnerGetOwner(m_value))};
767 : 0 : auto type{qofOwnerGetType(m_value)};
768 : 0 : std::ostringstream ostr{};
769 : 0 : ostr << type << " " << guid;
770 : 0 : return ostr.str();
771 : 0 : }
772 : : if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
773 : : {
774 : 0 : std::ostringstream ostr{};
775 : 0 : ostr << "'(";
776 : 0 : std::for_each(m_value.begin(), m_value.end(),
777 : 0 : [&ostr](auto rp){
778 : 0 : auto [id, wide, high] = rp;
779 : 0 : ostr << "(" << id << " " << wide << " " << high << " #f) ";
780 : : });
781 : 0 : ostr << ")";
782 : 0 : return ostr.str();
783 : 0 : }
784 : : else if constexpr(is_same_decayed_v<ValueType, std::string>)
785 : 9 : return m_value;
786 : : else if constexpr(is_same_decayed_v<ValueType, bool>)
787 : 4 : return m_value ? "True" : "False";
788 : : else if constexpr(std::is_arithmetic_v<ValueType>)
789 : 1 : return std::to_string(m_value);
790 : : else
791 : 0 : return "Serialization not implemented";
792 : : }
793 : :
794 : : template <typename ValueType> bool
795 : 0 : GncOptionValue<ValueType>::deserialize(const std::string& str) noexcept
796 : : {
797 : : if constexpr(std::is_same_v<ValueType, const QofInstance*>)
798 : : set_value(qof_instance_from_string(str, get_ui_type()));
799 : : if constexpr(std::is_same_v<ValueType, const GncOwner*>)
800 : : {
801 : 0 : std::istringstream istr{str};
802 : 0 : std::string type, guid;
803 : 0 : istr >> type >> guid;
804 : 0 : auto inst{qof_instance_from_string(guid, get_ui_type())};
805 : 0 : qofOwnerSetEntity(const_cast<GncOwner*>(m_value), inst);
806 : 0 : }
807 : : if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
808 : : {
809 : 0 : std::istringstream istr{str};
810 : 0 : GncOptionReportPlacementVec rpv;
811 : 0 : while (istr)
812 : : {
813 : : uint32_t id, wide, high;
814 : 0 : istr >> id >> wide >> high;
815 : 0 : rpv.emplace_back(id, wide, high);
816 : : }
817 : 0 : set_value(rpv);
818 : 0 : }
819 : : else if constexpr(is_same_decayed_v<ValueType, std::string>)
820 : 0 : set_value(str);
821 : : else if constexpr(is_same_decayed_v<ValueType, bool>)
822 : 0 : set_value(str == "True");
823 : : else if constexpr(is_same_decayed_v<ValueType, int>)
824 : 0 : set_value(stoi(str));
825 : : else if constexpr(is_same_decayed_v<ValueType, int64_t>)
826 : 0 : set_value(stoll(str));
827 : : else if constexpr(is_same_decayed_v<ValueType, double>)
828 : 0 : set_value(stod(str));
829 : : else
830 : 0 : return false;
831 : 0 : return true;
832 : : }
833 : :
834 : : std::string
835 : 3 : GncOptionAccountListValue::serialize() const noexcept
836 : : {
837 : 7 : static const std::string no_value{"No Value"};
838 : 3 : std::string retval;
839 : 3 : bool first = true;
840 : 3 : if (m_value.empty())
841 : 0 : return no_value;
842 : : gchar guidstr[GUID_ENCODING_LENGTH + 1];
843 : 8 : for (auto val : m_value)
844 : : {
845 : 5 : if (!first)
846 : 2 : retval += " ";
847 : 5 : first = false;
848 : 5 : guid_to_string_buff (&val, guidstr);
849 : 5 : retval += guidstr;
850 : : }
851 : 3 : return retval;
852 : 3 : }
853 : :
854 : : bool
855 : 0 : GncOptionAccountListValue::deserialize(const std::string& str) noexcept
856 : : {
857 : 0 : if (str.empty() || str.size() < GUID_ENCODING_LENGTH)
858 : 0 : return false;
859 : 0 : m_value.clear();
860 : 0 : m_value.reserve(str.size() / GUID_ENCODING_LENGTH);
861 : 0 : bool first = true;
862 : 0 : size_t pos{};
863 : 0 : while (pos + GUID_ENCODING_LENGTH < str.size())
864 : : {
865 : 0 : if (!first)
866 : 0 : ++pos;
867 : 0 : first = false;
868 : 0 : GncGUID guid{};
869 : 0 : string_to_guid(str.substr(pos, pos + GUID_ENCODING_LENGTH).c_str(), &guid);
870 : 0 : m_value.push_back(guid);
871 : 0 : pos += GUID_ENCODING_LENGTH;
872 : : }
873 : 0 : return true;
874 : : }
875 : :
876 : : std::string
877 : 2 : GncOptionAccountSelValue::serialize() const noexcept
878 : : {
879 : 4 : static const std::string no_value{"No Value"};
880 : 2 : if (guid_equal(guid_null(), &m_value))
881 : 0 : return no_value;
882 : :
883 : : gchar strbuff[GUID_ENCODING_LENGTH + 1];
884 : 2 : guid_to_string_buff (&m_value, strbuff);
885 : 4 : return strbuff;
886 : : }
887 : :
888 : : bool
889 : 0 : GncOptionAccountSelValue::deserialize(const std::string& str) noexcept
890 : : {
891 : 0 : set_value(reinterpret_cast<Account*>(qof_instance_from_string(str, get_ui_type())));
892 : 0 : return true;
893 : : }
894 : :
895 : : std::string
896 : 8 : GncOptionMultichoiceValue::serialize() const noexcept
897 : : {
898 : 12 : static const std::string no_value{""};
899 : 8 : std::string retval;
900 : 8 : bool first = true;
901 : 8 : bool list_context = m_ui_type == GncOptionUIType::LIST;
902 : 8 : if (m_value.empty())
903 : 0 : return no_value;
904 : :
905 : 8 : if (list_context)
906 : 2 : retval += '(';
907 : 17 : for (auto index : m_value)
908 : : {
909 : 9 : if (!first)
910 : 1 : retval += " ";
911 : 9 : first = false;
912 : 9 : retval += std::get<0>(m_choices[index]);
913 : : }
914 : 8 : if (list_context)
915 : 2 : retval += ')';
916 : 8 : return retval;
917 : 8 : }
918 : :
919 : : bool
920 : 0 : GncOptionMultichoiceValue::deserialize(const std::string& str) noexcept
921 : : {
922 : : static const auto uint16_t_max = std::numeric_limits<uint16_t>::max();
923 : 0 : if (str.empty())
924 : :
925 : 0 : return false;
926 : 0 : uint16_t pos{};
927 : 0 : while (pos < str.size())
928 : : {
929 : 0 : auto endpos{str.find(' ', pos)};
930 : 0 : if (endpos == std::string::npos)
931 : 0 : endpos = str.size();
932 : : //need a null-terminated char* to pass to permissible_value_index
933 : 0 : auto index{permissible_value_index(str.substr(pos, endpos).c_str())};
934 : 0 : if (index == uint16_t_max)
935 : 0 : return false;
936 : 0 : m_value.push_back(index);
937 : 0 : pos = endpos + 1;
938 : : }
939 : 0 : return true;
940 : : }
941 : :
942 : : template <typename ValueType> std::string
943 : 4 : GncOptionRangeValue<ValueType>::serialize() const noexcept
944 : : {
945 : : if constexpr (std::is_arithmetic_v<ValueType>)
946 : : {
947 : 4 : std::ostringstream ostr;
948 : : if constexpr(is_same_decayed_v<ValueType, double>)
949 : 2 : ostr << std::showpoint << std::fixed;
950 : 4 : ostr << m_value;
951 : 4 : return ostr.str();
952 : 4 : }
953 : : return "";
954 : : }
955 : :
956 : : template <typename ValueType> bool
957 : 0 : GncOptionRangeValue<ValueType>::deserialize(const std::string& str) noexcept
958 : : {
959 : : if constexpr(is_same_decayed_v<ValueType, int>)
960 : 0 : set_value(stoi(str));
961 : : else if constexpr(is_same_decayed_v<ValueType, double>)
962 : 0 : set_value(stod(str));
963 : 0 : return true;
964 : : }
965 : :
966 : : std::string
967 : 20 : GncOptionDateValue::serialize() const noexcept
968 : : {
969 : 20 : std::string retval{"("};
970 : 20 : if (m_period == RelativeDatePeriod::ABSOLUTE)
971 : : {
972 : 3 : retval += date_type_str[0];
973 : 3 : retval += " . ";
974 : 3 : retval += std::to_string(m_date);
975 : : }
976 : : else
977 : : {
978 : 17 : retval += date_type_str[1];
979 : 17 : retval += " . ";
980 : 17 : retval += gnc_relative_date_storage_string(m_period);
981 : : }
982 : 20 : retval += ")";
983 : 20 : return retval;
984 : : }
985 : :
986 : : bool
987 : 0 : GncOptionDateValue::deserialize(const std::string& str) noexcept
988 : : {
989 : : //The length of both "absolute" and "relative".
990 : : static constexpr size_t date_type_len{9};
991 : : // date_type_len plus the length of " . ".
992 : : static constexpr size_t date_value_pos{12};
993 : 0 : auto type_str{str.substr(0, date_type_len)};
994 : 0 : auto period_str{str.substr(date_value_pos)};
995 : 0 : if (type_str == "absolute")
996 : : {
997 : : // Need a cast to disambiguate from time64.
998 : 0 : set_value(static_cast<uint16_t>(std::stoll(period_str)));
999 : 0 : return true;
1000 : : }
1001 : 0 : else if (type_str == "relative ")
1002 : : {
1003 : 0 : auto period = gnc_relative_date_from_storage_string(period_str.c_str());
1004 : 0 : if (period == RelativeDatePeriod::ABSOLUTE)
1005 : : {
1006 : 0 : PWARN("Unknown period string in date option: '%s'",
1007 : : period_str.c_str());
1008 : 0 : return false;
1009 : : }
1010 : :
1011 : 0 : set_value(period);
1012 : 0 : return true;
1013 : : }
1014 : : else
1015 : : {
1016 : 0 : PWARN("Unknown date type string in date option: '%s'",
1017 : : type_str.c_str());
1018 : 0 : return false;
1019 : : }
1020 : 0 : }
1021 : :
1022 : : std::istream&
1023 : 3 : operator>> (std::istream& iss, GncOptionCommodityValue& opt)
1024 : : {
1025 : 3 : std::string instr;
1026 : 3 : iss >> instr;
1027 : 3 : if (!opt.deserialize(instr))
1028 : 1 : throw std::invalid_argument("Invalid commodity string in stream.");
1029 : 2 : return iss;
1030 : 3 : }
1031 : :
1032 : : template void GncOptionValue<bool>::set_value(bool);
1033 : : template void GncOptionValue<int>::set_value(int);
1034 : : template void GncOptionValue<int64_t>::set_value(int64_t);
1035 : : template void GncOptionValue<double>::set_value(double);
1036 : : template void GncOptionValue<char*>::set_value(char*);
1037 : : template void GncOptionValue<const char*>::set_value(const char*);
1038 : : template void GncOptionValue<std::string>::set_value(std::string);
1039 : : template void GncOptionValue<const QofQuery*>::set_value(const QofQuery*);
1040 : : template void GncOptionValue<const GncOwner*>::set_value(const GncOwner*);
1041 : : template void GncOptionValue<RelativeDatePeriod>::set_value(RelativeDatePeriod);
1042 : : template void GncOptionValue<uint16_t>::set_value(uint16_t);
1043 : : template void GncOptionValue<GncOptionAccountList>::set_value(GncOptionAccountList);
1044 : : template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_value(GncMultichoiceOptionIndexVec);
1045 : : template void GncOptionValue<GncOptionReportPlacementVec>::set_value(GncOptionReportPlacementVec);
1046 : : template void GncOptionValue<GncOptionDateFormat>::set_value(GncOptionDateFormat);
1047 : : template void GncOptionValue<bool>::set_default_value(bool);
1048 : : template void GncOptionValue<int>::set_default_value(int);
1049 : : template void GncOptionValue<int64_t>::set_default_value(int64_t);
1050 : : template void GncOptionValue<double>::set_default_value(double);
1051 : : template void GncOptionValue<char*>::set_default_value(char*);
1052 : : template void GncOptionValue<const char*>::set_default_value(const char*);
1053 : : template void GncOptionValue<std::string>::set_default_value(std::string);
1054 : : template void GncOptionValue<const QofQuery*>::set_default_value(const QofQuery*);
1055 : : template void GncOptionValue<const GncOwner*>::set_default_value(const GncOwner*);
1056 : : template void GncOptionValue<RelativeDatePeriod>::set_default_value(RelativeDatePeriod);
1057 : : template void GncOptionValue<uint16_t>::set_default_value(uint16_t);
1058 : : template void GncOptionValue<GncOptionAccountList>::set_default_value(GncOptionAccountList);
1059 : : template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_default_value(GncMultichoiceOptionIndexVec);
1060 : : template void GncOptionValue<GncOptionReportPlacementVec>::set_default_value(GncOptionReportPlacementVec);
1061 : : template void GncOptionValue<GncOptionDateFormat>::set_default_value(GncOptionDateFormat);
1062 : : template void GncOptionValue<bool>::reset_default_value();
1063 : : template void GncOptionValue<int>::reset_default_value();
1064 : : template void GncOptionValue<int64_t>::reset_default_value();
1065 : : template void GncOptionValue<double>::reset_default_value();
1066 : : template void GncOptionValue<char*>::reset_default_value();
1067 : : template void GncOptionValue<const char*>::reset_default_value();
1068 : : template void GncOptionValue<std::string>::reset_default_value();
1069 : : template void GncOptionValue<const QofQuery*>::reset_default_value();
1070 : : template void GncOptionValue<const GncOwner*>::reset_default_value();
1071 : : template void GncOptionValue<RelativeDatePeriod>::reset_default_value();
1072 : : template void GncOptionValue<uint16_t>::reset_default_value();
1073 : : template void GncOptionValue<GncOptionAccountList>::reset_default_value();
1074 : : template void GncOptionValue<GncMultichoiceOptionIndexVec>::reset_default_value();
1075 : : template void GncOptionValue<GncOptionReportPlacementVec>::reset_default_value();
1076 : : template void GncOptionValue<GncOptionDateFormat>::reset_default_value();
1077 : : template std::string GncOptionValue<bool>::serialize() const noexcept;
1078 : : template std::string GncOptionValue<int>::serialize() const noexcept;
1079 : : template std::string GncOptionValue<int64_t>::serialize() const noexcept;
1080 : : template std::string GncOptionValue<double>::serialize() const noexcept;
1081 : : template std::string GncOptionValue<char*>::serialize() const noexcept;
1082 : : template std::string GncOptionValue<const char*>::serialize() const noexcept;
1083 : : template std::string GncOptionValue<std::string>::serialize() const noexcept;
1084 : : template std::string GncOptionValue<const QofQuery*>::serialize() const noexcept;
1085 : : template std::string GncOptionValue<const GncOwner*>::serialize() const noexcept;
1086 : : template std::string GncOptionValue<GncOptionReportPlacementVec>::serialize() const noexcept;
1087 : : template std::string GncOptionValue<GncOptionDateFormat>::serialize() const noexcept;
1088 : : template std::string GncOptionRangeValue<int>::serialize() const noexcept;
1089 : : template std::string GncOptionRangeValue<double>::serialize() const noexcept;
1090 : : template bool GncOptionValue<bool>::deserialize(const std::string&) noexcept;
1091 : : template bool GncOptionValue<int>::deserialize(const std::string&) noexcept;
1092 : : template bool GncOptionValue<int64_t>::deserialize(const std::string&) noexcept;
1093 : : template bool GncOptionValue<double>::deserialize(const std::string&) noexcept;
1094 : : template bool GncOptionValue<char*>::deserialize(const std::string&) noexcept;
1095 : : template bool GncOptionValue<const char*>::deserialize(const std::string&) noexcept;
1096 : : template bool GncOptionValue<std::string>::deserialize(const std::string&) noexcept;
1097 : : template bool GncOptionValue<const QofQuery*>::deserialize(const std::string&) noexcept;
1098 : : template bool GncOptionValue<const GncOwner*>::deserialize(const std::string&) noexcept;
1099 : : template bool GncOptionValue<GncOptionReportPlacementVec>::deserialize(const std::string&) noexcept;
1100 : : template bool GncOptionValue<GncOptionDateFormat>::deserialize(const std::string&) noexcept;
1101 : : template bool GncOptionRangeValue<int>::deserialize(const std::string&) noexcept;
1102 : : template bool GncOptionRangeValue<double>::deserialize(const std::string&) noexcept;
|