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