/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Jon Evans * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef _PARAMETERS_H #define _PARAMETERS_H #include #include #include #include #include class PARAM_BASE { public: PARAM_BASE( std::string aJsonPath, bool aReadOnly ) : m_path( std::move( aJsonPath ) ), m_readOnly( aReadOnly ) {} virtual ~PARAM_BASE() = default; /** * Loads the value of this parameter from JSON to the underlying storage * @param aSettings is the JSON_SETTINGS object to load from. */ virtual void Load( JSON_SETTINGS* aSettings ) const = 0; /** * Stores the value of this parameter to the given JSON_SETTINGS object * @param aSettings is the JSON_SETTINGS object to store into. */ virtual void Store( JSON_SETTINGS* aSettings ) const = 0; virtual void SetDefault() = 0; /** * @return the path name of the parameter used to store it in the json file * mainly usefull in error messages */ const std::string& GetJsonPath() { return m_path; } protected: /** * the string used to store the param in json files */ std::string m_path; ///! True if the parameter pointer should never be overwritten bool m_readOnly; }; template class PARAM : public PARAM_BASE { public: PARAM( const std::string& aJsonPath, ValueType* aPtr, ValueType aDefault, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), m_ptr( aPtr ), m_default( aDefault ), m_min(), m_max(), m_use_minmax( false ) { } PARAM( const std::string& aJsonPath, ValueType* aPtr, ValueType aDefault, ValueType aMin, ValueType aMax, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), m_ptr( aPtr ), m_default( aDefault ), m_min( aMin ), m_max( aMax ), m_use_minmax( true ) { } void Load( JSON_SETTINGS* aSettings ) const override { if( m_readOnly ) return; ValueType val = m_default; if( OPT optval = aSettings->Get( m_path ) ) { val = *optval; if( m_use_minmax ) { if( m_max < val || val < m_min ) val = m_default; } } *m_ptr = val; } void Store( JSON_SETTINGS* aSettings) const override { aSettings->Set( m_path, *m_ptr ); } ValueType GetDefault() const { return m_default; } virtual void SetDefault() override { *m_ptr = m_default; } private: ValueType* m_ptr; ValueType m_default; ValueType m_min; ValueType m_max; bool m_use_minmax; }; /** * Like a normal param, but with custom getter and setter functions * @tparam ValueType is the value to store */ template class PARAM_LAMBDA : public PARAM_BASE { public: PARAM_LAMBDA( const std::string& aJsonPath, std::function aGetter, std::function aSetter, ValueType aDefault, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), m_default( aDefault ), m_getter( aGetter ), m_setter( aSetter ) { } void Load( JSON_SETTINGS* aSettings ) const override { if( m_readOnly ) return; ValueType val = m_default; if( std::is_same::value ) { if( OPT optval = aSettings->GetJson( m_path ) ) val = *optval; } else { if( OPT optval = aSettings->Get( m_path ) ) val = *optval; } m_setter( val ); } void Store( JSON_SETTINGS* aSettings) const override { try { aSettings->Set( m_path, m_getter() ); } catch( ... ) { } } ValueType GetDefault() const { return m_default; } virtual void SetDefault() override { m_setter( m_default ); } private: ValueType m_default; std::function m_getter; std::function m_setter; }; /** * Represents a parameter that has a scaling factor between the value in the file and the * value used internally (i.e. the value pointer). This basically only makes sense to use * with int or double as ValueType. * @tparam ValueType is the internal type: the file always stores a double. */ template class PARAM_SCALED: public PARAM_BASE { public: PARAM_SCALED( const std::string& aJsonPath, ValueType* aPtr, ValueType aDefault, double aScale = 1.0, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), m_ptr( aPtr ), m_default( aDefault ), m_min(), m_max(), m_use_minmax( false ), m_scale( aScale ) { } PARAM_SCALED( const std::string& aJsonPath, ValueType* aPtr, ValueType aDefault, ValueType aMin, ValueType aMax, double aScale = 1.0, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), m_ptr( aPtr ), m_default( aDefault ), m_min( aMin ), m_max( aMax ), m_use_minmax( true ), m_scale( aScale ) { } void Load( JSON_SETTINGS* aSettings ) const override { if( m_readOnly ) return; double dval = m_default * m_scale; if( OPT optval = aSettings->Get( m_path ) ) dval = *optval; ValueType val = KiROUND( dval / m_scale ); if( m_use_minmax ) { if( val > m_max || val < m_min ) val = m_default; } *m_ptr = val; } void Store( JSON_SETTINGS* aSettings) const override { aSettings->Set( m_path, *m_ptr * m_scale ); } ValueType GetDefault() const { return m_default; } virtual void SetDefault() override { *m_ptr = m_default; } private: ValueType* m_ptr; ValueType m_default; ValueType m_min; ValueType m_max; bool m_use_minmax; double m_scale; }; template class PARAM_LIST : public PARAM_BASE { public: PARAM_LIST( const std::string& aJsonPath, std::vector* aPtr, std::initializer_list aDefault, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), m_ptr( aPtr ), m_default( aDefault ) { } PARAM_LIST( const std::string& aJsonPath, std::vector* aPtr, std::vector aDefault, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), m_ptr( aPtr ), m_default( aDefault ) { } void Load( JSON_SETTINGS* aSettings ) const override { if( m_readOnly ) return; std::vector val = m_default; if( OPT js = aSettings->GetJson( m_path ) ) { if( js->is_array() ) { val.clear(); for( const auto& el : js->items() ) val.push_back( el.value().get() ); } } *m_ptr = val; } void Store( JSON_SETTINGS* aSettings) const override { nlohmann::json js = nlohmann::json::array(); for( const auto& el : *m_ptr ) js.push_back( el ); aSettings->Set( m_path, js ); } virtual void SetDefault() override { *m_ptr = m_default; } private: std::vector* m_ptr; std::vector m_default; }; /** * Represents a map of . The key parameter has to be a string in JSON. * * The key must be stored in UTF-8 format, so any translated strings or strings provided by the * user as a key must be converted to UTF-8 at the site where they are placed in the underlying * map that this PARAM_MAP points to. * * Values must also be in UTF-8, but if you use wxString as the value type, this conversion will * be done automatically by the to_json and from_json helpers defined in json_settings.cpp * * @tparam Value is the value type of the map */ template class PARAM_MAP : public PARAM_BASE { public: PARAM_MAP( const std::string& aJsonPath, std::map* aPtr, std::initializer_list> aDefault, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), m_ptr( aPtr ), m_default( aDefault ) { } void Load( JSON_SETTINGS* aSettings ) const override { if( m_readOnly ) return; std::map val = m_default; if( OPT js = aSettings->GetJson( m_path ) ) { if( js->is_object() ) { val.clear(); for( const auto& el : js->items() ) val[ el.key() ] = el.value().get(); } } *m_ptr = val; } void Store( JSON_SETTINGS* aSettings) const override { nlohmann::json js( {} ); for( const auto& el : *m_ptr ) js[ el.first ] = el.second; aSettings->Set( m_path, js ); } virtual void SetDefault() override { *m_ptr = m_default; } private: std::map* m_ptr; std::map m_default; }; #endif