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