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 3752 : 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 0 : OptionClassifier{section, name, key, doc_string},
95 30016 : m_ui_type(ui_type), m_value{value}, m_default_value{value} { }
96 53 : GncOptionValue(const GncOptionValue&) = default;
97 : GncOptionValue(GncOptionValue&&) = default;
98 : GncOptionValue& operator=(const GncOptionValue&) = default;
99 : GncOptionValue& operator=(GncOptionValue&&) = default;
100 0 : ~GncOptionValue() = default;
101 17 : ValueType get_value() const { return m_value; }
102 2 : 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 2 : bool is_changed() const noexcept { return m_value != m_default_value; }
109 20 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
110 19 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
111 0 : 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 0 : ~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 0 : 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 0 : ~GncOptionQofInstanceValue() = default;
185 : const QofInstance* get_value() const;
186 : const QofInstance* get_default_value() const;
187 5 : GncItem get_item() const { return m_value; }
188 5 : 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 6 : 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 0 : GncOptionCommodityValue(const char* section, const char* name,
220 : const char* key, const char* doc_string,
221 : gnc_commodity* value,
222 0 : GncOptionUIType ui_type = GncOptionUIType::COMMODITY) :
223 0 : OptionClassifier{section, name, key, doc_string},
224 0 : m_ui_type{ui_type}, m_is_currency{ui_type == GncOptionUIType::CURRENCY},
225 0 : m_namespace{gnc_commodity_get_namespace(value)},
226 0 : m_mnemonic{gnc_commodity_get_mnemonic(value)},
227 0 : m_default_namespace{gnc_commodity_get_namespace(value)},
228 0 : m_default_mnemonic{gnc_commodity_get_mnemonic(value)}
229 : {
230 0 : if (!validate(value))
231 0 : throw std::invalid_argument("Attempt to create GncOptionCommodityValue with currency UIType and non-currency value.");
232 0 : }
233 0 : 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 0 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
247 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
248 0 : 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 0 : 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 0 : max, step, GncOptionUIType::NUMBER_RANGE} {}
406 0 : 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 0 : OptionClassifier{section, name, key, doc_string}, m_ui_type{ui},
411 0 : m_value{value >= min && value <= max ? value : min},
412 0 : m_default_value{value >= min && value <= max ? value : min},
413 0 : m_min{min}, m_max{max}, m_step{step} {
414 : if constexpr(is_same_decayed_v<ValueType, int>)
415 0 : set_alternate(true);}
416 0 : 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 6 : ValueType get_value() const { return m_value; }
421 4 : ValueType get_default_value() const { return m_default_value; }
422 8 : bool validate(ValueType value) { return value >= m_min && value <= m_max; }
423 8 : void set_value(ValueType value)
424 : {
425 8 : if (this->validate(value))
426 : {
427 4 : m_value = value;
428 4 : m_dirty = true;
429 : }
430 : else
431 4 : throw std::invalid_argument("Validation failed, value not set.");
432 4 : }
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 0 : 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 0 : GncOptionMultichoiceValue(const char* section, const char* name,
532 : const char* key, const char* doc_string,
533 : const char* value,
534 : GncMultichoiceOptionChoices&& choices,
535 0 : GncOptionUIType ui_type = GncOptionUIType::MULTICHOICE) :
536 0 : OptionClassifier{section, name, key, doc_string},
537 0 : m_ui_type{ui_type},
538 0 : m_value{}, m_default_value{}, m_choices{std::move(choices)}
539 : {
540 0 : if (value)
541 : {
542 0 : if (auto index = find_key(value);
543 0 : index != uint16_t_max)
544 : {
545 0 : m_value.push_back(index);
546 0 : m_default_value.push_back(index);
547 : }
548 : }
549 0 : }
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 0 : 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 2 : const GncMultichoiceOptionIndexVec& get_multiple() const noexcept
611 : {
612 2 : return m_value;
613 : }
614 0 : const GncMultichoiceOptionIndexVec& get_default_multiple() const noexcept
615 : {
616 0 : 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 6 : bool validate(const GncMultichoiceOptionIndexVec& indexes) const noexcept
625 : {
626 20 : for (auto index : indexes)
627 10 : if (index >= m_choices.size())
628 2 : return false;
629 4 : 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 4 : void set_multiple(const GncMultichoiceOptionIndexVec& indexes)
685 : {
686 4 : if (validate(indexes))
687 3 : m_value = indexes;
688 : else
689 1 : throw std::invalid_argument("One of the supplied indexes was out of range.");
690 3 : }
691 0 : void set_default_multiple(const GncMultichoiceOptionIndexVec& indexes)
692 : {
693 0 : if (validate(indexes))
694 0 : m_value = m_default_value = indexes;
695 : else
696 0 : throw std::invalid_argument("One of the supplied indexes was out of range.");
697 0 : }
698 1 : uint16_t num_permissible_values() const noexcept
699 : {
700 1 : return m_choices.size();
701 : }
702 6 : uint16_t permissible_value_index(const char* key) const noexcept
703 : {
704 12 : return find_key(key);
705 : }
706 4 : const char* permissible_value(uint16_t index) const
707 : {
708 4 : 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 0 : bool is_changed() const noexcept { return m_value != m_default_value; }
718 2 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
719 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
720 0 : 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 0 : uint16_t find_key (const std::string& key) const noexcept
726 : {
727 0 : auto iter = std::find_if(m_choices.begin(), m_choices.end(),
728 0 : [key](auto choice) {
729 0 : return std::get<0>(choice) == key; });
730 0 : if (iter != m_choices.end())
731 0 : return iter - m_choices.begin();
732 : else
733 0 : 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 0 : GncOptionAccountListValue(const char* section, const char* name,
824 : const char* key, const char* doc_string,
825 : GncOptionUIType ui_type,
826 0 : const GncOptionAccountList& value, bool multi=true) :
827 0 : OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
828 0 : m_value{value}, m_default_value{std::move(value)}, m_allowed{},
829 0 : 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 0 : GncOptionAccountListValue(const char* section, const char* name,
838 : const char* key, const char* doc_string,
839 : GncOptionUIType ui_type,
840 : const GncOptionAccountList& value,
841 0 : GncOptionAccountTypeList&& allowed, bool multi=true) :
842 0 : OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
843 0 : m_value{}, m_default_value{}, m_allowed{std::move(allowed)},
844 0 : m_multiselect{multi} {
845 0 : if (!validate(value))
846 0 : throw std::invalid_argument("Supplied Value not in allowed set.");
847 0 : m_value = value;
848 0 : m_default_value = std::move(value);
849 0 : }
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 3 : void set_value (GncOptionAccountList values) {
860 3 : if (validate(values))
861 : {
862 : //throw!
863 2 : m_value = values;
864 2 : m_dirty = true;
865 : }
866 3 : }
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 22 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
878 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
879 0 : 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 0 : GncOptionAccountSelValue(const char* section, const char* name,
961 : const char* key, const char* doc_string,
962 : GncOptionUIType ui_type,
963 : const Account* value,
964 0 : GncOptionAccountTypeList&& allowed) :
965 0 : OptionClassifier{section, name, key, doc_string}, m_ui_type{ui_type},
966 0 : m_value{*guid_null()}, m_default_value{*guid_null()}, m_allowed{std::move(allowed)} {
967 0 : if (!validate(value))
968 0 : throw std::invalid_argument("Supplied Value not in allowed set.");
969 0 : m_value = m_default_value = *qof_entity_get_guid(value);
970 0 : }
971 :
972 : const Account* get_value() const;
973 : const Account* get_default_value() const;
974 : bool validate (const Account* value) const;
975 0 : void set_value (const Account* value) {
976 0 : if (validate(value))
977 : {
978 0 : auto guid{qof_entity_get_guid(value)};
979 0 : m_value = *guid;
980 0 : m_dirty = true;
981 : }
982 : //else throw
983 0 : }
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 0 : bool is_changed() const noexcept { return !guid_equal(&m_value, &m_default_value); }
997 0 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
998 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
999 0 : 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 0 : 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 8 : m_period_set{} {}
1064 4 : GncOptionDateValue(const char* section, const char* name,
1065 : const char* key, const char* doc_string,
1066 : GncOptionUIType ui_type,
1067 4 : RelativeDatePeriod period) :
1068 0 : OptionClassifier{section, name, key, doc_string},
1069 4 : m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
1070 4 : m_period{period}, m_default_period{period},
1071 32 : m_period_set{} {}
1072 0 : GncOptionDateValue(const char* section, const char* name,
1073 : const char* key, const char* doc_string,
1074 : GncOptionUIType ui_type,
1075 0 : const RelativeDatePeriodVec& period_set) :
1076 0 : OptionClassifier{section, name, key, doc_string},
1077 0 : m_ui_type{ui_type}, m_date{INT64_MAX}, m_default_date{INT64_MAX},
1078 0 : m_period{period_set.back()},
1079 0 : m_default_period{period_set.back()},
1080 0 : m_period_set{period_set} {}
1081 0 : 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 8 : RelativeDatePeriod get_period() const noexcept { return m_period; }
1088 0 : 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 6 : bool validate(time64 time) {
1095 6 : if (time > MINTIME && time < MAXTIME)
1096 6 : return true;
1097 0 : return false;
1098 : }
1099 31 : void set_value(RelativeDatePeriod value) {
1100 31 : if (validate(value))
1101 : {
1102 29 : m_period = value;
1103 29 : m_date = INT64_MAX;
1104 29 : m_dirty = true;
1105 : }
1106 31 : }
1107 6 : void set_value(time64 time) {
1108 6 : if (validate(time))
1109 : {
1110 6 : m_period = RelativeDatePeriod::ABSOLUTE;
1111 6 : m_date = time;
1112 6 : m_dirty = true;
1113 : }
1114 6 : }
1115 : void set_value(uint16_t index) noexcept;
1116 0 : void set_default_value(RelativeDatePeriod value) {
1117 0 : if (validate(value))
1118 : {
1119 0 : m_period = m_default_period = value;
1120 0 : m_date = m_default_date = INT64_MAX;
1121 : }
1122 0 : }
1123 0 : void set_default_value(time64 time) {
1124 0 : if (validate(time))
1125 : {
1126 0 : m_period = m_default_period = RelativeDatePeriod::ABSOLUTE;
1127 0 : m_date = m_default_date = time;
1128 : }
1129 0 : }
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 0 : bool is_changed() const noexcept { return m_period != m_default_period &&
1150 0 : m_date != m_default_date; }
1151 0 : GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
1152 0 : void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
1153 0 : 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 : @} */
|