GnuCash  5.6-150-g038405b370+
gnc-option-impl.hpp
Go to the documentation of this file.
1 /********************************************************************\
2  * gnc-option-impl.hpp -- 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 
33 #ifndef GNC_OPTION_IMPL_HPP_
34 #define GNC_OPTION_IMPL_HPP_
35 
36 #include "gnc-option.hpp"
37 
38 #include <config.h>
39 #include "qof.h"
40 #include "Account.h"
41 #include "gnc-budget.h"
42 #include "gnc-commodity.h"
43 #include "gnc-datetime.hpp"
44 #include <algorithm>
45 #include <string>
46 #include <utility>
47 #include <vector>
48 #include <exception>
49 #include <functional>
50 #include <variant>
51 #include <iostream>
52 #include <limits>
53 #include <cstdint>
54 
55 #include "gnc-option-uitype.hpp"
56 
57 
58 #ifndef SWIG
59 size_t constexpr classifier_size_max{50};
60 size_t constexpr sort_tag_size_max{10};
61 #endif
62 
70 {
71  std::string m_section;
72  std::string m_name;
73  std::string m_sort_tag;
74 // std::type_info m_kvp_type;
75  std::string m_doc_string;
76 };
77 
78 
79 #ifndef SWIG
80 auto constexpr uint16_t_max = std::numeric_limits<uint16_t>::max();
81 #endif
82 
86 template <typename ValueType>
88 {
89 public:
90  GncOptionValue(const char* section, const char* name,
91  const char* key, const char* doc_string,
92  ValueType value,
93  GncOptionUIType ui_type = GncOptionUIType::INTERNAL) :
94  OptionClassifier{section, name, key, doc_string},
95  m_ui_type(ui_type), m_value{value}, m_default_value{value} { }
96  GncOptionValue(const GncOptionValue&) = default;
97  GncOptionValue(GncOptionValue&&) = default;
98  GncOptionValue& operator=(const GncOptionValue&) = default;
99  GncOptionValue& operator=(GncOptionValue&&) = default;
100  ~GncOptionValue() = default;
101  ValueType get_value() const { return m_value; }
102  ValueType get_default_value() const { return m_default_value; }
103  void set_value(ValueType new_value);
104  void set_default_value(ValueType new_value);
105  void reset_default_value();
106  void mark_saved() noexcept { m_dirty = false; }
107  bool is_dirty() const noexcept { return m_dirty; }
108  bool is_changed() const noexcept { return m_value != m_default_value; }
109  GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
110  void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
111  bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
112  std::string serialize() const noexcept;
113  bool deserialize(const std::string& str) noexcept;
114 private:
115  GncOptionUIType m_ui_type;
116  ValueType m_value;
117  ValueType m_default_value;
118  bool m_dirty{false};
119 };
120 
121 
129 {
130  void operator()(GncOwner* o) {
131  g_free(o);
132  }
133 };
134 
135 using GncOwnerPtr = std::unique_ptr<GncOwner, GncOwnerDeleter>;
136 
138 public:
140  const char* section, const char* name,
141  const char* key, const char* doc_string,
142  const GncOwner* value,
143  GncOptionUIType ui_type = GncOptionUIType::INTERNAL);
146  ~GncOptionGncOwnerValue() = default;
147  const GncOwner* get_value() const;
148  const GncOwner* get_default_value() const;
149  void set_value(const GncOwner* new_value);
150  void set_default_value(const GncOwner* new_value);
151  void reset_default_value();
152  void mark_saved() noexcept { m_dirty = false; }
153  bool is_dirty() const noexcept { return m_dirty; }
154  bool is_changed() const noexcept;
155  GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
156  void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
157  bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
158  std::string serialize() const noexcept;
159  bool deserialize(const std::string& str) noexcept;
160 private:
161  GncOptionUIType m_ui_type;
162  GncOwnerPtr m_value;
163  GncOwnerPtr m_default_value;
164  bool m_dirty{false};
165 };
166 
172 using GncItem = std::pair<QofIdTypeConst, GncGUID>;
173 
175 public:
177  const char* section, const char* name,
178  const char* key, const char* doc_string,
179  const QofInstance* value,
180  GncOptionUIType ui_type = GncOptionUIType::INTERNAL);
184  ~GncOptionQofInstanceValue() = default;
185  const QofInstance* get_value() const;
186  const QofInstance* get_default_value() const;
187  GncItem get_item() const { return m_value; }
188  GncItem get_default_item() const { return m_default_value; }
189  void set_value(const QofInstance* new_value);
190  void set_default_value(const QofInstance* new_value);
191  void reset_default_value();
192  void mark_saved() noexcept { m_dirty = false; }
193  bool is_dirty() const noexcept { return m_dirty; }
194  bool is_changed() const noexcept;
195  GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
196  void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
197  bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
198  std::string serialize() const noexcept;
199  bool deserialize(const std::string& str) noexcept;
200 private:
201  GncOptionUIType m_ui_type;
202  GncItem m_value;
203  GncItem m_default_value;
204  bool m_dirty{false};
205 };
206 
216 {
217 public:
218  GncOptionCommodityValue() = delete;
219  GncOptionCommodityValue(const char* section, const char* name,
220  const char* key, const char* doc_string,
221  gnc_commodity* value,
222  GncOptionUIType ui_type = GncOptionUIType::COMMODITY) :
223  OptionClassifier{section, name, key, doc_string},
224  m_ui_type{ui_type}, m_is_currency{ui_type == GncOptionUIType::CURRENCY},
225  m_namespace{gnc_commodity_get_namespace(value)},
226  m_mnemonic{gnc_commodity_get_mnemonic(value)},
227  m_default_namespace{gnc_commodity_get_namespace(value)},
228  m_default_mnemonic{gnc_commodity_get_mnemonic(value)}
229  {
230  if (!validate(value))
231  throw std::invalid_argument("Attempt to create GncOptionCommodityValue with currency UIType and non-currency value.");
232  }
235  GncOptionCommodityValue& operator=(const GncOptionCommodityValue&) = default;
236  GncOptionCommodityValue& operator=(GncOptionCommodityValue&&) = default;
237  gnc_commodity* get_value() const;
238  gnc_commodity* get_default_value() const;
239  bool validate(gnc_commodity*) const noexcept;
240  void set_value(gnc_commodity* value);
241  void set_default_value(gnc_commodity* value);
242  void reset_default_value();
243  void mark_saved() noexcept { m_dirty = false; }
244  bool is_dirty() const noexcept { return m_dirty; }
245  bool is_changed() const noexcept;
246  GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
247  void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
248  bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
249  std::string serialize() const noexcept;
250  bool deserialize(const std::string& str) noexcept;
251 private:
252  GncOptionUIType m_ui_type;
253  bool m_is_currency;
254  std::string m_namespace;
255  std::string m_mnemonic;
256  std::string m_default_namespace;
257  std::string m_default_mnemonic;
258  bool m_dirty{false};
259 };
260 
261 QofInstance* qof_instance_from_string(const std::string& str,
262  GncOptionUIType type);
263 QofInstance* qof_instance_from_guid(GncGUID*, GncOptionUIType type);
264 std::string qof_instance_to_string(const QofInstance* inst);
265 
266 template <typename T>
268 {
269  static constexpr bool value =
270  std::is_same_v<std::decay_t<T>, GncOptionGncOwnerValue>;
271 };
272 
273 template <typename T> inline constexpr bool
274 is_GncOwnerValue_v = is_GncOwnerValue<T>::value;
275 
276 template <typename T>
278 {
279  static constexpr bool value =
280  std::is_same_v<std::decay_t<T>, GncOptionQofInstanceValue>;
281 };
282 
283 template <typename T> inline constexpr bool
284 is_QofInstanceValue_v = is_QofInstanceValue<T>::value;
285 
286 template <typename T>
288 {
289  static constexpr bool value =
290  std::is_same_v<std::decay_t<T>, GncOptionValue<const QofQuery*>>;
291 };
292 
293 template <typename T> inline constexpr bool
294 is_QofQueryValue_v = is_QofQueryValue<T>::value;
295 
296 /* These will work when m_value is a built-in class; GnuCash class and container
297  * values will need specialization unless they happen to define operators << and
298  * >>.
299  * Note that SWIG 3.0.12 chokes on elaborate enable_if so just hide the
300  * following templates from SWIG. (Ignoring doesn't work because SWIG still has
301  * to parse the templates to figure out the symbols.
302  */
303 #ifndef SWIG
304 template<class OptType,
305  typename std::enable_if_t<is_OptionClassifier_v<OptType> &&
306  ! (is_QofInstanceValue_v<OptType> ||
307  is_RangeValue_v<OptType>), int> = 0>
308 std::ostream& operator<<(std::ostream& oss, const OptType& opt)
309 {
310  oss << opt.get_value();
311  return oss;
312 }
313 
314 template<> inline std::ostream&
315 operator<< <GncOptionValue<bool>>(std::ostream& oss,
316  const GncOptionValue<bool>& opt)
317 {
318  oss << (opt.get_value() ? "#t" : "#f");
319  return oss;
320 }
321 
322 inline std::ostream&
323 operator<< (std::ostream& oss, const GncOptionCommodityValue& opt)
324 {
325  oss << opt.serialize();
326  return oss;
327 }
328 
329 template<class OptType,
330  typename std::enable_if_t<is_QofInstanceValue_v<OptType>, int> = 0>
331 inline std::ostream&
332 operator<< (std::ostream& oss, const OptType& opt)
333 {
334  auto value = opt.get_value();
335  oss << qof_instance_to_string(value);
336  return oss;
337 }
338 
339 template<class OptType,
340  typename std::enable_if_t<is_OptionClassifier_v<OptType> &&
341  !(is_QofInstanceValue_v<OptType> ||
342  is_RangeValue_v<OptType>), int> = 0>
343 std::istream& operator>>(std::istream& iss, OptType& opt)
344 {
345  if constexpr (std::is_same_v<std::decay_t<decltype(opt.get_value())>, const _gncOwner*> ||
346  std::is_same_v<std::decay_t<decltype(opt.get_value())>, const _QofQuery*> ||
347  std::is_same_v<std::decay_t<decltype(opt.get_value())>, GncOptionDateFormat>)
348  return iss;
349  else
350  {
351  std::decay_t<decltype(opt.get_value())> value;
352  iss >> value;
353  opt.set_value(value);
354  return iss;
355  }
356 }
357 
358 std::istream& operator>> (std::istream& iss, GncOptionCommodityValue& opt);
359 
360 template<class OptType,
361  typename std::enable_if_t<is_QofInstanceValue_v<OptType>, int> = 0>
362 std::istream&
363 operator>> (std::istream& iss, OptType& opt)
364 {
365  std::string instr;
366  iss >> instr;
367  opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
368  return iss;
369 }
370 
371 template<> inline std::istream&
372 operator>> <GncOptionValue<bool>>(std::istream& iss,
374 {
375  std::string instr;
376  iss >> instr;
377  opt.set_value(instr == "#t" ? true : false);
378  return iss;
379 }
380 
381 template<> inline std::istream&
382 operator>> <GncOptionValue<GncOptionReportPlacementVec>>(std::istream& iss,
384 {
385  uint32_t id, wide, high;
386  iss >> id >> wide >> high;
387  opt.set_value(GncOptionReportPlacementVec{{id, wide, high}});
388  return iss;
389 }
390 #endif // SWIG
391 
396 template <typename ValueType>
398 {
399 public:
400  GncOptionRangeValue(const char* section, const char* name,
401  const char* key, const char* doc_string,
402  ValueType value, ValueType min,
403  ValueType max, ValueType step) :
404  GncOptionRangeValue<ValueType>{section, name, key, doc_string, value, min,
405  max, step, GncOptionUIType::NUMBER_RANGE} {}
406  GncOptionRangeValue(const char* section, const char* name,
407  const char* key, const char* doc_string,
408  ValueType value, ValueType min,
409  ValueType max, ValueType step, GncOptionUIType ui) :
410  OptionClassifier{section, name, key, doc_string}, m_ui_type{ui},
411  m_value{value >= min && value <= max ? value : min},
412  m_default_value{value >= min && value <= max ? value : min},
413  m_min{min}, m_max{max}, m_step{step} {
414  if constexpr(is_same_decayed_v<ValueType, int>)
415  set_alternate(true);}
420  ValueType get_value() const { return m_value; }
421  ValueType get_default_value() const { return m_default_value; }
422  bool validate(ValueType value) { return value >= m_min && value <= m_max; }
423  void set_value(ValueType value)
424  {
425  if (this->validate(value))
426  {
427  m_value = value;
428  m_dirty = true;
429  }
430  else
431  throw std::invalid_argument("Validation failed, value not set.");
432  }
433  void set_default_value(ValueType value)
434  {
435  if (this->validate(value))
436  m_value = m_default_value = value;
437  else
438  throw std::invalid_argument("Validation failed, value not set.");
439  }
440  void get_limits(ValueType& upper, ValueType& lower, ValueType& step) const noexcept
441  {
442  upper = m_max;
443  lower = m_min;
444  step = m_step;
445  }
446  void reset_default_value() { m_value = m_default_value; }
447  void mark_saved() noexcept { m_dirty = false; }
448  bool is_dirty() const noexcept { return m_dirty; }
449  bool is_changed() const noexcept { return m_value != m_default_value; }
450  GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
451  void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
452  bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
453  bool is_alternate() const noexcept { return m_alternate; }
454  void set_alternate(bool value) noexcept { m_alternate = value; }
455  std::string serialize() const noexcept;
456  bool deserialize(const std::string& str) noexcept;
457 private:
458  GncOptionUIType m_ui_type = GncOptionUIType::NUMBER_RANGE;
459  ValueType m_value;
460  ValueType m_default_value;
461  ValueType m_min;
462  ValueType m_max;
463  ValueType m_step;
464  bool m_alternate{false};
465  bool m_dirty{false};
466 };
467 
468 template<class OptType,
469  typename std::enable_if_t<is_RangeValue_v<OptType>, int> = 0>
470 inline std::ostream&
471 operator<< (std::ostream& oss, const OptType& opt)
472 {
473  if (opt.get_ui_type() == GncOptionUIType::PLOT_SIZE)
474  oss << (opt.is_alternate() ? "pixels" : "percent") << " ";
475  oss << opt.get_value();
476  return oss;
477 }
478 
479 template<class OptType,
480  typename std::enable_if_t<is_RangeValue_v<OptType>, int> = 0>
481 inline std::istream&
482 operator>> (std::istream& iss, OptType& opt)
483 {
484  if (opt.get_ui_type() == GncOptionUIType::PLOT_SIZE)
485  {
486  std::string alt;
487  iss >> alt;
488  opt.set_alternate(strncmp(alt.c_str(), "percent",
489  strlen("percent")) == 0);
490  }
491  if constexpr (std::is_same_v<std::decay_t<OptType>,
493  {
494  double d;
495  iss >> d;
496  opt.set_value(d);
497  }
498  else
499  {
500  int i;
501  iss >> i;
502  opt.set_value(i);
503  }
504  return iss;
505 }
506 
507 using GncMultichoiceOptionEntry = std::tuple<const std::string,
508  const std::string,
509  GncOptionMultichoiceKeyType>;
510 using GncMultichoiceOptionIndexVec = std::vector<uint16_t>;
511 using GncMultichoiceOptionChoices = std::vector<GncMultichoiceOptionEntry>;
512 
529 {
530 public:
531  GncOptionMultichoiceValue(const char* section, const char* name,
532  const char* key, const char* doc_string,
533  const char* value,
534  GncMultichoiceOptionChoices&& choices,
535  GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
536  OptionClassifier{section, name, key, doc_string},
537  m_ui_type{ui_type},
538  m_value{}, m_default_value{}, m_choices{std::move(choices)}
539  {
540  if (value)
541  {
542  if (auto index = find_key(value);
543  index != uint16_t_max)
544  {
545  m_value.push_back(index);
546  m_default_value.push_back(index);
547  }
548  }
549  }
550 
551  GncOptionMultichoiceValue(const char* section, const char* name,
552  const char* key, const char* doc_string,
553  uint16_t index,
554  GncMultichoiceOptionChoices&& choices,
555  GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
556  OptionClassifier{section, name, key, doc_string},
557  m_ui_type{ui_type},
558  m_value{}, m_default_value{}, m_choices{std::move(choices)}
559  {
560  if (index < m_choices.size())
561  {
562  m_value.push_back(index);
563  m_default_value.push_back(index);
564  }
565  }
566 
567  GncOptionMultichoiceValue(const char* section, const char* name,
568  const char* key, const char* doc_string,
569  GncMultichoiceOptionIndexVec&& indices,
570  GncMultichoiceOptionChoices&& choices,
571  GncOptionUIType ui_type = GncOptionUIType::LIST) :
572  OptionClassifier{section, name, key, doc_string},
573  m_ui_type{ui_type},
574  m_value{indices}, m_default_value{std::move(indices)},
575  m_choices{std::move(choices)} {}
578  GncOptionMultichoiceValue& operator=(const GncOptionMultichoiceValue&) = default;
580 
581  const std::string& get_value() const
582  {
583  auto vec{m_value.size() > 0 ? m_value : m_default_value};
584  if (vec.size() == 0)
585  return c_empty_string;
586  if (vec.size() == 1)
587  return std::get<0>(m_choices.at(vec[0]));
588  else
589  return c_list_string;
590 
591  }
592  const std::string& get_default_value() const
593  {
594  if (m_default_value.size() == 1)
595  return std::get<0>(m_choices.at(m_default_value[0]));
596  else if (m_default_value.size() == 0)
597  return c_empty_string;
598  else
599  return c_list_string;
600  }
601 
602  uint16_t get_index() const
603  {
604  if (m_value.size() > 0)
605  return m_value[0];
606  if (m_default_value.size() > 0)
607  return m_default_value[0];
608  return 0;
609  }
610  const GncMultichoiceOptionIndexVec& get_multiple() const noexcept
611  {
612  return m_value;
613  }
614  const GncMultichoiceOptionIndexVec& get_default_multiple() const noexcept
615  {
616  return m_default_value;
617  }
618  bool validate(const std::string& value) const noexcept
619  {
620  auto index = find_key(value);
621  return index != uint16_t_max;
622 
623  }
624  bool validate(const GncMultichoiceOptionIndexVec& indexes) const noexcept
625  {
626  for (auto index : indexes)
627  if (index >= m_choices.size())
628  return false;
629  return true;
630 
631  }
632  void set_value(const std::string& value)
633  {
634  auto index = find_key(value);
635  if (index != uint16_t_max)
636  {
637  m_value.clear();
638  m_value.push_back(index);
639  m_dirty = true;
640  }
641  else
642  throw std::invalid_argument("Value not a valid choice.");
643 
644  }
645  void set_value(uint16_t index)
646  {
647  if (index < m_choices.size())
648  {
649  m_value.clear();
650  m_value.push_back(index);
651  m_dirty = true;
652  }
653  else
654  throw std::invalid_argument("Value not a valid choice.");
655 
656  }
657  void set_default_value(const std::string& value)
658  {
659  auto index = find_key(value);
660  if (index != uint16_t_max)
661  {
662  m_value.clear();
663  m_value.push_back(index);
664  m_default_value.clear();
665  m_default_value.push_back(index);
666  }
667  else
668  throw std::invalid_argument("Value not a valid choice.");
669 
670  }
671  void set_default_value(uint16_t index)
672  {
673  if (index < m_choices.size())
674  {
675  m_value.clear();
676  m_value.push_back(index);
677  m_default_value.clear();
678  m_default_value.push_back(index);
679  }
680  else
681  throw std::invalid_argument("Value not a valid choice.");
682 
683  }
684  void set_multiple(const GncMultichoiceOptionIndexVec& indexes)
685  {
686  if (validate(indexes))
687  m_value = indexes;
688  else
689  throw std::invalid_argument("One of the supplied indexes was out of range.");
690  }
691  void set_default_multiple(const GncMultichoiceOptionIndexVec& indexes)
692  {
693  if (validate(indexes))
694  m_value = m_default_value = indexes;
695  else
696  throw std::invalid_argument("One of the supplied indexes was out of range.");
697  }
698  uint16_t num_permissible_values() const noexcept
699  {
700  return m_choices.size();
701  }
702  uint16_t permissible_value_index(const char* key) const noexcept
703  {
704  return find_key(key);
705  }
706  const char* permissible_value(uint16_t index) const
707  {
708  return std::get<0>(m_choices.at(index)).c_str();
709  }
710  const char* permissible_value_name(uint16_t index) const
711  {
712  return std::get<1>(m_choices.at(index)).c_str();
713  }
714  void reset_default_value() { m_value = m_default_value; }
715  void mark_saved() noexcept { m_dirty = false; }
716  bool is_dirty() const noexcept { return m_dirty; }
717  bool is_changed() const noexcept { return m_value != m_default_value; }
718  GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
719  void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
720  bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
721  GncOptionMultichoiceKeyType get_keytype(unsigned i) const { return std::get<2>(m_choices.at(i)); }
722  std::string serialize() const noexcept;
723  bool deserialize(const std::string& str) noexcept;
724 private:
725  uint16_t find_key (const std::string& key) const noexcept
726  {
727  auto iter = std::find_if(m_choices.begin(), m_choices.end(),
728  [key](auto choice) {
729  return std::get<0>(choice) == key; });
730  if (iter != m_choices.end())
731  return iter - m_choices.begin();
732  else
733  return uint16_t_max;
734 
735  }
736  GncOptionUIType m_ui_type;
737  GncMultichoiceOptionIndexVec m_value;
738  GncMultichoiceOptionIndexVec m_default_value;
739  GncMultichoiceOptionChoices m_choices;
740  bool m_dirty{false};
741  static const std::string c_empty_string;
742  static const std::string c_list_string;
743 };
744 
745 template<> inline std::ostream&
746 operator<< <GncOptionMultichoiceValue>(std::ostream& oss,
747  const GncOptionMultichoiceValue& opt)
748 {
749  auto vec{opt.get_multiple()};
750  bool first{true};
751  for (auto index : vec)
752  {
753  if (first)
754  first = false;
755  else
756  oss << " ";
757  oss << opt.permissible_value(index);
758  }
759  return oss;
760 }
761 
762 template<> inline std::istream&
763 operator>> <GncOptionMultichoiceValue>(std::istream& iss,
765 {
766  GncMultichoiceOptionIndexVec values;
767  while (true)
768  {
769  std::string str;
770  std::getline(iss, str, ' ');
771  if (!str.empty())
772  {
773  auto index = opt.permissible_value_index(str.c_str());
774  if (index != uint16_t_max)
775  values.push_back(index);
776  else
777  {
778  std::string err = str + " is not one of ";
779  err += opt.m_name;
780  err += "'s permissible values.";
781  throw std::invalid_argument(err);
782  }
783  }
784  else
785  break;
786  }
787  opt.set_multiple(values);
788  iss.clear();
789  return iss;
790 }
791 
792 
793 using GncOptionAccountList = std::vector<GncGUID>;
794 
795 using GncOptionAccountTypeList = std::vector<GNCAccountType>;
796 
815 {
816 public:
817  GncOptionAccountListValue(const char* section, const char* name,
818  const char* key, const char* doc_string,
819  GncOptionUIType ui_type, bool multi=true) :
820  OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
821  m_value{}, m_default_value{}, m_allowed{}, m_multiselect{multi} {}
822 
823  GncOptionAccountListValue(const char* section, const char* name,
824  const char* key, const char* doc_string,
825  GncOptionUIType ui_type,
826  const GncOptionAccountList& value, bool multi=true) :
827  OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
828  m_value{value}, m_default_value{std::move(value)}, m_allowed{},
829  m_multiselect{multi} {}
830  GncOptionAccountListValue(const char* section, const char* name,
831  const char* key, const char* doc_string,
832  GncOptionUIType ui_type,
833  GncOptionAccountTypeList&& allowed, bool multi=true) :
834  OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
835  m_value{}, m_default_value{}, m_allowed{std::move(allowed)},
836  m_multiselect{multi} {}
837  GncOptionAccountListValue(const char* section, const char* name,
838  const char* key, const char* doc_string,
839  GncOptionUIType ui_type,
840  const GncOptionAccountList& value,
841  GncOptionAccountTypeList&& allowed, bool multi=true) :
842  OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
843  m_value{}, m_default_value{}, m_allowed{std::move(allowed)},
844  m_multiselect{multi} {
845  if (!validate(value))
846  throw std::invalid_argument("Supplied Value not in allowed set.");
847  m_value = value;
848  m_default_value = std::move(value);
849  }
850 
851  /* These aren't const& because if m_default_value hasn't been set
852  * get_default_value finds the first account that matches the allowed types
853  * and returns a GncOptionAccountList containing it. That's a stack variable
854  * and must be returned by value.
855  */
856  GncOptionAccountList get_value() const;
857  GncOptionAccountList get_default_value() const;
858  bool validate (const GncOptionAccountList& values) const;
859  void set_value (GncOptionAccountList values) {
860  if (validate(values))
861  {
862  //throw!
863  m_value = values;
864  m_dirty = true;
865  }
866  }
867  void set_default_value (GncOptionAccountList values) {
868  if (validate(values))
869  //throw!
870  m_value = m_default_value = values;
871  }
872  GList* account_type_list() const noexcept;
873  void reset_default_value() { m_value = m_default_value; }
874  void mark_saved() noexcept { m_dirty = false; }
875  bool is_dirty() const noexcept { return m_dirty; }
876  bool is_changed() const noexcept;
877  GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
878  void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
879  bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
880  bool is_multiselect() const noexcept { return m_multiselect; }
881  std::string serialize() const noexcept;
882  bool deserialize(const std::string& str) noexcept;
883 private:
884  GncOptionUIType m_ui_type;
885  GncOptionAccountList m_value;
886  GncOptionAccountList m_default_value;
887  GncOptionAccountTypeList m_allowed;
888  bool m_multiselect;
889  bool m_dirty{false};
890 };
891 
892 template<> inline std::ostream&
893 operator<< <GncOptionAccountListValue>(std::ostream& oss,
894  const GncOptionAccountListValue& opt)
895 {
896  auto values{opt.get_value()};
897  bool first = true;
898  for (auto value : values)
899  {
900  if (first)
901  first = false;
902  else
903  oss << " ";
904  char strbuff[GUID_ENCODING_LENGTH+1];
905  guid_to_string_buff (&value, strbuff);
906  oss << strbuff;
907  }
908  return oss;
909 }
910 
911 template<> inline std::istream&
912 operator>> <GncOptionAccountListValue>(std::istream& iss,
914 {
915  GncOptionAccountList values;
916  while (true)
917  {
918  std::string str;
919  std::getline(iss, str, ' ');
920  if (!str.empty())
921  {
922  auto guid{qof_entity_get_guid(qof_instance_from_string(str, opt.get_ui_type()))};
923  values.push_back(*guid);
924  }
925  else
926  break;
927  }
928  opt.set_value(values);
929  iss.clear();
930  return iss;
931 }
932 
933 /* @class GncOptionAccountSelValue
934  * Like GncOptionAccountListValue but contains only a single account.
935  */
936 
938 {
939 public:
940  GncOptionAccountSelValue(const char* section, const char* name,
941  const char* key, const char* doc_string,
942  GncOptionUIType ui_type) :
943  OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
944  m_value{*guid_null()}, m_default_value{*guid_null()}, m_allowed{} {}
945 
946  GncOptionAccountSelValue(const char* section, const char* name,
947  const char* key, const char* doc_string,
948  GncOptionUIType ui_type,
949  const Account* value) :
950  OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
951  m_value{*qof_entity_get_guid(value)},
952  m_default_value{*qof_entity_get_guid(value)}, m_allowed{} {}
953  GncOptionAccountSelValue(const char* section, const char* name,
954  const char* key, const char* doc_string,
955  GncOptionUIType ui_type,
956  GncOptionAccountTypeList&& allowed) :
957  OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
958  m_value{*guid_null()}, m_default_value{*guid_null()},
959  m_allowed{std::move(allowed)} {}
960  GncOptionAccountSelValue(const char* section, const char* name,
961  const char* key, const char* doc_string,
962  GncOptionUIType ui_type,
963  const Account* value,
964  GncOptionAccountTypeList&& allowed) :
965  OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
966  m_value{*guid_null()}, m_default_value{*guid_null()}, m_allowed{std::move(allowed)} {
967  if (!validate(value))
968  throw std::invalid_argument("Supplied Value not in allowed set.");
969  m_value = m_default_value = *qof_entity_get_guid(value);
970  }
971 
972  const Account* get_value() const;
973  const Account* get_default_value() const;
974  bool validate (const Account* value) const;
975  void set_value (const Account* value) {
976  if (validate(value))
977  {
978  auto guid{qof_entity_get_guid(value)};
979  m_value = *guid;
980  m_dirty = true;
981  }
982  //else throw
983  }
984  void set_default_value (const Account* value) {
985  if (validate(value))
986  {
987  auto guid{qof_entity_get_guid(value)};
988  m_value = m_default_value = *guid;
989  }
990  //else throw
991  }
992  GList* account_type_list() const noexcept;
993  void reset_default_value() { m_value = m_default_value; }
994  void mark_saved() noexcept { m_dirty = false; }
995  bool is_dirty() const noexcept { return m_dirty; }
996  bool is_changed() const noexcept { return !guid_equal(&m_value, &m_default_value); }
997  GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
998  void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
999  bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
1000  std::string serialize() const noexcept;
1001  bool deserialize(const std::string& str) noexcept;
1002 private:
1003  GncOptionUIType m_ui_type;
1004  GncGUID m_value;
1005  GncGUID m_default_value;
1006  GncOptionAccountTypeList m_allowed;
1007  bool m_dirty{false};
1008 };
1009 
1010 template<> inline std::ostream&
1011 operator<< <GncOptionAccountSelValue>(std::ostream& oss,
1012  const GncOptionAccountSelValue& opt)
1013 {
1014  auto value{opt.get_value()};
1015  oss << qof_instance_to_string(QOF_INSTANCE(value));
1016  return oss;
1017 }
1018 
1019 template<> inline std::istream&
1020 operator>> <GncOptionAccountSelValue>(std::istream& iss,
1022 {
1023  Account* value{nullptr};
1024  std::string str;
1025  std::getline(iss, str, ' ');
1026  if (!str.empty())
1027  value = (Account*)qof_instance_from_string(str, opt.get_ui_type());
1028  opt.set_value(value);
1029  iss.clear();
1030  return iss;
1031 }
1032 
1037 /*
1038 gnc-date-option-show-time? -- option_data[1]
1039 gnc-date-option-get-subtype -- option_data[0]
1040 gnc-date-option-value-type m_value
1041 gnc-date-option-absolute-time m_type == RelativeDatePeriod::ABSOLUTE
1042 gnc-date-option-relative-time m_type != RelativeDatePeriod::ABSOLUTE
1043  */
1044 
1046 {
1047 public:
1048  GncOptionDateValue(const char* section, const char* name,
1049  const char* key, const char* doc_string,
1050  GncOptionUIType ui_type) :
1051  OptionClassifier{section, name, key, doc_string},
1052  m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
1053  m_period{RelativeDatePeriod::TODAY},
1054  m_default_period{RelativeDatePeriod::TODAY},
1055  m_period_set{} {}
1056  GncOptionDateValue(const char* section, const char* name,
1057  const char* key, const char* doc_string,
1058  GncOptionUIType ui_type, time64 time) :
1059  OptionClassifier{section, name, key, doc_string},
1060  m_ui_type{ui_type}, m_date{time}, m_default_date{time},
1061  m_period{RelativeDatePeriod::ABSOLUTE},
1062  m_default_period{RelativeDatePeriod::ABSOLUTE},
1063  m_period_set{} {}
1064  GncOptionDateValue(const char* section, const char* name,
1065  const char* key, const char* doc_string,
1066  GncOptionUIType ui_type,
1067  RelativeDatePeriod period) :
1068  OptionClassifier{section, name, key, doc_string},
1069  m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
1070  m_period{period}, m_default_period{period},
1071  m_period_set{} {}
1072  GncOptionDateValue(const char* section, const char* name,
1073  const char* key, const char* doc_string,
1074  GncOptionUIType ui_type,
1075  const RelativeDatePeriodVec& period_set) :
1076  OptionClassifier{section, name, key, doc_string},
1077  m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
1078  m_period{period_set.back()},
1079  m_default_period{period_set.back()},
1080  m_period_set{period_set} {}
1081  GncOptionDateValue(const GncOptionDateValue&) = default;
1083  GncOptionDateValue& operator=(const GncOptionDateValue&) = default;
1084  GncOptionDateValue& operator=(GncOptionDateValue&&) = default;
1085  time64 get_value() const noexcept;
1086  time64 get_default_value() const noexcept;
1087  RelativeDatePeriod get_period() const noexcept { return m_period; }
1088  RelativeDatePeriod get_default_period() const noexcept { return m_default_period; }
1089  uint16_t get_period_index() const noexcept;
1090  uint16_t get_default_period_index() const noexcept;
1091  std::ostream& out_stream(std::ostream& oss) const noexcept;
1092  std::istream& in_stream(std::istream& iss);
1093  bool validate(RelativeDatePeriod value);
1094  bool validate(time64 time) {
1095  if (time > MINTIME && time < MAXTIME)
1096  return true;
1097  return false;
1098  }
1099  void set_value(RelativeDatePeriod value) {
1100  if (validate(value))
1101  {
1102  m_period = value;
1103  m_date = INT64_MAX;
1104  m_dirty = true;
1105  }
1106  }
1107  void set_value(time64 time) {
1108  if (validate(time))
1109  {
1110  m_period = RelativeDatePeriod::ABSOLUTE;
1111  m_date = time;
1112  m_dirty = true;
1113  }
1114  }
1115  void set_value(uint16_t index) noexcept;
1116  void set_default_value(RelativeDatePeriod value) {
1117  if (validate(value))
1118  {
1119  m_period = m_default_period = value;
1120  m_date = m_default_date = INT64_MAX;
1121  }
1122  }
1123  void set_default_value(time64 time) {
1124  if (validate(time))
1125  {
1126  m_period = m_default_period = RelativeDatePeriod::ABSOLUTE;
1127  m_date = m_default_date = time;
1128  }
1129  }
1130  uint16_t num_permissible_values() const noexcept
1131  {
1132  return m_period_set.size();
1133  }
1134  uint16_t permissible_value_index(const char* key) const noexcept;
1135  const char* permissible_value(uint16_t index) const
1136  {
1137  return gnc_relative_date_storage_string(m_period_set.at(index));
1138  }
1139  const char* permissible_value_name(uint16_t index) const
1140  {
1141  return gnc_relative_date_display_string(m_period_set.at(index));
1142  }
1143  void reset_default_value() {
1144  m_period = m_default_period;
1145  m_date = m_default_date;
1146  }
1147  void mark_saved() noexcept { m_dirty = false; }
1148  bool is_dirty() const noexcept { return m_dirty; }
1149  bool is_changed() const noexcept { return m_period != m_default_period &&
1150  m_date != m_default_date; }
1151  GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
1152  void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
1153  bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
1154  const RelativeDatePeriodVec& get_period_set() const { return m_period_set; }
1155  std::string serialize() const noexcept;
1156  bool deserialize(const std::string& str) noexcept;
1157 private:
1158  GncOptionUIType m_ui_type;
1159  time64 m_date;
1160  time64 m_default_date;
1161  RelativeDatePeriod m_period;
1162  RelativeDatePeriod m_default_period;
1163  RelativeDatePeriodVec m_period_set;
1164  bool m_dirty{false};
1165 };
1166 
1167 template<> inline std::ostream&
1168 operator<< <GncOptionDateValue>(std::ostream& oss,
1169  const GncOptionDateValue& opt)
1170 {
1171  return opt.out_stream(oss);
1172 }
1173 
1174 template<> inline std::istream&
1175 operator>> <GncOptionDateValue>(std::istream& iss,
1176  GncOptionDateValue& opt)
1177 {
1178  return opt.in_stream(iss);
1179 }
1180 
1181 
1182 #endif //GNC_OPTION_IMPL_HPP_
1183 
std::pair< QofIdTypeConst, GncGUID > GncItem
class GncOptionQofinstanceValue
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.
STRUCTS.
GnuCash Budgets.
OptionUITypes.
A legal date value is a pair of either a RelativeDatePeriod, the absolute flag and a time64...
GList * account_type_list() const noexcept
Create a GList of account types to pass to gnc_account_sel_set_acct_filters.
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
Set one or more accounts on which to report, optionally restricted to certain account types...
This class is the parent of all option implementations.
C++ Public interface for individual options.
Account handling public routines.
Multichoice options have a vector of valid options (GncMultichoiceOptionChoices) and validate the sel...
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.
class GncOptionCommodityValue Commodities are stored with their namespace and mnemonic instead of the...
Used for numeric ranges and plot sizes.
class GncOptionGncOwnerValue
const GncGUID * qof_entity_get_guid(gconstpointer ent)
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.
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
GncOptionUIType
Used by GncOptionClassifier to indicate to dialog-options what control should be displayed for the op...
Commodity handling public routines.