Branch data Line data Source code
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 : :
24 : : /** @addtogroup Engine
25 : : @{ */
26 : : /** @addtogroup Options
27 : : @{ */
28 : : /** @file gnc-option-impl.hpp
29 : : @brief Implementation templates and specializtions for GncOption values.
30 : : Objecte created by these templates are wrapped by the GncOption variant.
31 : : @author Copyright 2019-2021 John Ralls <jralls@ceridwen.us>
32 : : */
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 : :
63 : : /** @struct OptionClassifier
64 : : * This class is the parent of all option implementations. It contains the
65 : : * elements that the optiondb uses to retrieve option values and that the
66 : : * options dialog determines on which tab to place the option, in what order,
67 : : * and what string to display as a tooltip.
68 : : */
69 : : struct OptionClassifier
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 : :
83 : : /** @class GncOptionValue
84 : : * The generic option-value class. Most option types can use this template.
85 : : */
86 : : template <typename ValueType>
87 : : class GncOptionValue : public OptionClassifier
88 : : {
89 : : public:
90 : 49394 : 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 : 246970 : m_ui_type(ui_type), m_value{value}, m_default_value{value} { }
96 : 1056 : GncOptionValue(const GncOptionValue&) = default;
97 : : GncOptionValue(GncOptionValue&&) = default;
98 : : GncOptionValue& operator=(const GncOptionValue&) = default;
99 : : GncOptionValue& operator=(GncOptionValue&&) = default;
100 : 1056 : ~GncOptionValue() = default;
101 : 44477 : ValueType get_value() const { return m_value; }
102 : 19 : 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 : 1 : void mark_saved() noexcept { m_dirty = false; }
107 : 8 : bool is_dirty() const noexcept { return m_dirty; }
108 : 6650 : bool is_changed() const noexcept { return m_value != m_default_value; }
109 : 20 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
110 : 40 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
111 : 1936 : 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 : :
122 : : /** class GncOptionGncOwnerValue
123 : : *
124 : : * Unlike QofInstance based classes GncOwners are created on the fly, aren't
125 : : * placed in QofCollection, and therefore their lifetimes have to be managed.
126 : : * We use GncOwnerPtr for the purpose.
127 : : */
128 : : struct GncOwnerDeleter
129 : : {
130 : 0 : void operator()(GncOwner* o) {
131 : 0 : g_free(o);
132 : 0 : }
133 : : };
134 : :
135 : : using GncOwnerPtr = std::unique_ptr<GncOwner, GncOwnerDeleter>;
136 : :
137 : : class GncOptionGncOwnerValue: public OptionClassifier {
138 : : public:
139 : : GncOptionGncOwnerValue(
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);
144 : : GncOptionGncOwnerValue(const GncOptionGncOwnerValue& from);
145 : : GncOptionGncOwnerValue(GncOptionGncOwnerValue&&) = default;
146 : 21 : ~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 : 0 : void mark_saved() noexcept { m_dirty = false; }
153 : 0 : bool is_dirty() const noexcept { return m_dirty; }
154 : : bool is_changed() const noexcept;
155 : 22 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
156 : 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
157 : 0 : 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 : :
167 : : /** class GncOptionQofinstanceValue
168 : : *
169 : : * QofInstances know what type they are but getting them to tell you is a pain
170 : : * so we put them in a pair with a type identifier.
171 : : */
172 : : using GncItem = std::pair<QofIdTypeConst, GncGUID>;
173 : :
174 : : class GncOptionQofInstanceValue: public OptionClassifier {
175 : : public:
176 : : GncOptionQofInstanceValue(
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);
181 : : GncOptionQofInstanceValue(const GncOptionQofInstanceValue& from);
182 : : GncOptionQofInstanceValue(GncOptionQofInstanceValue&&) = default;
183 : : GncOptionQofInstanceValue& operator=(GncOptionQofInstanceValue&&) = default;
184 : 62 : ~GncOptionQofInstanceValue() = default;
185 : : const QofInstance* get_value() const;
186 : : const QofInstance* get_default_value() const;
187 : 67 : GncItem get_item() const { return m_value; }
188 : 67 : 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 : 0 : void mark_saved() noexcept { m_dirty = false; }
193 : 0 : bool is_dirty() const noexcept { return m_dirty; }
194 : : bool is_changed() const noexcept;
195 : 70 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
196 : 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
197 : 0 : 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 : :
207 : : /** class GncOptionCommodityValue
208 : : * Commodities are stored with their namespace and mnemonic instead of their gncGUID
209 : : * so that they can be correctly retrieved even if they're deleted and recreated.
210 : : * Additionally if GncOptionCommodityValue is created with GncOptionUIType::CURRENCY
211 : : * it will throw std::invalid_argument if one attempts to set a value that isn't a
212 : : * currency.
213 : : */
214 : :
215 : : class GncOptionCommodityValue : public OptionClassifier
216 : : {
217 : : public:
218 : : GncOptionCommodityValue() = delete;
219 : 2278 : GncOptionCommodityValue(const char* section, const char* name,
220 : : const char* key, const char* doc_string,
221 : : gnc_commodity* value,
222 : 2278 : GncOptionUIType ui_type = GncOptionUIType::COMMODITY) :
223 : : OptionClassifier{section, name, key, doc_string},
224 : 2278 : m_ui_type{ui_type}, m_is_currency{ui_type == GncOptionUIType::CURRENCY},
225 : 4556 : m_namespace{gnc_commodity_get_namespace(value)},
226 : 4556 : m_mnemonic{gnc_commodity_get_mnemonic(value)},
227 : 4556 : m_default_namespace{gnc_commodity_get_namespace(value)},
228 : 15946 : m_default_mnemonic{gnc_commodity_get_mnemonic(value)}
229 : : {
230 : 2278 : if (!validate(value))
231 : 0 : throw std::invalid_argument("Attempt to create GncOptionCommodityValue with currency UIType and non-currency value.");
232 : 2278 : }
233 : 2278 : GncOptionCommodityValue(const GncOptionCommodityValue&) = default;
234 : : GncOptionCommodityValue(GncOptionCommodityValue&&) = default;
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 : 0 : void mark_saved() noexcept { m_dirty = false; }
244 : 0 : bool is_dirty() const noexcept { return m_dirty; }
245 : : bool is_changed() const noexcept;
246 : 2 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
247 : 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
248 : 2 : 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>
267 : : struct is_GncOwnerValue
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>
277 : : struct is_QofInstanceValue
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>
287 : : struct is_QofQueryValue
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 : 3 : 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 : 0 : return iss;
349 : : else
350 : : {
351 : 2 : std::decay_t<decltype(opt.get_value())> value;
352 : 3 : iss >> value;
353 : 3 : opt.set_value(value);
354 : 3 : return iss;
355 : 2 : }
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 : 1 : operator>> (std::istream& iss, OptType& opt)
364 : : {
365 : 1 : std::string instr;
366 : 1 : iss >> instr;
367 : 1 : opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
368 : 1 : return iss;
369 : 1 : }
370 : :
371 : : template<> inline std::istream&
372 : 2 : operator>> <GncOptionValue<bool>>(std::istream& iss,
373 : : GncOptionValue<bool>& opt)
374 : : {
375 : 2 : std::string instr;
376 : 2 : iss >> instr;
377 : 2 : opt.set_value(instr == "#t" ? true : false);
378 : 2 : return iss;
379 : 2 : }
380 : :
381 : : template<> inline std::istream&
382 : 0 : operator>> <GncOptionValue<GncOptionReportPlacementVec>>(std::istream& iss,
383 : : GncOptionValue<GncOptionReportPlacementVec>& opt)
384 : : {
385 : : uint32_t id, wide, high;
386 : 0 : iss >> id >> wide >> high;
387 : 0 : opt.set_value(GncOptionReportPlacementVec{{id, wide, high}});
388 : 0 : return iss;
389 : : }
390 : : #endif // SWIG
391 : :
392 : : /** @class GncOptionRangeValue
393 : : * Used for numeric ranges and plot sizes.
394 : : */
395 : :
396 : : template <typename ValueType>
397 : : class GncOptionRangeValue : public OptionClassifier
398 : : {
399 : : public:
400 : 300 : 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 : 300 : max, step, GncOptionUIType::NUMBER_RANGE} {}
406 : 712 : 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 : 712 : OptionClassifier{section, name, key, doc_string}, m_ui_type{ui},
411 : 712 : m_value{value >= min && value <= max ? value : min},
412 : 712 : m_default_value{value >= min && value <= max ? value : min},
413 : 3560 : m_min{min}, m_max{max}, m_step{step} {
414 : : if constexpr(is_same_decayed_v<ValueType, int>)
415 : 712 : set_alternate(true);}
416 : 712 : GncOptionRangeValue(const GncOptionRangeValue<ValueType>&) = default;
417 : : GncOptionRangeValue(GncOptionRangeValue<ValueType>&&) = default;
418 : : GncOptionRangeValue<ValueType>& operator=(const GncOptionRangeValue<ValueType>&) = default;
419 : : GncOptionRangeValue<ValueType>& operator=(GncOptionRangeValue<ValueType>&&) = default;
420 : 4637 : ValueType get_value() const { return m_value; }
421 : 8 : ValueType get_default_value() const { return m_default_value; }
422 : 160 : bool validate(ValueType value) { return value >= m_min && value <= m_max; }
423 : 160 : void set_value(ValueType value)
424 : : {
425 : 160 : if (this->validate(value))
426 : : {
427 : 156 : m_value = value;
428 : 156 : m_dirty = true;
429 : : }
430 : : else
431 : 4 : throw std::invalid_argument("Validation failed, value not set.");
432 : 156 : }
433 : 0 : void set_default_value(ValueType value)
434 : : {
435 : 0 : if (this->validate(value))
436 : 0 : m_value = m_default_value = value;
437 : : else
438 : 0 : throw std::invalid_argument("Validation failed, value not set.");
439 : 0 : }
440 : 2 : void get_limits(ValueType& upper, ValueType& lower, ValueType& step) const noexcept
441 : : {
442 : 2 : upper = m_max;
443 : 2 : lower = m_min;
444 : 2 : step = m_step;
445 : 2 : }
446 : 0 : void reset_default_value() { m_value = m_default_value; }
447 : 0 : void mark_saved() noexcept { m_dirty = false; }
448 : 0 : bool is_dirty() const noexcept { return m_dirty; }
449 : 0 : bool is_changed() const noexcept { return m_value != m_default_value; }
450 : 6 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
451 : 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
452 : 0 : bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
453 : 0 : bool is_alternate() const noexcept { return m_alternate; }
454 : 413 : 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 : 4 : operator>> (std::istream& iss, OptType& opt)
483 : : {
484 : 4 : if (opt.get_ui_type() == GncOptionUIType::PLOT_SIZE)
485 : : {
486 : 0 : std::string alt;
487 : 0 : iss >> alt;
488 : 0 : opt.set_alternate(strncmp(alt.c_str(), "percent",
489 : : strlen("percent")) == 0);
490 : 0 : }
491 : : if constexpr (std::is_same_v<std::decay_t<OptType>,
492 : : GncOptionRangeValue<double>>)
493 : : {
494 : : double d;
495 : 2 : iss >> d;
496 : 2 : opt.set_value(d);
497 : : }
498 : : else
499 : : {
500 : : int i;
501 : 2 : iss >> i;
502 : 2 : opt.set_value(i);
503 : : }
504 : 2 : 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 : :
513 : : /** @class GncOptionMultichoiceValue
514 : : * Multichoice options have a vector of valid options
515 : : * (GncMultichoiceOptionChoices) and validate the selection as being one of
516 : : * those values. The value is the index of the selected item in the vector.
517 : :
518 : : * GncMultichoiceOptionEntry is a tuple of two strings and a
519 : : * GncOptionMultichoiceKeyType value; the first string is the internal value of
520 : : * the option, the second is the display name that should be localized at the
521 : : * point of use (so mark it with N_() or (N_ ) when creating the multichoices)
522 : : * and the third is an enum value indicating whether the key should be
523 : : * interpreted as a Scheme symbol, a string, or a number.
524 : : *
525 : : *
526 : : */
527 : :
528 : : class GncOptionMultichoiceValue : public OptionClassifier
529 : : {
530 : : public:
531 : 22899 : GncOptionMultichoiceValue(const char* section, const char* name,
532 : : const char* key, const char* doc_string,
533 : : const char* value,
534 : : GncMultichoiceOptionChoices&& choices,
535 : 22899 : GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
536 : : OptionClassifier{section, name, key, doc_string},
537 : 22899 : m_ui_type{ui_type},
538 : 114495 : m_value{}, m_default_value{}, m_choices{std::move(choices)}
539 : : {
540 : 22899 : if (value)
541 : : {
542 : 22899 : if (auto index = find_key(value);
543 : 22899 : index != uint16_t_max)
544 : : {
545 : 22894 : m_value.push_back(index);
546 : 22894 : m_default_value.push_back(index);
547 : : }
548 : : }
549 : 22899 : }
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)} {}
576 : 22900 : GncOptionMultichoiceValue(const GncOptionMultichoiceValue&) = default;
577 : : GncOptionMultichoiceValue(GncOptionMultichoiceValue&&) = default;
578 : : GncOptionMultichoiceValue& operator=(const GncOptionMultichoiceValue&) = default;
579 : : GncOptionMultichoiceValue& operator=(GncOptionMultichoiceValue&&) = default;
580 : :
581 : 8 : const std::string& get_value() const
582 : : {
583 : 8 : auto vec{m_value.size() > 0 ? m_value : m_default_value};
584 : 8 : if (vec.size() == 0)
585 : 0 : return c_empty_string;
586 : 8 : if (vec.size() == 1)
587 : 7 : return std::get<0>(m_choices.at(vec[0]));
588 : : else
589 : 1 : return c_list_string;
590 : :
591 : 8 : }
592 : 0 : const std::string& get_default_value() const
593 : : {
594 : 0 : if (m_default_value.size() == 1)
595 : 0 : return std::get<0>(m_choices.at(m_default_value[0]));
596 : 0 : else if (m_default_value.size() == 0)
597 : 0 : return c_empty_string;
598 : : else
599 : 0 : return c_list_string;
600 : : }
601 : :
602 : 2 : uint16_t get_index() const
603 : : {
604 : 2 : if (m_value.size() > 0)
605 : 2 : return m_value[0];
606 : 0 : if (m_default_value.size() > 0)
607 : 0 : return m_default_value[0];
608 : 0 : return 0;
609 : : }
610 : 14901 : const GncMultichoiceOptionIndexVec& get_multiple() const noexcept
611 : : {
612 : 14901 : return m_value;
613 : : }
614 : 13 : const GncMultichoiceOptionIndexVec& get_default_multiple() const noexcept
615 : : {
616 : 13 : return m_default_value;
617 : : }
618 : 3 : bool validate(const std::string& value) const noexcept
619 : : {
620 : 3 : auto index = find_key(value);
621 : 3 : return index != uint16_t_max;
622 : :
623 : : }
624 : 9105 : bool validate(const GncMultichoiceOptionIndexVec& indexes) const noexcept
625 : : {
626 : 18213 : for (auto index : indexes)
627 : 9110 : if (index >= m_choices.size())
628 : 2 : return false;
629 : 9103 : return true;
630 : :
631 : : }
632 : 3 : void set_value(const std::string& value)
633 : : {
634 : 3 : auto index = find_key(value);
635 : 3 : if (index != uint16_t_max)
636 : : {
637 : 2 : m_value.clear();
638 : 2 : m_value.push_back(index);
639 : 2 : m_dirty = true;
640 : : }
641 : : else
642 : 1 : throw std::invalid_argument("Value not a valid choice.");
643 : :
644 : 2 : }
645 : 0 : void set_value(uint16_t index)
646 : : {
647 : 0 : if (index < m_choices.size())
648 : : {
649 : 0 : m_value.clear();
650 : 0 : m_value.push_back(index);
651 : 0 : m_dirty = true;
652 : : }
653 : : else
654 : 0 : throw std::invalid_argument("Value not a valid choice.");
655 : :
656 : 0 : }
657 : 0 : void set_default_value(const std::string& value)
658 : : {
659 : 0 : auto index = find_key(value);
660 : 0 : if (index != uint16_t_max)
661 : : {
662 : 0 : m_value.clear();
663 : 0 : m_value.push_back(index);
664 : 0 : m_default_value.clear();
665 : 0 : m_default_value.push_back(index);
666 : : }
667 : : else
668 : 0 : throw std::invalid_argument("Value not a valid choice.");
669 : :
670 : 0 : }
671 : 0 : void set_default_value(uint16_t index)
672 : : {
673 : 0 : if (index < m_choices.size())
674 : : {
675 : 0 : m_value.clear();
676 : 0 : m_value.push_back(index);
677 : 0 : m_default_value.clear();
678 : 0 : m_default_value.push_back(index);
679 : : }
680 : : else
681 : 0 : throw std::invalid_argument("Value not a valid choice.");
682 : :
683 : 0 : }
684 : 9027 : void set_multiple(const GncMultichoiceOptionIndexVec& indexes)
685 : : {
686 : 9027 : if (validate(indexes))
687 : 9026 : m_value = indexes;
688 : : else
689 : 1 : throw std::invalid_argument("One of the supplied indexes was out of range.");
690 : 9026 : }
691 : 76 : void set_default_multiple(const GncMultichoiceOptionIndexVec& indexes)
692 : : {
693 : 76 : if (validate(indexes))
694 : 76 : m_value = m_default_value = indexes;
695 : : else
696 : 0 : throw std::invalid_argument("One of the supplied indexes was out of range.");
697 : 76 : }
698 : 476 : uint16_t num_permissible_values() const noexcept
699 : : {
700 : 476 : return m_choices.size();
701 : : }
702 : 9106 : uint16_t permissible_value_index(const char* key) const noexcept
703 : : {
704 : 18212 : return find_key(key);
705 : : }
706 : 16021 : const char* permissible_value(uint16_t index) const
707 : : {
708 : 16021 : return std::get<0>(m_choices.at(index)).c_str();
709 : : }
710 : 2 : const char* permissible_value_name(uint16_t index) const
711 : : {
712 : 2 : return std::get<1>(m_choices.at(index)).c_str();
713 : : }
714 : 0 : void reset_default_value() { m_value = m_default_value; }
715 : 0 : void mark_saved() noexcept { m_dirty = false; }
716 : 0 : bool is_dirty() const noexcept { return m_dirty; }
717 : 2902 : bool is_changed() const noexcept { return m_value != m_default_value; }
718 : 24014 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
719 : 36 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
720 : 1172 : 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 : 31999 : uint16_t find_key (const std::string& key) const noexcept
726 : : {
727 : 31999 : auto iter = std::find_if(m_choices.begin(), m_choices.end(),
728 : 63998 : [key](auto choice) {
729 : 67561 : return std::get<0>(choice) == key; });
730 : 31999 : if (iter != m_choices.end())
731 : 31994 : return iter - m_choices.begin();
732 : : else
733 : 5 : 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 : 4 : operator>> <GncOptionMultichoiceValue>(std::istream& iss,
764 : : GncOptionMultichoiceValue& opt)
765 : : {
766 : 4 : GncMultichoiceOptionIndexVec values;
767 : : while (true)
768 : : {
769 : 6 : std::string str;
770 : 6 : std::getline(iss, str, ' ');
771 : 6 : if (!str.empty())
772 : : {
773 : 4 : auto index = opt.permissible_value_index(str.c_str());
774 : 4 : if (index != uint16_t_max)
775 : 2 : values.push_back(index);
776 : : else
777 : : {
778 : 2 : std::string err = str + " is not one of ";
779 : 2 : err += opt.m_name;
780 : 2 : err += "'s permissible values.";
781 : 2 : throw std::invalid_argument(err);
782 : 2 : }
783 : : }
784 : : else
785 : 2 : break;
786 : 8 : }
787 : 2 : opt.set_multiple(values);
788 : 2 : iss.clear();
789 : 2 : return iss;
790 : 4 : }
791 : :
792 : :
793 : : using GncOptionAccountList = std::vector<GncGUID>;
794 : :
795 : : using GncOptionAccountTypeList = std::vector<GNCAccountType>;
796 : :
797 : : /** @class GncOptionAccountListValue
798 : : *
799 : : * Set one or more accounts on which to report, optionally restricted to certain
800 : : * account types. Many calls to make-account-list-option will pass a get-default
801 : : * function that retrieves all of the accounts of a list of types.
802 : : *
803 : : * Some reports (examples/daily-reports.scm and standard/ account-piechart.scm,
804 : : * advanced-portfolio.scm, category-barchart.scm, net-charts.scm, and
805 : : * portfolio.scm) also provide a validator that rejects accounts that don't meet
806 : : * an account-type criterion.
807 : : *
808 : : * There are two types of option, account-list which permits more than one
809 : : * account selection and account-sel, which doesn't.
810 : : *
811 : :
812 : : */
813 : :
814 : : class GncOptionAccountListValue : public OptionClassifier
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 : 3172 : GncOptionAccountListValue(const char* section, const char* name,
824 : : const char* key, const char* doc_string,
825 : : GncOptionUIType ui_type,
826 : 3172 : const GncOptionAccountList& value, bool multi=true) :
827 : 3172 : OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
828 : 3172 : m_value{value}, m_default_value{std::move(value)}, m_allowed{},
829 : 19032 : 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 : 154 : GncOptionAccountListValue(const char* section, const char* name,
838 : : const char* key, const char* doc_string,
839 : : GncOptionUIType ui_type,
840 : : const GncOptionAccountList& value,
841 : 154 : GncOptionAccountTypeList&& allowed, bool multi=true) :
842 : 154 : OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
843 : 154 : m_value{}, m_default_value{}, m_allowed{std::move(allowed)},
844 : 770 : m_multiselect{multi} {
845 : 154 : if (!validate(value))
846 : 1 : throw std::invalid_argument("Supplied Value not in allowed set.");
847 : 153 : m_value = value;
848 : 153 : m_default_value = std::move(value);
849 : 157 : }
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 : 2876 : void set_value (GncOptionAccountList values) {
860 : 2876 : if (validate(values))
861 : : {
862 : : //throw!
863 : 2803 : m_value = values;
864 : 2803 : m_dirty = true;
865 : : }
866 : 2876 : }
867 : 0 : void set_default_value (GncOptionAccountList values) {
868 : 0 : if (validate(values))
869 : : //throw!
870 : 0 : m_value = m_default_value = values;
871 : 0 : }
872 : : GList* account_type_list() const noexcept;
873 : 0 : void reset_default_value() { m_value = m_default_value; }
874 : 0 : void mark_saved() noexcept { m_dirty = false; }
875 : 2 : bool is_dirty() const noexcept { return m_dirty; }
876 : : bool is_changed() const noexcept;
877 : 2993 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
878 : 6 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
879 : 78 : bool is_internal() { return m_ui_type == GncOptionUIType::INTERNAL; }
880 : 0 : 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 : 3 : operator>> <GncOptionAccountListValue>(std::istream& iss,
913 : : GncOptionAccountListValue& opt)
914 : : {
915 : 3 : GncOptionAccountList values;
916 : : while (true)
917 : : {
918 : 7 : std::string str;
919 : 7 : std::getline(iss, str, ' ');
920 : 7 : if (!str.empty())
921 : : {
922 : 4 : auto guid{qof_entity_get_guid(qof_instance_from_string(str, opt.get_ui_type()))};
923 : 4 : values.push_back(*guid);
924 : : }
925 : : else
926 : 3 : break;
927 : 11 : }
928 : 3 : opt.set_value(values);
929 : 3 : iss.clear();
930 : 3 : return iss;
931 : 3 : }
932 : :
933 : : /* @class GncOptionAccountSelValue
934 : : * Like GncOptionAccountListValue but contains only a single account.
935 : : */
936 : :
937 : : class GncOptionAccountSelValue : public OptionClassifier
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 : 33 : GncOptionAccountSelValue(const char* section, const char* name,
961 : : const char* key, const char* doc_string,
962 : : GncOptionUIType ui_type,
963 : : const Account* value,
964 : 33 : GncOptionAccountTypeList&& allowed) :
965 : 33 : OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
966 : 165 : m_value{*guid_null()}, m_default_value{*guid_null()}, m_allowed{std::move(allowed)} {
967 : 33 : if (!validate(value))
968 : 0 : throw std::invalid_argument("Supplied Value not in allowed set.");
969 : 33 : m_value = m_default_value = *qof_entity_get_guid(value);
970 : 33 : }
971 : :
972 : : const Account* get_value() const;
973 : : const Account* get_default_value() const;
974 : : bool validate (const Account* value) const;
975 : 3 : void set_value (const Account* value) {
976 : 3 : if (validate(value))
977 : : {
978 : 3 : auto guid{qof_entity_get_guid(value)};
979 : 3 : m_value = *guid;
980 : 3 : m_dirty = true;
981 : : }
982 : : //else throw
983 : 3 : }
984 : 0 : void set_default_value (const Account* value) {
985 : 0 : if (validate(value))
986 : : {
987 : 0 : auto guid{qof_entity_get_guid(value)};
988 : 0 : m_value = m_default_value = *guid;
989 : : }
990 : : //else throw
991 : 0 : }
992 : : GList* account_type_list() const noexcept;
993 : 0 : void reset_default_value() { m_value = m_default_value; }
994 : 0 : void mark_saved() noexcept { m_dirty = false; }
995 : 0 : bool is_dirty() const noexcept { return m_dirty; }
996 : 40 : bool is_changed() const noexcept { return !guid_equal(&m_value, &m_default_value); }
997 : 2 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
998 : 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
999 : 2 : 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 : 0 : operator>> <GncOptionAccountSelValue>(std::istream& iss,
1021 : : GncOptionAccountSelValue& opt)
1022 : : {
1023 : 0 : Account* value{nullptr};
1024 : 0 : std::string str;
1025 : 0 : std::getline(iss, str, ' ');
1026 : 0 : if (!str.empty())
1027 : 0 : value = (Account*)qof_instance_from_string(str, opt.get_ui_type());
1028 : 0 : opt.set_value(value);
1029 : 0 : iss.clear();
1030 : 0 : return iss;
1031 : 0 : }
1032 : :
1033 : : /** @class GncOptionDateValue
1034 : : * A legal date value is a pair of either a RelativeDatePeriod, the absolute
1035 : : * flag and a time64, or for legacy purposes the absolute flag and a timespec.
1036 : : */
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 : :
1045 : : class GncOptionDateValue : public OptionClassifier
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 : 1 : GncOptionDateValue(const char* section, const char* name,
1057 : : const char* key, const char* doc_string,
1058 : 1 : GncOptionUIType ui_type, time64 time) :
1059 : : OptionClassifier{section, name, key, doc_string},
1060 : 1 : m_ui_type{ui_type}, m_date{time}, m_default_date{time},
1061 : 1 : m_period{RelativeDatePeriod::ABSOLUTE},
1062 : 1 : m_default_period{RelativeDatePeriod::ABSOLUTE},
1063 : 5 : m_period_set{} {}
1064 : 5 : GncOptionDateValue(const char* section, const char* name,
1065 : : const char* key, const char* doc_string,
1066 : : GncOptionUIType ui_type,
1067 : 5 : RelativeDatePeriod period) :
1068 : : OptionClassifier{section, name, key, doc_string},
1069 : 5 : m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
1070 : 5 : m_period{period}, m_default_period{period},
1071 : 25 : m_period_set{} {}
1072 : 4558 : GncOptionDateValue(const char* section, const char* name,
1073 : : const char* key, const char* doc_string,
1074 : : GncOptionUIType ui_type,
1075 : 4558 : const RelativeDatePeriodVec& period_set) :
1076 : : OptionClassifier{section, name, key, doc_string},
1077 : 4558 : m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
1078 : 9116 : m_period{period_set.back()},
1079 : 4558 : m_default_period{period_set.back()},
1080 : 27348 : m_period_set{period_set} {}
1081 : 4559 : GncOptionDateValue(const GncOptionDateValue&) = default;
1082 : : GncOptionDateValue(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 : 6426 : RelativeDatePeriod get_period() const noexcept { return m_period; }
1088 : 6 : 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 : 8456 : bool validate(time64 time) {
1095 : 8456 : if (time > MINTIME && time < MAXTIME)
1096 : 8456 : return true;
1097 : 0 : return false;
1098 : : }
1099 : 209 : void set_value(RelativeDatePeriod value) {
1100 : 209 : if (validate(value))
1101 : : {
1102 : 207 : m_period = value;
1103 : 207 : m_date = INT64_MAX;
1104 : 207 : m_dirty = true;
1105 : : }
1106 : 209 : }
1107 : 8453 : void set_value(time64 time) {
1108 : 8453 : if (validate(time))
1109 : : {
1110 : 8453 : m_period = RelativeDatePeriod::ABSOLUTE;
1111 : 8453 : m_date = time;
1112 : 8453 : m_dirty = true;
1113 : : }
1114 : 8453 : }
1115 : : void set_value(uint16_t index) noexcept;
1116 : 46 : void set_default_value(RelativeDatePeriod value) {
1117 : 46 : if (validate(value))
1118 : : {
1119 : 46 : m_period = m_default_period = value;
1120 : 46 : m_date = m_default_date = INT64_MAX;
1121 : : }
1122 : 46 : }
1123 : 3 : void set_default_value(time64 time) {
1124 : 3 : if (validate(time))
1125 : : {
1126 : 3 : m_period = m_default_period = RelativeDatePeriod::ABSOLUTE;
1127 : 3 : m_date = m_default_date = time;
1128 : : }
1129 : 3 : }
1130 : 0 : uint16_t num_permissible_values() const noexcept
1131 : : {
1132 : 0 : return m_period_set.size();
1133 : : }
1134 : : uint16_t permissible_value_index(const char* key) const noexcept;
1135 : 0 : const char* permissible_value(uint16_t index) const
1136 : : {
1137 : 0 : return gnc_relative_date_storage_string(m_period_set.at(index));
1138 : : }
1139 : 0 : const char* permissible_value_name(uint16_t index) const
1140 : : {
1141 : 0 : return gnc_relative_date_display_string(m_period_set.at(index));
1142 : : }
1143 : 0 : void reset_default_value() {
1144 : 0 : m_period = m_default_period;
1145 : 0 : m_date = m_default_date;
1146 : 0 : }
1147 : 0 : void mark_saved() noexcept { m_dirty = false; }
1148 : 2 : bool is_dirty() const noexcept { return m_dirty; }
1149 : 690 : bool is_changed() const noexcept { return m_period != m_default_period &&
1150 : 690 : m_date != m_default_date; }
1151 : 2 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
1152 : 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
1153 : 276 : 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 : 13 : operator>> <GncOptionDateValue>(std::istream& iss,
1176 : : GncOptionDateValue& opt)
1177 : : {
1178 : 13 : return opt.in_stream(iss);
1179 : : }
1180 : :
1181 : :
1182 : : #endif //GNC_OPTION_IMPL_HPP_
1183 : : /**@}
1184 : : @} */
|