|
|
/*
* This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 CERN * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * @author Maciej Suminski <maciej.suminski@cern.ch> * * 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 <http://www.gnu.org/licenses/>.
*/
#ifndef PROPERTY_H
#define PROPERTY_H
#include <wx/any.h>
#include <wx/string.h>
#include <wx/bitmap.h>
#include <wx/propgrid/property.h>
#include <functional>
#include <map>
#include <memory>
#include <typeindex>
#include <type_traits>
class wxPGProperty;class INSPECTABLE;class PROPERTY_BASE;
template<typename T>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<x>::type ).hash_code()
template<typename Owner, typename T>class GETTER_BASE{public: virtual ~GETTER_BASE() {}
virtual T operator()( Owner* aOwner ) const = 0;};
template<typename Owner, typename T, typename FuncType>class GETTER : public GETTER_BASE<Owner, T>{public: GETTER( FuncType aFunc ) : m_func( aFunc ) { }
T operator()( Owner* aOwner ) const override { return ( aOwner->*m_func )(); }
private: FuncType m_func;};
template<typename Owner, typename T>class SETTER_BASE{public: virtual ~SETTER_BASE() {}
virtual void operator()( Owner* aOwner, T aValue ) = 0;};
template<typename Owner, typename T, typename FuncType>class SETTER : public SETTER_BASE<Owner, T>{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<typename Owner, typename T, typename Base = Owner>class METHOD{public: constexpr static GETTER_BASE<Owner, T>* Wrap( T (Base::*aFunc)() ) { return new GETTER<Owner, T, T (Base::*)()>( aFunc ); }
constexpr static GETTER_BASE<Owner, T>* Wrap( const T (Base::*aFunc)() ) { return new GETTER<Owner, T, T (Base::*)()>( aFunc ); }
constexpr static GETTER_BASE<Owner, T>* Wrap( const T& (Base::*aFunc)() ) { return new GETTER<Owner, T, const T& (Base::*)()>( aFunc ); }
constexpr static GETTER_BASE<Owner, T>* Wrap( T (Base::*aFunc)() const ) { return new GETTER<Owner, T, T (Base::*)() const>( aFunc ); }
constexpr static GETTER_BASE<Owner, T>* Wrap( const T (Base::*aFunc)() const ) { return new GETTER<Owner, T, const T (Base::*)() const>( aFunc ); }
constexpr static GETTER_BASE<Owner, T>* Wrap( const T& (Base::*aFunc)() const ) { return new GETTER<Owner, T, const T& (Base::*)() const>( aFunc ); }
constexpr static SETTER_BASE<Owner, T>* Wrap( void (Base::*aFunc)( T ) ) { return aFunc ? new SETTER<Owner, T, void (Base::*)( T )>( aFunc ) : nullptr; }
constexpr static SETTER_BASE<Owner, T>* Wrap( void (Base::*aFunc)( T& ) ) { return aFunc ? new SETTER<Owner, T, void (Base::*)( T& )>( aFunc ) : nullptr; }
constexpr static SETTER_BASE<Owner, T>* Wrap( void (Base::*aFunc)( const T& ) ) { return aFunc ? new SETTER<Owner, T, void (Base::*)( const T& )>( 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<bool(INSPECTABLE*)> 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<typename T> void set( void* aObject, T aValue ) { wxAny a = aValue; setter( aObject, a ); }
template<typename T> T get( void* aObject ) { wxAny a = getter( aObject );
if ( !( std::is_enum<T>::value && a.CheckType<int>() ) && !a.CheckType<T>() ) 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<bool(INSPECTABLE*)> m_availFunc;
friend class INSPECTABLE;};
template<typename Owner, typename T, typename Base = Owner>class PROPERTY : public PROPERTY_BASE{public: typedef typename std::decay<T>::type BASE_TYPE; typedef void (Base::*SETTER)( T );
template<typename SetType, typename GetType> PROPERTY( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )(), PROPERTY_DISPLAY aDisplay = DEFAULT ) : PROPERTY( aName, METHOD<Owner, T, Base>::Wrap( aSetter ), METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay ) { }
template<typename SetType, typename GetType> PROPERTY( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )() const, PROPERTY_DISPLAY aDisplay = DEFAULT ) : PROPERTY( aName, METHOD<Owner, T, Base>::Wrap( aSetter ), METHOD<Owner, T, Base>::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<Owner, T>* s, GETTER_BASE<Owner, T>* 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<T>() ) throw std::invalid_argument( "Invalid type requested" );
Owner* o = reinterpret_cast<Owner*>( obj ); BASE_TYPE value = wxANY_AS(v, BASE_TYPE); (*m_setter)( o, value ); }
virtual wxAny getter( void* obj ) const override { Owner* o = reinterpret_cast<Owner*>( obj ); wxAny res = (*m_getter)( o ); return res; }
///> Set method
std::unique_ptr<SETTER_BASE<Owner, T>> m_setter;
///> Get method
std::unique_ptr<GETTER_BASE<Owner, T>> 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<typename Owner, typename T, typename Base = Owner>class PROPERTY_ENUM : public PROPERTY<Owner, T, Base>{public: template<typename SetType, typename GetType> PROPERTY_ENUM( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )(), PROPERTY_DISPLAY aDisplay = PROPERTY_DISPLAY::DEFAULT ) : PROPERTY<Owner, T, Base>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ), METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay ) { if ( std::is_enum<T>::value ) { m_choices = ENUM_MAP<T>::Instance().Choices(); wxASSERT_MSG( m_choices.GetCount() > 0, "No enum choices defined" ); } }
template<typename SetType, typename GetType> PROPERTY_ENUM( const wxString& aName, void ( Base::*aSetter )( SetType ), GetType( Base::*aGetter )() const, PROPERTY_DISPLAY aDisplay = PROPERTY_DISPLAY::DEFAULT ) : PROPERTY<Owner, T, Base>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ), METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay ) { if ( std::is_enum<T>::value ) { m_choices = ENUM_MAP<T>::Instance().Choices(); wxASSERT_MSG( m_choices.GetCount() > 0, "No enum choices defined" ); } }
virtual void setter( void* obj, wxAny& v ) override { wxCHECK( !( PROPERTY<Owner, T, Base>::IsReadOnly() ), /*void*/ ); Owner* o = reinterpret_cast<Owner*>( obj );
if( v.CheckType<T>() ) { T value = wxANY_AS(v, T); (*PROPERTY<Owner, T, Base>::m_setter)( o, value ); } else if (v.CheckType<int>() ) { int value = wxANY_AS(v, int); (*PROPERTY<Owner, T, Base>::m_setter)( o, static_cast<T>( value ) ); } else { throw std::invalid_argument( "Invalid type requested" ); } }
virtual wxAny getter( void* obj ) const override { Owner* o = reinterpret_cast<Owner*>( obj ); wxAny res = static_cast<T>( (*PROPERTY<Owner, T, Base>::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<typename Base, typename Derived>class TYPE_CAST : public TYPE_CAST_BASE{public: TYPE_CAST() { }
void* operator()( void* aPointer ) const override { Base* base = reinterpret_cast<Base*>( aPointer ); return static_cast<Derived*>( base ); }
const void* operator()( const void* aPointer ) const override { const Base* base = reinterpret_cast<const Base*>( aPointer ); return static_cast<const Derived*>( base ); }
size_t BaseHash() const override { return TYPE_HASH( Base ); }
size_t DerivedHash() const override { return TYPE_HASH( Derived ); }};
template<typename T>class ENUM_MAP{public: static ENUM_MAP<T>& Instance() { static ENUM_MAP<T> inst; return inst; }
ENUM_MAP& Map( T aValue, const wxString& aName ) { m_choices.Add( aName, static_cast<int>( 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<int>( value ) );
if( idx >= 0 && idx < (int) m_choices.GetCount() ) return m_choices.GetLabel( static_cast<int>( 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<wxString, T> m_reverseMap; T m_undefined; // Returned if the string is not recognized
ENUM_MAP<T>() { }};
// Helper macros to handle enum types
#define DECLARE_ENUM_TO_WXANY(type)\
template<>\ class wxAnyValueTypeImpl<type> : public wxAnyValueTypeImplBase<type>\ {\ WX_DECLARE_ANY_VALUE_TYPE(wxAnyValueTypeImpl<type>)\ public:\ wxAnyValueTypeImpl() : wxAnyValueTypeImplBase<type>() {}\ virtual ~wxAnyValueTypeImpl() {}\ virtual bool ConvertValue( const wxAnyValueBuffer& src,\ wxAnyValueType* dstType, wxAnyValueBuffer& dst ) const override\ {\ type value = GetValue(src);\ ENUM_MAP<type>& conv = ENUM_MAP<type>::Instance();\ if( dstType->CheckType<wxString>() )\ {\ wxAnyValueTypeImpl<wxString>::SetValue( conv.ToString( value ), dst );\ return true;\ }\ if( dstType->CheckType<int>() )\ {\ wxAnyValueTypeImpl<int>::SetValue( static_cast<int>(value), dst );\ return true;\ }\ else\ {\ return false;\ }\ }\ };
#define IMPLEMENT_ENUM_TO_WXANY(type)\
WX_IMPLEMENT_ANY_VALUE_TYPE(wxAnyValueTypeImpl<type>)
#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<owner,type> prop##_owner##_name_( "##name#", setter, getter );\};*/#endif /* PROPERTY_H */
|