GnuCash  5.6-139-g03622b03d0+
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 
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 ui_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 
55 static GncOwner*
56 make_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 
65 GncOptionGncOwnerValue::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 
73 GncOptionGncOwnerValue::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 
79 void
80 GncOptionGncOwnerValue::set_value(const GncOwner* new_value)
81 {
82  m_value.reset(make_owner_ptr(new_value));
83  m_dirty = true;
84 }
85 
86 void
87 GncOptionGncOwnerValue::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 
93 const GncOwner*
94 GncOptionGncOwnerValue::get_value() const
95 {
96  return m_value.get();
97 }
98 
99 const GncOwner*
100 GncOptionGncOwnerValue::get_default_value() const
101 {
102  return m_default_value.get();
103 }
104 
105 void
106 GncOptionGncOwnerValue::reset_default_value()
107 {
108  gncOwnerCopy(m_default_value.get(), m_value.get());
109 }
110 
111 bool
112 GncOptionGncOwnerValue::is_changed() const noexcept
113 {
114  return gncOwnerEqual(m_value.get(), m_default_value.get());
115 }
116 
117 bool
118 GncOptionGncOwnerValue::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 
139 std::string
140 GncOptionGncOwnerValue::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 
150 static GncItem
151 make_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 
160 static inline QofBook*
161 get_current_book(void)
162 {
163  return qof_session_get_book(gnc_get_current_session());
164 }
165 
166 static inline Account*
167 get_current_root_account(void)
168 {
169  return gnc_book_get_root_account(get_current_book());
170 }
171 
172 static const QofInstance*
173 qof_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 
181 GncOptionQofInstanceValue::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 
192 GncOptionQofInstanceValue::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 }
199 void
200 GncOptionQofInstanceValue::set_value(const QofInstance* new_value)
201 {
202  m_value = make_gnc_item(new_value);
203  m_dirty = true;
204 }
205 
206 void
207 GncOptionQofInstanceValue::set_default_value(const QofInstance *new_value)
208 {
209  m_value = m_default_value = make_gnc_item(new_value);
210 
211 }
212 
213 const QofInstance*
214 GncOptionQofInstanceValue::get_value() const
215 {
216  return qof_instance_from_gnc_item(m_value);
217 }
218 
219 const QofInstance*
220 GncOptionQofInstanceValue::get_default_value() const
221 {
222  return qof_instance_from_gnc_item(m_default_value);
223 }
224 
225 void
226 GncOptionQofInstanceValue::reset_default_value()
227 {
228  m_value = m_default_value;
229 }
230 
231 bool
232 GncOptionQofInstanceValue::is_changed() const noexcept
233 {
234  return m_value != m_default_value;
235 }
236 
237 bool
238 GncOptionQofInstanceValue::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 
258 std::string
259 GncOptionQofInstanceValue::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 
286 static gnc_commodity*
287 gnc_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 
296 gnc_commodity*
297 GncOptionCommodityValue::get_value() const
298 {
299  return gnc_commodity_from_namespace_and_mnemonic(m_namespace, m_mnemonic);
300 }
301 
302 gnc_commodity*
303 GncOptionCommodityValue::get_default_value() const
304 {
305  return gnc_commodity_from_namespace_and_mnemonic(m_default_namespace,
306  m_default_mnemonic);
307 }
308 
309 void
310 GncOptionCommodityValue::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 
319 void
320 GncOptionCommodityValue::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 
328 void
329 GncOptionCommodityValue::reset_default_value()
330 {
331  m_mnemonic = m_default_mnemonic;
332  m_namespace = m_default_namespace;
333 }
334 
335 bool
336 GncOptionCommodityValue::is_changed() const noexcept
337 {
338  return m_namespace != m_default_namespace || m_mnemonic != m_default_mnemonic;
339 }
340 
341 bool
342 GncOptionCommodityValue::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 
351 std::string
352 GncOptionCommodityValue::serialize() const noexcept
353 {
354  if (m_is_currency)
355  return m_mnemonic;
356  else
357  return m_namespace + ":" + m_mnemonic;
358 }
359 
360 bool
361 GncOptionCommodityValue::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 
384 bool
385 GncOptionAccountListValue::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 
409 GncOptionAccountList
410 GncOptionAccountListValue::get_value() const
411 {
412  return !m_value.empty() ? m_value : get_default_value();
413 }
414 
415 GncOptionAccountList
416 GncOptionAccountListValue::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 
446 bool
447 GncOptionAccountListValue::is_changed() const noexcept
448 {
449  return m_value != m_default_value;
450 }
451 
452 
453 
461 GList*
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 
472 bool
473 GncOptionAccountSelValue::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 
483 const Account*
484 GncOptionAccountSelValue::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 
491 const Account*
492 GncOptionAccountSelValue::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 
532 GList*
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 
543 bool
544 GncOptionDateValue::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 
553 time64
554 GncOptionDateValue::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 
561 time64
562 GncOptionDateValue::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  */
572 uint16_t
573 GncOptionDateValue::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 
582 uint16_t
583 GncOptionDateValue::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 
593 void
594 GncOptionDateValue::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 
603 uint16_t
604 GncOptionDateValue::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 
614 static const char* date_type_str[] {"absolute", "relative"};
615 
616 std::ostream&
617 GncOptionDateValue::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 
627 std::istream&
628 GncOptionDateValue::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 
671 qof_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 
709 qof_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 
723 std::string
724 qof_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 
732 template <typename ValueType> void
733 GncOptionValue<ValueType>::set_value(ValueType new_value)
734 {
735  m_value = new_value;
736  m_dirty = true;
737 }
738 
739 template <typename ValueType> void
741 {
742  m_value = m_default_value = new_value;
743 }
744 
745 template <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  */
756 template <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 
794 template <typename ValueType> bool
795 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  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 
834 std::string
835 GncOptionAccountListValue::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 
854 bool
855 GncOptionAccountListValue::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);
871  pos += GUID_ENCODING_LENGTH;
872  }
873  return true;
874 }
875 
876 std::string
877 GncOptionAccountSelValue::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 
888 bool
889 GncOptionAccountSelValue::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 
895 std::string
896 GncOptionMultichoiceValue::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 
919 bool
920 GncOptionMultichoiceValue::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 
942 template <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 
956 template <typename ValueType> bool
957 GncOptionRangeValue<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 
966 std::string
967 GncOptionDateValue::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 
986 bool
987 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  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 
1022 std::istream&
1023 operator>> (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 
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);
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);
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);
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;
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
std::pair< QofIdTypeConst, GncGUID > GncItem
class GncOptionQofinstanceValue
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...
Business Interface: Object OWNERs.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
RelativeDatePeriod gnc_relative_date_from_storage_string(const char *str)
Convert a relative date storage string back to a RelativeDatePeriod value.
GList * gnc_account_get_descendants_sorted(const Account *account)
This function returns a GList containing all the descendants of the specified account, sorted at each level.
Definition: Account.cpp:3047
const char * gnc_relative_date_display_string(RelativeDatePeriod per)
Provide the string representation of a relative date for displaying value to a user.
The generic option-value class.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
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:212
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3282
STRUCTS.
GncGUID guid_new_return(void)
Generate a new id.
Definition: guid.cpp:158
OptionUITypes.
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Given a string, replace the given guid with the parsed one unless the given value is null...
GList * account_type_list() const noexcept
Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
gboolean gncOwnerEqual(const GncOwner *a, const GncOwner *b)
Assess equality by checking.
Definition: gncOwner.c:404
STL namespace.
QofCollection * qof_instance_get_collection(gconstpointer ptr)
Return the collection this instance belongs to.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
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:173
time64 gnc_relative_date_to_time64(RelativeDatePeriod period)
Convert a RelativeDatePeriod value to a concrete time64 by applying the value to the current time...
QofInstance * qofOwnerGetOwner(const GncOwner *owner)
return the owner itself as an entity.
Definition: gncOwner.c:275
This class is the parent of all option implementations.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
const gchar * QofIdType
QofIdType declaration.
Definition: qofid.h:80
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:574
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:204
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
RelativeDatePeriod
Reporting periods relative to the current date.
General utilities for dealing with accounting periods.
QofIdTypeConst qofOwnerGetType(const GncOwner *owner)
return the type for the collection.
Definition: gncOwner.c:230
void qofOwnerSetEntity(GncOwner *owner, QofInstance *ent)
set the owner from the entity.
Definition: gncOwner.c:319
Implementation templates and specializtions for GncOption values.
class GncOptionCommodityValue Commodities are stored with their namespace and mnemonic instead of the...
Used for numeric ranges and plot sizes.
const GncGUID * qof_entity_get_guid(gconstpointer ent)
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:130
const char * gnc_relative_date_storage_string(RelativeDatePeriod per)
Provide the string representation of a relative date for persisting the value.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:521
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
The type used to store guids in C.
Definition: guid.h:75
A Query.
Definition: qofquery.cpp:74
GncOwner * gncOwnerNew(void)
These two functions are mainly for the convenience of scheme code.
Definition: gncOwner.c:57
GncOptionUIType
Used by GncOptionClassifier to indicate to dialog-options what control should be displayed for the op...
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2064