/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 CERN * @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 class wxPGProperty; class INSPECTABLE; class PROPERTY_BASE; template class ENUM_MAP; ///> Common property types enum PROPERTY_DISPLAY { DEFAULT, ///< Default property for a given type DISTANCE, ///< Display value expressed in distance units (mm/inch) DEGREE, ///< Display value expressed in degrees DECIDEGREE ///< Convert decidegrees to degrees for display }; ///> 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()( Owner* aOwner ) const = 0; }; template class GETTER : public GETTER_BASE { public: GETTER( FuncType aFunc ) : m_func( aFunc ) { } T operator()( 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 ) { } void operator()( Owner* aOwner, T aValue ) override { wxCHECK( m_func, /*void*/ ); ( aOwner->*m_func )( aValue ); } private: FuncType m_func; }; template class METHOD { public: constexpr 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; }; class PROPERTY_BASE { private: ///> Used to generate unique IDs size_t nextId = 0; public: PROPERTY_BASE( const wxString& aName, PROPERTY_DISPLAY aDisplay = DEFAULT ) : m_id( nextId ), m_name( aName ), m_display( aDisplay ), m_availFunc( [](INSPECTABLE*)->bool { return true; } ) { ++nextId; } virtual ~PROPERTY_BASE() { } const wxString& Name() const { return m_name; } /** * Returns 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; } /** * Sets the possible values for for the property. */ virtual void SetChoices( const wxPGChoices& aChoices ) { wxFAIL; // only possible for PROPERTY_ENUM } /** * Returns true if this PROPERTY has a limited set of possible values. * @see PROPERTY_BASE::Choices() */ virtual bool HasChoices() const { return false; } /** * Returns true if aObject offers this PROPERTY. */ bool Available( INSPECTABLE* aObject ) const { return m_availFunc( aObject ); } /** * Sets a callback function to determine whether an object provides this property. */ void SetAvailableFunc( std::function aFunc ) { m_availFunc = aFunc; } /** * Returns type-id of the Owner class. */ virtual size_t OwnerHash() const = 0; /** * Returns type-id of the Base class. */ virtual size_t BaseHash() const = 0; /** * Returns type-id of the property type. */ virtual size_t TypeHash() const = 0; /** * Returns unique ID of the property. */ size_t Id() const { return m_id; } virtual bool IsReadOnly() const = 0; PROPERTY_DISPLAY GetDisplay() const { return m_display; } protected: template void set( void* aObject, T aValue ) { wxAny a = aValue; setter( aObject, a ); } template T get( void* aObject ) { wxAny a = getter( aObject ); if ( !( std::is_enum::value && a.CheckType() ) && !a.CheckType() ) throw std::invalid_argument("Invalid requested type"); return wxANY_AS(a, T); } virtual void setter( void* aObject, wxAny& aValue ) = 0; virtual wxAny getter( void* aObject ) const = 0; private: const size_t m_id; const wxString m_name; const PROPERTY_DISPLAY m_display; ///> Condition that determines whether the property is available std::function m_availFunc; friend class INSPECTABLE; }; template class PROPERTY : public PROPERTY_BASE { public: typedef typename std::decay::type BASE_TYPE; typedef void (Base::*SETTER)( T ); template PROPERTY( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )(), PROPERTY_DISPLAY aDisplay = DEFAULT ) : PROPERTY( aName, METHOD::Wrap( aSetter ), METHOD::Wrap( aGetter ), aDisplay ) { } template PROPERTY( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )() const, PROPERTY_DISPLAY aDisplay = DEFAULT ) : PROPERTY( aName, METHOD::Wrap( aSetter ), METHOD::Wrap( aGetter ), aDisplay ) { } 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 IsReadOnly() const override { return !m_setter; } protected: PROPERTY( const wxString& aName, SETTER_BASE* s, GETTER_BASE* g, PROPERTY_DISPLAY aDisplay ) : PROPERTY_BASE( aName, aDisplay ), 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( !IsReadOnly(), /*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( void* obj ) const override { 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 = PROPERTY_DISPLAY::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, "No enum choices defined" ); } } template PROPERTY_ENUM( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )() const, PROPERTY_DISPLAY aDisplay = PROPERTY_DISPLAY::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, "No enum choices defined" ); } } virtual void setter( void* obj, wxAny& v ) override { wxCHECK( !( PROPERTY::IsReadOnly() ), /*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" ); } } virtual wxAny getter( void* obj ) const override { Owner* o = reinterpret_cast( obj ); wxAny res = static_cast( (*PROPERTY::m_getter)( o ) ); return res; } const wxPGChoices& Choices() const override { return m_choices; } void SetChoices( const wxPGChoices& aChoices ) override { m_choices = aChoices; } bool HasChoices() const override { return m_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 ) { wxASSERT_MSG( m_choices.Index( aName ) == wxNOT_FOUND, "Redefined string for a value in ENUM_MAP" ); 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; } const 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( 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 */