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 along
20 * with this program. If not, see <http://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
39void PROPERTY_MANAGER::RegisterType( TYPE_ID aType, const wxString& aName )
40{
41 wxASSERT( m_classNames.count( aType ) == 0 );
42 m_classNames.emplace( aType, aName );
43}
44
45
46PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aProperty ) const
47{
48 if( m_dirty )
49 const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
50
51 auto it = m_classes.find( aType );
52
53 if( it == m_classes.end() )
54 return nullptr;
55
56 const CLASS_DESC& classDesc = it->second;
57
58 for( PROPERTY_BASE* property : classDesc.m_allProperties )
59 {
60 if( !aProperty.CmpNoCase( property->Name() ) )
61 return property;
62 }
63
64 return nullptr;
65}
66
67
68const std::vector<PROPERTY_BASE*>& PROPERTY_MANAGER::GetProperties( TYPE_ID aType ) const
69{
70 if( m_dirty )
71 const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
72
73 auto it = m_classes.find( aType );
74
75 if( it == m_classes.end() )
76 return EMPTY_PROP_LIST;
77
78 return it->second.m_allProperties;
79}
80
81
82const std::map<PROPERTY_BASE*, int>& PROPERTY_MANAGER::GetDisplayOrder( TYPE_ID aType ) const
83{
84 if( m_dirty )
85 const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
86
87 auto it = m_classes.find( aType );
88
89 if( it == m_classes.end() )
91
92 return it->second.m_displayOrder;
93}
94
95
96const std::vector<wxString>& PROPERTY_MANAGER::GetGroupDisplayOrder( TYPE_ID aType ) const
97{
98 if( m_dirty )
99 const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
100
101 auto it = m_classes.find( aType );
102
103 if( it == m_classes.end() )
105
106 return it->second.m_groupDisplayOrder;
107}
108
109
110const void* PROPERTY_MANAGER::TypeCast( const void* aSource, TYPE_ID aBase, TYPE_ID aTarget ) const
111{
112 if( aBase == aTarget )
113 return aSource;
114
115 auto classDesc = m_classes.find( aBase );
116
117 if( classDesc == m_classes.end() )
118 return aSource;
119
120 const std::map<TYPE_ID, std::unique_ptr<TYPE_CAST_BASE>>& converters = classDesc->second.m_typeCasts;
121 auto converter = converters.find( aTarget );
122
123 if( converter == converters.end() ) // explicit type cast not found
124 return IsOfType( aBase, aTarget ) ? aSource : nullptr;
125
126 return (*converter->second)( aSource );
127}
128
129
130PROPERTY_BASE& PROPERTY_MANAGER::AddProperty( PROPERTY_BASE* aProperty, const wxString& aGroup )
131{
132 const wxString& name = aProperty->Name();
133 TYPE_ID hash = aProperty->OwnerHash();
134 CLASS_DESC& classDesc = getClass( hash );
135
136 classDesc.m_ownProperties.emplace( name, aProperty );
137 classDesc.m_ownDisplayOrder.emplace_back( aProperty );
138
139 aProperty->SetGroup( aGroup );
140
141 if( !classDesc.m_groups.count( aGroup ) )
142 {
143 classDesc.m_groupDisplayOrder.emplace_back( aGroup );
144 classDesc.m_groups.insert( aGroup );
145 }
146
147 m_dirty = true;
148 return *aProperty;
149}
150
151
152PROPERTY_BASE& PROPERTY_MANAGER::ReplaceProperty( size_t aBase, const wxString& aName, PROPERTY_BASE* aNew,
153 const wxString& aGroup )
154{
155 CLASS_DESC& classDesc = getClass( aNew->OwnerHash() );
156 classDesc.m_replaced.insert( std::make_pair( aBase, aName ) );
157 return AddProperty( aNew, aGroup );
158}
159
160
162{
163 TYPE_ID derivedHash = aCast->DerivedHash();
164 CLASS_DESC& classDesc = getClass( aCast->BaseHash() );
165
166 wxASSERT_MSG( classDesc.m_typeCasts.count( derivedHash ) == 0, wxT( "Such converter already exists" ) );
167 classDesc.m_typeCasts.emplace( derivedHash, aCast );
168}
169
170
172{
173 wxASSERT_MSG( aDerived != aBase, wxT( "Class cannot inherit from itself" ) );
174
175 CLASS_DESC& derived = getClass( aDerived );
176 derived.m_bases.push_back( getClass( aBase ) );
177 m_dirty = true;
178
179 wxASSERT_MSG( derived.m_bases.size() == 1 || derived.m_typeCasts.count( aBase ) == 1,
180 wxT( "You need to add a TYPE_CAST for classes inheriting from multiple bases" ) );
181}
182
183
184void PROPERTY_MANAGER::Mask( TYPE_ID aDerived, TYPE_ID aBase, const wxString& aName )
185{
186 wxASSERT_MSG( aDerived != aBase, wxT( "Class cannot mask from itself" ) );
187
188 CLASS_DESC& derived = getClass( aDerived );
189 derived.m_maskedBaseProperties.insert( std::make_pair( aBase, aName ) );
190 m_dirty = true;
191}
192
193
194void PROPERTY_MANAGER::OverrideAvailability( TYPE_ID aDerived, TYPE_ID aBase, const wxString& aName,
195 std::function<bool( INSPECTABLE* )> aFunc )
196{
197 wxASSERT_MSG( aDerived != aBase, wxT( "Class cannot override from itself" ) );
198
199 CLASS_DESC& derived = getClass( aDerived );
200 derived.m_availabilityOverrides[std::make_pair( aBase, aName )] = std::move( aFunc );
201 m_dirty = true;
202}
203
204
205void PROPERTY_MANAGER::OverrideWriteability( TYPE_ID aDerived, TYPE_ID aBase, const wxString& aName,
206 std::function<bool( INSPECTABLE* )> aFunc )
207{
208 wxASSERT_MSG( aDerived != aBase, wxT( "Class cannot override from itself" ) );
209
210 CLASS_DESC& derived = getClass( aDerived );
211 derived.m_writeabilityOverrides[std::make_pair( aBase, aName )] = std::move( aFunc );
212 m_dirty = true;
213}
214
215
217{
218 if( !aProp->Available( aItem ) )
219 return false;
220
221 CLASS_DESC& derived = getClass( aItemClass );
222
223 auto it = derived.m_availabilityOverrides.find( std::make_pair( aProp->BaseHash(), aProp->Name() ) );
224
225 if( it != derived.m_availabilityOverrides.end() )
226 return it->second( aItem );
227
228 return true;
229}
230
231
233{
234 if( !aProp->Writeable( aItem ) )
235 return false;
236
237 CLASS_DESC& derived = getClass( aItemClass );
238
239 auto it = derived.m_writeabilityOverrides.find( std::make_pair( aProp->BaseHash(), aProp->Name() ) );
240
241 if( it != derived.m_writeabilityOverrides.end() )
242 return it->second( aItem );
243
244 return true;
245}
246
247
248bool PROPERTY_MANAGER::IsOfType( TYPE_ID aDerived, TYPE_ID aBase ) const
249{
250 if( aDerived == aBase )
251 return true;
252
253 auto derived = m_classes.find( aDerived );
254 wxCHECK( derived != m_classes.end(), false ); // missing class description
255
256 // traverse the hierarchy seeking for the base class
257 for( const std::reference_wrapper<CLASS_DESC>& base : derived->second.m_bases )
258 {
259 if( IsOfType( base.get().m_id, aBase ) )
260 return true;
261 }
262
263 return false;
264}
265
266
268{
269 for( std::pair<const TYPE_ID, CLASS_DESC>& classEntry : m_classes )
270 classEntry.second.rebuild();
271
272 m_dirty = false;
273}
274
275
277{
278 auto it = m_classes.find( aTypeId );
279
280 if( it == m_classes.end() )
281 tie( it, std::ignore ) = m_classes.emplace( aTypeId, CLASS_DESC( aTypeId ) );
282
283 return it->second;
284}
285
286
288{
289 std::set<std::pair<size_t, wxString>> replaced;
290 std::set<std::pair<size_t, wxString>> masked;
291 m_allProperties.clear();
293
294 // We need to keep properties sorted to be able to use std::set_* functions
295 sort( m_allProperties.begin(), m_allProperties.end() );
296
297 std::vector<wxString> displayOrder;
298 std::set<wxString> groups;
299
300 auto collectGroups =
301 [&]( std::set<wxString>& aSet, std::vector<wxString>& aResult )
302 {
303 auto collectGroupsRecursive =
304 []( auto& aSelf, std::set<wxString>& aSetR, std::vector<wxString>& aResultR,
305 const CLASS_DESC& aClassR ) -> void
306 {
307 for( const wxString& group : aClassR.m_groupDisplayOrder )
308 {
309 if( !aSetR.count( group ) )
310 {
311 aSetR.insert( group );
312 aResultR.emplace_back( group );
313 }
314 }
315
316 for( const CLASS_DESC& base : aClassR.m_bases )
317 aSelf( aSelf, aSetR, aResultR, base );
318 };
319
320 collectGroupsRecursive( collectGroupsRecursive, aSet, aResult, *this );
321 };
322
323 // TODO(JE): This currently relies on rebuild() happening after all properties are added
324 // separate out own groups vs. all groups to fix
325 collectGroups( groups, displayOrder );
326 m_groupDisplayOrder = std::move( displayOrder );
327}
328
329
330void PROPERTY_MANAGER::CLASS_DESC::collectPropsRecur( std::vector<PROPERTY_BASE*>& aResult,
331 std::set<std::pair<size_t, wxString>>& aReplaced,
332 std::map<PROPERTY_BASE*, int>& aDisplayOrder,
333 std::set<std::pair<size_t, wxString>>& aMasked ) const
334{
335 for( const std::pair<size_t, wxString>& replacedEntry : m_replaced )
336 aReplaced.emplace( replacedEntry );
337
338 for( const std::pair<size_t, wxString>& maskedEntry : m_maskedBaseProperties )
339 aMasked.emplace( maskedEntry );
340
341 /*
342 * We want to insert our own properties in forward order, but earlier than anything already in
343 * the list (which will have been added by a subclass of us)
344 */
345 int displayOrderStart = 0;
346
347 if( !aDisplayOrder.empty() )
348 {
349 int firstSoFar = std::min_element( aDisplayOrder.begin(), aDisplayOrder.end(),
350 []( const std::pair<PROPERTY_BASE*, int>& aFirst,
351 const std::pair<PROPERTY_BASE*, int>& aSecond )
352 {
353 return aFirst.second < aSecond.second;
354 } )->second;
355
356 displayOrderStart = firstSoFar - m_ownProperties.size();
357 }
358
359 int idx = 0;
360
361 for( PROPERTY_BASE* property : m_ownDisplayOrder )
362 {
363 std::set<std::pair<size_t, wxString>>::key_type propertyKey = std::make_pair( property->OwnerHash(),
364 property->Name() );
365 // Do not store replaced properties
366 if( aReplaced.count( propertyKey ) )
367 continue;
368
369 // Do not store masked properties
370 if( aMasked.count( propertyKey ) )
371 continue;
372
373 aDisplayOrder[property] = displayOrderStart + idx++;
374 aResult.push_back( property );
375 }
376
377 // Iterate backwards so that replaced properties appear before base properties
378 for( std::reference_wrapper<CLASS_DESC> base : std::ranges::reverse_view( m_bases ) )
379 base.get().collectPropsRecur( aResult, aReplaced, aDisplayOrder, aMasked );
380}
381
382
384{
385 CLASSES_INFO rv;
386
387 for( std::pair<const TYPE_ID, CLASS_DESC>& classEntry : m_classes )
388 {
390
391 info.type = classEntry.first;
392 info.name = m_classNames[classEntry.first];
393
394 for( PROPERTY_BASE* prop : classEntry.second.m_allProperties )
395 info.properties.push_back( prop );
396
397 rv.push_back( info );
398 }
399
400 return rv;
401}
402
403
405{
406 auto callListeners =
407 [&]( TYPE_ID typeId )
408 {
409 auto listeners = m_listeners.find( typeId );
410
411 if( listeners != m_listeners.end() )
412 {
413 for( const PROPERTY_LISTENER& listener : listeners->second )
414 listener( aObject, aProperty, m_managedCommit );
415 }
416 };
417
418 CLASS_DESC& objectClass = getClass( TYPE_HASH( *aObject ) );
419
420 callListeners( objectClass.m_id );
421
422 for( CLASS_DESC& superClass : objectClass.m_bases )
423 callListeners( superClass.m_id );
424}
425
426
428{
429 wxCHECK2_MSG( PROPERTY_MANAGER::Instance().m_managedCommit == nullptr,
430 return, wxT( "Can't have more than one managed commit at a time!" ) );
431
433}
434
435
437{
438 wxASSERT_MSG( PROPERTY_MANAGER::Instance().m_managedCommit != nullptr,
439 wxT( "Something went wrong: m_managedCommit already null!" ) );
440
442}
const char * name
Definition: DXF_plotter.cpp:62
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition: commit.h:73
Class that other classes need to inherit from, in order to be inspectable.
Definition: inspectable.h:37
PROPERTY_BASE & SetGroup(const wxString &aGroup)
Definition: property.h:343
virtual size_t BaseHash() const =0
Return type-id of the Base class.
virtual bool Writeable(INSPECTABLE *aObject) const
Definition: property.h:278
bool Available(INSPECTABLE *aObject) const
Return true if aObject offers this PROPERTY.
Definition: property.h:250
const wxString & Name() const
Definition: property.h:218
virtual size_t OwnerHash() const =0
Return type-id of the Owner class.
PROPERTY_COMMIT_HANDLER(COMMIT *aCommit)
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:74
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())
Definition: property_mgr.h:342
CLASSES_INFO GetAllClasses()
std::vector< CLASS_INFO > CLASSES_INFO
Definition: property_mgr.h:238
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
Definition: property_mgr.h:346
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()
Definition: property_mgr.h:76
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
Definition: property_mgr.h:339
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)
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 ...
const std::map< PROPERTY_BASE *, int > & GetDisplayOrder(TYPE_ID aType) const
std::map< TYPE_ID, std::vector< PROPERTY_LISTENER > > m_listeners
Definition: property_mgr.h:344
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.
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
Map of all available types.
Definition: property_mgr.h:336
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)
Definition: property.h:72
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
Definition: property_mgr.h:49
size_t TYPE_ID
Unique type identifier.
Definition: property_mgr.h:47
Returns metadata for a specific type.
Definition: property_mgr.h:275
std::vector< std::reference_wrapper< CLASS_DESC > > m_bases
Properties unique to this type (i.e. not inherited)
Definition: property_mgr.h:287
std::set< std::pair< size_t, wxString > > m_replaced
Recreates the list of properties.
Definition: property_mgr.h:320
std::vector< wxString > m_groupDisplayOrder
Non-owning list of classes's direct properties in display order.
Definition: property_mgr.h:311
std::map< std::pair< size_t, wxString >, std::function< bool(INSPECTABLE *)> > m_availabilityOverrides
Overrides for base class property writeable status.
Definition: property_mgr.h:299
std::map< std::pair< size_t, wxString >, std::function< bool(INSPECTABLE *)> > m_writeabilityOverrides
All properties (both unique to the type and inherited)
Definition: property_mgr.h:302
std::map< wxString, std::unique_ptr< PROPERTY_BASE > > m_ownProperties
Type converters available for this type.
Definition: property_mgr.h:290
std::vector< PROPERTY_BASE * > m_ownDisplayOrder
The property groups provided by this class.
Definition: property_mgr.h:314
std::set< std::pair< size_t, wxString > > m_maskedBaseProperties
Overrides for base class property availabilities.
Definition: property_mgr.h:296
std::map< PROPERTY_BASE *, int > m_displayOrder
List of property groups provided by this class in display order.
Definition: property_mgr.h:308
std::vector< PROPERTY_BASE * > m_allProperties
Compiled display order for all properties.
Definition: property_mgr.h:305
std::map< TYPE_ID, std::unique_ptr< TYPE_CAST_BASE > > m_typeCasts
Properties from bases that should be masked (hidden) on this subclass.
Definition: property_mgr.h:293
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
void rebuild()
Traverses the class inheritance hierarchy bottom-to-top, gathering all properties available to a type...
const TYPE_ID m_id
Types after which this type inherits.
Definition: property_mgr.h:284
std::set< wxString > m_groups
Replaced properties (TYPE_ID / name)
Definition: property_mgr.h:317