LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-option-impl.hpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 41.7 % 393 164
Test Date: 2026-11-02 18:19:43 Functions: 32.4 % 204 66
Legend: Lines:     hit not hit

            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              :    @} */
        

Generated by: LCOV version 2.0-1