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