GnuCash  4.11-354-g0815ab64c1
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 "options.h"
25 #include "gnc-option-impl.hpp"
26 #include <gnc-datetime.hpp>
27 #include <guid.hpp>
28 #include <cassert>
29 #include <sstream>
30 #include <numeric>
31 
32 extern "C"
33 {
34 #include "gnc-accounting-period.h"
35 #include "gnc-ui-util.h"
36 }
37 
38 static const QofLogModule log_module{"gnc.options"};
39 
40 const std::string GncOptionMultichoiceValue::c_empty_string{""};
41 const std::string GncOptionMultichoiceValue::c_list_string{"multiple values"};
42 
43 using GncItem = std::pair<QofIdTypeConst, GncGUID>;
44 
45 static GncItem
46 make_gnc_item(const QofInstance* inst)
47 {
48  if (!inst)
49  return std::make_pair<QofIdTypeConst, GncGUID>("", guid_new_return());
51  auto guid{qof_instance_get_guid(inst)};
52  return std::make_pair(std::move(type), std::move(*const_cast<GncGUID*>(guid)));
53 }
54 
55 static const QofInstance*
56 qof_instance_from_gnc_item(const GncItem& item)
57 {
58  auto [type, guid] = item;
59  auto book{gnc_get_current_book()};
60  auto coll{qof_book_get_collection(book, type)};
61  return static_cast<QofInstance*>(qof_collection_lookup_entity(coll, &guid));
62 }
63 
64 static bool
65 operator!=(const GncItem& left, const GncItem& right)
66 {
67  auto [ltype, lguid]{left};
68  auto [rtype, rguid]{right};
69  return strcmp(rtype, ltype) && !guid_equal(&rguid, &lguid);
70 }
71 
72 GncOptionQofInstanceValue::GncOptionQofInstanceValue(
73  const char* section, const char* name,
74  const char* key, const char* doc_string,
75  const QofInstance* value, GncOptionUIType ui_type) :
76  OptionClassifier{section, name, key, doc_string},
77  m_ui_type(ui_type), m_value{},
78  m_default_value{} {
79  m_value = make_gnc_item(value);
80  m_default_value = make_gnc_item(value);
81 }
82 
83 GncOptionQofInstanceValue::GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from) :
84  OptionClassifier{from.m_section, from.m_name, from.m_sort_tag,
85  from.m_doc_string},
86  m_ui_type(from.get_ui_type()), m_value{from.get_item()},
87  m_default_value{from.get_default_item()}
88 {
89 }
90 void
91 GncOptionQofInstanceValue::set_value(const QofInstance* new_value)
92 {
93  m_value = make_gnc_item(new_value);
94 }
95 
96 void
97 GncOptionQofInstanceValue::set_default_value(const QofInstance *new_value)
98 {
99  m_value = m_default_value = make_gnc_item(new_value);
100 
101 }
102 
103 const QofInstance*
104 GncOptionQofInstanceValue::get_value() const
105 {
106  return qof_instance_from_gnc_item(m_value);
107 }
108 
109 const QofInstance*
110 GncOptionQofInstanceValue::get_default_value() const
111 {
112  return qof_instance_from_gnc_item(m_default_value);
113 }
114 
115 void
116 GncOptionQofInstanceValue::reset_default_value()
117 {
118  m_value = m_default_value;
119 }
120 
121 bool
122 GncOptionQofInstanceValue::is_changed() const noexcept
123 {
124  return m_value != m_default_value;
125 }
126 
127 bool
128 GncOptionQofInstanceValue::deserialize(const std::string& str) noexcept
129 {
130  QofInstance* inst{};
131  // Commodities are often serialized as Namespace::Mnemonic or just Mnemonic
132  try {
133  auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
134  inst = qof_instance_from_guid(&guid, m_ui_type);
135  if (inst)
136  {
137  m_value = make_gnc_item(inst);
138  return true;
139  }
140  }
141  catch (const gnc::guid_syntax_exception& err)
142  {
143  PWARN("Failed to convert %s to a GUID", str.c_str());
144  }
145  return false;
146 }
147 
148 std::string
149 GncOptionQofInstanceValue::serialize() const noexcept
150 {
151  auto inst{get_value()};
152  std::string retval;
153  if (GNC_IS_COMMODITY(inst))
154  {
155  auto commodity{GNC_COMMODITY(inst)};
156  if (!gnc_commodity_is_currency(commodity))
157  {
158  auto name_space{gnc_commodity_get_namespace(GNC_COMMODITY(inst))};
159  if (name_space && *name_space != '\0')
160  {
161  retval = name_space;
162  retval += ":";
163  }
164  }
165  retval += gnc_commodity_get_mnemonic(GNC_COMMODITY(inst));
166  return retval;
167  }
168  else
169  {
170  gnc::GUID guid{m_value.second};
171  retval = guid.to_string();
172  }
173  return retval;
174 }
175 
176 static gnc_commodity*
177 gnc_commodity_from_namespace_and_mnemonic(std::string_view name_space,
178  std::string_view mnemonic)
179 {
180  auto book{gnc_get_current_book()};
182  return gnc_commodity_table_lookup(table, name_space.data(),
183  mnemonic.data());
184 }
185 
186 gnc_commodity*
187 GncOptionCommodityValue::get_value() const
188 {
189  return gnc_commodity_from_namespace_and_mnemonic(m_namespace, m_mnemonic);
190 }
191 
192 gnc_commodity*
193 GncOptionCommodityValue::get_default_value() const
194 {
195  return gnc_commodity_from_namespace_and_mnemonic(m_default_namespace,
196  m_default_mnemonic);
197 }
198 
199 void
200 GncOptionCommodityValue::set_value(gnc_commodity* value)
201 {
202  if (!validate(value))
203  throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
204  m_mnemonic = gnc_commodity_get_mnemonic(value);
205  m_namespace = gnc_commodity_get_namespace(value);
206 }
207 
208 void
209 GncOptionCommodityValue::set_default_value(gnc_commodity* value)
210 {
211  if (!validate(value))
212  throw std::invalid_argument("Value not a currency when required or not a commodity. Value not set.");
213  m_mnemonic = m_default_mnemonic = gnc_commodity_get_mnemonic(value);
214  m_namespace = m_default_namespace = gnc_commodity_get_namespace(value);
215 }
216 
217 void
218 GncOptionCommodityValue::reset_default_value()
219 {
220  m_mnemonic = m_default_mnemonic;
221  m_namespace = m_default_namespace;
222 }
223 
224 bool
225 GncOptionCommodityValue::is_changed() const noexcept
226 {
227  return m_namespace != m_default_namespace || m_mnemonic != m_default_mnemonic;
228 }
229 
230 bool
231 GncOptionCommodityValue::validate(gnc_commodity* comm) const noexcept
232 {
233  if (!GNC_IS_COMMODITY(comm))
234  return false;
235  if (m_is_currency && !gnc_commodity_is_currency(comm))
236  return false;
237  return true;
238 }
239 
240 std::string
241 GncOptionCommodityValue::serialize() const noexcept
242 {
243  if (m_is_currency)
244  return m_mnemonic;
245  else
246  return m_namespace + ":" + m_mnemonic;
247 }
248 
249 bool
250 GncOptionCommodityValue::deserialize(const std::string& str) noexcept
251 {
252  auto sep{str.find(":")};
253  gnc_commodity* comm{};
254  std::string mnemonic, name_space;
255  if (sep != std::string::npos)
256  {
257  name_space = str.substr(0, sep);
258  mnemonic = str.substr(sep + 1, -1);
259  }
260  else
261  {
262  name_space = "CURRENCY";
263  mnemonic = str;
264  }
265  comm = gnc_commodity_from_namespace_and_mnemonic(name_space, mnemonic);
266  if (!validate(comm))
267  return false;
268  m_namespace = std::move(name_space);
269  m_mnemonic = std::move(mnemonic);
270  return true;
271 }
272 
273 bool
274 GncOptionAccountListValue::validate(const GncOptionAccountList& values) const
275 {
276  if (values.empty())
277  return true;
278  if ((get_ui_type() == GncOptionUIType::ACCOUNT_SEL || !m_multiselect) &&
279  values.size() != 1)
280  {
281  std::cerr << "GncOptionAccountListValue::validate: Multiple values for a non-multiselect option." << std::endl;
282  return false;
283  }
284  if (m_allowed.empty())
285  return true;
286  auto book{gnc_get_current_book()};
287  for(auto& guid : values)
288  {
289  if (std::find(m_allowed.begin(), m_allowed.end(),
290  xaccAccountGetType(xaccAccountLookup(&guid, book))) == m_allowed.end())
291  {
292  std::cerr << "GncOptionAccountListValue::validate: Account " << gnc::GUID(guid).to_string() << " is not of an allowed type" << std::endl;
293  return false; }
294  }
295  return true;
296 }
297 
298 GncOptionAccountList
299 GncOptionAccountListValue::get_value() const
300 {
301  return !m_value.empty() ? m_value : get_default_value();
302 }
303 
304 GncOptionAccountList
305 GncOptionAccountListValue::get_default_value() const
306 {
307  if (!m_default_value.empty())
308  return m_default_value;
309 
310  /* If no default has been set and there's an allowed set then find the first
311  * account that matches one of the allowed account types.
312  */
313  GncOptionAccountList retval{};
314  if (m_allowed.empty())
315  return retval;
316 
317  auto root{gnc_get_current_root_account()};
318  auto account_list{gnc_account_get_descendants_sorted(root)};
319  if (!account_list)
320  return retval;
321 
322  auto book{gnc_get_current_book()};
323  for (auto node = account_list; node; node = g_list_next (node))
324  {
325  if (std::find(m_allowed.begin(), m_allowed.end(),
326  xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
327  {
328  retval.push_back(*qof_entity_get_guid(GNC_ACCOUNT(node->data)));
329  break;
330  }
331  }
332  g_list_free(account_list);
333  return retval;
334 }
335 
336 static bool
337 operator==(const GncGUID& l, const GncGUID& r)
338 {
339  return guid_equal(&l, &r);
340 }
341 
342 bool
343 GncOptionAccountListValue::is_changed() const noexcept
344 {
345  return m_value != m_default_value;
346 }
347 
348 
349 
357 GList*
359 {
360  if (m_allowed.empty())
361  return nullptr;
362  GList* retval{nullptr};
363  for (auto type : m_allowed)
364  retval = g_list_prepend(retval, GINT_TO_POINTER(type));
365  return g_list_reverse(retval);
366 }
367 
368 bool
369 GncOptionAccountSelValue::validate(const Account* value) const
370 {
371  if (m_allowed.empty() || !value)
372  return true;
373  if (std::find(m_allowed.begin(), m_allowed.end(),
374  xaccAccountGetType(value)) == m_allowed.end())
375  return false;
376  return true;
377 }
378 
379 const Account*
380 GncOptionAccountSelValue::get_value() const
381 {
382  auto book{gnc_get_current_book()};
383  return guid_equal(guid_null(), &m_value) ? get_default_value() :
384  xaccAccountLookup(&m_value, book);
385 }
386 
387 const Account*
388 GncOptionAccountSelValue::get_default_value() const
389 {
390 
391  if (!guid_equal(guid_null(), &m_default_value))
392  {
393  auto book{gnc_get_current_book()};
394  return xaccAccountLookup(&m_default_value, book);
395  }
396 
397  /* If no default has been set and there's an allowed set then find the first
398  * account that matches one of the allowed account types.
399  */
400  if (m_allowed.empty())
401  return nullptr;
402 
403  const Account* retval{nullptr};
404  auto root{gnc_get_current_root_account()};
405  auto account_list{gnc_account_get_descendants_sorted(root)};
406  if (!account_list)
407  return nullptr;
408 
409  for (auto node = account_list; node; node = g_list_next (node))
410  if (std::find(m_allowed.begin(), m_allowed.end(),
411  xaccAccountGetType(GNC_ACCOUNT(node->data))) != m_allowed.end())
412  {
413  retval = GNC_ACCOUNT(node->data);
414  break;
415  }
416  g_list_free(account_list);
417  return retval;
418 }
419 
420 
428 GList*
430 {
431  if (m_allowed.empty())
432  return nullptr;
433  GList* retval{nullptr};
434  for (auto type : m_allowed)
435  retval = g_list_prepend(retval, GINT_TO_POINTER(type));
436  return g_list_reverse(retval);
437 }
438 
439 bool
440 GncOptionDateValue::validate(RelativeDatePeriod value) {
441  if (m_period_set.empty())
442  return true; // No restrictions
443  if (std::find(m_period_set.begin(), m_period_set.end(),
444  value) != m_period_set.end())
445  return true;
446  return false;
447 }
448 
449 time64
450 GncOptionDateValue::get_value() const noexcept
451 {
452  if (m_period == RelativeDatePeriod::ABSOLUTE)
453  return m_date;
454  return gnc_relative_date_to_time64(m_period);
455 }
456 
457 time64
458 GncOptionDateValue::get_default_value() const noexcept
459 {
460  if (m_default_period == RelativeDatePeriod::ABSOLUTE)
461  return m_default_date;
462  return gnc_relative_date_to_time64(m_default_period);
463 }
464 
465 /* Use asserts for pre- and post-conditions to deliberately crash if they're not
466  * met as the program design should prevent that from happening.
467  */
468 size_t
469 GncOptionDateValue::get_period_index() const noexcept
470 {
471  assert (m_period != RelativeDatePeriod::ABSOLUTE);
472  assert(!m_period_set.empty());
473  auto item{std::find(m_period_set.begin(), m_period_set.end(), m_period)};
474  assert(item != m_period_set.end());
475  return item - m_period_set.begin();
476 }
477 
478 size_t
479 GncOptionDateValue::get_default_period_index() const noexcept
480 {
481  assert(m_period != RelativeDatePeriod::ABSOLUTE);
482  assert(!m_period_set.empty());
483  auto item{std::find(m_period_set.begin(), m_period_set.end(),
484  m_default_period)};
485  assert (item != m_period_set.end());
486  return item - m_period_set.begin();
487 }
488 
489 void
490 GncOptionDateValue::set_value(size_t index) noexcept
491 {
492  assert(!m_period_set.empty());
493  assert(index < m_period_set.size());
494  m_date = INT64_MAX;
495  m_period = m_period_set[index];
496 }
497 
498 size_t
499 GncOptionDateValue::permissible_value_index(const char* key) const noexcept
500 {
501  auto index = std::find_if(m_period_set.begin(), m_period_set.end(),
502  [key](auto period) -> bool {
503  return strcmp(gnc_relative_date_display_string(period),
504  key) == 0;
505  });
506  return index != m_period_set.end() ? index - m_period_set.begin() : 0;
507 }
508 
509 static const char* date_type_str[] {"absolute", "relative"};
510 
511 std::ostream&
512 GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
513 {
514  if (m_period == RelativeDatePeriod::ABSOLUTE)
515  oss << date_type_str[0] << " . " << m_date;
516  else
517  oss << date_type_str[1] << " . " <<
519  return oss;
520 }
521 
522 std::istream&
523 GncOptionDateValue::in_stream(std::istream& iss)
524 {
525  char type_str[10]; //The length of both "absolute" and "relative" plus 1.
526  iss.getline(type_str, sizeof(type_str), '.');
527  if(!iss)
528  throw std::invalid_argument("Date Type separator missing");
529  /* strcmp is safe, istream::getline null terminates the buffer. */
530  if (strcmp(type_str, "absolute ") == 0)
531  {
532  time64 time;
533  iss >> time;
534  set_value(time);
535  if (iss.get() != ')')
536  iss.unget();
537  }
538  else if (strcmp(type_str, "relative ") == 0)
539  {
540  std::string period_str;
541  iss >> period_str;
542  if (period_str.back() == ')')
543  period_str.pop_back();
544  auto period = gnc_relative_date_from_storage_string(period_str.c_str());
545  if (period == RelativeDatePeriod::ABSOLUTE)
546  {
547  std::string err{"Unknown period string in date option: '"};
548  err += period_str;
549  err += "'";
550  throw std::invalid_argument(err);
551  }
552 
553  set_value(period);
554  }
555  else
556  {
557  std::string err{"Unknown date type string in date option: '"};
558  err += type_str;
559  err += "'";
560  throw std::invalid_argument{err};
561  }
562  return iss;
563 }
564 
566 qof_instance_from_guid(GncGUID* guid, GncOptionUIType type)
567 {
568  QofIdType qof_type;
569  switch(type)
570  {
571  case GncOptionUIType::BUDGET:
572  qof_type = "Budget";
573  break;
574  case GncOptionUIType::JOB:
575  qof_type = "gncJob";
576  break;
577  case GncOptionUIType::CUSTOMER:
578  qof_type = "gncCustomer";
579  break;
580  case GncOptionUIType::VENDOR:
581  qof_type = "gncVendor";
582  break;
583  case GncOptionUIType::EMPLOYEE:
584  qof_type = "gncEmployee";
585  break;
586  case GncOptionUIType::INVOICE:
587  qof_type = "gncInvoice";
588  break;
589  case GncOptionUIType::TAX_TABLE:
590  qof_type = "gncTaxTable";
591  break;
592  case GncOptionUIType::ACCOUNT_LIST:
593  case GncOptionUIType::ACCOUNT_SEL:
594  default:
595  qof_type = "Account";
596  break;
597  }
598  auto book{gnc_get_current_book()};
599  auto col{qof_book_get_collection(book, qof_type)};
600  return QOF_INSTANCE(qof_collection_lookup_entity(col, guid));
601 }
602 
604 qof_instance_from_string(const std::string& str, GncOptionUIType type)
605 {
606  QofInstance* retval{nullptr};
607  try {
608  auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
609  retval = qof_instance_from_guid(&guid, type);
610  }
611  catch (const gnc::guid_syntax_exception& err)
612  {
613  PWARN("Failed to convert %s to a GUID", str.c_str());
614  }
615  return retval;
616 }
617 
618 std::string
619 qof_instance_to_string(const QofInstance* inst)
620 {
621  std::string retval;
622  gnc::GUID guid{*qof_instance_get_guid(inst)};
623  retval = guid.to_string();
624  return retval;
625 }
626 
627 template <typename ValueType> void
628 GncOptionValue<ValueType>::set_value(ValueType new_value)
629 {
630  m_value = new_value;
631 }
632 
633 template <typename ValueType> void
635 {
636  m_value = m_default_value = new_value;
637 }
638 
639 template <typename ValueType> void
641 {
642  m_value = m_default_value;
643 }
644 
645 /* Missing on purpose: QofQuery because for current usage it's serialized with
646  * gnc_query2scm. The future is to replace QofQuery with SQL queries so there's
647  * not much point to spending the time to create a std::string serialization for
648  * them.
649  */
650 template <typename ValueType> std::string
652 {
653  static const std::string no_value{"No Value"};
654  if constexpr(std::is_same_v<ValueType, const QofInstance*>)
655  return m_value ? qof_instance_to_string(m_value) : no_value;
656  if constexpr(std::is_same_v<ValueType, const GncOwner*>)
657  {
658  if (!m_value)
659  return no_value;
660  auto guid{qof_instance_to_string(qofOwnerGetOwner(m_value))};
661  auto type{qofOwnerGetType(m_value)};
662  std::ostringstream ostr{};
663  ostr << type << " " << guid;
664  return ostr.str();
665  }
666  if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
667  {
668  std::ostringstream ostr{};
669  ostr << "'(";
670  std::for_each(m_value.begin(), m_value.end(),
671  [&ostr](auto rp){
672  auto [id, wide, high] = rp;
673  ostr << "(" << id << " " << wide << " " << high << " #f) ";
674  });
675  ostr << ")";
676  return ostr.str();
677  }
678  else if constexpr(is_same_decayed_v<ValueType, std::string>)
679  return m_value;
680  else if constexpr(is_same_decayed_v<ValueType, bool>)
681  return m_value ? "True" : "False";
682  else if constexpr(std::is_arithmetic_v<ValueType>)
683  return std::to_string(m_value);
684  else
685  return "Serialization not implemented";
686 }
687 
688 template <typename ValueType> bool
689 GncOptionValue<ValueType>::deserialize(const std::string& str) noexcept
690 {
691  if constexpr(std::is_same_v<ValueType, const QofInstance*>)
692  set_value(qof_instance_from_string(str, get_ui_type()));
693  if constexpr(std::is_same_v<ValueType, const GncOwner*>)
694  {
695  std::istringstream istr{str};
696  std::string type, guid;
697  istr >> type >> guid;
698  auto inst{qof_instance_from_string(guid, get_ui_type())};
699  qofOwnerSetEntity(const_cast<GncOwner*>(m_value), inst);
700  }
701  if constexpr(std::is_same_v<ValueType, GncOptionReportPlacementVec>)
702  {
703  std::istringstream istr{str};
704  GncOptionReportPlacementVec rpv;
705  while (istr)
706  {
707  uint32_t id, wide, high;
708  istr >> id >> wide >> high;
709  rpv.emplace_back(id, wide, high);
710  }
711  set_value(rpv);
712  }
713  else if constexpr(is_same_decayed_v<ValueType, std::string>)
714  set_value(str);
715  else if constexpr(is_same_decayed_v<ValueType, bool>)
716  set_value(str == "True");
717  else if constexpr(is_same_decayed_v<ValueType, int>)
718  set_value(stoi(str));
719  else if constexpr(is_same_decayed_v<ValueType, int64_t>)
720  set_value(stoll(str));
721  else if constexpr(is_same_decayed_v<ValueType, double>)
722  set_value(stod(str));
723  else
724  return false;
725  return true;
726 }
727 
728 std::string
729 GncOptionAccountListValue::serialize() const noexcept
730 {
731  static const std::string no_value{"No Value"};
732  std::string retval;
733  bool first = true;
734  if (m_value.empty())
735  return no_value;
736  for (auto val : m_value)
737  {
738  if (!first)
739  retval += " ";
740  first = false;
741  retval += guid_to_string(&val);
742  }
743  return retval;
744 }
745 
746 bool
747 GncOptionAccountListValue::deserialize(const std::string& str) noexcept
748 {
749  if (str.empty() || str.size() < GUID_ENCODING_LENGTH)
750  return false;
751  m_value.clear();
752  m_value.reserve(str.size() / GUID_ENCODING_LENGTH);
753  bool first = true;
754  size_t pos{};
755  while (pos + GUID_ENCODING_LENGTH < str.size())
756  {
757  if (!first)
758  ++pos;
759  first = false;
760  GncGUID guid{};
761  string_to_guid(str.substr(pos, pos + GUID_ENCODING_LENGTH).c_str(), &guid);
762  m_value.push_back(guid);
763  pos += GUID_ENCODING_LENGTH;
764  }
765  return true;
766 }
767 
768 std::string
769 GncOptionAccountSelValue::serialize() const noexcept
770 {
771  static const std::string no_value{"No Value"};
772  return guid_equal(guid_null(), &m_value) ? no_value : guid_to_string(&m_value);
773 }
774 
775 bool
776 GncOptionAccountSelValue::deserialize(const std::string& str) noexcept
777 {
778  set_value(reinterpret_cast<Account*>(qof_instance_from_string(str, get_ui_type())));
779  return true;
780 }
781 
782 std::string
783 GncOptionMultichoiceValue::serialize() const noexcept
784 {
785  static const std::string no_value{"No Value"};
786  std::string retval;
787  bool first = true;
788  if (m_value.empty())
789  return no_value;
790  for (auto index : m_value)
791  {
792  if (!first)
793  retval += " ";
794  first = false;
795  retval += std::get<0>(m_choices[index]);
796  }
797  return retval;
798 }
799 
800 bool
801 GncOptionMultichoiceValue::deserialize(const std::string& str) noexcept
802 {
803  static const auto size_t_max = std::numeric_limits<std::size_t>::max();
804  if (str.empty())
805 
806  return false;
807  size_t pos{};
808  while (pos < str.size())
809  {
810  auto endpos{str.find(' ', pos)};
811  if (endpos == std::string::npos)
812  endpos = str.size();
813  //need a null-terminated char* to pass to permissible_value_index
814  auto index{permissible_value_index(str.substr(pos, endpos).c_str())};
815  if (index == size_t_max)
816  return false;
817  m_value.push_back(index);
818  pos = endpos + 1;
819  }
820  return true;
821 }
822 
823 template <typename ValueType> std::string
825 {
826  if constexpr (std::is_arithmetic_v<ValueType>)
827  return std::to_string(m_value);
828  return "";
829 }
830 
831 template <typename ValueType> bool
832 GncOptionRangeValue<ValueType>::deserialize(const std::string& str) noexcept
833 {
834  if constexpr(is_same_decayed_v<ValueType, int>)
835  set_value(stoi(str));
836  else if constexpr(is_same_decayed_v<ValueType, double>)
837  set_value(stod(str));
838  return true;
839 }
840 
841 std::string
842 GncOptionDateValue::serialize() const noexcept
843 {
844  std::string retval{"("};
845  if (m_period == RelativeDatePeriod::ABSOLUTE)
846  {
847  retval += date_type_str[0];
848  retval += " . ";
849  retval += std::to_string(m_date);
850  }
851  else
852  {
853  retval += date_type_str[1];
854  retval += " . ";
855  retval += gnc_relative_date_storage_string(m_period);
856  }
857  retval += ")";
858  return retval;
859 }
860 
861 bool
862 GncOptionDateValue::deserialize(const std::string& str) noexcept
863 {
864  //The length of both "absolute" and "relative".
865  static constexpr size_t date_type_len{9};
866  // date_type_len plus the length of " . ".
867  static constexpr size_t date_value_pos{12};
868  auto type_str{str.substr(0, date_type_len)};
869  auto period_str{str.substr(date_value_pos)};
870  if (type_str == "absolute")
871  {
872  // Need a cast to disambiguate from time64.
873  set_value(static_cast<size_t>(std::stoll(period_str)));
874  return true;
875  }
876  else if (type_str == "relative ")
877  {
878  auto period = gnc_relative_date_from_storage_string(period_str.c_str());
879  if (period == RelativeDatePeriod::ABSOLUTE)
880  {
881  PWARN("Unknown period string in date option: '%s'",
882  period_str.c_str());
883  return false;
884  }
885 
886  set_value(period);
887  return true;
888  }
889  else
890  {
891  PWARN("Unknown date type string in date option: '%s'",
892  type_str.c_str());
893  return false;
894  }
895 }
896 
897 std::istream&
898 operator>> (std::istream& iss, GncOptionCommodityValue& opt)
899 {
900  std::string instr;
901  iss >> instr;
902  if (!opt.deserialize(instr))
903  throw std::invalid_argument("Invalid commodity string in stream.");
904  return iss;
905 }
906 
921 template void GncOptionValue<bool>::set_value(bool);
922 template void GncOptionValue<int>::set_value(int);
923 template void GncOptionValue<int64_t>::set_value(int64_t);
924 template void GncOptionValue<double>::set_value(double);
925 template void GncOptionValue<char*>::set_value(char*);
926 template void GncOptionValue<const char*>::set_value(const char*);
927 template void GncOptionValue<std::string>::set_value(std::string);
931 template void GncOptionValue<size_t>::set_value(size_t);
932 template void GncOptionValue<GncOptionAccountList>::set_value(GncOptionAccountList);
933 template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_value(GncMultichoiceOptionIndexVec);
934 template void GncOptionValue<GncOptionReportPlacementVec>::set_value(GncOptionReportPlacementVec);
935 template void GncOptionValue<bool>::set_default_value(bool);
936 template void GncOptionValue<int>::set_default_value(int);
937 template void GncOptionValue<int64_t>::set_default_value(int64_t);
938 template void GncOptionValue<double>::set_default_value(double);
939 template void GncOptionValue<char*>::set_default_value(char*);
940 template void GncOptionValue<const char*>::set_default_value(const char*);
941 template void GncOptionValue<std::string>::set_default_value(std::string);
945 template void GncOptionValue<size_t>::set_default_value(size_t);
946 template void GncOptionValue<GncOptionAccountList>::set_default_value(GncOptionAccountList);
947 template void GncOptionValue<GncMultichoiceOptionIndexVec>::set_default_value(GncMultichoiceOptionIndexVec);
948 template void GncOptionValue<GncOptionReportPlacementVec>::set_default_value(GncOptionReportPlacementVec);
963 template std::string GncOptionValue<bool>::serialize() const noexcept;
964 template std::string GncOptionValue<int>::serialize() const noexcept;
965 template std::string GncOptionValue<int64_t>::serialize() const noexcept;
966 template std::string GncOptionValue<double>::serialize() const noexcept;
967 template std::string GncOptionValue<char*>::serialize() const noexcept;
968 template std::string GncOptionValue<const char*>::serialize() const noexcept;
969 template std::string GncOptionValue<std::string>::serialize() const noexcept;
970 template std::string GncOptionValue<const QofQuery*>::serialize() const noexcept;
971 template std::string GncOptionValue<const GncOwner*>::serialize() const noexcept;
972 template std::string GncOptionValue<GncOptionReportPlacementVec>::serialize() const noexcept;
973 template std::string GncOptionRangeValue<int>::serialize() const noexcept;
974 template std::string GncOptionRangeValue<double>::serialize() const noexcept;
975 template bool GncOptionValue<bool>::deserialize(const std::string&) noexcept;
976 template bool GncOptionValue<int>::deserialize(const std::string&) noexcept;
977 template bool GncOptionValue<int64_t>::deserialize(const std::string&) noexcept;
978 template bool GncOptionValue<double>::deserialize(const std::string&) noexcept;
979 template bool GncOptionValue<char*>::deserialize(const std::string&) noexcept;
980 template bool GncOptionValue<const char*>::deserialize(const std::string&) noexcept;
981 template bool GncOptionValue<std::string>::deserialize(const std::string&) noexcept;
982 template bool GncOptionValue<const QofQuery*>::deserialize(const std::string&) noexcept;
983 template bool GncOptionValue<const GncOwner*>::deserialize(const std::string&) noexcept;
984 template bool GncOptionValue<GncOptionReportPlacementVec>::deserialize(const std::string&) noexcept;
985 template bool GncOptionRangeValue<int>::deserialize(const std::string&) noexcept;
986 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.
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...
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:3044
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.
utility functions for the GnuCash UI
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
Definition: qofid.cpp:215
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3279
GncGUID guid_new_return(void)
Generate a new id.
Definition: guid.cpp:159
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.
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.
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:276
This class is the parent of all option implmentations.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
const gchar * QofIdType
QofIdType declaration.
Definition: qofid.h:85
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:231
void qofOwnerSetEntity(GncOwner *owner, QofInstance *ent)
set the owner from the entity.
Definition: gncOwner.c:320
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:76
gchar * guid_to_string(const GncGUID *guid)
The guid_to_string() routine returns a null-terminated string encoding of the id. ...
Definition: guid.cpp:165
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition: guid.cpp:131
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:530
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
The type used to store guids in C.
Definition: guid.h:75
A Query.
Definition: qofquery.cpp:77
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:2050