KiCad PCB EDA Suite
Loading...
Searching...
No Matches
property_mgr.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
7 * @author Maciej Suminski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 3
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <inspectable.h>
25#include <properties/property.h>
26
27#include <algorithm>
28#include <ranges>
29#include <utility>
30#include <wx/wx.h>
31
32
33// Global to prevent simultaneous multi-threaded static initialization
34static const std::vector<PROPERTY_BASE*> EMPTY_PROP_LIST;
35static const std::map<PROPERTY_BASE*, int> EMPTY_PROP_DISPLAY_ORDER;
36std::vector<wxString> EMPTY_GROUP_DISPLAY_ORDER;
37
38
39PROPERTY_MANAGER::~PROPERTY_MANAGER() noexcept = default;
40
41
42void PROPERTY_MANAGER::RegisterType( TYPE_ID aType, const wxString& aName )
43{
44 wxASSERT( m_classNames.count( aType ) == 0 );
45 m_classNames.emplace( aType, aName );
46}
47
48
49PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aProperty ) const
50{
51 if( m_dirty )
52 const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
53
54 auto it = m_classes.find( aType );
55
56 if( it == m_classes.end() )
57 return nullptr;
58
59 const CLASS_DESC& classDesc = it->second;
60
61 for( PROPERTY_BASE* property : classDesc.m_allProperties )
62 {
63 if( !aProperty.CmpNoCase( property->Name() ) )
64 return property;
65 }
66
67 return nullptr;
68}
69
70
71PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( const INSPECTABLE* aObject, const wxString& aProperty ) const
72{
73 if( PROPERTY_BASE* prop = GetProperty( TYPE_HASH( *aObject ), aProperty ) )
74 return prop;
75
76 for( PROPERTY_BASE* dynamicProp : aObject->GetDynamicProperties() )
77 {
78 if( !aProperty.CmpNoCase( dynamicProp->Name() ) )
79 return dynamicProp;
80 }
81
82 return nullptr;
83}
84
85
86const std::vector<PROPERTY_BASE*>& PROPERTY_MANAGER::GetProperties( TYPE_ID aType ) const
87{
88 if( m_dirty )
89 const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
90
91 auto it = m_classes.find( aType );
92
93 if( it == m_classes.end() )
94 return EMPTY_PROP_LIST;
95
96 return it->second.m_allProperties;
97}
98
99
100std::vector<PROPERTY_BASE*> PROPERTY_MANAGER::GetProperties( const INSPECTABLE* aObject ) const
101{
102 std::vector<PROPERTY_BASE*> result = GetProperties( TYPE_HASH( *aObject ) );
103
104 for( PROPERTY_BASE* prop : aObject->GetDynamicProperties() )
105 result.push_back( prop );
106
107 return result;
108}
109
110
111std::vector<PROPERTY_BASE*>
112PROPERTY_MANAGER::GetCommonDynamicProperties( const std::vector<const INSPECTABLE*>& aObjects ) const
113{
114 if( aObjects.empty() )
115 return {};
116
117 std::vector<PROPERTY_BASE*> common = aObjects.front()->GetDynamicProperties();
118
119 for( size_t ii = 1; ii < aObjects.size() && !common.empty(); ++ii )
120 {
121 std::set<wxString> names;
122
123 for( PROPERTY_BASE* prop : aObjects[ii]->GetDynamicProperties() )
124 names.insert( prop->Name() );
125
126 std::erase_if( common,
127 [&]( PROPERTY_BASE* aProp )
128 {
129 return !names.contains( aProp->Name() );
130 } );
131 }
132
133 return common;
134}
135
136
137const std::map<PROPERTY_BASE*, int>& PROPERTY_MANAGER::GetDisplayOrder( TYPE_ID aType ) const
138{
139 if( m_dirty )
140 const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
141
142 auto it = m_classes.find( aType );
143
144 if( it == m_classes.end() )
146
147 return it->second.m_displayOrder;
148}
149
150
151const std::vector<wxString>& PROPERTY_MANAGER::GetGroupDisplayOrder( TYPE_ID aType ) const
152{
153 if( m_dirty )
154 const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
155
156 auto it = m_classes.find( aType );
157
158 if( it == m_classes.end() )
160
161 return it->second.m_groupDisplayOrder;
162}
163
164
165const void* PROPERTY_MANAGER::TypeCast( const void* aSource, TYPE_ID aBase, TYPE_ID aTarget ) const
166{
167 if( aBase == aTarget )
168 return aSource;
169
170 auto classDesc = m_classes.find( aBase );
171
172 if( classDesc == m_classes.end() )
173 return aSource;
174
175 const std::map<TYPE_ID, std::unique_ptr<TYPE_CAST_BASE>>& converters = classDesc->second.m_typeCasts;
176 auto converter = converters.find( aTarget );
177
178 if( converter == converters.end() ) // explicit type cast not found
179 return IsOfType( aBase, aTarget ) ? aSource : nullptr;
180
181 return (*converter->second)( aSource );
182}
183
184
185PROPERTY_BASE& PROPERTY_MANAGER::AddProperty( PROPERTY_BASE* aProperty, const wxString& aGroup )
186{
187 const wxString& name = aProperty->Name();
188 TYPE_ID hash = aProperty->OwnerHash();
189 CLASS_DESC& classDesc = getClass( hash );
190
191 auto [it, inserted] = classDesc.m_ownProperties.try_emplace( name, aProperty );
192
193 if( !inserted )
194 {
195 // Property with this name already exists; delete the duplicate and return the existing one.
196 delete aProperty;
197 return *it->second;
198 }
199
200 classDesc.m_ownDisplayOrder.emplace_back( aProperty );
201
202 aProperty->SetGroup( aGroup );
203
204 if( !classDesc.m_groups.count( aGroup ) )
205 {
206 classDesc.m_groupDisplayOrder.emplace_back( aGroup );
207 classDesc.m_groups.insert( aGroup );
208 }
209
210 m_dirty = true;
211 return *aProperty;
212}
213
214
215PROPERTY_BASE& PROPERTY_MANAGER::ReplaceProperty( size_t aBase, const wxString& aName, PROPERTY_BASE* aNew,
216 const wxString& aGroup )
217{
218 CLASS_DESC& classDesc = getClass( aNew->OwnerHash() );
219 classDesc.m_replaced.insert( std::make_pair( aBase, aName ) );
220 return AddProperty( aNew, aGroup );
221}
222
223
225{
226 TYPE_ID derivedHash = aCast->DerivedHash();
227 CLASS_DESC& classDesc = getClass( aCast->BaseHash() );
228
229 wxASSERT_MSG( classDesc.m_typeCasts.count( derivedHash ) == 0, wxT( "Such converter already exists" ) );
230 classDesc.m_typeCasts.emplace( derivedHash, aCast );
231}
232
233
235{
236 wxASSERT_MSG( aDerived != aBase, wxT( "Class cannot inherit from itself" ) );
237
238 CLASS_DESC& derived = getClass( aDerived );
239 derived.m_bases.push_back( getClass( aBase ) );
240 m_dirty = true;
241
242 wxASSERT_MSG( derived.m_bases.size() == 1 || derived.m_typeCasts.count( aBase ) == 1,
243 wxT( "You need to add a TYPE_CAST for classes inheriting from multiple bases" ) );
244}
245
246
247void PROPERTY_MANAGER::Mask( TYPE_ID aDerived, TYPE_ID aBase, const wxString& aName )
248{
249 wxASSERT_MSG( aDerived != aBase, wxT( "Class cannot mask from itself" ) );
250
251 CLASS_DESC& derived = getClass( aDerived );
252 derived.m_maskedBaseProperties.insert( std::make_pair( aBase, aName ) );
253 m_dirty = true;
254}
255
256
257void PROPERTY_MANAGER::OverrideAvailability( TYPE_ID aDerived, TYPE_ID aBase, const wxString& aName,
258 std::function<bool( INSPECTABLE* )> aFunc )
259{
260 wxASSERT_MSG( aDerived != aBase, wxT( "Class cannot override from itself" ) );
261
262 CLASS_DESC& derived = getClass( aDerived );
263 derived.m_availabilityOverrides[std::make_pair( aBase, aName )] = std::move( aFunc );
264 m_dirty = true;
265}
266
267
268void PROPERTY_MANAGER::OverrideWriteability( TYPE_ID aDerived, TYPE_ID aBase, const wxString& aName,
269 std::function<bool( INSPECTABLE* )> aFunc )
270{
271 wxASSERT_MSG( aDerived != aBase, wxT( "Class cannot override from itself" ) );
272
273 CLASS_DESC& derived = getClass( aDerived );
274 derived.m_writeabilityOverrides[std::make_pair( aBase, aName )] = std::move( aFunc );
275 m_dirty = true;
276}
277
278
280{
281 if( !aProp->Available( aItem ) )
282 return false;
283
284 CLASS_DESC& derived = getClass( aItemClass );
285
286 auto it = derived.m_availabilityOverrides.find( std::make_pair( aProp->BaseHash(), aProp->Name() ) );
287
288 if( it != derived.m_availabilityOverrides.end() )
289 return it->second( aItem );
290
291 return true;
292}
293
294
296{
297 if( !aProp->Writeable( aItem ) )
298 return false;
299
300 CLASS_DESC& derived = getClass( aItemClass );
301
302 auto it = derived.m_writeabilityOverrides.find( std::make_pair( aProp->BaseHash(), aProp->Name() ) );
303
304 if( it != derived.m_writeabilityOverrides.end() )
305 return it->second( aItem );
306
307 return true;
308}
309
310
311bool PROPERTY_MANAGER::IsOfType( TYPE_ID aDerived, TYPE_ID aBase ) const
312{
313 if( aDerived == aBase )
314 return true;
315
316 auto derived = m_classes.find( aDerived );
317 wxCHECK( derived != m_classes.end(), false ); // missing class description
318
319 // traverse the hierarchy seeking for the base class
320 for( const std::reference_wrapper<CLASS_DESC>& base : derived->second.m_bases )
321 {
322 if( IsOfType( base.get().m_id, aBase ) )
323 return true;
324 }
325
326 return false;
327}
328
329
331{
332 for( std::pair<const TYPE_ID, CLASS_DESC>& classEntry : m_classes )
333 classEntry.second.rebuild();
334
335 m_dirty = false;
336}
337
338
340{
341 auto it = m_classes.find( aTypeId );
342
343 if( it == m_classes.end() )
344 tie( it, std::ignore ) = m_classes.emplace( aTypeId, CLASS_DESC( aTypeId ) );
345
346 return it->second;
347}
348
349
351 m_id( aId )
352{
353 m_groupDisplayOrder.emplace_back( wxEmptyString );
354 m_groups.insert( wxEmptyString );
355}
356
357
359PROPERTY_MANAGER::CLASS_DESC::CLASS_DESC( CLASS_DESC&& ) noexcept = default;
360
361
363{
364 std::set<std::pair<size_t, wxString>> replaced;
365 std::set<std::pair<size_t, wxString>> masked;
366 m_allProperties.clear();
367 m_displayOrder.clear();
369
370 // We need to keep properties sorted to be able to use std::set_* functions
371 sort( m_allProperties.begin(), m_allProperties.end() );
372
373 std::vector<wxString> displayOrder;
374 std::set<wxString> groups;
375
376 auto collectGroups =
377 [&]( std::set<wxString>& aSet, std::vector<wxString>& aResult )
378 {
379 auto collectGroupsRecursive =
380 []( auto& aSelf, std::set<wxString>& aSetR, std::vector<wxString>& aResultR,
381 const CLASS_DESC& aClassR ) -> void
382 {
383 for( const wxString& group : aClassR.m_groupDisplayOrder )
384 {
385 if( !aSetR.count( group ) )
386 {
387 aSetR.insert( group );
388 aResultR.emplace_back( group );
389 }
390 }
391
392 for( const CLASS_DESC& base : aClassR.m_bases )
393 aSelf( aSelf, aSetR, aResultR, base );
394 };
395
396 collectGroupsRecursive( collectGroupsRecursive, aSet, aResult, *this );
397 };
398
399 // TODO(JE): This currently relies on rebuild() happening after all properties are added
400 // separate out own groups vs. all groups to fix
401 collectGroups( groups, displayOrder );
402 m_groupDisplayOrder = std::move( displayOrder );
403}
404
405
406void PROPERTY_MANAGER::CLASS_DESC::collectPropsRecur( std::vector<PROPERTY_BASE*>& aResult,
407 std::set<std::pair<size_t, wxString>>& aReplaced,
408 std::map<PROPERTY_BASE*, int>& aDisplayOrder,
409 std::set<std::pair<size_t, wxString>>& aMasked ) const
410{
411 for( const std::pair<size_t, wxString>& replacedEntry : m_replaced )
412 aReplaced.emplace( replacedEntry );
413
414 for( const std::pair<size_t, wxString>& maskedEntry : m_maskedBaseProperties )
415 aMasked.emplace( maskedEntry );
416
417 /*
418 * We want to insert our own properties in forward order, but earlier than anything already in
419 * the list (which will have been added by a subclass of us)
420 */
421 int displayOrderStart = 0;
422
423 if( !aDisplayOrder.empty() )
424 {
425 int firstSoFar = std::min_element( aDisplayOrder.begin(), aDisplayOrder.end(),
426 []( const std::pair<PROPERTY_BASE*, int>& aFirst,
427 const std::pair<PROPERTY_BASE*, int>& aSecond )
428 {
429 return aFirst.second < aSecond.second;
430 } )->second;
431
432 displayOrderStart = firstSoFar - m_ownProperties.size();
433 }
434
435 int idx = 0;
436
437 for( PROPERTY_BASE* property : m_ownDisplayOrder )
438 {
439 std::set<std::pair<size_t, wxString>>::key_type propertyKey = std::make_pair( property->OwnerHash(),
440 property->Name() );
441 // Do not store replaced properties
442 if( aReplaced.count( propertyKey ) )
443 continue;
444
445 // Do not store masked properties
446 if( aMasked.count( propertyKey ) )
447 continue;
448
449 aDisplayOrder[property] = displayOrderStart + idx++;
450 aResult.push_back( property );
451 }
452
453 // Iterate backwards so that replaced properties appear before base properties
454 for( std::reference_wrapper<CLASS_DESC> base : std::ranges::reverse_view( m_bases ) )
455 base.get().collectPropsRecur( aResult, aReplaced, aDisplayOrder, aMasked );
456}
457
458
459std::vector<PROPERTY_MANAGER::CLASS_INFO> PROPERTY_MANAGER::GetAllClasses()
460{
461 std::vector<CLASS_INFO> rv;
462
463 for( std::pair<const TYPE_ID, CLASS_DESC>& classEntry : m_classes )
464 {
466
467 info.type = classEntry.first;
468 info.name = m_classNames[classEntry.first];
469
470 for( PROPERTY_BASE* prop : classEntry.second.m_allProperties )
471 info.properties.push_back( prop );
472
473 rv.push_back( info );
474 }
475
476 return rv;
477}
478
479
481{
482 auto callListeners =
483 [&]( TYPE_ID typeId )
484 {
485 auto listeners = m_listeners.find( typeId );
486
487 if( listeners != m_listeners.end() )
488 {
489 for( const auto& [ handle, listener ] : listeners->second )
490 listener( aObject, aProperty, m_managedCommit );
491 }
492 };
493
494 CLASS_DESC& objectClass = getClass( TYPE_HASH( *aObject ) );
495
496 callListeners( objectClass.m_id );
497
498 for( const std::reference_wrapper<CLASS_DESC>& superClass : objectClass.m_bases )
499 callListeners( superClass.get().m_id );
500}
501
502
504{
505 // This is a hard failure. wxCHECK2() will just allow us to crash later in a less-pertinent place
506 wxASSERT_MSG( PROPERTY_MANAGER::Instance().m_managedCommit == nullptr,
507 wxT( "Can't have more than one managed commit at a time!" ) );
508
510 m_ownsCommit = true;
511}
512
513
515{
516 wxASSERT_MSG( PROPERTY_MANAGER::Instance().m_managedCommit != nullptr,
517 wxT( "Something went wrong: m_managedCommit already null!" ) );
518
519 if( m_ownsCommit )
521}
522
523
526{
528 m_listeners[aType].emplace_back( handle, std::move( aListenerFunc ) );
529 return { aType, handle };
530}
531
532
534{
535 auto it = m_listeners.find( aType );
536
537 if( it == m_listeners.end() )
538 return;
539
540 auto& vec = it->second;
541 vec.erase( std::remove_if( vec.begin(), vec.end(),
542 [aHandle]( const auto& aPair )
543 {
544 return aPair.first == aHandle;
545 } ),
546 vec.end() );
547}
548
549
554
555
558{
559 if( this != &aOther )
560 {
561 reset();
562 m_type = aOther.m_type;
563 m_handle = aOther.m_handle;
564 aOther.m_handle = 0;
565 }
566
567 return *this;
568}
569
570
const char * name
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:68
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
virtual std::vector< PROPERTY_BASE * > GetDynamicProperties() const
Return dynamically-computed properties specific to this object instance (e.g.
Definition inspectable.h:53
PROPERTY_BASE & SetGroup(const wxString &aGroup)
Definition property.h:378
virtual size_t BaseHash() const =0
Return type-id of the Base class.
virtual bool Writeable(INSPECTABLE *aObject) const
Definition property.h:300
bool Available(INSPECTABLE *aObject) const
Return true if aObject offers this PROPERTY.
Definition property.h:267
const wxString & Name() const
Definition property.h:221
virtual size_t OwnerHash() const =0
Return type-id of the Owner class.
PROPERTY_COMMIT_HANDLER(COMMIT *aCommit)
Move-only RAII wrapper around a single listener registration.
PROPERTY_LISTENER_HANDLE m_handle
PROPERTY_LISTENER_SUBSCRIPTION & operator=(const PROPERTY_LISTENER_SUBSCRIPTION &)=delete
const std::vector< PROPERTY_BASE * > & GetProperties(TYPE_ID aType) const
Return all properties for a specific type.
bool m_dirty
Flag indicating that the list of properties needs to be rebuild (RebuildProperties())
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
bool IsWriteableFor(TYPE_ID aItemClass, PROPERTY_BASE *aProp, INSPECTABLE *aItem)
Checks overriden availability and original availability of a property, returns false if the property ...
void PropertyChanged(INSPECTABLE *aObject, PROPERTY_BASE *aProperty)
Callback to alert the notification system that a property has changed.
COMMIT * m_managedCommit
void Mask(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName)
Sets a base class property as masked in a derived class.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
void Rebuild()
Rebuild the list of all registered properties.
PROPERTY_BASE * GetProperty(TYPE_ID aType, const wxString &aProperty) const
Return a property for a specific type.
std::unordered_map< TYPE_ID, CLASS_DESC > m_classes
Map of all available types.
~PROPERTY_MANAGER() noexcept
const void * TypeCast(const void *aSource, TYPE_ID aBase, TYPE_ID aTarget) const
Cast a type to another type.
CLASS_DESC & getClass(TYPE_ID aTypeId)
Returns metadata for a specific type.
void RegisterType(TYPE_ID aType, const wxString &aName)
Associate a name with a type.
bool IsAvailableFor(TYPE_ID aItemClass, PROPERTY_BASE *aProp, INSPECTABLE *aItem)
Checks overriden availability and original availability of a property, returns false if the property ...
std::vector< CLASS_INFO > GetAllClasses()
class PROPERTY_LISTENER_SUBSCRIPTION RegisterListener(TYPE_ID aType, PROPERTY_LISTENER aListenerFunc)
Register a listener for the given type and return a move-only subscription that auto-unregisters in i...
const std::map< PROPERTY_BASE *, int > & GetDisplayOrder(TYPE_ID aType) const
PROPERTY_LISTENER_HANDLE m_nextListenerHandle
void UnregisterListener(TYPE_ID aType, PROPERTY_LISTENER_HANDLE aHandle)
Remove a single listener registration by its handle.
void OverrideAvailability(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName, std::function< bool(INSPECTABLE *)> aFunc)
Sets an override availability functor for a base class property of a given derived class.
bool IsOfType(TYPE_ID aDerived, TYPE_ID aBase) const
Return true if aDerived is inherited from aBase.
PROPERTY_BASE & ReplaceProperty(size_t aBase, const wxString &aName, PROPERTY_BASE *aNew, const wxString &aGroup=wxEmptyString)
Replace an existing property for a specific type.
std::map< TYPE_ID, std::vector< std::pair< PROPERTY_LISTENER_HANDLE, PROPERTY_LISTENER > > > m_listeners
void OverrideWriteability(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName, std::function< bool(INSPECTABLE *)> aFunc)
Sets an override writeability functor for a base class property of a given derived class.
std::unordered_map< TYPE_ID, wxString > m_classNames
std::vector< PROPERTY_BASE * > GetCommonDynamicProperties(const std::vector< const INSPECTABLE * > &aObjects) const
Return the dynamic properties of the first object whose names every other object also has,...
const std::vector< wxString > & GetGroupDisplayOrder(TYPE_ID aType) const
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
virtual size_t BaseHash() const =0
virtual size_t DerivedHash() const =0
#define TYPE_HASH(x)
Macro to generate unique identifier for a type.
Definition property.h:74
static const std::vector< PROPERTY_BASE * > EMPTY_PROP_LIST
static const std::map< PROPERTY_BASE *, int > EMPTY_PROP_DISPLAY_ORDER
std::vector< wxString > EMPTY_GROUP_DISPLAY_ORDER
std::function< void(INSPECTABLE *, PROPERTY_BASE *, COMMIT *)> PROPERTY_LISTENER
std::size_t PROPERTY_LISTENER_HANDLE
Opaque handle returned by RegisterListener so a specific registration can be removed without disturbi...
size_t TYPE_ID
Unique type identifier.
Structure holding type meta-data.
std::vector< std::reference_wrapper< CLASS_DESC > > m_bases
Types after which this type inherits.
std::set< std::pair< size_t, wxString > > m_replaced
Replaced properties (TYPE_ID / name)
std::vector< wxString > m_groupDisplayOrder
List of property groups provided by this class in display order.
std::map< std::pair< size_t, wxString >, std::function< bool(INSPECTABLE *)> > m_availabilityOverrides
Overrides for base class property availabilities.
std::map< std::pair< size_t, wxString >, std::function< bool(INSPECTABLE *)> > m_writeabilityOverrides
Overrides for base class property writeable status.
std::map< wxString, std::unique_ptr< PROPERTY_BASE > > m_ownProperties
Properties unique to this type (i.e. not inherited)
std::vector< PROPERTY_BASE * > m_ownDisplayOrder
Non-owning list of classes's direct properties in display order.
std::set< std::pair< size_t, wxString > > m_maskedBaseProperties
Properties from bases that should be masked (hidden) on this subclass.
std::map< PROPERTY_BASE *, int > m_displayOrder
Compiled display order for all properties.
std::vector< PROPERTY_BASE * > m_allProperties
All properties (both unique to the type and inherited)
std::map< TYPE_ID, std::unique_ptr< TYPE_CAST_BASE > > m_typeCasts
Type converters available for this type.
void collectPropsRecur(std::vector< PROPERTY_BASE * > &aResult, std::set< std::pair< size_t, wxString > > &aReplaced, std::map< PROPERTY_BASE *, int > &aDisplayOrder, std::set< std::pair< size_t, wxString > > &aMasked) const
Traverses the class inheritance hierarchy bottom-to-top, gathering all properties available to a type...
void rebuild()
Recreates the list of properties.
const TYPE_ID m_id
Unique type identifier (obtained using TYPE_HASH)
std::set< wxString > m_groups
The property groups provided by this class.
wxString result
Test unit parsing edge cases and error handling.