KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 <[email protected]>
5 * Copyright (C) 2014 Henner Zeller <[email protected]>
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 <core/kicad_algo.h>
27#include <eda_pattern_match.h>
28#include <lib_tree_item.h>
29#include <pgm_base.h>
30#include <string_utils.h>
31
32
33
34void LIB_TREE_NODE::RebuildSearchTerms( const std::vector<wxString>& aShownColumns )
35{
37
38 for( const auto& [name, value] : m_Fields )
39 {
40 if( alg::contains( aShownColumns, name ) )
41 m_SearchTerms.push_back( SEARCH_TERM( value, 4 ) );
42 }
43}
44
45
46void LIB_TREE_NODE::AssignIntrinsicRanks( const std::vector<wxString>& aShownColumns, bool presorted )
47{
48 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
49 child->RebuildSearchTerms( aShownColumns );
50
51 std::vector<LIB_TREE_NODE*> sort_buf;
52
53 if( presorted )
54 {
55 int max = m_Children.size() - 1;
56
57 for( int i = 0; i <= max; ++i )
58 m_Children[i]->m_IntrinsicRank = max - i;
59 }
60 else
61 {
62 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
63 sort_buf.push_back( child.get() );
64
65 std::sort( sort_buf.begin(), sort_buf.end(),
66 []( LIB_TREE_NODE* a, LIB_TREE_NODE* b ) -> bool
67 {
68 return StrNumCmp( a->m_Name, b->m_Name, true ) > 0;
69 } );
70
71 for( int i = 0; i < (int) sort_buf.size(); ++i )
72 sort_buf[i]->m_IntrinsicRank = i;
73 }
74}
75
76
77void LIB_TREE_NODE::SortNodes( bool aUseScores )
78{
79 std::sort( m_Children.begin(), m_Children.end(),
80 [&]( std::unique_ptr<LIB_TREE_NODE>& a, std::unique_ptr<LIB_TREE_NODE>& b )
81 {
82 return Compare( *a, *b, aUseScores );
83 } );
84
85 for( std::unique_ptr<LIB_TREE_NODE>& node: m_Children )
86 node->SortNodes( aUseScores );
87}
88
89
90bool LIB_TREE_NODE::Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& aNode2,
91 bool aUseScores )
92{
93 if( aNode1.m_Type != aNode2.m_Type )
94 return aNode1.m_Type < aNode2.m_Type;
95
96 // Recently used sorts at top
97 if( aNode1.m_IsRecentlyUsedGroup )
98 {
99 if( aNode2.m_IsRecentlyUsedGroup )
100 {
101 // Make sure "-- Recently Used" is always at the top
102 // Start by checking the name of aNode2, because we want to satisfy the irreflexive
103 // property of the strict weak ordering.
104 if( aNode2.m_IsRecentlyUsedGroup )
105 return false;
106 else if( aNode1.m_IsRecentlyUsedGroup )
107 return true;
108
109 return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
110 }
111 else
112 {
113 return true;
114 }
115 }
116 else if( aNode2.m_Name.StartsWith( wxT( "-- " ) ) )
117 {
118 return false;
119 }
120
121 // Pinned nodes go next
122 if( aNode1.m_Pinned && !aNode2.m_Pinned )
123 return true;
124 else if( aNode2.m_Pinned && !aNode1.m_Pinned )
125 return false;
126
127 if( aUseScores )
128 {
129 // Exact matches form a strictly higher tier than any accumulation of partial matches.
130 if( aNode1.m_ExactMatch != aNode2.m_ExactMatch )
131 return aNode1.m_ExactMatch;
132
133 if( aNode1.m_Score != aNode2.m_Score )
134 return aNode1.m_Score > aNode2.m_Score;
135 }
136
137 if( aNode1.m_IntrinsicRank != aNode2.m_IntrinsicRank )
138 return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
139
140 return reinterpret_cast<const void*>( &aNode1 ) < reinterpret_cast<const void*>( &aNode2 );
141}
142
143
145 m_Parent( nullptr ),
146 m_Type( TYPE::INVALID ),
147 m_IntrinsicRank( 0 ),
148 m_Score( 0 ),
149 m_ExactMatch( false ),
150 m_Pinned( false ),
151 m_PinCount( 0 ),
152 m_Unit( 0 ),
153 m_IsRoot( false ),
154 m_IsPower( false ),
155 m_IsRecentlyUsedGroup( false ),
157{}
158
159
161{
162 m_Parent = aParent;
163 m_Type = TYPE::UNIT;
164
165 m_Unit = aUnit;
166 m_LibId = aParent->m_LibId;
167 m_Name = aItem->GetUnitName( aUnit );
168
169 m_IntrinsicRank = -aUnit;
170}
171
172
173void LIB_TREE_NODE_UNIT::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
174 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
175{
176 m_Score = 1;
177 m_ExactMatch = false;
178
179 // aMatchers test results are inherited from parent
180 if( !aMatchers.empty() )
181 {
182 m_Score = m_Parent->m_Score;
183 m_ExactMatch = m_Parent->m_ExactMatch;
184 }
185
186 if( aFilter && !(*aFilter)(*this) )
187 m_Score = 0;
188}
189
190
192{
193 m_Type = TYPE::ITEM;
194 m_Parent = aParent;
195
196 m_LibId.SetLibNickname( aItem->GetLibNickname() );
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
207
208 m_IsRoot = aItem->IsRoot();
209 m_IsPower = aItem->IsPowerSymbol();
210
211 if( aItem->GetSubUnitCount() > 1 )
212 {
213 for( int u = 1; u <= aItem->GetSubUnitCount(); ++u )
214 AddUnit( aItem, u );
215 }
216}
217
218
220{
221 LIB_TREE_NODE_UNIT* unit = new LIB_TREE_NODE_UNIT( this, aItem, aUnit );
222 m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( unit ) );
223 return *unit;
224}
225
226
228{
229 m_LibId.SetLibNickname( aItem->GetLIB_ID().GetLibNickname() );
230 m_LibId.SetLibItemName( aItem->GetName() );
231
232 m_Name = aItem->GetName();
233 m_Desc = aItem->GetDesc();
234
235 aItem->GetChooserFields( m_Fields );
236
238
239 m_IsRoot = aItem->IsRoot();
240 m_IsPower = aItem->IsPowerSymbol();
241 m_Children.clear();
242
243 for( int u = 1; u <= aItem->GetSubUnitCount(); ++u )
244 AddUnit( aItem, u );
245}
246
247
248void LIB_TREE_NODE_ITEM::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
249 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
250{
251 m_Score = 1;
252 m_ExactMatch = false;
253
254 for( const std::unique_ptr<EDA_COMBINED_MATCHER>& matcher : aMatchers )
255 {
256 bool exact = false;
257 int score = matcher->ScoreTerms( m_SearchTerms, &exact );
258
259 if( score == 0 )
260 {
261 m_Score = 0;
262 m_ExactMatch = false;
263 break;
264 }
265
266 m_Score += score;
267 m_ExactMatch |= exact;
268 }
269
270 if( aFilter && !(*aFilter)(*this) )
271 m_Score = 0;
272
273 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
274 child->UpdateScore( aMatchers, 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( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
300 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
301{
302 if( m_Children.empty() )
303 {
304 m_Score = 1;
305 m_ExactMatch = false;
306
307 for( const std::unique_ptr<EDA_COMBINED_MATCHER>& matcher : aMatchers )
308 {
309 bool exact = false;
310 int score = matcher->ScoreTerms( m_SearchTerms, &exact );
311
312 if( score == 0 )
313 {
314 m_Score = 0;
315 m_ExactMatch = false;
316 break;
317 }
318
319 m_Score += score;
320 m_ExactMatch |= exact;
321 }
322 }
323 else
324 {
325 m_Score = 0;
326 m_ExactMatch = false;
327
328 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
329 {
330 child->UpdateScore( aMatchers, aFilter );
331 m_Score = std::max( m_Score, child->m_Score );
332 m_ExactMatch |= child->m_ExactMatch;
333 }
334 }
335}
336
337
339{
340 m_Type = TYPE::ROOT;
341}
342
343
344LIB_TREE_NODE_LIBRARY& LIB_TREE_NODE_ROOT::AddLib( wxString const& aName, wxString const& aDesc )
345{
346 LIB_TREE_NODE_LIBRARY* lib = new LIB_TREE_NODE_LIBRARY( this, aName, aDesc );
347 m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( lib ) );
348 return *lib;
349}
350
351
352void LIB_TREE_NODE_ROOT::RemoveGroup( bool aRecentlyUsedGroup, bool aAlreadyPlacedGroup )
353{
354 m_Children.erase( std::remove_if( m_Children.begin(), m_Children.end(),
355 [&]( std::unique_ptr<LIB_TREE_NODE>& aNode )
356 {
357 if( aRecentlyUsedGroup && aNode->m_IsRecentlyUsedGroup )
358 return true;
359
360 if( aAlreadyPlacedGroup && aNode->m_IsAlreadyPlacedGroup )
361 return true;
362
363 return false;
364 } ),
365 m_Children.end() );
366}
367
368
370{
371 m_Children.clear();
372}
373
374
375void LIB_TREE_NODE_ROOT::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
376 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
377{
378 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
379 child->UpdateScore( aMatchers, aFilter );
380}
381
const char * name
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).
virtual wxString GetLibNickname() const =0
virtual int GetSubUnitCount() const
For items with units, return the number of units.
virtual wxString GetUnitName(int aUnit) const
For items with units, return an identifier for unit x.
virtual wxString GetFootprint()
For items with footprint fields.
virtual bool IsPowerSymbol() const
For symbols that could be a power symbol.
virtual LIB_ID GetLIB_ID() const =0
virtual wxString GetDesc()=0
virtual bool IsRoot() const
For items having aliases, IsRoot() indicates the principal item.
virtual wxString GetName() const =0
virtual std::vector< SEARCH_TERM > & GetSearchTerms()=0
virtual int GetPinCount()
The pin count for symbols or the unique pad count for footprints.
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...
Node type: LIB_ID.
void UpdateScore(const std::vector< std::unique_ptr< EDA_COMBINED_MATCHER > > &aMatchers, 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 ...
void UpdateScore(const std::vector< std::unique_ptr< EDA_COMBINED_MATCHER > > &aMatchers, std::function< bool(LIB_TREE_NODE &aNode)> *aFilter) override
Update the score for this part.
LIB_TREE_NODE_ITEM & AddItem(LIB_TREE_ITEM *aItem)
Construct a new alias node, add it to this library, and return it.
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.
LIB_TREE_NODE_ROOT()
Construct the root node.
void UpdateScore(const std::vector< std::unique_ptr< EDA_COMBINED_MATCHER > > &aMatchers, std::function< bool(LIB_TREE_NODE &aNode)> *aFilter) override
Update the score for this part.
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(const std::vector< std::unique_ptr< EDA_COMBINED_MATCHER > > &aMatchers, std::function< bool(LIB_TREE_NODE &aNode)> *aFilter) override
Update the score for this part.
void SortNodes(bool aUseScores)
Sort child nodes quickly and recursively (IntrinsicRanks must have been set).
void RebuildSearchTerms(const std::vector< wxString > &aShownColumns)
Rebuild search terms from source search terms and shown fields.
static bool Compare(LIB_TREE_NODE const &aNode1, LIB_TREE_NODE const &aNode2, bool aUseScores)
Compare two nodes.
std::vector< SEARCH_TERM > m_sourceSearchTerms
bool m_IsAlreadyPlacedGroup
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
int m_IntrinsicRank
The rank of the item before any search terms are applied.
void AssignIntrinsicRanks(const std::vector< wxString > &aShownColumns, bool presorted=false)
Store intrinsic ranks on all children of this node.
Abstract pattern-matching tool and implementations.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:100
see class PGM_BASE
A structure for storing weighted search terms.