diff --git a/common/property_mgr.cpp b/common/property_mgr.cpp index 271ad9a131..40e8cb4a30 100644 --- a/common/property_mgr.cpp +++ b/common/property_mgr.cpp @@ -46,6 +46,7 @@ PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aPr { 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() ) @@ -108,6 +109,15 @@ void PROPERTY_MANAGER::AddProperty( PROPERTY_BASE* aProperty ) } +void PROPERTY_MANAGER::ReplaceProperty( size_t aBase, const wxString& aName, PROPERTY_BASE* aNew ) +{ + wxASSERT( aBase == aNew->BaseHash() ); + CLASS_DESC& classDesc = getClass( aNew->OwnerHash() ); + classDesc.m_replaced.insert( std::make_pair( aBase, aName ) ); + AddProperty( aNew ); +} + + void PROPERTY_MANAGER::AddTypeCast( TYPE_CAST_BASE* aCast ) { TYPE_ID derivedHash = aCast->DerivedHash(); @@ -169,7 +179,7 @@ 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 ) ) ); + tie( it, std::ignore ) = m_classes.emplace( aTypeId, CLASS_DESC( aTypeId ) ); return it->second; } @@ -177,18 +187,28 @@ PROPERTY_MANAGER::CLASS_DESC& PROPERTY_MANAGER::getClass( TYPE_ID aTypeId ) void PROPERTY_MANAGER::CLASS_DESC::rebuild() { + PROPERTY_SET replaced( m_replaced ); m_allProperties.clear(); - collectPropsRecur( m_allProperties ); + collectPropsRecur( m_allProperties, replaced ); // 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 +void PROPERTY_MANAGER::CLASS_DESC::collectPropsRecur( PROPERTY_LIST& aResult, PROPERTY_SET& aReplaced ) const { - for( const auto& base : m_bases ) - base.get().collectPropsRecur( aResult ); + for( const auto& replacedEntry : m_replaced ) + aReplaced.emplace( replacedEntry ); - for( auto& property : m_ownProperties ) - aResult.push_back( property.second.get() ); + for( auto& propertyData : m_ownProperties ) + { + PROPERTY_BASE* property = propertyData.second.get(); + + // Do not store replaced properties + if( aReplaced.count( std::make_pair( property->OwnerHash(), property->Name() ) ) == 0 ) + aResult.push_back( property ); + } + + for( const auto& base : m_bases ) + base.get().collectPropsRecur( aResult, aReplaced ); } diff --git a/include/property.h b/include/property.h index c54af7373a..5cc0cd2dee 100644 --- a/include/property.h +++ b/include/property.h @@ -165,11 +165,16 @@ public: class PROPERTY_BASE { +private: + ///> Used to generate unique IDs + size_t nextId = 0; + public: PROPERTY_BASE( const wxString& aName, PROPERTY_DISPLAY aDisplay = DEFAULT ) - : m_name( aName ), m_display( aDisplay ), + : m_id( nextId ), m_name( aName ), m_display( aDisplay ), m_availFunc( [](INSPECTABLE*)->bool { return true; } ) { + ++nextId; } virtual ~PROPERTY_BASE() @@ -191,6 +196,14 @@ public: 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() @@ -217,15 +230,28 @@ public: } /** - * Returns the type-id of the Owner class. + * Returns type-id of the Owner class. */ virtual size_t OwnerHash() const = 0; /** - * Returns the type-id of the property type. + * 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 @@ -246,7 +272,7 @@ protected: { wxAny a = getter( aObject ); - if ( !(std::is_enum::value && a.CheckType() ) && !a.CheckType() ) + if ( !( std::is_enum::value && a.CheckType() ) && !a.CheckType() ) throw std::invalid_argument("Invalid requested type"); return wxANY_AS(a, T); @@ -256,6 +282,7 @@ protected: virtual wxAny getter( void* aObject ) const = 0; private: + const size_t m_id; const wxString m_name; const PROPERTY_DISPLAY m_display; @@ -296,6 +323,11 @@ public: return m_ownerHash; } + size_t BaseHash() const override + { + return m_baseHash; + } + size_t TypeHash() const override { return m_typeHash; @@ -310,7 +342,8 @@ 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_typeHash( TYPE_HASH( BASE_TYPE ) ) + m_ownerHash( TYPE_HASH( Owner ) ), m_baseHash( TYPE_HASH( Base ) ), + m_typeHash( TYPE_HASH( BASE_TYPE ) ) { } @@ -344,6 +377,9 @@ protected: ///> 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; }; @@ -357,22 +393,28 @@ public: 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 ), - m_choices( ENUM_MAP::Instance().Choices() ) + : PROPERTY( aName, METHOD::Wrap( aSetter ), + METHOD::Wrap( aGetter ), aDisplay ) { - wxASSERT_MSG( m_choices.GetCount() > 0, "No enum choices defined" ); + 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 ), - m_choices( ENUM_MAP::Instance().Choices() ) + : PROPERTY( aName, METHOD::Wrap( aSetter ), + METHOD::Wrap( aGetter ), aDisplay ) { - wxASSERT_MSG( m_choices.GetCount() > 0, "No enum choices defined" ); + 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 @@ -408,6 +450,11 @@ public: return m_choices; } + void SetChoices( const wxPGChoices& aChoices ) override + { + m_choices = aChoices; + } + bool HasChoices() const override { return m_choices.GetCount() > 0; @@ -478,11 +525,6 @@ public: return *this; } - void Reset() - { - m_choices.Clear(); - } - const wxString& ToString( T value ) const { return m_choices.GetLabel( static_cast( value ) ); @@ -493,11 +535,6 @@ public: return m_choices; } - void SetChoices( const wxPGChoices& aChoices ) - { - m_choices = aChoices; - } - private: wxPGChoices m_choices; diff --git a/include/property_mgr.h b/include/property_mgr.h index a9e4cd17ba..1fb084e255 100644 --- a/include/property_mgr.h +++ b/include/property_mgr.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,8 @@ using TYPE_ID = size_t; using PROPERTY_LIST = std::vector; +using PROPERTY_SET = std::set>; + /** * Provides class metadata. Each class handled by PROPERTY_MANAGER * needs to be described using AddProperty(), AddTypeCast() and InheritsAfter() methods. @@ -121,6 +124,18 @@ public: */ void AddProperty( PROPERTY_BASE* aProperty ); + /** + * Replaces an existing property for a specific type. + * + * It is used to modify a property that has been inherited from a base class. + * This method is used instead of AddProperty(). + * + * @param aBase is the base class type the delivers the original property. + * @param aName is the name of the replaced property. + * @param aNew is the property replacing the inherited one. + */ + void ReplaceProperty( size_t aBase, const wxString& aName, PROPERTY_BASE* aNew ); + /** * Registers a type converter. Required prior TypeCast() usage. * @@ -186,12 +201,15 @@ private: ///> All properties (both unique to the type and inherited) std::vector m_allProperties; + ///> Replaced properties (TYPE_ID / name) + PROPERTY_SET m_replaced; + ///> 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; + void collectPropsRecur( PROPERTY_LIST& aResult, PROPERTY_SET& aReplaced ) const; }; ///> Returns metadata for a specific type diff --git a/qa/common/test_property.cpp b/qa/common/test_property.cpp index b87274b5c0..356938c85c 100644 --- a/qa/common/test_property.cpp +++ b/qa/common/test_property.cpp @@ -100,6 +100,10 @@ public: int m_cond; }; +class E : public D +{ +}; + static struct ENUM_GLOB_DESC { ENUM_GLOB_DESC() @@ -162,6 +166,9 @@ static struct CLASS_D_DESC propMgr.AddProperty( new PROPERTY_ENUM( "enumClass", &D::setClassEnum, &D::getClassEnum ) ); propMgr.AddProperty( new PROPERTY( "point_alias", &D::setPoint, &D::getPoint ) ); + propMgr.ReplaceProperty( TYPE_HASH( C ), "bool", + new PROPERTY( "replaced_bool", &D::setBool, &D::getBool ) ); + // lines below are needed to indicate multiple inheritance propMgr.AddTypeCast( new TYPE_CAST ); propMgr.AddTypeCast( new TYPE_CAST ); @@ -174,6 +181,26 @@ static struct CLASS_D_DESC } } _CLASS_D_DESC; +static struct CLASS_E_DESC +{ + CLASS_E_DESC() + { + wxArrayInt values; + values.Add( enum_glob::TEST1 ); + values.Add( enum_glob::TEST3 ); + wxArrayString labels; + labels.Add( "T1" ); + labels.Add( "T3" ); + wxPGChoices newChoices( labels, values ); + + PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance(); + auto prop = new PROPERTY_ENUM( "enumGlob", + &E::setGlobEnum, &E::getGlobEnum ); + prop->SetChoices( newChoices ); + propMgr.ReplaceProperty( TYPE_HASH( D ), "enumGlob", prop ); + } +} _CLASS_E_DESC; + ENUM_TO_WXANY( D::enum_class ); @@ -229,6 +256,7 @@ BOOST_AUTO_TEST_CASE( VirtualMethods ) // Non-existing properties BOOST_AUTO_TEST_CASE( NotexistingProperties ) { + ptr = &d; BOOST_CHECK_EQUAL( ptr->Set( "does not exist", 5 ), false ); BOOST_CHECK_EQUAL( ptr->Get( "neither" ).has_value(), false ); } @@ -236,7 +264,8 @@ BOOST_AUTO_TEST_CASE( NotexistingProperties ) // Request data using incorrect type BOOST_AUTO_TEST_CASE( IncorrectType ) { - BOOST_CHECK_THROW( ptr->Get( "bool" ), std::invalid_argument ); + ptr = &d; + BOOST_CHECK_THROW( ptr->Get( "A" ), std::invalid_argument ); } // Type-casting (for types with multiple inheritance) @@ -256,9 +285,9 @@ BOOST_AUTO_TEST_CASE( EnumGlob ) BOOST_CHECK( prop->HasChoices() ); wxArrayInt values; - values.Add( static_cast( enum_glob::TEST1 ) ); - values.Add( static_cast( enum_glob::TEST2 ) ); - values.Add( static_cast( enum_glob::TEST3 ) ); + values.Add( enum_glob::TEST1 ); + values.Add( enum_glob::TEST2 ); + values.Add( enum_glob::TEST3 ); wxArrayString labels; labels.Add( "TEST1" ); labels.Add( "TEST2" ); @@ -285,9 +314,9 @@ BOOST_AUTO_TEST_CASE( EnumClass ) BOOST_CHECK( prop->HasChoices() ); wxArrayInt values; - values.Add( static_cast( D::enum_class::TESTA ) ); - values.Add( static_cast( D::enum_class::TESTB ) ); - values.Add( static_cast( D::enum_class::TESTC ) ); + values.Add( D::enum_class::TESTA ); + values.Add( D::enum_class::TESTB ); + values.Add( D::enum_class::TESTC ); wxArrayString labels; labels.Add( "TESTA" ); labels.Add( "TESTB" ); @@ -322,7 +351,7 @@ BOOST_AUTO_TEST_CASE( Availability ) BOOST_CHECK( propCond->Available( ptr ) ); } -// Using a different name for a parent propety +// Using a different name for a parent property BOOST_AUTO_TEST_CASE( Alias ) { ptr = &d; @@ -336,4 +365,44 @@ BOOST_AUTO_TEST_CASE( Alias ) BOOST_CHECK_EQUAL( *ptr->Get( "point_alias" ), wxPoint( 300, 300 ) ); } +// Property renaming +BOOST_AUTO_TEST_CASE( Rename ) +{ + PROPERTY_BASE* prop; + + prop = propMgr.GetProperty( TYPE_HASH( D ), "bool" ); + BOOST_CHECK_EQUAL( prop, nullptr ); + + prop = propMgr.GetProperty( TYPE_HASH( D ), "replaced_bool" ); + BOOST_CHECK( prop ); +} + +// Different subset of enum values for a property +BOOST_AUTO_TEST_CASE( AlternativeEnum ) +{ + PROPERTY_BASE* prop = propMgr.GetProperty( TYPE_HASH( E ), "enumGlob" ); + BOOST_CHECK( prop->HasChoices() ); + + wxArrayInt values; + values.Add( enum_glob::TEST1 ); + values.Add( enum_glob::TEST3 ); + wxArrayString labels; + labels.Add( "T1" ); + labels.Add( "T3" ); + + 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_SUITE_END()