7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2024-11-21 22:25:03 +00:00
kicad/include/properties/property.h
2024-04-12 23:32:18 -04:00

779 lines
24 KiB
C++

/*
* 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 <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 <core/wx_stl_compat.h>
#include <origin_transforms.h>
#include <properties/color4d_variant.h>
#include <properties/eda_angle_variant.h>
#include <properties/property_validator.h>
#include <wx/any.h>
#include <wx/string.h>
#include <wx/bitmap.h>
#include <wx/font.h> // required for propgrid
#include <wx/validate.h> // required for propgrid
#include <wx/propgrid/property.h>
#ifdef DEBUG
#include <wx/wxcrt.h>
#endif
#include <functional>
#include <unordered_map>
#include <memory>
#include <typeindex>
#include <type_traits>
#include "std_optional_variants.h"
class wxPGProperty;
class INSPECTABLE;
class PROPERTY_BASE;
template<typename T>
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_AREA, ///< Area expressed in distance units-squared (mm/inch)
PT_COORD, ///< Coordinate expressed in distance units (mm/inch)
PT_DEGREE, ///< Angle expressed in degrees
PT_DECIDEGREE, ///< Angle expressed in decidegrees
PT_RATIO
};
///< 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()( const 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 )
{
wxCHECK( m_func, /*void*/ );
}
T operator()( const 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 )
{
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<typename Owner, typename T, typename Base = Owner>
class METHOD
{
public:
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, 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;
};
#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_hideFromLibraryEditors( false ),
m_hideFromDesignEditors( false ),
m_hideFromRulesEditor( 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<bool(INSPECTABLE*)> aFunc )
{
m_availFunc = std::move( aFunc );
return *this;
}
virtual bool Writeable( INSPECTABLE* aObject ) const
{
return m_writeableFunc( aObject );
}
PROPERTY_BASE& SetWriteableFunc( std::function<bool(INSPECTABLE*)> aFunc )
{
m_writeableFunc = std::move( 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;
}
bool IsHiddenFromDesignEditors() const { return m_hideFromDesignEditors; }
PROPERTY_BASE& SetIsHiddenFromDesignEditors( bool aIsHidden = true )
{
m_hideFromDesignEditors = 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<typename T>
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<T, wxVariant>::value )
{
wxVariant var = static_cast<wxVariant>( aValue );
wxAny pv = getter( aObject );
if( pv.CheckType<unsigned>() )
{
a = static_cast<unsigned>( var.GetLong() );
}
else if( pv.CheckType<std::optional<int>>() )
{
auto* data = static_cast<STD_OPTIONAL_INT_VARIANT_DATA*>( var.GetData() );
a = data->Value();
}
else if( pv.CheckType<std::optional<double>>() )
{
auto* data = static_cast<STD_OPTIONAL_DOUBLE_VARIANT_DATA*>( var.GetData() );
a = data->Value();
}
else if( pv.CheckType<EDA_ANGLE>() )
{
EDA_ANGLE_VARIANT_DATA* ad = static_cast<EDA_ANGLE_VARIANT_DATA*>( var.GetData() );
a = ad->Angle();
}
else if( pv.CheckType<KIGFX::COLOR4D>() )
{
COLOR4D_VARIANT_DATA* cd = static_cast<COLOR4D_VARIANT_DATA*>( var.GetData() );
a = cd->Color();
}
}
setter( aObject, a );
}
template<typename T>
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<bool>() )
a = a.RawAs<bool>() ? 1 : 0;
if ( !( std::is_enum<T>::value && a.CheckType<int>() ) && !a.CheckType<T>() )
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;
bool m_hideFromPropertiesManager; // Do not show in Properties Manager
bool m_hideFromLibraryEditors; // Do not show in Properties Manager of symbol or
// footprint editors
bool m_hideFromDesignEditors; // Do not show in Properties Manager of schematic or
// board editors
bool m_hideFromRulesEditor; // Do not show in Custom Rules editor autocomplete
/// Optional group identifier
wxString m_group;
std::function<bool(INSPECTABLE*)> m_availFunc; ///< Eval to determine if prop is available
std::function<bool(INSPECTABLE*)> m_writeableFunc; ///< Eval to determine if prop is read-only
PROPERTY_VALIDATOR_FN m_validator;
friend class INSPECTABLE;
};
template<typename Owner, typename T, typename Base = Owner>
class PROPERTY : public PROPERTY_BASE
{
public:
using BASE_TYPE = typename std::decay<T>::type;
template<typename SetType, typename GetType>
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<Owner, T, Base>::Wrap( aSetter ),
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay, aCoordType )
{
}
template<typename SetType, typename GetType>
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<Owner, T, Base>::Wrap( aSetter ),
METHOD<Owner, T, Base>::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<Owner, T>* s, GETTER_BASE<Owner, T>* 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<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( const void* obj ) const override
{
const Owner* o = reinterpret_cast<const 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 = PT_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, wxT( "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 = PT_DEFAULT,
ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType = ORIGIN_TRANSFORMS::NOT_A_COORD )
: PROPERTY<Owner, T, Base>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ),
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay, aCoordType )
{
if ( std::is_enum<T>::value )
{
m_choices = ENUM_MAP<T>::Instance().Choices();
wxASSERT_MSG( m_choices.GetCount() > 0, wxT( "No enum choices defined" ) );
}
}
void setter( void* obj, wxAny& v ) override
{
wxCHECK( ( PROPERTY<Owner, T, Base>::m_setter ), /*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" );
}
}
wxAny getter( const void* obj ) const override
{
const Owner* o = reinterpret_cast<const Owner*>( obj );
wxAny res = static_cast<T>( (*PROPERTY<Owner, T, Base>::m_getter)( o ) );
return res;
}
const wxPGChoices& Choices() const override
{
return m_choices.GetCount() > 0 ? m_choices : ENUM_MAP<T>::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<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;
}
bool IsValueDefined( T value ) const
{
int idx = m_choices.Index( static_cast<int>( 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<wxString, T> 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<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( ! conv.IsValueDefined( value ) ) \
{ \
return false; \
} \
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 */