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
20 * along with this program. If not, see <https://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, bool aUseScores )
91{
92 if( aNode1.m_Type != aNode2.m_Type )
93 return aNode1.m_Type < aNode2.m_Type;
94
95 // Recently used sorts at top
96 if( aNode1.m_IsRecentlyUsedGroup )
97 {
98 if( aNode2.m_IsRecentlyUsedGroup )
99 {
100 // Make sure "-- Recently Used" is always at the top
101 // Start by checking the name of aNode2, because we want to satisfy the irreflexive
102 // property of the strict weak ordering.
103 if( aNode2.m_IsRecentlyUsedGroup )
104 return false;
105 else if( aNode1.m_IsRecentlyUsedGroup )
106 return true;
107
108 return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
109 }
110 else
111 {
112 return true;
113 }
114 }
115 else if( aNode2.m_Name.StartsWith( wxT( "-- " ) ) )
116 {
117 return false;
118 }
119
120 // Pinned nodes go next
121 if( aNode1.m_Pinned && !aNode2.m_Pinned )
122 return true;
123 else if( aNode2.m_Pinned && !aNode1.m_Pinned )
124 return false;
125
126 if( aUseScores )
127 {
128 // Exact matches form a strictly higher tier than any accumulation of partial matches.
129 if( aNode1.m_ExactMatch != aNode2.m_ExactMatch )
130 return aNode1.m_ExactMatch;
131
132 if( aNode1.m_Score != aNode2.m_Score )
133 return aNode1.m_Score > aNode2.m_Score;
134 }
135
136 if( aNode1.m_IntrinsicRank != aNode2.m_IntrinsicRank )
137 return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
138
139 return reinterpret_cast<const void*>( &aNode1 ) < reinterpret_cast<const void*>( &aNode2 );
140}
141
142
144 m_Parent( nullptr ),
145 m_Type( TYPE::INVALID ),
146 m_IntrinsicRank( 0 ),
147 m_Score( 0 ),
148 m_ExactMatch( false ),
149 m_Pinned( false ),
150 m_PinCount( 0 ),
151 m_Unit( 0 ),
152 m_IsRoot( false ),
153 m_IsPower( false ),
154 m_IsRecentlyUsedGroup( false ),
156{}
157
158
160{
161 m_Parent = aParent;
162 m_Type = TYPE::UNIT;
163
164 m_Unit = aUnit;
165 m_LibId = aParent->m_LibId;
166 m_Name = aItem->GetUnitName( aUnit );
167
168 m_IntrinsicRank = -aUnit;
169}
170
171
172void LIB_TREE_NODE_UNIT::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
173 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
174{
175 m_Score = 1;
176 m_ExactMatch = false;
177
178 // aMatchers test results are inherited from parent
179 if( !aMatchers.empty() )
180 {
181 m_Score = m_Parent->m_Score;
182 m_ExactMatch = m_Parent->m_ExactMatch;
183 }
184
185 if( aFilter && !(*aFilter)(*this) )
186 m_Score = 0;
187}
188
189
191{
192 m_Type = TYPE::ITEM;
193 m_Parent = aParent;
194
195 m_LibId.SetLibNickname( aItem->GetLibNickname() );
196 m_LibId.SetLibItemName( aItem->GetName() );
197
198 m_Name = aItem->GetName();
199 m_Desc = aItem->GetDesc();
200 m_Footprint = aItem->GetFootprint();
201 m_PinCount = aItem->GetPinCount();
202
203 aItem->GetChooserFields( m_Fields );
204
206
207 m_IsRoot = aItem->IsRoot();
208 m_IsPower = aItem->IsPowerSymbol();
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{
228 m_LibId.SetLibNickname( aItem->GetLIB_ID().GetLibNickname() );
229 m_LibId.SetLibItemName( aItem->GetName() );
230
231 m_Name = aItem->GetName();
232 m_Desc = aItem->GetDesc();
233
234 aItem->GetChooserFields( m_Fields );
235
237
238 m_IsRoot = aItem->IsRoot();
239 m_IsPower = aItem->IsPowerSymbol();
240 m_Children.clear();
241
242 for( int u = 1; u <= aItem->GetSubUnitCount(); ++u )
243 AddUnit( aItem, u );
244}
245
246
247void LIB_TREE_NODE_ITEM::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
248 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
249{
250 m_Score = 1;
251 m_ExactMatch = false;
252
253 for( const std::unique_ptr<EDA_COMBINED_MATCHER>& matcher : aMatchers )
254 {
255 bool exact = false;
256 int score = matcher->ScoreTerms( m_SearchTerms, &exact );
257
258 if( score == 0 )
259 {
260 m_Score = 0;
261 m_ExactMatch = false;
262 break;
263 }
264
265 m_Score += score;
266 m_ExactMatch |= exact;
267 }
268
269 if( aFilter && !(*aFilter)(*this) )
270 m_Score = 0;
271
272 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
273 child->UpdateScore( aMatchers, aFilter );
274}
275
276
277LIB_TREE_NODE_LIBRARY::LIB_TREE_NODE_LIBRARY( LIB_TREE_NODE* aParent, wxString const& aName, wxString const& aDesc )
278{
279 m_Type = TYPE::LIBRARY;
280 m_Name = aName;
281 m_Desc = aDesc;
282 m_Parent = aParent;
283 m_LibId.SetLibNickname( aName );
284
285 // Use the source list, otherwise RebuildSearchTerms wipes it on the next rebuild.
286 m_sourceSearchTerms.emplace_back( SEARCH_TERM( aName, 8, true ) );
287}
288
289
291{
292 LIB_TREE_NODE_ITEM* item = new LIB_TREE_NODE_ITEM( this, aItem );
293 m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( item ) );
294 return *item;
295}
296
297
298void LIB_TREE_NODE_LIBRARY::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
299 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
300{
301 if( m_Children.empty() )
302 {
303 m_Score = 1;
304 m_ExactMatch = false;
305
306 for( const std::unique_ptr<EDA_COMBINED_MATCHER>& matcher : aMatchers )
307 {
308 bool exact = false;
309 int score = matcher->ScoreTerms( m_SearchTerms, &exact );
310
311 if( score == 0 )
312 {
313 m_Score = 0;
314 m_ExactMatch = false;
315 break;
316 }
317
318 m_Score += score;
319 m_ExactMatch |= exact;
320 }
321 }
322 else
323 {
324 m_Score = 0;
325 m_ExactMatch = false;
326
327 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
328 {
329 child->UpdateScore( aMatchers, aFilter );
330 m_Score = std::max( m_Score, child->m_Score );
331 m_ExactMatch |= child->m_ExactMatch;
332 }
333 }
334}
335
336
338{
339 m_Type = TYPE::ROOT;
340}
341
342
343LIB_TREE_NODE_LIBRARY& LIB_TREE_NODE_ROOT::AddLib( wxString const& aName, wxString const& aDesc )
344{
345 LIB_TREE_NODE_LIBRARY* lib = new LIB_TREE_NODE_LIBRARY( this, aName, aDesc );
346 m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( lib ) );
347 return *lib;
348}
349
350
351void LIB_TREE_NODE_ROOT::RemoveGroup( bool aRecentlyUsedGroup, bool aAlreadyPlacedGroup )
352{
353 m_Children.erase( std::remove_if( m_Children.begin(), m_Children.end(),
354 [&]( std::unique_ptr<LIB_TREE_NODE>& aNode )
355 {
356 if( aRecentlyUsedGroup && aNode->m_IsRecentlyUsedGroup )
357 return true;
358
359 if( aAlreadyPlacedGroup && aNode->m_IsAlreadyPlacedGroup )
360 return true;
361
362 return false;
363 } ),
364 m_Children.end() );
365}
366
367
369{
370 m_Children.clear();
371}
372
373
374void LIB_TREE_NODE_ROOT::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
375 std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
376{
377 for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
378 child->UpdateScore( aMatchers, aFilter );
379}
380
const char * name
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
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.
std::vector< std::unique_ptr< LIB_TREE_NODE > > 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:96
see class PGM_BASE
A structure for storing weighted search terms.