KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
lib_tree_model.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) 2017 Chris Pavlina <pavlina.chris@gmail.com>
5 * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
6 * Copyright (C) 2023 CERN
7 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * 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 <lib_tree_model.h>
24
25#include <algorithm>
26#include <eda_pattern_match.h>
27#include <lib_tree_item.h>
28#include <pgm_base.h>
29#include <string_utils.h>
30
31
32
34{
35 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
36 child->ResetScore();
37
38 m_Score = 0;
39}
40
41
43{
44 std::vector<LIB_TREE_NODE*> sort_buf;
45
46 if( presorted )
47 {
48 int max = m_Children.size() - 1;
49
50 for( int i = 0; i <= max; ++i )
51 m_Children[i]->m_IntrinsicRank = max - i;
52 }
53 else
54 {
55 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
56 sort_buf.push_back( child.get() );
57
58 std::sort( sort_buf.begin(), sort_buf.end(),
59 []( LIB_TREE_NODE* a, LIB_TREE_NODE* b ) -> bool
60 {
61 return StrNumCmp( a->m_Name, b->m_Name, true ) > 0;
62 } );
63
64 for( int i = 0; i < (int) sort_buf.size(); ++i )
65 sort_buf[i]->m_IntrinsicRank = i;
66 }
67}
68
69
70void LIB_TREE_NODE::SortNodes( bool aUseScores )
71{
72 std::sort( m_Children.begin(), m_Children.end(),
73 [&]( std::unique_ptr<LIB_TREE_NODE>& a, std::unique_ptr<LIB_TREE_NODE>& b )
74 {
75 return Compare( *a, *b, aUseScores );
76 } );
77
78 for( std::unique_ptr<LIB_TREE_NODE>& node: m_Children )
79 node->SortNodes( aUseScores );
80}
81
82
83bool LIB_TREE_NODE::Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& aNode2,
84 bool aUseScores )
85{
86 if( aNode1.m_Type != aNode2.m_Type )
87 return aNode1.m_Type < aNode2.m_Type;
88
89 // Recently used sorts at top
90 if( aNode1.m_IsRecentlyUsedGroup )
91 {
92 if( aNode2.m_IsRecentlyUsedGroup )
93 {
94 // Make sure "-- Recently Used" is always at the top
95 // Start by checking the name of aNode2, because we want to satisfy the irreflexive
96 // property of the strict weak ordering.
97 if( aNode2.m_IsRecentlyUsedGroup )
98 return false;
99 else if( aNode1.m_IsRecentlyUsedGroup )
100 return true;
101
102 return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
103 }
104 else
105 {
106 return true;
107 }
108 }
109 else if( aNode2.m_Name.StartsWith( wxT( "-- " ) ) )
110 {
111 return false;
112 }
113
114 // Pinned nodes go next
115 if( aNode1.m_Pinned && !aNode2.m_Pinned )
116 return true;
117 else if( aNode2.m_Pinned && !aNode1.m_Pinned )
118 return false;
119
120 if( aUseScores && aNode1.m_Score != aNode2.m_Score )
121 return aNode1.m_Score > aNode2.m_Score;
122
123 if( aNode1.m_IntrinsicRank != aNode2.m_IntrinsicRank )
124 return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
125
126 return reinterpret_cast<const void*>( &aNode1 ) < reinterpret_cast<const void*>( &aNode2 );
127}
128
129
131 : m_Parent( nullptr ),
132 m_Type( TYPE::INVALID ),
133 m_IntrinsicRank( 0 ),
134 m_Score( 0 ),
135 m_Pinned( false ),
136 m_PinCount( 0 ),
137 m_Unit( 0 ),
138 m_IsRoot( false ),
139 m_IsRecentlyUsedGroup( false ),
140 m_IsAlreadyPlacedGroup( false )
141{}
142
143
145{
146 static void* locale = nullptr;
147 static wxString namePrefix;
148
149 // Fetching translations can take a surprising amount of time when loading libraries,
150 // so only do it when necessary.
151 if( Pgm().GetLocale() != locale )
152 {
153 namePrefix = _( "Unit" );
154 locale = Pgm().GetLocale();
155 }
156
157 m_Parent = aParent;
158 m_Type = TYPE::UNIT;
159
160 m_Unit = aUnit;
161 m_LibId = aParent->m_LibId;
162
163 m_Name = namePrefix + " " + aItem->GetUnitReference( aUnit );
164
165 if( aItem->HasUnitDisplayName( aUnit ) )
166 m_Desc = aItem->GetUnitDisplayName( aUnit );
167 else
168 m_Desc = wxEmptyString;
169
170 m_IntrinsicRank = -aUnit;
171}
172
173
174void LIB_TREE_NODE_UNIT::UpdateScore( EDA_COMBINED_MATCHER* aMatcher, const wxString& aLib,
175 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
176{
177 // aMatcher test results are inherited from parent
178 if( aMatcher )
180
181 // aFilter test is subtractive
182 if( aFilter && !(*aFilter)(*this) )
183 m_Score = 0;
184
185 // show all nodes if no search/filter/etc. criteria are given
186 if( !aMatcher && aLib.IsEmpty() && ( !aFilter || (*aFilter)(*this) ) )
187 m_Score = 1;
188}
189
190
192{
193 m_Type = TYPE::ITEM;
194 m_Parent = aParent;
195
197 m_LibId.SetLibItemName( aItem->GetName() );
198
199 m_Name = aItem->GetName();
200 m_Desc = aItem->GetDesc();
201 m_Footprint = aItem->GetFootprint();
202 m_PinCount = aItem->GetPinCount();
203
204 aItem->GetChooserFields( m_Fields );
205
206 m_SearchTerms = aItem->GetSearchTerms();
207
208 m_IsRoot = aItem->IsRoot();
209
210 if( aItem->GetSubUnitCount() > 1 )
211 {
212 for( int u = 1; u <= aItem->GetSubUnitCount(); ++u )
213 AddUnit( aItem, u );
214 }
215}
216
217
219{
220 LIB_TREE_NODE_UNIT* unit = new LIB_TREE_NODE_UNIT( this, aItem, aUnit );
221 m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( unit ) );
222 return *unit;
223}
224
225
227{
229 m_LibId.SetLibItemName( aItem->GetName() );
230
231 m_Name = aItem->GetName();
232 m_Desc = aItem->GetDesc();
233
234 aItem->GetChooserFields( m_Fields );
235
236 m_SearchTerms = aItem->GetSearchTerms();
237
238 m_IsRoot = aItem->IsRoot();
239 m_Children.clear();
240
241 for( int u = 1; u <= aItem->GetSubUnitCount(); ++u )
242 AddUnit( aItem, u );
243}
244
245
246void LIB_TREE_NODE_ITEM::UpdateScore( EDA_COMBINED_MATCHER* aMatcher, const wxString& aLib,
247 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
248{
249 // aMatcher test is additive, but if we don't match the given term at all, it nulls out
250 if( aMatcher )
251 {
252 int currentScore = aMatcher->ScoreTerms( m_SearchTerms );
253
254 // This is a hack: the second phase of search in the adapter will look for a tokenized
255 // LIB_ID and send the lib part down here. While we generally want to prune ourselves
256 // out here (by setting score to -1) the first time we fail to match a search term,
257 // we want to give the same search term a second chance if it has been split from a library
258 // name.
259 if( ( m_Score >= 0 || !aLib.IsEmpty() ) && currentScore > 0 )
260 m_Score += currentScore;
261 else
262 m_Score = -1; // Item has failed to match this term, rule it out
263 }
264
265 // aFilter test is subtractive
266 if( aFilter && !(*aFilter)(*this) )
267 m_Score = 0;
268
269 // show all nodes if no search/filter/etc. criteria are given
270 if( !aMatcher && aLib.IsEmpty() && ( !aFilter || (*aFilter)(*this) ) )
271 m_Score = 1;
272
273 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
274 child->UpdateScore( aMatcher, aLib, aFilter );
275}
276
277
279 wxString const& aDesc )
280{
281 m_Type = TYPE::LIBRARY;
282 m_Name = aName;
283 m_Desc = aDesc;
284 m_Parent = aParent;
285 m_LibId.SetLibNickname( aName );
286
287 m_SearchTerms.emplace_back( SEARCH_TERM( aName, 8 ) );
288}
289
290
292{
293 LIB_TREE_NODE_ITEM* item = new LIB_TREE_NODE_ITEM( this, aItem );
294 m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( item ) );
295 return *item;
296}
297
298
299void LIB_TREE_NODE_LIBRARY::UpdateScore( EDA_COMBINED_MATCHER* aMatcher, const wxString& aLib,
300 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
301{
302 int maxChildScore = 0;
303
304 // children are additive
305 if( !m_Children.empty() )
306 {
307 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
308 {
309 child->UpdateScore( aMatcher, aLib, aFilter );
310 maxChildScore = std::max( maxChildScore, child->m_Score );
311 }
312
313 m_Score = std::max( m_Score, maxChildScore );
314 }
315
316 // aLib test is additive
317 if( !aLib.IsEmpty() && m_Name.Lower().Matches( aLib ) )
318 m_Score += 1;
319
320 // aMatcher test is additive
321 if( aMatcher )
322 {
323 int ownScore = aMatcher->ScoreTerms( m_SearchTerms );
324 m_Score += ownScore;
325
326 // If we have a hit on a library, show all children in that library that pass the filter
327 if( maxChildScore <= 0 && ownScore > 0 )
328 {
329 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
330 {
331 if( !aFilter || (*aFilter)( *this ) )
332 child->ForceScore( 1 );
333
334 maxChildScore = std::max( maxChildScore, child->m_Score );
335 }
336 }
337 }
338
339 // If all children are excluded for one reason or another, drop the library from the list
340 if( !m_Children.empty() && maxChildScore <= 0 )
341 m_Score = 0;
342
343 // show all nodes if no search/filter/etc. criteria are given
344 if( m_Children.empty() && !aMatcher && aLib.IsEmpty() && ( !aFilter || (*aFilter)(*this) ) )
345 m_Score = 1;
346}
347
348
350{
351 m_Type = TYPE::ROOT;
352}
353
354
355LIB_TREE_NODE_LIBRARY& LIB_TREE_NODE_ROOT::AddLib( wxString const& aName, wxString const& aDesc )
356{
357 LIB_TREE_NODE_LIBRARY* lib = new LIB_TREE_NODE_LIBRARY( this, aName, aDesc );
358 m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( lib ) );
359 return *lib;
360}
361
362
363void LIB_TREE_NODE_ROOT::RemoveGroup( bool aRecentlyUsedGroup, bool aAlreadyPlacedGroup )
364{
365 m_Children.erase( std::remove_if( m_Children.begin(), m_Children.end(),
366 [&]( std::unique_ptr<LIB_TREE_NODE>& aNode )
367 {
368 if( aRecentlyUsedGroup && aNode->m_IsRecentlyUsedGroup )
369 return true;
370
371 if( aAlreadyPlacedGroup && aNode->m_IsAlreadyPlacedGroup )
372 return true;
373
374 return false;
375 } ),
376 m_Children.end() );
377}
378
379
381{
382 m_Children.clear();
383}
384
385
386void LIB_TREE_NODE_ROOT::UpdateScore( EDA_COMBINED_MATCHER* aMatcher, const wxString& aLib,
387 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
388{
389 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
390 child->UpdateScore( aMatcher, aLib, aFilter );
391}
392
int ScoreTerms(std::vector< SEARCH_TERM > &aWeightedTerms)
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition: lib_id.cpp:110
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition: lib_id.cpp:99
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:87
A mix-in to provide polymorphism between items stored in libraries (symbols, aliases and footprints).
Definition: lib_tree_item.h:41
virtual wxString GetUnitReference(int aUnit)
For items with units, return an identifier for unit x.
Definition: lib_tree_item.h:84
virtual wxString GetLibNickname() const =0
virtual int GetSubUnitCount() const
For items with units, return the number of units.
Definition: lib_tree_item.h:79
virtual std::vector< SEARCH_TERM > GetSearchTerms()
Definition: lib_tree_item.h:59
virtual bool HasUnitDisplayName(int aUnit)
For items with units, return true if a display name is set for x.
Definition: lib_tree_item.h:94
virtual wxString GetFootprint()
For items with footprint fields.
Definition: lib_tree_item.h:69
virtual LIB_ID GetLIB_ID() const =0
virtual wxString GetDesc()=0
virtual bool IsRoot() const
For items having aliases, IsRoot() indicates the principal item.
Definition: lib_tree_item.h:64
virtual wxString GetName() const =0
virtual int GetPinCount()
The pin count for symbols or the unique pad count for footprints.
Definition: lib_tree_item.h:74
virtual wxString GetUnitDisplayName(int aUnit)
For items with units, return a display name for unit x.
Definition: lib_tree_item.h:89
virtual void GetChooserFields(std::map< wxString, wxString > &aColumnMap)
Retrieves a key/value map of the fields on this item that should be exposed to the library browser/ch...
Definition: lib_tree_item.h:57
Node type: LIB_ID.
void UpdateScore(EDA_COMBINED_MATCHER *aMatcher, const wxString &aLib, std::function< bool(LIB_TREE_NODE &aNode)> *aFilter) override
Perform the actual search.
LIB_TREE_NODE_ITEM(LIB_TREE_NODE_ITEM const &_)=delete
The addresses of CMP_TREE_NODEs are used as unique IDs for the wxDataViewModel, so don't let them be ...
LIB_TREE_NODE_UNIT & AddUnit(LIB_TREE_ITEM *aItem, int aUnit)
Add a new unit to the component and return it.
void Update(LIB_TREE_ITEM *aItem)
Update the node using data from a LIB_ALIAS object.
Node type: library.
LIB_TREE_NODE_LIBRARY(LIB_TREE_NODE_LIBRARY const &_)=delete
The addresses of CMP_TREE_NODEs are used as unique IDs for the wxDataViewModel, so don't let them be ...
LIB_TREE_NODE_ITEM & AddItem(LIB_TREE_ITEM *aItem)
Construct a new alias node, add it to this library, and return it.
void UpdateScore(EDA_COMBINED_MATCHER *aMatcher, const wxString &aLib, std::function< bool(LIB_TREE_NODE &aNode)> *aFilter) override
Update the score for this part.
void RemoveGroup(bool aRecentlyUsedGroup, bool aAlreadyPlacedGroup)
Remove a library node from the root.
LIB_TREE_NODE_LIBRARY & AddLib(wxString const &aName, wxString const &aDesc)
Construct an empty library node, add it to the root, and return it.
void UpdateScore(EDA_COMBINED_MATCHER *aMatcher, const wxString &aLib, std::function< bool(LIB_TREE_NODE &aNode)> *aFilter) override
Update the score for this part.
LIB_TREE_NODE_ROOT()
Construct the root node.
void Clear()
Clear the tree.
Node type: unit of component.
LIB_TREE_NODE_UNIT(LIB_TREE_NODE_UNIT const &_)=delete
The addresses of CMP_TREE_NODEs are used as unique IDs for the wxDataViewModel, so don't let them be ...
void UpdateScore(EDA_COMBINED_MATCHER *aMatcher, const wxString &aLib, std::function< bool(LIB_TREE_NODE &aNode)> *aFilter) override
Update the score for this part.
Model class in the component selector Model-View-Adapter (mediated MVC) architecture.
void SortNodes(bool aUseScores)
Sort child nodes quickly and recursively (IntrinsicRanks must have been set).
static bool Compare(LIB_TREE_NODE const &aNode1, LIB_TREE_NODE const &aNode2, bool aUseScores)
Compare two nodes.
enum TYPE m_Type
bool m_IsRecentlyUsedGroup
std::vector< SEARCH_TERM > m_SearchTerms
std::map< wxString, wxString > m_Fields
List of weighted search terms.
PTR_VECTOR m_Children
wxString m_Footprint
LIB_TREE_NODE * m_Parent
virtual void ResetScore()
Initialize scores recursively.
int m_IntrinsicRank
The rank of the item before any search terms are applied.
void AssignIntrinsicRanks(bool presorted=false)
Store intrinsic ranks on all children of this node.
virtual wxLocale * GetLocale()
Definition: pgm_base.h:174
#define _(s)
Abstract pattern-matching tool and implementations.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
A structure for storing weighted search terms.