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