KiCad PCB EDA Suite
Loading...
Searching...
No Matches
lib_tree_model_adapter.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 <eda_base_frame.h>
24#include <eda_pattern_match.h>
25#include <kiface_base.h>
26#include <kiplatform/ui.h>
30#include <widgets/ui_common.h>
31#include <wx/tokenzr.h>
32#include <wx/wupdlock.h>
33#include <wx/settings.h>
34#include <wx/dc.h>
35#include <string_utils.h>
36
37
38static const int kDataViewIndent = 20;
39
40
41class LIB_TREE_RENDERER : public wxDataViewCustomRenderer
42{
43public:
45 m_canvasItem( false )
46 {}
47
48 wxSize GetSize() const override
49 {
50 return wxSize( GetOwner()->GetWidth(), GetTextExtent( m_text ).y + 2 );
51 }
52
53 bool GetValue( wxVariant& aValue ) const override
54 {
55 aValue = m_text;
56 return true;
57 }
58
59 bool SetValue( const wxVariant& aValue ) override
60 {
61 m_text = aValue.GetString();
62 return true;
63 }
64
65 void SetAttr( const wxDataViewItemAttr& aAttr ) override
66 {
67 // Use strikethrough as a proxy for is-canvas-item
68 m_canvasItem = aAttr.GetStrikethrough();
69
70 wxDataViewItemAttr realAttr = aAttr;
71 realAttr.SetStrikethrough( false );
72
73 wxDataViewCustomRenderer::SetAttr( realAttr );
74 }
75
76 bool Render( wxRect aRect, wxDC *dc, int aState ) override
77 {
78 RenderBackground( dc, aRect );
79
80 if( m_canvasItem )
81 {
82 wxPoint points[6];
83 points[0] = aRect.GetTopLeft();
84 points[1] = aRect.GetTopRight() + wxPoint( -4, 0 );
85 points[2] = aRect.GetTopRight() + wxPoint( 0, aRect.GetHeight() / 2 );
86 points[3] = aRect.GetBottomRight() + wxPoint( -4, 1 );
87 points[4] = aRect.GetBottomLeft() + wxPoint( 0, 1 );
88 points[5] = aRect.GetTopLeft();
89
90 dc->SetPen( KIPLATFORM::UI::IsDarkTheme() ? *wxWHITE_PEN : *wxBLACK_PEN );
91 dc->DrawLines( 6, points );
92 }
93
94 aRect.Deflate( 1 );
95
96#ifdef __WXOSX__
97 // We should be able to pass wxDATAVIEW_CELL_SELECTED into RenderText() and have it do
98 // the right thing -- but it picks wxSYS_COLOUR_HIGHLIGHTTEXT on MacOS (instead
99 // of wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT).
100 if( aState & wxDATAVIEW_CELL_SELECTED )
101 dc->SetTextForeground( wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT ) );
102
103 RenderText( m_text, 0, aRect, dc, 0 );
104#else
105 RenderText( m_text, 0, aRect, dc, aState );
106#endif
107 return true;
108 }
109
110private:
112 wxString m_text;
113};
114
115
116wxDataViewItem LIB_TREE_MODEL_ADAPTER::ToItem( const LIB_TREE_NODE* aNode )
117{
118 return wxDataViewItem( const_cast<void*>( static_cast<void const*>( aNode ) ) );
119}
120
121
123{
124 return static_cast<LIB_TREE_NODE*>( aItem.GetID() );
125}
126
127
129 const wxString& aPinnedKey,
130 APP_SETTINGS_BASE::LIB_TREE& aSettingsStruct ) :
131 m_widget( nullptr ),
132 m_parent( aParent ),
133 m_cfg( aSettingsStruct ),
134 m_sort_mode( BEST_MATCH ),
135 m_show_units( true ),
136 m_preselect_unit( 0 ),
137 m_freeze( 0 ),
138 m_filter( nullptr )
139{
140 // Default column widths. Do not translate these names.
141 m_colWidths[ _HKI( "Item" ) ] = 300;
142 m_colWidths[ _HKI( "Description" ) ] = 600;
143
144 m_availableColumns = { _HKI( "Item" ), _HKI( "Description" ) };
145
146 for( const std::pair<const wxString, int>& pair : m_cfg.column_widths )
147 m_colWidths[pair.first] = pair.second;
148
150
151 if( m_shownColumns.empty() )
152 m_shownColumns = { _HKI( "Item" ), _HKI( "Description" ) };
153
154 if( m_shownColumns[0] != _HKI( "Item" ) )
155 m_shownColumns.insert( m_shownColumns.begin(), _HKI( "Item" ) );
156}
157
158
160{}
161
162
163std::vector<wxString> LIB_TREE_MODEL_ADAPTER::GetOpenLibs() const
164{
165 std::vector<wxString> openLibs;
166 wxDataViewItem rootItem( nullptr );
167 wxDataViewItemArray children;
168
169 GetChildren( rootItem, children );
170
171 for( const wxDataViewItem& child : children )
172 {
173 if( m_widget->IsExpanded( child ) )
174 openLibs.emplace_back( ToNode( child )->m_LibId.GetLibNickname().wx_str() );
175 }
176
177 return openLibs;
178}
179
180
181void LIB_TREE_MODEL_ADAPTER::OpenLibs( const std::vector<wxString>& aLibs )
182{
183 wxWindowUpdateLocker updateLock( m_widget );
184
185 for( const wxString& lib : aLibs )
186 {
187 wxDataViewItem item = FindItem( LIB_ID( lib, wxEmptyString ) );
188
189 if( item.IsOk() )
190 m_widget->Expand( item );
191 }
192}
193
194
196{
197 if( m_widget )
198 {
200 m_cfg.column_widths.clear();
201
202 for( const std::pair<const wxString, wxDataViewColumn*>& pair : m_colNameMap )
203 m_cfg.column_widths[pair.first] = pair.second->GetWidth();
204
206 }
207}
208
209
211{
212 m_show_units = aShow;
213}
214
215
216void LIB_TREE_MODEL_ADAPTER::SetPreselectNode( const LIB_ID& aLibId, int aUnit )
217{
218 m_preselect_lib_id = aLibId;
219 m_preselect_unit = aUnit;
220}
221
222
224 const wxString& aDesc,
225 bool pinned )
226{
227 LIB_TREE_NODE_LIBRARY& lib_node = m_tree.AddLib( aNodeName, aDesc );
228
229 lib_node.m_Pinned = pinned;
230
231 return lib_node;
232}
233
234
236 const wxString& aDesc,
237 const std::vector<LIB_TREE_ITEM*>& aItemList,
238 bool pinned, bool presorted )
239{
240 LIB_TREE_NODE_LIBRARY& lib_node = DoAddLibraryNode( aNodeName, aDesc, pinned );
241
242 for( LIB_TREE_ITEM* item: aItemList )
243 lib_node.AddItem( item );
244
245 lib_node.AssignIntrinsicRanks( presorted );
246
247 return lib_node;
248}
249
250
251void LIB_TREE_MODEL_ADAPTER::RemoveGroup( bool aRecentGroup, bool aPlacedGroup )
252{
253 m_tree.RemoveGroup( aRecentGroup, aPlacedGroup );
254}
255
256
257void LIB_TREE_MODEL_ADAPTER::UpdateSearchString( const wxString& aSearch, bool aState )
258{
259 {
260 wxWindowUpdateLocker updateLock( m_widget );
261
262 // Even with the updateLock, wxWidgets sometimes ties its knickers in a knot trying to
263 // run a wxdataview_selection_changed_callback() on a row that has been deleted.
264 // https://bugs.launchpad.net/kicad/+bug/1756255
265 m_widget->UnselectAll();
266
267 // This collapse is required before the call to "Freeze()" below. Once Freeze()
268 // is called, GetParent() will return nullptr. While this works for some calls, it
269 // segfaults when we have any expanded elements b/c the sub units in the tree don't
270 // have explicit references that are maintained over a search
271 // The tree will be expanded again below when we get our matches
272 //
273 // Also note that this cannot happen when we have deleted a symbol as GTK will also
274 // iterate over the tree in this case and find a symbol that has an invalid link
275 // and crash https://gitlab.com/kicad/code/kicad/-/issues/6910
276 if( !aState && !aSearch.IsNull() && m_tree.m_Children.size() )
277 {
278 for( std::unique_ptr<LIB_TREE_NODE>& child: m_tree.m_Children )
279 m_widget->Collapse( wxDataViewItem( &*child ) );
280 }
281
282 // DO NOT REMOVE THE FREEZE/THAW. This freeze/thaw is a flag for this model adapter
283 // that tells it when it shouldn't trust any of the data in the model. When set, it will
284 // not return invalid data to the UI, since this invalid data can cause crashes.
285 // This is different than the update locker, which locks the UI aspects only.
286 Freeze();
287 BeforeReset();
288
290
291 // Don't cause KiCad to hang if someone accidentally pastes the PCB or schematic into
292 // the search box.
293 constexpr int MAX_TERMS = 100;
294
295 wxStringTokenizer tokenizer( aSearch );
296 int termCount = 0;
297
298 while( tokenizer.HasMoreTokens() && termCount < MAX_TERMS )
299 {
300 // First search for the full token, in case it appears in a search string
301 wxString term = tokenizer.GetNextToken().Lower();
302 EDA_COMBINED_MATCHER termMatcher( term, CTX_LIBITEM );
303
304 m_tree.UpdateScore( &termMatcher, wxEmptyString, m_filter );
305 termCount++;
306
307 if( term.Contains( ":" ) )
308 {
309 // Next search for the library:item_name
310 wxString lib = term.BeforeFirst( ':' );
311 wxString itemName = term.AfterFirst( ':' );
312 EDA_COMBINED_MATCHER itemNameMatcher( itemName, CTX_LIBITEM );
313
314 m_tree.UpdateScore( &itemNameMatcher, lib, m_filter );
315 }
316 }
317
318 if( termCount == 0 )
319 {
320 // No terms processed; just run the filter
321 m_tree.UpdateScore( nullptr, wxEmptyString, m_filter );
322 }
323
325 AfterReset();
326 Thaw();
327 }
328
329 const LIB_TREE_NODE* firstMatch = ShowResults();
330
331 if( firstMatch )
332 {
333 wxDataViewItem item = ToItem( firstMatch );
334 m_widget->Select( item );
335
336 // Make sure the *parent* item is visible. The selected item is the first (shown) child
337 // of the parent. So it's always right below the parent, and this way the user can also
338 // see what library the selected part belongs to, without having a case where the selection
339 // is off the screen (unless the window is a single row high, which is unlikely).
340 //
341 // This also happens to circumvent https://bugs.launchpad.net/kicad/+bug/1804400 which
342 // appears to be a GTK+3 bug.
343 {
344 wxDataViewItem parent = GetParent( item );
345
346 if( parent.IsOk() )
347 m_widget->EnsureVisible( parent );
348 }
349
350 m_widget->EnsureVisible( item );
351 }
352}
353
354
355void LIB_TREE_MODEL_ADAPTER::AttachTo( wxDataViewCtrl* aDataViewCtrl )
356{
357 m_widget = aDataViewCtrl;
358 aDataViewCtrl->SetIndent( kDataViewIndent );
359 aDataViewCtrl->AssociateModel( this );
361}
362
363
365{
366 m_widget->ClearColumns();
367
368 m_columns.clear();
369 m_colIdxMap.clear();
370 m_colNameMap.clear();
371
372 // The Item column is always shown
373 doAddColumn( wxT( "Item" ) );
374
375 for( const wxString& colName : m_shownColumns )
376 {
377 if( !m_colNameMap.count( colName ) )
378 doAddColumn( colName, colName == wxT( "Description" ) );
379 }
380}
381
382
384{
385 Freeze();
386 BeforeReset();
387
389
390 AfterReset();
391 Thaw();
392}
393
394
396{
398 aTreeNode->m_Pinned = true;
399
400 resortTree();
401 m_widget->EnsureVisible( ToItem( aTreeNode ) );
402}
403
404
406{
408 aTreeNode->m_Pinned = false;
409
410 resortTree();
411 // Keep focus at top when unpinning
412}
413
414
416{
418
419 for( const std::unique_ptr<LIB_TREE_NODE>& lib: m_tree.m_Children )
420 {
421 if( lib->m_IsRecentlyUsedGroup )
422 lib->m_Name = wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" );
423 else if( lib->m_IsAlreadyPlacedGroup )
424 lib->m_Name = wxT( "-- " ) + _( "Already Placed" ) + wxT( " --" );
425 }
426}
427
428
429wxDataViewColumn* LIB_TREE_MODEL_ADAPTER::doAddColumn( const wxString& aHeader, bool aTranslate )
430{
431 wxString translatedHeader = aTranslate ? wxGetTranslation( aHeader ) : aHeader;
432
433 // The extent of the text doesn't take into account the space on either side
434 // in the header, so artificially pad it
435 wxSize headerMinWidth = KIUI::GetTextSize( translatedHeader + wxT( "MMM" ), m_widget );
436
437 if( !m_colWidths.count( aHeader ) || m_colWidths[aHeader] < headerMinWidth.x )
438 m_colWidths[aHeader] = headerMinWidth.x;
439
440 int index = (int) m_columns.size();
441
442 wxDataViewColumn* col = new wxDataViewColumn(
443 translatedHeader, new LIB_TREE_RENDERER(), index, m_colWidths[aHeader], wxALIGN_NOT,
444 wxDATAVIEW_CELL_INERT | static_cast<int>( wxDATAVIEW_COL_RESIZABLE ) );
445 m_widget->AppendColumn( col );
446
447 col->SetMinWidth( headerMinWidth.x );
448
449 m_columns.emplace_back( col );
450 m_colNameMap[aHeader] = col;
451 m_colIdxMap[m_columns.size() - 1] = aHeader;
452
453 return col;
454}
455
456
457void LIB_TREE_MODEL_ADAPTER::addColumnIfNecessary( const wxString& aHeader )
458{
459 if( m_colNameMap.count( aHeader ) )
460 return;
461
462 // Columns will be created later
463 m_colNameMap[aHeader] = nullptr;
464 m_availableColumns.emplace_back( aHeader );
465}
466
467
468void LIB_TREE_MODEL_ADAPTER::SetShownColumns( const std::vector<wxString>& aColumnNames )
469{
470 bool recreate = m_shownColumns != aColumnNames;
471
472 m_shownColumns = aColumnNames;
473
474 if( recreate && m_widget )
476}
477
478
479LIB_ID LIB_TREE_MODEL_ADAPTER::GetAliasFor( const wxDataViewItem& aSelection ) const
480{
481 const LIB_TREE_NODE* node = ToNode( aSelection );
482 return node ? node->m_LibId : LIB_ID();
483}
484
485
486int LIB_TREE_MODEL_ADAPTER::GetUnitFor( const wxDataViewItem& aSelection ) const
487{
488 const LIB_TREE_NODE* node = ToNode( aSelection );
489 return node ? node->m_Unit : 0;
490}
491
492
493LIB_TREE_NODE::TYPE LIB_TREE_MODEL_ADAPTER::GetTypeFor( const wxDataViewItem& aSelection ) const
494{
495 const LIB_TREE_NODE* node = ToNode( aSelection );
496 return node ? node->m_Type : LIB_TREE_NODE::TYPE::INVALID;
497}
498
499
500LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::GetTreeNodeFor( const wxDataViewItem& aSelection ) const
501{
502 return ToNode( aSelection );
503}
504
505
507{
508 int n = 0;
509
510 for( const std::unique_ptr<LIB_TREE_NODE>& lib: m_tree.m_Children )
511 n += lib->m_Children.size();
512
513 return n;
514}
515
516
517wxDataViewItem LIB_TREE_MODEL_ADAPTER::FindItem( const LIB_ID& aLibId )
518{
519 for( std::unique_ptr<LIB_TREE_NODE>& lib: m_tree.m_Children )
520 {
521 if( lib->m_Name != aLibId.GetLibNickname().wx_str() )
522 continue;
523
524 // if part name is not specified, return the library node
525 if( aLibId.GetLibItemName() == "" )
526 return ToItem( lib.get() );
527
528 for( std::unique_ptr<LIB_TREE_NODE>& alias: lib->m_Children )
529 {
530 if( alias->m_Name == aLibId.GetLibItemName().wx_str() )
531 return ToItem( alias.get() );
532 }
533
534 break; // could not find the part in the requested library
535 }
536
537 return wxDataViewItem();
538}
539
540
542{
544}
545
546
547unsigned int LIB_TREE_MODEL_ADAPTER::GetChildren( const wxDataViewItem& aItem,
548 wxDataViewItemArray& aChildren ) const
549{
550 const LIB_TREE_NODE* node = ( aItem.IsOk() ? ToNode( aItem ) : &m_tree );
551 unsigned int count = 0;
552
553 if( node->m_Type == LIB_TREE_NODE::TYPE::ROOT
554 || node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
555 || ( m_show_units && node->m_Type == LIB_TREE_NODE::TYPE::ITEM ) )
556 {
557 for( std::unique_ptr<LIB_TREE_NODE> const& child: node->m_Children )
558 {
559 if( child->m_Score > 0 )
560 {
561 aChildren.Add( ToItem( &*child ) );
562 ++count;
563 }
564 }
565 }
566
567 return count;
568}
569
570
572{
573 wxDataViewColumn* col = nullptr;
574 size_t idx = 0;
575 int totalWidth = 0;
576 wxString header;
577
578 for( ; idx < m_columns.size() - 1; idx++ )
579 {
580 wxASSERT( m_colIdxMap.count( idx ) );
581
582 col = m_columns[idx];
583 header = m_colIdxMap[idx];
584
585 wxASSERT( m_colWidths.count( header ) );
586
587 col->SetWidth( m_colWidths[header] );
588 totalWidth += col->GetWidth();
589 }
590
591 int remainingWidth = m_widget->GetSize().x - totalWidth;
592 header = m_columns[idx]->GetTitle();
593
594 m_columns[idx]->SetWidth( std::max( m_colWidths[header], remainingWidth ) );
595}
596
597
599{
600 // Yes, this is an enormous hack. But it works on all platforms, it doesn't suffer
601 // the On^2 sorting issues that ItemChanged() does on OSX, and it doesn't lose the
602 // user's scroll position (which re-attaching or deleting/re-inserting columns does).
603 static int walk = 1;
604
605 std::vector<int> widths;
606
607 for( const wxDataViewColumn* col : m_columns )
608 widths.emplace_back( col->GetWidth() );
609
610 wxASSERT( widths.size() );
611
612 // Only use the widths read back if they are non-zero.
613 // GTK returns the displayed width of the column, which is not calculated immediately
614 if( widths[0] > 0 )
615 {
616 size_t i = 0;
617
618 for( const auto& [ colName, colPtr ] : m_colNameMap )
619 m_colWidths[ colName ] = widths[i++];
620 }
621
622 auto colIt = m_colWidths.begin();
623
624 colIt->second += walk;
625 colIt++;
626
627 if( colIt != m_colWidths.end() )
628 colIt->second -= walk;
629
630 for( const auto& [ colName, colPtr ] : m_colNameMap )
631 {
632 if( colPtr == m_columns[0] )
633 continue;
634
635 wxASSERT( m_colWidths.count( colName ) );
636 colPtr->SetWidth( m_colWidths[ colName ] );
637 }
638
639 walk = -walk;
640}
641
642
643bool LIB_TREE_MODEL_ADAPTER::HasContainerColumns( const wxDataViewItem& aItem ) const
644{
645 return IsContainer( aItem );
646}
647
648
649bool LIB_TREE_MODEL_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const
650{
651 LIB_TREE_NODE* node = ToNode( aItem );
652 return node ? node->m_Children.size() : true;
653}
654
655
656wxDataViewItem LIB_TREE_MODEL_ADAPTER::GetParent( const wxDataViewItem& aItem ) const
657{
658 if( m_freeze )
659 return ToItem( nullptr );
660
661 LIB_TREE_NODE* node = ToNode( aItem );
662 LIB_TREE_NODE* parent = node ? node->m_Parent : nullptr;
663
664 // wxDataViewModel has no root node, but rather top-level elements have
665 // an invalid (null) parent.
666 if( !node || !parent || parent->m_Type == LIB_TREE_NODE::TYPE::ROOT )
667 return ToItem( nullptr );
668 else
669 return ToItem( parent );
670}
671
672
673void LIB_TREE_MODEL_ADAPTER::GetValue( wxVariant& aVariant,
674 const wxDataViewItem& aItem,
675 unsigned int aCol ) const
676{
677 if( IsFrozen() )
678 {
679 aVariant = wxEmptyString;
680 return;
681 }
682
683 LIB_TREE_NODE* node = ToNode( aItem );
684 wxCHECK( node, /* void */ );
685 wxString valueStr;
686
687 switch( aCol )
688 {
689 case NAME_COL:
690 if( node->m_Pinned )
691 valueStr = GetPinningSymbol() + UnescapeString( node->m_Name );
692 else
693 valueStr = UnescapeString( node->m_Name );
694
695 break;
696
697 default:
698 if( m_colIdxMap.count( aCol ) )
699 {
700 const wxString& key = m_colIdxMap.at( aCol );
701
702 if( key == wxT( "Description" ) )
703 valueStr = UnescapeString( node->m_Desc );
704 else if( node->m_Fields.count( key ) )
705 valueStr = UnescapeString( node->m_Fields.at( key ) );
706 else
707 valueStr = wxEmptyString;
708 }
709
710 break;
711 }
712
713 valueStr.Replace( wxS( "\n" ), wxS( " " ) ); // Clear line breaks
714
715 aVariant = valueStr;
716}
717
718
719bool LIB_TREE_MODEL_ADAPTER::GetAttr( const wxDataViewItem& aItem,
720 unsigned int aCol,
721 wxDataViewItemAttr& aAttr ) const
722{
723 if( IsFrozen() )
724 return false;
725
726 LIB_TREE_NODE* node = ToNode( aItem );
727 wxCHECK( node, false );
728
729 if( node->m_Type == LIB_TREE_NODE::TYPE::ITEM )
730 {
731 if( !node->m_IsRoot && aCol == 0 )
732 {
733 // Names of non-root aliases are italicized
734 aAttr.SetItalic( true );
735 return true;
736 }
737 }
738
739 return false;
740}
741
742
743void recursiveDescent( LIB_TREE_NODE& aNode, const std::function<int( const LIB_TREE_NODE* )>& f )
744{
745 for( std::unique_ptr<LIB_TREE_NODE>& node: aNode.m_Children )
746 {
747 int r = f( node.get() );
748
749 if( r == 0 )
750 break;
751 else if( r == -1 )
752 continue;
753
754 recursiveDescent( *node, f );
755 }
756}
757
758
760{
761 const LIB_TREE_NODE* firstMatch = nullptr;
762
763 // Expand parents of leaf nodes with some level of matching
765 [&]( const LIB_TREE_NODE* n )
766 {
767 if( n->m_Type == LIB_TREE_NODE::TYPE::ITEM && n->m_Score > 1 )
768 {
769 if( !firstMatch )
770 firstMatch = n;
771 else if( n->m_Score > firstMatch->m_Score )
772 firstMatch = n;
773
774 m_widget->ExpandAncestors( ToItem( n ) );
775 }
776
777 return 1; // keep going to expand ancestors of all found items
778 } );
779
780 // If no matches, find and show the preselect node
781 if( !firstMatch && m_preselect_lib_id.IsValid() )
782 {
784 [&]( const LIB_TREE_NODE* n )
785 {
786 // Don't match the recent and already placed libraries
787 if( n->m_Name.StartsWith( "-- " ) )
788 return -1; // Skip this node and its children
789
790 if( n->m_Type == LIB_TREE_NODE::TYPE::ITEM
791 && ( n->m_Children.empty() || !m_preselect_unit )
792 && m_preselect_lib_id == n->m_LibId )
793 {
794 firstMatch = n;
795 m_widget->ExpandAncestors( ToItem( n ) );
796 return 0;
797 }
798 else if( n->m_Type == LIB_TREE_NODE::TYPE::UNIT
801 {
802 firstMatch = n;
803 m_widget->ExpandAncestors( ToItem( n ) );
804 return 0;
805 }
806
807 return 1;
808 } );
809 }
810
811 // If still no matches expand a single library if there is only one
812 if( !firstMatch )
813 {
814 int libraries = 0;
815
816 for( const std::unique_ptr<LIB_TREE_NODE>& child : m_tree.m_Children )
817 {
818 if( !child->m_Name.StartsWith( "-- " ) )
819 libraries++;
820 }
821
822 if( libraries != 1 )
823 return nullptr;
824
826 [&]( const LIB_TREE_NODE* n )
827 {
828 if( n->m_Type == LIB_TREE_NODE::TYPE::ITEM )
829 {
830 firstMatch = n;
831 m_widget->ExpandAncestors( ToItem( n ) );
832 return 0;
833 }
834
835 return 1;
836 } );
837 }
838
839 return firstMatch;
840}
841
842
The base frame for deriving all KiCad main window classes.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
bool IsValid() const
Check if this LID_ID is valid.
Definition: lib_id.h:172
const UTF8 & GetLibItemName() const
Definition: lib_id.h:102
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
LIB_TREE_MODEL_ADAPTER(EDA_BASE_FRAME *aParent, const wxString &aPinnedKey, APP_SETTINGS_BASE::LIB_TREE &aSettingsStruct)
Create the adapter.
std::vector< wxString > GetOpenLibs() const
int GetUnitFor(const wxDataViewItem &aSelection) const
Return the unit for the given item.
LIB_TREE_NODE::TYPE GetTypeFor(const wxDataViewItem &aSelection) const
Return node type for the given item.
APP_SETTINGS_BASE::LIB_TREE & m_cfg
bool GetAttr(const wxDataViewItem &aItem, unsigned int aCol, wxDataViewItemAttr &aAttr) const override
Get any formatting for an item.
std::map< wxString, int > m_colWidths
void FinishTreeInitialization()
A final-stage initialization to be called after the window hierarchy has been realized and the window...
void addColumnIfNecessary(const wxString &aHeader)
void PinLibrary(LIB_TREE_NODE *aTreeNode)
virtual PROJECT::LIB_TYPE_T getLibType()=0
virtual wxDataViewItem GetCurrentDataViewItem()
void SetPreselectNode(const LIB_ID &aLibId, int aUnit)
Set the symbol name to be selected if there are no search results.
static LIB_TREE_NODE * ToNode(wxDataViewItem aItem)
Convert wxDataViewItem -> #SYM_TREE_NODE.
LIB_ID GetAliasFor(const wxDataViewItem &aSelection) const
Return the alias for the given item.
void AttachTo(wxDataViewCtrl *aDataViewCtrl)
Attach to a wxDataViewCtrl and initialize it.
void SetShownColumns(const std::vector< wxString > &aColumnNames)
Sets which columns are shown in the widget.
static wxDataViewItem ToItem(const LIB_TREE_NODE *aNode)
Convert #SYM_TREE_NODE -> wxDataViewItem.
bool IsContainer(const wxDataViewItem &aItem) const override
Check whether an item can have children.
static const wxString GetPinningSymbol()
void ShowUnits(bool aShow)
Whether or not to show units.
LIB_TREE_NODE * GetTreeNodeFor(const wxDataViewItem &aSelection) const
unsigned int GetChildren(const wxDataViewItem &aItem, wxDataViewItemArray &aChildren) const override
Populate a list of all the children of an item.
void SaveSettings()
Save the column widths to the config file.
@ NAME_COL
Library or library item name column.
std::map< unsigned, wxString > m_colIdxMap
wxDataViewColumn * doAddColumn(const wxString &aHeader, bool aTranslate=true)
std::vector< wxDataViewColumn * > m_columns
std::vector< wxString > GetShownColumns() const
int GetItemCount() const
Return the number of symbols loaded in the tree.
const LIB_TREE_NODE * ShowResults()
Find and expand successful search results.
std::vector< wxString > m_shownColumns
void RemoveGroup(bool aRecentlyUsedGroup, bool aAlreadyPlacedGroup)
Remove one of the system groups from the library.
void GetValue(wxVariant &aVariant, const wxDataViewItem &aItem, unsigned int aCol) const override
Get the value of an item.
void UnpinLibrary(LIB_TREE_NODE *aTreeNode)
std::map< wxString, wxDataViewColumn * > m_colNameMap
bool HasContainerColumns(const wxDataViewItem &aItem) const override
Check whether a container has columns too.
wxDataViewItem GetParent(const wxDataViewItem &aItem) const override
Get the parent of an item.
LIB_TREE_NODE_LIBRARY & DoAddLibraryNode(const wxString &aNodeName, const wxString &aDesc, bool pinned)
std::vector< wxString > m_availableColumns
LIB_TREE_NODE_LIBRARY & DoAddLibrary(const wxString &aNodeName, const wxString &aDesc, const std::vector< LIB_TREE_ITEM * > &aItemList, bool pinned, bool presorted)
Add the given list of symbols by alias.
wxDataViewItem FindItem(const LIB_ID &aLibId)
Returns tree item corresponding to part.
void UpdateSearchString(const wxString &aSearch, bool aState)
Set the search string provided by the user.
std::function< bool(LIB_TREE_NODE &aNode)> * m_filter
void OpenLibs(const std::vector< wxString > &aLibs)
Node type: library.
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.
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).
enum TYPE m_Type
std::map< wxString, wxString > m_Fields
List of weighted search terms.
PTR_VECTOR m_Children
LIB_TREE_NODE * m_Parent
virtual void ResetScore()
Initialize scores recursively.
void AssignIntrinsicRanks(bool presorted=false)
Store intrinsic ranks on all children of this node.
void SetAttr(const wxDataViewItemAttr &aAttr) override
bool SetValue(const wxVariant &aValue) override
wxSize GetSize() const override
bool GetValue(wxVariant &aValue) const override
bool Render(wxRect aRect, wxDC *dc, int aState) override
void PinLibrary(const wxString &aLibrary, enum LIB_TYPE_T aLibType)
Definition: project.cpp:188
void UnpinLibrary(const wxString &aLibrary, enum LIB_TYPE_T aLibType)
Definition: project.cpp:225
wxString wx_str() const
Definition: utf8.cpp:45
#define _HKI(x)
static void recursiveDescent(wxSizer *aSizer, std::map< int, wxString > &aLabels)
#define _(s)
Base window classes and related definitions.
Abstract pattern-matching tool and implementations.
@ CTX_LIBITEM
void recursiveDescent(LIB_TREE_NODE &aNode, const std::function< int(const LIB_TREE_NODE *)> &f)
static const int kDataViewIndent
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition: wxgtk/ui.cpp:48
KICOMMON_API wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition: ui_common.cpp:77
wxString UnescapeString(const wxString &aSource)
std::vector< wxString > columns
Ordered list of visible columns in the tree.
Definition: app_settings.h:133
std::map< wxString, int > column_widths
Column widths, keyed by header name.
Definition: app_settings.h:134
std::vector< wxString > open_libs
list of libraries the user has open in the tree.
Definition: app_settings.h:135
Functions to provide common constants and other functions to assist in making a consistent UI.