Properties (introspection)
Introduces classes: - INSPECTED: base class for types taking advantage of generic properties system. - PROPERTY*: meta-data storing information about properties - PROPERTY_MANAGER: singleton class to get properties data
This commit is contained in:
parent
89698a727b
commit
cbd5004fd4
|
@ -327,6 +327,7 @@ set( COMMON_SRCS
|
||||||
printout.cpp
|
printout.cpp
|
||||||
project.cpp
|
project.cpp
|
||||||
properties.cpp
|
properties.cpp
|
||||||
|
property_mgr.cpp
|
||||||
ptree.cpp
|
ptree.cpp
|
||||||
rc_item.cpp
|
rc_item.cpp
|
||||||
refdes_utils.cpp
|
refdes_utils.cpp
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "property_mgr.h"
|
||||||
|
#include "property.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
static wxString EMPTY_STRING( wxEmptyString );
|
||||||
|
|
||||||
|
|
||||||
|
void PROPERTY_MANAGER::RegisterType( TYPE_ID aType, const wxString& aName )
|
||||||
|
{
|
||||||
|
wxASSERT( m_classNames.count( aType ) == 0 );
|
||||||
|
m_classNames.emplace( aType, aName );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const wxString& PROPERTY_MANAGER::ResolveType( TYPE_ID aType ) const
|
||||||
|
{
|
||||||
|
auto it = m_classNames.find( aType );
|
||||||
|
return it != m_classNames.end() ? it->second : EMPTY_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aProperty ) const
|
||||||
|
{
|
||||||
|
wxASSERT_MSG( !m_dirty, "Have not called PROPERTY_MANAGER::Rebuild(), "
|
||||||
|
"property list not up-to-date" );
|
||||||
|
auto it = m_classes.find( aType );
|
||||||
|
|
||||||
|
if( it == m_classes.end() )
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const CLASS_DESC& classDesc = it->second;
|
||||||
|
|
||||||
|
for( const auto& property : classDesc.m_allProperties )
|
||||||
|
{
|
||||||
|
if( property->Name() == aProperty )
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const PROPERTY_LIST& PROPERTY_MANAGER::GetProperties( TYPE_ID aType ) const
|
||||||
|
{
|
||||||
|
wxASSERT_MSG( !m_dirty, "Have not called PROPERTY_MANAGER::Rebuild(), "
|
||||||
|
"property list not up-to-date" );
|
||||||
|
|
||||||
|
static const PROPERTY_LIST empty;
|
||||||
|
auto it = m_classes.find( aType );
|
||||||
|
|
||||||
|
if( it == m_classes.end() )
|
||||||
|
return empty;
|
||||||
|
|
||||||
|
return it->second.m_allProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const void* PROPERTY_MANAGER::TypeCast( const void* aSource, TYPE_ID aBase, TYPE_ID aTarget ) const
|
||||||
|
{
|
||||||
|
if( aBase == aTarget )
|
||||||
|
return aSource;
|
||||||
|
|
||||||
|
auto classDesc = m_classes.find( aBase );
|
||||||
|
|
||||||
|
if( classDesc == m_classes.end() )
|
||||||
|
return aSource;
|
||||||
|
|
||||||
|
auto& converters = classDesc->second.m_typeCasts;
|
||||||
|
auto converter = converters.find( aTarget );
|
||||||
|
|
||||||
|
if( converter == converters.end() ) // explicit type cast not found
|
||||||
|
return IsOfType( aBase, aTarget ) ? aSource : nullptr;
|
||||||
|
|
||||||
|
return (*converter->second)( aSource );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PROPERTY_MANAGER::AddProperty( PROPERTY_BASE* aProperty )
|
||||||
|
{
|
||||||
|
const wxString& name = aProperty->Name();
|
||||||
|
TYPE_ID hash = aProperty->OwnerHash();
|
||||||
|
CLASS_DESC& classDesc = getClass( hash );
|
||||||
|
classDesc.m_ownProperties.emplace( name, aProperty );
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PROPERTY_MANAGER::AddTypeCast( TYPE_CAST_BASE* aCast )
|
||||||
|
{
|
||||||
|
TYPE_ID derivedHash = aCast->DerivedHash();
|
||||||
|
CLASS_DESC& classDesc = getClass( aCast->BaseHash() );
|
||||||
|
auto& typeCasts = classDesc.m_typeCasts;
|
||||||
|
wxASSERT_MSG( typeCasts.count( derivedHash ) == 0, "Such converter already exists" );
|
||||||
|
typeCasts.emplace( derivedHash, aCast );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PROPERTY_MANAGER::InheritsAfter( TYPE_ID aDerived, TYPE_ID aBase )
|
||||||
|
{
|
||||||
|
wxASSERT_MSG( aDerived != aBase, "Class cannot inherit from itself" );
|
||||||
|
|
||||||
|
CLASS_DESC& derived = getClass( aDerived );
|
||||||
|
CLASS_DESC& base = getClass( aBase );
|
||||||
|
derived.m_bases.push_back( base );
|
||||||
|
m_dirty = true;
|
||||||
|
|
||||||
|
wxASSERT_MSG( derived.m_bases.size() == 1 || derived.m_typeCasts.count( aBase ) == 1,
|
||||||
|
"You need to add a TYPE_CAST for classes inheriting from multiple bases" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PROPERTY_MANAGER::IsOfType( TYPE_ID aDerived, TYPE_ID aBase ) const
|
||||||
|
{
|
||||||
|
if( aDerived == aBase )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto derived = m_classes.find( aDerived );
|
||||||
|
wxCHECK( derived != m_classes.end(), false ); // missing class description
|
||||||
|
|
||||||
|
// traverse the hierarchy seeking for the base class
|
||||||
|
for( auto& base : derived->second.m_bases )
|
||||||
|
{
|
||||||
|
if( IsOfType( base.get().m_id, aBase ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PROPERTY_MANAGER::Rebuild()
|
||||||
|
{
|
||||||
|
wxASSERT_MSG( m_dirty, "Excessive Rebuild() call" );
|
||||||
|
|
||||||
|
for( auto& classEntry : m_classes )
|
||||||
|
{
|
||||||
|
classEntry.second.rebuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PROPERTY_MANAGER::CLASS_DESC& PROPERTY_MANAGER::getClass( TYPE_ID aTypeId )
|
||||||
|
{
|
||||||
|
auto it = m_classes.find( aTypeId );
|
||||||
|
|
||||||
|
if( it == m_classes.end() )
|
||||||
|
tie( it, std::ignore ) = m_classes.emplace( std::make_pair( aTypeId, CLASS_DESC( aTypeId ) ) );
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PROPERTY_MANAGER::CLASS_DESC::rebuild()
|
||||||
|
{
|
||||||
|
m_allProperties.clear();
|
||||||
|
collectPropsRecur( m_allProperties );
|
||||||
|
// We need to keep properties sorted to be able to use std::set_* functions
|
||||||
|
sort( m_allProperties.begin(), m_allProperties.end() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PROPERTY_MANAGER::CLASS_DESC::collectPropsRecur( PROPERTY_LIST& aResult ) const
|
||||||
|
{
|
||||||
|
for( const auto& base : m_bases )
|
||||||
|
base.get().collectPropsRecur( aResult );
|
||||||
|
|
||||||
|
for( auto& property : m_ownProperties )
|
||||||
|
aResult.push_back( property.second.get() );
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* 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 INSPECTABLE_H
|
||||||
|
#define INSPECTABLE_H
|
||||||
|
|
||||||
|
#include "property_mgr.h"
|
||||||
|
#include "property.h"
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that other classes need to inherit from, in order to be inspectable.
|
||||||
|
*/
|
||||||
|
class INSPECTABLE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~INSPECTABLE()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Set( PROPERTY_BASE* aProperty, wxAny& aValue )
|
||||||
|
{
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
TYPE_ID thisType = TYPE_HASH( *this );
|
||||||
|
void* object = propMgr.TypeCast( this, thisType, aProperty->OwnerHash() );
|
||||||
|
|
||||||
|
if( object )
|
||||||
|
aProperty->setter( object, aValue );
|
||||||
|
|
||||||
|
return object != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool Set( PROPERTY_BASE* aProperty, T aValue )
|
||||||
|
{
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
TYPE_ID thisType = TYPE_HASH( *this );
|
||||||
|
void* object = propMgr.TypeCast( this, thisType, aProperty->OwnerHash() );
|
||||||
|
|
||||||
|
if( object )
|
||||||
|
aProperty->set<T>( object, aValue );
|
||||||
|
|
||||||
|
return object != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool Set( const wxString& aProperty, T aValue )
|
||||||
|
{
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
TYPE_ID thisType = TYPE_HASH( *this );
|
||||||
|
PROPERTY_BASE* prop = propMgr.GetProperty( thisType, aProperty );
|
||||||
|
void* object = nullptr;
|
||||||
|
|
||||||
|
if( prop )
|
||||||
|
{
|
||||||
|
object = propMgr.TypeCast( this, thisType, prop->OwnerHash() );
|
||||||
|
|
||||||
|
if( object )
|
||||||
|
prop->set<T>( object, aValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
return object != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxAny Get( PROPERTY_BASE* aProperty )
|
||||||
|
{
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
TYPE_ID thisType = TYPE_HASH( *this );
|
||||||
|
void* object = propMgr.TypeCast( this, thisType, aProperty->OwnerHash() );
|
||||||
|
return object ? aProperty->getter( object ) : wxAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T Get( PROPERTY_BASE* aProperty )
|
||||||
|
{
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
TYPE_ID thisType = TYPE_HASH( *this );
|
||||||
|
void* object = propMgr.TypeCast( this, thisType, aProperty->OwnerHash() );
|
||||||
|
return object ? aProperty->get<T>( object ) : wxAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
boost::optional<T> Get( const wxString& aProperty )
|
||||||
|
{
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
TYPE_ID thisType = TYPE_HASH( *this );
|
||||||
|
PROPERTY_BASE* prop = propMgr.GetProperty( thisType, aProperty );
|
||||||
|
boost::optional<T> ret;
|
||||||
|
|
||||||
|
if( prop )
|
||||||
|
{
|
||||||
|
void* object = propMgr.TypeCast( this, thisType, prop->OwnerHash() );
|
||||||
|
|
||||||
|
if( object )
|
||||||
|
ret = prop->get<T>( object );
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* INSPECTABLE_H */
|
|
@ -0,0 +1,557 @@
|
||||||
|
/*
|
||||||
|
* 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/wx.h>
|
||||||
|
#include <wx/any.h>
|
||||||
|
#include <wx/string.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
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PROPERTY_BASE( const wxString& aName, PROPERTY_DISPLAY aDisplay = DEFAULT )
|
||||||
|
: m_name( aName ), m_display( aDisplay ),
|
||||||
|
m_availFunc( [](INSPECTABLE*)->bool { return true; } )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 the type-id of the Owner class.
|
||||||
|
*/
|
||||||
|
virtual size_t OwnerHash() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type-id of the property type.
|
||||||
|
*/
|
||||||
|
virtual size_t TypeHash() const = 0;
|
||||||
|
|
||||||
|
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 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 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_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;
|
||||||
|
|
||||||
|
///> 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>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ),
|
||||||
|
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay ),
|
||||||
|
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>( aName, METHOD<Owner, T, Base>::Wrap( aSetter ),
|
||||||
|
METHOD<Owner, T, Base>::Wrap( aGetter ), aDisplay ),
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
wxASSERT_MSG( m_choices.Index( aName ) == wxNOT_FOUND, "Redefined string for a value in ENUM_MAP" );
|
||||||
|
m_choices.Add( aName, static_cast<int>( aValue ) );
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
m_choices.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxString& ToString( T value ) const
|
||||||
|
{
|
||||||
|
return m_choices.GetLabel( static_cast<int>( value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxPGChoices& Choices() const
|
||||||
|
{
|
||||||
|
return m_choices;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetChoices( const wxPGChoices& aChoices )
|
||||||
|
{
|
||||||
|
m_choices = aChoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxPGChoices m_choices;
|
||||||
|
|
||||||
|
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 */
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
* 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_MGR_H
|
||||||
|
#define PROPERTY_MGR_H
|
||||||
|
|
||||||
|
#include <wx/string.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <common.h>
|
||||||
|
|
||||||
|
class PROPERTY_BASE;
|
||||||
|
class TYPE_CAST_BASE;
|
||||||
|
|
||||||
|
///> Unique type identifier
|
||||||
|
using TYPE_ID = size_t;
|
||||||
|
|
||||||
|
using PROPERTY_LIST = std::vector<PROPERTY_BASE*>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides class metadata. Each class handled by PROPERTY_MANAGER
|
||||||
|
* needs to be described using AddProperty(), AddTypeCast() and InheritsAfter() methods.
|
||||||
|
*
|
||||||
|
* Enum types use a dedicated property type (PROPERTY_ENUM), define its possible values
|
||||||
|
* with ENUM_MAP class, then describe the type using macros:
|
||||||
|
* - DECLARE_ENUM_TO_WXANY (in header files)
|
||||||
|
* - IMPLEMENT_ENUM_TO_WXANY (in source files)
|
||||||
|
* - ENUM_TO_WXANY (*most often used*; combines DECLARE and IMPLEMENT macros,
|
||||||
|
* if there is no need to share the description using header files)
|
||||||
|
*
|
||||||
|
* Once all classes are described, the property list must be build using
|
||||||
|
* Rebuild() method.
|
||||||
|
*/
|
||||||
|
class PROPERTY_MANAGER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static PROPERTY_MANAGER& Instance()
|
||||||
|
{
|
||||||
|
static PROPERTY_MANAGER pm;
|
||||||
|
return pm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates a name with a type.
|
||||||
|
*
|
||||||
|
* Builds a map to provide faster type look-up.
|
||||||
|
*
|
||||||
|
* @param aType is the type identifier (obtained using TYPE_HASH()).
|
||||||
|
* @param aName is the type name.
|
||||||
|
*/
|
||||||
|
void RegisterType( TYPE_ID aType, const wxString& aName );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns name of a type.
|
||||||
|
*
|
||||||
|
* @param aType is the type identifier (obtained using TYPE_HASH()).
|
||||||
|
* @return Name of the type or empty string, if not available.
|
||||||
|
*/
|
||||||
|
const wxString& ResolveType( TYPE_ID aType ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a property for a specific type.
|
||||||
|
*
|
||||||
|
* @param aType is the type identifier (obtained using TYPE_HASH()).
|
||||||
|
* @param aProperty is the property name used during class registration.
|
||||||
|
* @return Requested property or null pointer if requested property does not exist.
|
||||||
|
*/
|
||||||
|
PROPERTY_BASE* GetProperty( TYPE_ID aType, const wxString& aProperty ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all properties for a specific type.
|
||||||
|
*
|
||||||
|
* @param aType is the type identifier (obtained using TYPE_HASH()).
|
||||||
|
* @return Vector storing all properties of the requested type.
|
||||||
|
*/
|
||||||
|
const PROPERTY_LIST& GetProperties( TYPE_ID aType ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Casts a type to another type. Used for correct type-casting of types with
|
||||||
|
* multi-inheritance. Requires registration of an appropriate converter (AddTypeCast).
|
||||||
|
*
|
||||||
|
* @param aSource is a pointer to the casted object.
|
||||||
|
* @param aBase is aSource type identifier (obtained using TYPE_HASH()).
|
||||||
|
* @param aTarget is the desired type identifier (obtained using TYPE_HASH()).
|
||||||
|
* @return Properly casted pointer of aTarget type. *
|
||||||
|
*
|
||||||
|
* @see AddTypeCast
|
||||||
|
*/
|
||||||
|
const void* TypeCast( const void* aSource, TYPE_ID aBase, TYPE_ID aTarget ) const;
|
||||||
|
|
||||||
|
void* TypeCast( void* aSource, TYPE_ID aBase, TYPE_ID aTarget ) const
|
||||||
|
{
|
||||||
|
return const_cast<void*>( TypeCast( (const void*) aSource, aBase, aTarget ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a property.
|
||||||
|
*
|
||||||
|
* @param aProperty is the property to register.
|
||||||
|
*/
|
||||||
|
void AddProperty( PROPERTY_BASE* aProperty );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a type converter. Required prior TypeCast() usage.
|
||||||
|
*
|
||||||
|
* @param aCast is the type converter to register.
|
||||||
|
*/
|
||||||
|
void AddTypeCast( TYPE_CAST_BASE* aCast );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declares an inheritance relationship between types.
|
||||||
|
*
|
||||||
|
* @param aBase is the base type identifier (obtained using TYPE_HASH()).
|
||||||
|
* @param aDerived is the derived type identifier (obtained using TYPE_HASH()).
|
||||||
|
*/
|
||||||
|
void InheritsAfter( TYPE_ID aDerived, TYPE_ID aBase );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if aDerived is inherited from aBase.
|
||||||
|
*/
|
||||||
|
bool IsOfType( TYPE_ID aDerived, TYPE_ID aBase ) const;
|
||||||
|
|
||||||
|
EDA_UNITS GetUnits() const
|
||||||
|
{
|
||||||
|
return m_units;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUnits( EDA_UNITS aUnits )
|
||||||
|
{
|
||||||
|
m_units = aUnits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuilds the list of all registered properties. Needs to be called
|
||||||
|
* once before GetProperty()/GetProperties() are used.
|
||||||
|
*/
|
||||||
|
void Rebuild();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PROPERTY_MANAGER()
|
||||||
|
: m_dirty( false ), m_units( EDA_UNITS::MILLIMETRES )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///> Structure holding type meta-data
|
||||||
|
struct CLASS_DESC
|
||||||
|
{
|
||||||
|
CLASS_DESC( TYPE_ID aId )
|
||||||
|
: m_id( aId )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///> Unique type identifier (obtained using TYPE_HASH)
|
||||||
|
const TYPE_ID m_id;
|
||||||
|
|
||||||
|
///> Types after which this type inherits
|
||||||
|
std::vector<std::reference_wrapper<CLASS_DESC>> m_bases;
|
||||||
|
|
||||||
|
///> Properties unique to this type (i.e. not inherited)
|
||||||
|
std::map<wxString, std::unique_ptr<PROPERTY_BASE>> m_ownProperties;
|
||||||
|
|
||||||
|
///> Type converters available for this type
|
||||||
|
std::map<TYPE_ID, std::unique_ptr<TYPE_CAST_BASE>> m_typeCasts;
|
||||||
|
|
||||||
|
///> All properties (both unique to the type and inherited)
|
||||||
|
std::vector<PROPERTY_BASE*> m_allProperties;
|
||||||
|
|
||||||
|
///> Recreates the list of properties
|
||||||
|
void rebuild();
|
||||||
|
|
||||||
|
///> Traverses the class inheritance hierarchy bottom-to-top, gathering
|
||||||
|
///> all properties available to a type
|
||||||
|
void collectPropsRecur( PROPERTY_LIST& aResult ) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
///> Returns metadata for a specific type
|
||||||
|
CLASS_DESC& getClass( TYPE_ID aTypeId );
|
||||||
|
|
||||||
|
std::unordered_map<TYPE_ID, wxString> m_classNames;
|
||||||
|
|
||||||
|
///> Map of all available types
|
||||||
|
std::unordered_map<TYPE_ID, CLASS_DESC> m_classes;
|
||||||
|
|
||||||
|
/// Flag indicating that the list of properties needs to be rebuild (RebuildProperties())
|
||||||
|
bool m_dirty;
|
||||||
|
|
||||||
|
EDA_UNITS m_units;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///> Helper macro to map type hashes to names
|
||||||
|
#define REGISTER_TYPE(x) PROPERTY_MANAGER::Instance().RegisterType(TYPE_HASH(x), TYPE_NAME(x))
|
||||||
|
|
||||||
|
#endif /* PROPERTY_MGR_H */
|
|
@ -45,6 +45,7 @@ set( common_srcs
|
||||||
test_format_units.cpp
|
test_format_units.cpp
|
||||||
test_lib_table.cpp
|
test_lib_table.cpp
|
||||||
test_kicad_string.cpp
|
test_kicad_string.cpp
|
||||||
|
test_property.cpp
|
||||||
test_refdes_utils.cpp
|
test_refdes_utils.cpp
|
||||||
test_title_block.cpp
|
test_title_block.cpp
|
||||||
test_utf8.cpp
|
test_utf8.cpp
|
||||||
|
|
|
@ -0,0 +1,339 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 CERN
|
||||||
|
* @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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unit_test_utils/unit_test_utils.h>
|
||||||
|
#include <wx/gdicmn.h> // wxPoint
|
||||||
|
|
||||||
|
#include <inspectable.h>
|
||||||
|
#include <property_mgr.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class A : public INSPECTABLE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void setA( int a ) = 0;
|
||||||
|
virtual int getA() const = 0;
|
||||||
|
virtual const int& getA2() const { return m_a; }
|
||||||
|
|
||||||
|
virtual void setPoint( const wxPoint& p ) { m_p = p; }
|
||||||
|
void setPoint2( wxPoint& p ) { m_p = p; }
|
||||||
|
void setPoint3( wxPoint p ) { m_p = p; }
|
||||||
|
void setPoint4( wxPoint p ) { m_p = p; }
|
||||||
|
|
||||||
|
const wxPoint& getPoint() const { return m_p; }
|
||||||
|
wxPoint getPoint2() const { return m_p; }
|
||||||
|
wxPoint getPoint3() { return m_p; }
|
||||||
|
const wxPoint& getPoint4() const { return m_p; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int m_a;
|
||||||
|
wxPoint m_p;
|
||||||
|
};
|
||||||
|
|
||||||
|
class B : public A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void setA( int a ) override { m_a = a; }
|
||||||
|
int getA() const override { return m_a; }
|
||||||
|
|
||||||
|
void setC( int a ) { m_c = a; }
|
||||||
|
int getC() const { return m_c; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_c;
|
||||||
|
};
|
||||||
|
|
||||||
|
class C : public INSPECTABLE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool getBool() const { return m_bool; }
|
||||||
|
void setBool( bool a ) { m_bool = a; }
|
||||||
|
|
||||||
|
int getNew() const { return m_m; }
|
||||||
|
void setNew( int m ) { m_m = m; }
|
||||||
|
|
||||||
|
int m_m;
|
||||||
|
bool m_bool;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum enum_glob { TEST1 = 0, TEST2 = 1, TEST3 = 4 };
|
||||||
|
|
||||||
|
class D : public A, public C
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum enum_class { TESTA = 0, TESTB = 1, TESTC = 4 };
|
||||||
|
|
||||||
|
// note 2x factor
|
||||||
|
virtual void setA( int a ) override { m_aa = 2 * a; }
|
||||||
|
virtual int getA() const override { return m_aa; }
|
||||||
|
|
||||||
|
enum_glob getGlobEnum() const { return m_enum_glob; }
|
||||||
|
void setGlobEnum( enum_glob val ) { m_enum_glob = val; }
|
||||||
|
|
||||||
|
enum_class getClassEnum() const { return m_enum_class; }
|
||||||
|
void setClassEnum( enum_class val ) { m_enum_class = val; }
|
||||||
|
|
||||||
|
void setCond( int a ) { m_cond = a; }
|
||||||
|
int getCond() const { return m_cond; }
|
||||||
|
|
||||||
|
enum_glob m_enum_glob;
|
||||||
|
enum_class m_enum_class;
|
||||||
|
int m_aa;
|
||||||
|
int m_cond;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ENUM_GLOB_DESC
|
||||||
|
{
|
||||||
|
ENUM_GLOB_DESC()
|
||||||
|
{
|
||||||
|
ENUM_MAP<enum_glob>::Instance()
|
||||||
|
.Map( enum_glob::TEST1, "TEST1" )
|
||||||
|
.Map( enum_glob::TEST2, "TEST2" )
|
||||||
|
.Map( enum_glob::TEST3, "TEST3" );
|
||||||
|
}
|
||||||
|
} _ENUM_GLOB_DESC;
|
||||||
|
|
||||||
|
ENUM_TO_WXANY( enum_glob );
|
||||||
|
|
||||||
|
static struct CLASS_A_DESC
|
||||||
|
{
|
||||||
|
CLASS_A_DESC()
|
||||||
|
{
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
propMgr.AddProperty( new PROPERTY<A, int>( "A", &A::setA, &A::getA ) );
|
||||||
|
propMgr.AddProperty( new PROPERTY<A, int>( "A2", &A::setA, &A::getA2 ) );
|
||||||
|
propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point", &A::setPoint, &A::getPoint ) );
|
||||||
|
propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point2", &A::setPoint, &A::getPoint2 ) );
|
||||||
|
|
||||||
|
propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point3", &A::setPoint3, &A::getPoint3 ) );
|
||||||
|
propMgr.AddProperty( new PROPERTY<A, wxPoint>( "point4", &A::setPoint4, &A::getPoint4 ) );
|
||||||
|
}
|
||||||
|
} _CLASS_A_DESC;
|
||||||
|
|
||||||
|
static struct CLASS_B_DESC
|
||||||
|
{
|
||||||
|
CLASS_B_DESC()
|
||||||
|
{
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
propMgr.InheritsAfter( TYPE_HASH( B ), TYPE_HASH( A ) );
|
||||||
|
propMgr.AddProperty( new PROPERTY<B, int>( "C", &B::setC, &B::getC ) );
|
||||||
|
}
|
||||||
|
} _CLASS_B_DESC;
|
||||||
|
|
||||||
|
static struct CLASS_C_DESC
|
||||||
|
{
|
||||||
|
CLASS_C_DESC()
|
||||||
|
{
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
propMgr.AddProperty( new PROPERTY<C, bool>( "bool", &C::setBool, &C::getBool ) );
|
||||||
|
propMgr.AddProperty( new PROPERTY<C, int>( "new", &C::setNew, &C::getNew ) );
|
||||||
|
}
|
||||||
|
} _CLASS_C_DESC;
|
||||||
|
|
||||||
|
static struct CLASS_D_DESC
|
||||||
|
{
|
||||||
|
CLASS_D_DESC()
|
||||||
|
{
|
||||||
|
ENUM_MAP<D::enum_class>::Instance()
|
||||||
|
.Map( D::enum_class::TESTA, "TESTA" )
|
||||||
|
.Map( D::enum_class::TESTB, "TESTB" )
|
||||||
|
.Map( D::enum_class::TESTC, "TESTC" );
|
||||||
|
|
||||||
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||||
|
propMgr.AddProperty( new PROPERTY_ENUM<D, enum_glob>( "enumGlob", &D::setGlobEnum, &D::getGlobEnum ) );
|
||||||
|
propMgr.AddProperty( new PROPERTY_ENUM<D, D::enum_class>( "enumClass", &D::setClassEnum, &D::getClassEnum ) );
|
||||||
|
propMgr.AddProperty( new PROPERTY<D, wxPoint, A>( "point_alias", &D::setPoint, &D::getPoint ) );
|
||||||
|
|
||||||
|
// lines below are needed to indicate multiple inheritance
|
||||||
|
propMgr.AddTypeCast( new TYPE_CAST<D, A> );
|
||||||
|
propMgr.AddTypeCast( new TYPE_CAST<D, C> );
|
||||||
|
propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( A ) );
|
||||||
|
propMgr.InheritsAfter( TYPE_HASH( D ), TYPE_HASH( C ) );
|
||||||
|
|
||||||
|
auto cond = new PROPERTY<D, int>( "cond", &D::setCond, &D::getCond );
|
||||||
|
cond->SetAvailableFunc( [=](INSPECTABLE* aItem)->bool { return *aItem->Get<int>( "A" ) > 50; } );
|
||||||
|
propMgr.AddProperty( cond );
|
||||||
|
}
|
||||||
|
} _CLASS_D_DESC;
|
||||||
|
|
||||||
|
ENUM_TO_WXANY( D::enum_class );
|
||||||
|
|
||||||
|
|
||||||
|
struct PropertiesFixture
|
||||||
|
{
|
||||||
|
PropertiesFixture()
|
||||||
|
: propMgr( PROPERTY_MANAGER::Instance() )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
B b;
|
||||||
|
D d;
|
||||||
|
A* ptr;
|
||||||
|
PROPERTY_MANAGER& propMgr;
|
||||||
|
};
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE( Properties, PropertiesFixture )
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( Init )
|
||||||
|
{
|
||||||
|
propMgr.Rebuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic Set() & Get()
|
||||||
|
BOOST_AUTO_TEST_CASE( SetGet )
|
||||||
|
{
|
||||||
|
ptr = &b;
|
||||||
|
ptr->Set( "A", 100 );
|
||||||
|
ptr->Set( "point", wxPoint( 100, 200 ) );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 100 );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 100, 200 ) );
|
||||||
|
|
||||||
|
ptr = &d;
|
||||||
|
ptr->Set( "enumGlob", enum_glob::TEST2 );
|
||||||
|
ptr->Set( "enumClass", D::enum_class::TESTC );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<enum_glob>( "enumGlob" ), enum_glob::TEST2 );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<D::enum_class>( "enumClass" ), D::enum_class::TESTC );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Virtual methods
|
||||||
|
BOOST_AUTO_TEST_CASE( VirtualMethods )
|
||||||
|
{
|
||||||
|
// D::setA() saves a doubled value, while B::setA() saves unmodified value
|
||||||
|
ptr = &b;
|
||||||
|
ptr->Set( "A", 23 );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 23 ); // unmodified == 23
|
||||||
|
|
||||||
|
ptr = &d;
|
||||||
|
ptr->Set( "A", 23 );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<int>( "A" ), 46 ); // doubled == 46
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-existing properties
|
||||||
|
BOOST_AUTO_TEST_CASE( NotexistingProperties )
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL( ptr->Set<int>( "does not exist", 5 ), false );
|
||||||
|
BOOST_CHECK_EQUAL( ptr->Get<int>( "neither" ).has_value(), false );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request data using incorrect type
|
||||||
|
BOOST_AUTO_TEST_CASE( IncorrectType )
|
||||||
|
{
|
||||||
|
BOOST_CHECK_THROW( ptr->Get<wxPoint>( "bool" ), std::invalid_argument );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-casting (for types with multiple inheritance)
|
||||||
|
BOOST_AUTO_TEST_CASE( TypeCasting )
|
||||||
|
{
|
||||||
|
ptr = &d;
|
||||||
|
A* D_to_A = static_cast<A*>( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( A ) ) );
|
||||||
|
BOOST_CHECK_EQUAL( D_to_A, dynamic_cast<A*>( ptr ) );
|
||||||
|
|
||||||
|
C* D_to_C = static_cast<C*>( propMgr.TypeCast( ptr, TYPE_HASH( D ), TYPE_HASH( C ) ) );
|
||||||
|
BOOST_CHECK_EQUAL( D_to_C, dynamic_cast<C*>( ptr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( EnumGlob )
|
||||||
|
{
|
||||||
|
PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( D ), "enumGlob" );
|
||||||
|
BOOST_CHECK( prop->HasChoices() );
|
||||||
|
|
||||||
|
wxArrayInt values;
|
||||||
|
values.Add( static_cast<int>( enum_glob::TEST1 ) );
|
||||||
|
values.Add( static_cast<int>( enum_glob::TEST2 ) );
|
||||||
|
values.Add( static_cast<int>( enum_glob::TEST3 ) );
|
||||||
|
wxArrayString labels;
|
||||||
|
labels.Add( "TEST1" );
|
||||||
|
labels.Add( "TEST2" );
|
||||||
|
labels.Add( "TEST3" );
|
||||||
|
|
||||||
|
const wxPGChoices& v = prop->Choices();
|
||||||
|
BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
|
||||||
|
BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
|
||||||
|
|
||||||
|
for (int i = 0; i < values.GetCount(); ++i )
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < labels.GetCount(); ++i )
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( EnumClass )
|
||||||
|
{
|
||||||
|
PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( D ), "enumClass" );
|
||||||
|
BOOST_CHECK( prop->HasChoices() );
|
||||||
|
|
||||||
|
wxArrayInt values;
|
||||||
|
values.Add( static_cast<int>( D::enum_class::TESTA ) );
|
||||||
|
values.Add( static_cast<int>( D::enum_class::TESTB ) );
|
||||||
|
values.Add( static_cast<int>( D::enum_class::TESTC ) );
|
||||||
|
wxArrayString labels;
|
||||||
|
labels.Add( "TESTA" );
|
||||||
|
labels.Add( "TESTB" );
|
||||||
|
labels.Add( "TESTC" );
|
||||||
|
|
||||||
|
const wxPGChoices& v = prop->Choices();
|
||||||
|
BOOST_CHECK_EQUAL( v.GetCount(), values.GetCount() );
|
||||||
|
BOOST_CHECK_EQUAL( v.GetCount(), labels.GetCount() );
|
||||||
|
|
||||||
|
for (int i = 0; i < values.GetCount(); ++i )
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL( v.GetValue( i ), values[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < labels.GetCount(); ++i )
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL( v.GetLabel( i ), labels[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests conditional properties (which may depend on values or other properties)
|
||||||
|
BOOST_AUTO_TEST_CASE( Availability )
|
||||||
|
{
|
||||||
|
PROPERTY_BASE* propCond = propMgr.GetProperty( TYPE_HASH( D ), "cond" );
|
||||||
|
ptr = &d;
|
||||||
|
|
||||||
|
// "cond" property is available only when "a" field is greater than 50
|
||||||
|
d.setA( 0 );
|
||||||
|
BOOST_CHECK( !propCond->Available( ptr ) );
|
||||||
|
|
||||||
|
d.setA( 100 );
|
||||||
|
BOOST_CHECK( propCond->Available( ptr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using a different name for a parent propety
|
||||||
|
BOOST_AUTO_TEST_CASE( Alias )
|
||||||
|
{
|
||||||
|
ptr = &d;
|
||||||
|
|
||||||
|
ptr->Set( "point", wxPoint( 100, 100 ) );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 100, 100 ) );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 100, 100 ) );
|
||||||
|
|
||||||
|
ptr->Set( "point_alias", wxPoint( 300, 300 ) );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point" ), wxPoint( 300, 300 ) );
|
||||||
|
BOOST_CHECK_EQUAL( *ptr->Get<wxPoint>( "point_alias" ), wxPoint( 300, 300 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue