/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 CERN * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors. * * @author Tomasz Wlostowski * @author Maciej Suminski * * 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 PROPERTY_H #define PROPERTY_H #include #include #include #include #include #include #include #include #include // required for propgrid #include // required for propgrid #include #include #include #include #include #include class wxPGProperty; class INSPECTABLE; class PROPERTY_BASE; template class ENUM_MAP; ///< Common property types enum PROPERTY_DISPLAY { PT_DEFAULT, ///< Default property for a given type PT_SIZE, ///< Size expressed in distance units (mm/inch) PT_COORD, ///< Coordinate expressed in distance units (mm/inch) PT_DEGREE, ///< Angle expressed in degrees PT_DECIDEGREE ///< Angle expressed in decidegrees }; ///< Macro to generate unique identifier for a type #define TYPE_HASH( x ) typeid( x ).hash_code() #define TYPE_NAME( x ) typeid( x ).name() //#define TYPE_HASH( x ) typeid( std::decay::type ).hash_code() template class GETTER_BASE { public: virtual ~GETTER_BASE() {} virtual T operator()( const Owner* aOwner ) const = 0; }; template class GETTER : public GETTER_BASE { public: GETTER( FuncType aFunc ) : m_func( aFunc ) { wxCHECK( m_func, /*void*/ ); } T operator()( const Owner* aOwner ) const override { return ( aOwner->*m_func )(); } private: FuncType m_func; }; template class SETTER_BASE { public: virtual ~SETTER_BASE() {} virtual void operator()( Owner* aOwner, T aValue ) = 0; }; template class SETTER : public SETTER_BASE { public: SETTER( FuncType aFunc ) : m_func( aFunc ) { wxCHECK( m_func, /*void*/ ); } void operator()( Owner* aOwner, T aValue ) override { ( aOwner->*m_func )( aValue ); } private: FuncType m_func; }; #if defined( _MSC_VER ) #pragma warning( push ) #pragma warning( disable : 5266 ) // 'const' qualifier on return type has no effect #endif template class METHOD { public: static GETTER_BASE* Wrap( T (Base::*aFunc)() ) { return new GETTER( aFunc ); } constexpr static GETTER_BASE* Wrap( const T (Base::*aFunc)() ) { return new GETTER( aFunc ); } constexpr static GETTER_BASE* Wrap( const T& (Base::*aFunc)() ) { return new GETTER( aFunc ); } constexpr static GETTER_BASE* Wrap( T (Base::*aFunc)() const ) { return new GETTER( aFunc ); } constexpr static GETTER_BASE* Wrap( const T (Base::*aFunc)() const ) { return new GETTER( aFunc ); } constexpr static GETTER_BASE* Wrap( const T& (Base::*aFunc)() const ) { return new GETTER( aFunc ); } constexpr static SETTER_BASE* Wrap( void (Base::*aFunc)( T ) ) { return aFunc ? new SETTER( aFunc ) : nullptr; } constexpr static SETTER_BASE* Wrap( void (Base::*aFunc)( T& ) ) { return aFunc ? new SETTER( aFunc ) : nullptr; } constexpr static SETTER_BASE* Wrap( void (Base::*aFunc)( const T& ) ) { return aFunc ? new SETTER( aFunc ) : nullptr; } METHOD() = delete; }; #if defined( _MSC_VER ) #pragma warning( pop ) #endif class PROPERTY_BASE { private: ///< Used to generate unique IDs. Must come up front so it's initialized before ctor. public: PROPERTY_BASE( const wxString& aName, PROPERTY_DISPLAY aDisplay = PT_DEFAULT, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType = ORIGIN_TRANSFORMS::NOT_A_COORD ) : m_name( aName ), m_display( aDisplay ), m_coordType( aCoordType ), m_hideFromPropertiesManager( false ), m_hideFromRulesEditor( false ), m_hideFromLibraryEditors( false ), m_availFunc( [](INSPECTABLE*)->bool { return true; } ), m_writeableFunc( [](INSPECTABLE*)->bool { return true; } ), m_validator( NullValidator ) { } virtual ~PROPERTY_BASE() { } const wxString& Name() const { return m_name; } /** * Return a limited set of possible values (e.g. enum). Check with HasChoices() if a particular * PROPERTY provides such set. */ virtual const wxPGChoices& Choices() const { static wxPGChoices empty; return empty; } /** * Set the possible values for for the property. */ virtual void SetChoices( const wxPGChoices& aChoices ) { wxFAIL; // only possible for PROPERTY_ENUM } /** * Return true if this PROPERTY has a limited set of possible values. * @see PROPERTY_BASE::Choices() */ virtual bool HasChoices() const { return false; } /** * Return true if aObject offers this PROPERTY. */ bool Available( INSPECTABLE* aObject ) const { return m_availFunc( aObject ); } /** * Set a callback function to determine whether an object provides this property. */ PROPERTY_BASE& SetAvailableFunc( std::function aFunc ) { m_availFunc = aFunc; return *this; } virtual bool Writeable( INSPECTABLE* aObject ) const { return m_writeableFunc( aObject ); } PROPERTY_BASE& SetWriteableFunc( std::function aFunc ) { m_writeableFunc = aFunc; return *this; } /** * Return type-id of the Owner class. */ virtual size_t OwnerHash() const = 0; /** * Return type-id of the Base class. */ virtual size_t BaseHash() const = 0; /** * Return type-id of the property type. */ virtual size_t TypeHash() const = 0; PROPERTY_DISPLAY Display() const { return m_display; } PROPERTY_BASE& SetDisplay( PROPERTY_DISPLAY aDisplay ) { m_display = aDisplay; return *this; } ORIGIN_TRANSFORMS::COORD_TYPES_T CoordType() const { return m_coordType; } PROPERTY_BASE& SetCoordType( ORIGIN_TRANSFORMS::COORD_TYPES_T aType ) { m_coordType = aType; return *this; } bool IsHiddenFromPropertiesManager() const { return m_hideFromPropertiesManager; } PROPERTY_BASE& SetIsHiddenFromPropertiesManager( bool aHide = true ) { m_hideFromPropertiesManager = aHide; return *this; } bool IsHiddenFromRulesEditor() const { return m_hideFromRulesEditor; } PROPERTY_BASE& SetIsHiddenFromRulesEditor( bool aHide = true ) { m_hideFromRulesEditor = aHide; return *this; } bool IsHiddenFromLibraryEditors() const { return m_hideFromLibraryEditors; } PROPERTY_BASE& SetIsHiddenFromLibraryEditors( bool aIsHidden = true ) { m_hideFromLibraryEditors = aIsHidden; return *this; } wxString Group() const { return m_group; } PROPERTY_BASE& SetGroup( const wxString& aGroup ) { m_group = aGroup; return *this; } PROPERTY_BASE& SetValidator( PROPERTY_VALIDATOR_FN&& aValidator ) { m_validator = aValidator; return *this; } VALIDATOR_RESULT Validate( const wxAny&& aValue, EDA_ITEM* aItem ) { return m_validator( std::move( aValue ), aItem ); } static VALIDATOR_RESULT NullValidator( const wxAny&& aValue, EDA_ITEM* aItem ) { return std::nullopt; } protected: template void set( void* aObject, T aValue ) { wxAny a = aValue; // wxVariant will be type "long" even if the property is supposed to be // unsigned. Let's trust that we're coming from the property grid where // we used a UInt editor. if( std::is_same::value ) { wxVariant var = static_cast( aValue ); wxAny pv = getter( aObject ); if( pv.CheckType() ) { a = static_cast( var.GetLong() ); } else if( pv.CheckType() ) { EDA_ANGLE_VARIANT_DATA* ad = static_cast( var.GetData() ); a = ad->Angle(); } else if( pv.CheckType() ) { COLOR4D_VARIANT_DATA* cd = static_cast( var.GetData() ); a = cd->Color(); } } setter( aObject, a ); } template T get( const void* aObject ) const { wxAny a = getter( aObject ); // We don't currently have a bool type, so change it to a numeric if( a.CheckType() ) a = a.RawAs() ? 1 : 0; if ( !( std::is_enum::value && a.CheckType() ) && !a.CheckType() ) throw std::invalid_argument( "Invalid requested type" ); return wxANY_AS( a, T ); } private: virtual void setter( void* aObject, wxAny& aValue ) = 0; virtual wxAny getter( const void* aObject ) const = 0; private: /** * Permanent identifier for this property. Property names are an API contract; changing them * after release will impact the Custom DRC Rules system as well as the automatic API binding * system. Never rename properties; instead deprecate them and hide them from the GUI. */ const wxString m_name; /// The display style controls how properties are edited in the properties manager GUI PROPERTY_DISPLAY m_display; /// The coordinate type controls how distances are mapped to the user coordinate system ORIGIN_TRANSFORMS::COORD_TYPES_T m_coordType; /// Some properties should not be shown in the Properties Manager GUI bool m_hideFromPropertiesManager; /// Some properties should not be shown in the Custom Rules editor autocomplete bool m_hideFromRulesEditor; /// This property should only be shown in the design editor, not the library editor bool m_hideFromLibraryEditors; /// Optional group identifier wxString m_group; std::function m_availFunc; ///< Eval to determine if prop is available std::function m_writeableFunc; ///< Eval to determine if prop is read-only PROPERTY_VALIDATOR_FN m_validator; friend class INSPECTABLE; }; template class PROPERTY : public PROPERTY_BASE { public: using BASE_TYPE = typename std::decay::type; template PROPERTY( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )(), PROPERTY_DISPLAY aDisplay = PT_DEFAULT, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType = ORIGIN_TRANSFORMS::NOT_A_COORD ) : PROPERTY( aName, METHOD::Wrap( aSetter ), METHOD::Wrap( aGetter ), aDisplay, aCoordType ) { } template PROPERTY( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )() const, PROPERTY_DISPLAY aDisplay = PT_DEFAULT, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType = ORIGIN_TRANSFORMS::NOT_A_COORD ) : PROPERTY( aName, METHOD::Wrap( aSetter ), METHOD::Wrap( aGetter ), aDisplay, aCoordType ) { } size_t OwnerHash() const override { return m_ownerHash; } size_t BaseHash() const override { return m_baseHash; } size_t TypeHash() const override { return m_typeHash; } bool Writeable( INSPECTABLE* aObject ) const override { return m_setter && PROPERTY_BASE::Writeable( aObject ); } protected: PROPERTY( const wxString& aName, SETTER_BASE* s, GETTER_BASE* g, PROPERTY_DISPLAY aDisplay, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType ) : PROPERTY_BASE( aName, aDisplay, aCoordType ), m_setter( s ), m_getter( g ), m_ownerHash( TYPE_HASH( Owner ) ), m_baseHash( TYPE_HASH( Base ) ), m_typeHash( TYPE_HASH( BASE_TYPE ) ) { } virtual ~PROPERTY() {} virtual void setter( void* obj, wxAny& v ) override { wxCHECK( m_setter, /*void*/ ); if( !v.CheckType() ) throw std::invalid_argument( "Invalid type requested" ); Owner* o = reinterpret_cast( obj ); BASE_TYPE value = wxANY_AS(v, BASE_TYPE); (*m_setter)( o, value ); } virtual wxAny getter( const void* obj ) const override { const Owner* o = reinterpret_cast( obj ); wxAny res = (*m_getter)( o ); return res; } ///< Set method std::unique_ptr> m_setter; ///< Get method std::unique_ptr> m_getter; ///< Owner class type-id const size_t m_ownerHash; ///< Base class type-id const size_t m_baseHash; ///< Property value type-id const size_t m_typeHash; }; template class PROPERTY_ENUM : public PROPERTY { public: template PROPERTY_ENUM( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )(), PROPERTY_DISPLAY aDisplay = PT_DEFAULT ) : PROPERTY( aName, METHOD::Wrap( aSetter ), METHOD::Wrap( aGetter ), aDisplay ) { if ( std::is_enum::value ) { m_choices = ENUM_MAP::Instance().Choices(); wxASSERT_MSG( m_choices.GetCount() > 0, wxT( "No enum choices defined" ) ); } } template PROPERTY_ENUM( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )() const, PROPERTY_DISPLAY aDisplay = PT_DEFAULT, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType = ORIGIN_TRANSFORMS::NOT_A_COORD ) : PROPERTY( aName, METHOD::Wrap( aSetter ), METHOD::Wrap( aGetter ), aDisplay, aCoordType ) { if ( std::is_enum::value ) { m_choices = ENUM_MAP::Instance().Choices(); wxASSERT_MSG( m_choices.GetCount() > 0, wxT( "No enum choices defined" ) ); } } void setter( void* obj, wxAny& v ) override { wxCHECK( ( PROPERTY::m_setter ), /*void*/ ); Owner* o = reinterpret_cast( obj ); if( v.CheckType() ) { T value = wxANY_AS(v, T); (*PROPERTY::m_setter)( o, value ); } else if (v.CheckType() ) { int value = wxANY_AS(v, int); (*PROPERTY::m_setter)( o, static_cast( value ) ); } else { throw std::invalid_argument( "Invalid type requested" ); } } wxAny getter( const void* obj ) const override { const Owner* o = reinterpret_cast( obj ); wxAny res = static_cast( (*PROPERTY::m_getter)( o ) ); return res; } const wxPGChoices& Choices() const override { return m_choices.GetCount() > 0 ? m_choices : ENUM_MAP::Instance().Choices(); } void SetChoices( const wxPGChoices& aChoices ) override { m_choices = aChoices; } bool HasChoices() const override { return Choices().GetCount() > 0; } protected: wxPGChoices m_choices; }; class TYPE_CAST_BASE { public: virtual ~TYPE_CAST_BASE() {} virtual void* operator()( void* aPointer ) const = 0; virtual const void* operator()( const void* aPointer ) const = 0; virtual size_t BaseHash() const = 0; virtual size_t DerivedHash() const = 0; }; template class TYPE_CAST : public TYPE_CAST_BASE { public: TYPE_CAST() { } void* operator()( void* aPointer ) const override { Base* base = reinterpret_cast( aPointer ); return static_cast( base ); } const void* operator()( const void* aPointer ) const override { const Base* base = reinterpret_cast( aPointer ); return static_cast( base ); } size_t BaseHash() const override { return TYPE_HASH( Base ); } size_t DerivedHash() const override { return TYPE_HASH( Derived ); } }; template class ENUM_MAP { public: static ENUM_MAP& Instance() { static ENUM_MAP inst; return inst; } ENUM_MAP& Map( T aValue, const wxString& aName ) { m_choices.Add( aName, static_cast( aValue ) ); m_reverseMap[ aName ] = aValue; return *this; } ENUM_MAP& Undefined( T aValue ) { m_undefined = aValue; return *this; } const wxString& ToString( T value ) const { static const wxString s_undef = "UNDEFINED"; int idx = m_choices.Index( static_cast( value ) ); if( idx >= 0 && idx < (int) m_choices.GetCount() ) return m_choices.GetLabel( static_cast( idx ) ); else return s_undef; } bool IsValueDefined( T value ) const { int idx = m_choices.Index( static_cast( value ) ); if( idx >= 0 && idx < (int) m_choices.GetCount() ) return true; return false; } T ToEnum( const wxString value ) { if( m_reverseMap.count( value ) ) return m_reverseMap[ value ]; else return m_undefined; } wxPGChoices& Choices() { return m_choices; } private: wxPGChoices m_choices; std::unordered_map m_reverseMap; T m_undefined; // Returned if the string is not recognized ENUM_MAP() { } }; // Helper macros to handle enum types #define DECLARE_ENUM_TO_WXANY( type ) \ template <> \ class wxAnyValueTypeImpl : public wxAnyValueTypeImplBase \ { \ WX_DECLARE_ANY_VALUE_TYPE( wxAnyValueTypeImpl ) \ public: \ wxAnyValueTypeImpl() : wxAnyValueTypeImplBase() {} \ virtual ~wxAnyValueTypeImpl() {} \ virtual bool ConvertValue( const wxAnyValueBuffer& src, wxAnyValueType* dstType, \ wxAnyValueBuffer& dst ) const override \ { \ type value = GetValue( src ); \ ENUM_MAP& conv = ENUM_MAP::Instance(); \ if( ! conv.IsValueDefined( value ) ) \ { \ return false; \ } \ if( dstType->CheckType() ) \ { \ wxAnyValueTypeImpl::SetValue( conv.ToString( value ), dst ); \ return true; \ } \ if( dstType->CheckType() ) \ { \ wxAnyValueTypeImpl::SetValue( static_cast( value ), dst ); \ return true; \ } \ else \ { \ return false; \ } \ } \ }; #define IMPLEMENT_ENUM_TO_WXANY( type ) WX_IMPLEMENT_ANY_VALUE_TYPE( wxAnyValueTypeImpl ) #define ENUM_TO_WXANY( type ) \ DECLARE_ENUM_TO_WXANY( type ) \ IMPLEMENT_ENUM_TO_WXANY( type ) ///< Macro to define read-only fields (no setter method available) #define NO_SETTER( owner, type ) ( ( void ( owner::* )( type ) ) nullptr ) /* #define DECLARE_PROPERTY(owner,type,name,getter,setter) \ namespace anonymous {\ static PROPERTY prop##_owner##_name_( "##name#", setter, getter );\ }; */ #endif /* PROPERTY_H */