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