KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_net_inspector_panel.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <properties/property.h>
23
24#include <advanced_config.h>
26#include <confirm.h>
31#include <footprint.h>
34#include <net_chain_bridging.h>
35#include <pad.h>
36#include <pcb_edit_frame.h>
37#include <pcb_painter.h>
38#include <pgm_base.h>
40#include <string_utils.h>
41#include <validators.h>
43#include <eda_pattern_match.h>
44#include <map>
45#include <cmath>
46
47#include <wx/wupdlock.h>
48#include <wx/filedlg.h>
49#include <kiplatform/ui.h>
50
51#include <algorithm>
52#include <thread_pool.h>
54
56 NET_INSPECTOR_PANEL( parent, aFrame ),
57 m_frame( aFrame ),
58 m_dataModel( new DATA_MODEL( *this ) )
59{
60 m_board = m_frame->GetBoard();
61
62 m_netsList->AssociateModel( &*m_dataModel );
63
64 // Rebuild nets list
65 buildNetsList( true );
66
67 // Register the panel to receive board change notifications
68 if( m_board != nullptr )
69 {
71 m_board->AddListener( this );
72 }
73
74 // Connect to board events
75 m_frame->Bind( EDA_EVT_UNITS_CHANGED, &PCB_NET_INSPECTOR_PANEL::onUnitsChanged, this );
76
77 // Connect to wxDataViewCtrl events
78 m_netsList->Bind( wxEVT_DATAVIEW_ITEM_EXPANDED, &PCB_NET_INSPECTOR_PANEL::OnExpandCollapseRow, this );
79 m_netsList->Bind( wxEVT_DATAVIEW_ITEM_COLLAPSED, &PCB_NET_INSPECTOR_PANEL::OnExpandCollapseRow, this );
80 m_netsList->Bind( wxEVT_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK, &PCB_NET_INSPECTOR_PANEL::OnHeaderContextMenu, this );
81 m_netsList->Bind( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &PCB_NET_INSPECTOR_PANEL::OnNetsListContextMenu, this );
82 m_netsList->Bind( wxEVT_DATAVIEW_ITEM_ACTIVATED, &PCB_NET_INSPECTOR_PANEL::OnNetsListItemActivated, this );
83 m_netsList->Bind( wxEVT_DATAVIEW_COLUMN_SORTED, &PCB_NET_INSPECTOR_PANEL::OnColumnSorted, this );
84}
85
87{
89
90 m_netsList->AssociateModel( nullptr );
91
92 // Disconnect from board events
93 m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &PCB_NET_INSPECTOR_PANEL::onUnitsChanged, this );
94
95 // Connect to wxDataViewCtrl events
96 m_netsList->Unbind( wxEVT_DATAVIEW_ITEM_EXPANDED, &PCB_NET_INSPECTOR_PANEL::OnExpandCollapseRow, this );
97 m_netsList->Unbind( wxEVT_DATAVIEW_ITEM_COLLAPSED, &PCB_NET_INSPECTOR_PANEL::OnExpandCollapseRow, this );
98 m_netsList->Unbind( wxEVT_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK, &PCB_NET_INSPECTOR_PANEL::OnHeaderContextMenu, this );
99 m_netsList->Unbind( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &PCB_NET_INSPECTOR_PANEL::OnNetsListContextMenu, this );
100 m_netsList->Unbind( wxEVT_DATAVIEW_ITEM_ACTIVATED, &PCB_NET_INSPECTOR_PANEL::OnNetsListItemActivated, this );
101 m_netsList->Unbind( wxEVT_DATAVIEW_COLUMN_SORTED, &PCB_NET_INSPECTOR_PANEL::OnColumnSorted, this );
102}
103
104
105/*****************************************************************************************
106 *
107 * Grid / model columns configuration
108 *
109 * ***************************************************************************************/
110
111
113{
114 m_columns.clear();
115
116 // Set up the column display vector
117 m_columns.emplace_back( 0u, UNDEFINED_LAYER, _( "Name" ), _( "Net Name" ), CSV_COLUMN_DESC::CSV_QUOTE, false );
118 m_columns.emplace_back( 1u, UNDEFINED_LAYER, _( "Net Chain" ), _( "Net Chain" ), CSV_COLUMN_DESC::CSV_QUOTE, false );
119 m_columns.emplace_back( 2u, UNDEFINED_LAYER, _( "Netclass" ), _( "Netclass" ), CSV_COLUMN_DESC::CSV_QUOTE, false );
120
122 {
123 m_columns.emplace_back( 3u, UNDEFINED_LAYER, _( "Total Delay" ), _( "Net Delay" ), CSV_COLUMN_DESC::CSV_NONE,
124 true );
125 }
126 else
127 {
128 m_columns.emplace_back( 3u, UNDEFINED_LAYER, _( "Total Length" ), _( "Net Length" ), CSV_COLUMN_DESC::CSV_NONE,
129 true );
130 }
131
133 {
134 m_columns.emplace_back( 4u, UNDEFINED_LAYER, _( "Net Chain Delay" ), _( "Net Chain Delay" ),
136 }
137 else
138 {
139 m_columns.emplace_back( 4u, UNDEFINED_LAYER, _( "Net Chain Length" ), _( "Net Chain Length" ),
141 }
142
143 m_columns.emplace_back( 5u, UNDEFINED_LAYER, _( "Via Count" ), _( "Via Count" ), CSV_COLUMN_DESC::CSV_NONE, false );
144
146 {
147 m_columns.emplace_back( 6u, UNDEFINED_LAYER, _( "Via Delay" ), _( "Via Delay" ), CSV_COLUMN_DESC::CSV_NONE,
148 true );
149 m_columns.emplace_back( 7u, UNDEFINED_LAYER, _( "Track Delay" ), _( "Track Delay" ), CSV_COLUMN_DESC::CSV_NONE,
150 true );
151 m_columns.emplace_back( 8u, UNDEFINED_LAYER, _( "Die Delay" ), _( "Die Delay" ), CSV_COLUMN_DESC::CSV_NONE,
152 true );
153 }
154 else
155 {
156 m_columns.emplace_back( 6u, UNDEFINED_LAYER, _( "Via Length" ), _( "Via Length" ), CSV_COLUMN_DESC::CSV_NONE,
157 true );
158 m_columns.emplace_back( 7u, UNDEFINED_LAYER, _( "Track Length" ), _( "Track Length" ),
160 m_columns.emplace_back( 8u, UNDEFINED_LAYER, _( "Die Length" ), _( "Die Length" ), CSV_COLUMN_DESC::CSV_NONE,
161 true );
162 }
163
164 m_columns.emplace_back( 9u, UNDEFINED_LAYER, _( "Unrouted Length" ), _( "Unrouted Length" ),
166
167 m_columns.emplace_back( 10u, UNDEFINED_LAYER, _( "Pad Count" ), _( "Pad Count" ), CSV_COLUMN_DESC::CSV_NONE,
168 false );
169
170 const std::vector<std::function<void( void )>> add_col{
171 [&]()
172 {
173 m_netsList->AppendTextColumn( m_columns[COLUMN_NAME].display_name, m_columns[COLUMN_NAME],
174 wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
175 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE );
176 },
177 [&]()
178 {
179 m_netsList->AppendTextColumn( m_columns[COLUMN_NET_CHAIN].display_name, m_columns[COLUMN_NET_CHAIN],
180 wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
181 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
182 },
183 [&]()
184 {
185 m_netsList->AppendTextColumn( m_columns[COLUMN_NETCLASS].display_name, m_columns[COLUMN_NETCLASS],
186 wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
187 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
188 },
189 [&]()
190 {
191 m_netsList->AppendTextColumn( m_columns[COLUMN_TOTAL_LENGTH].display_name, m_columns[COLUMN_TOTAL_LENGTH],
192 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
193 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
194 },
195 [&]()
196 {
197 m_netsList->AppendTextColumn( m_columns[COLUMN_NET_CHAIN_LENGTH].display_name,
198 m_columns[COLUMN_NET_CHAIN_LENGTH], wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
199 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
200 },
201 [&]()
202 {
203 m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_COUNT].display_name, m_columns[COLUMN_VIA_COUNT],
204 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
205 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
206 },
207 [&]()
208 {
209 m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_LENGTH].display_name, m_columns[COLUMN_VIA_LENGTH],
210 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
211 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
212 },
213 [&]()
214 {
215 m_netsList->AppendTextColumn( m_columns[COLUMN_BOARD_LENGTH].display_name, m_columns[COLUMN_BOARD_LENGTH],
216 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
217 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
218 },
219 [&]()
220 {
221 m_netsList->AppendTextColumn( m_columns[COLUMN_PAD_DIE_LENGTH].display_name,
222 m_columns[COLUMN_PAD_DIE_LENGTH], wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
223 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
224 },
225 [&]()
226 {
227 m_netsList->AppendTextColumn( m_columns[COLUMN_UNROUTED_LENGTH].display_name,
228 m_columns[COLUMN_UNROUTED_LENGTH], wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
229 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE
230 | wxDATAVIEW_COL_SORTABLE );
231 },
232 [&]()
233 {
234 m_netsList->AppendTextColumn( m_columns[COLUMN_PAD_COUNT].display_name, m_columns[COLUMN_PAD_COUNT],
235 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
236 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
237 }
238 };
239
240 // If we have not yet loaded the first board, use a dummy local settings object to ensure we
241 // don't over-write existing board settings (note that PCB_EDIT_FRAME loads the local settings
242 // object prior to loading the board; the two are not synced and we need to account for that)
243 PANEL_NET_INSPECTOR_SETTINGS* cfg = nullptr;
244
245 if( m_boardLoaded )
246 {
248 cfg = &localSettings.m_NetInspectorPanel;
249 }
250 else
251 {
253 }
254
255 // Reset the column display settings if column count doesn't match
256 const int totalNumColumns = (int) add_col.size() + m_board->GetCopperLayerCount();
257
258 if( (int) cfg->col_order.size() != totalNumColumns
259 || (int) cfg->col_hidden.size() != totalNumColumns )
260 {
261 cfg->col_order.resize( totalNumColumns );
262 cfg->col_hidden.resize( totalNumColumns );
263
264 for( int i = 0; i < totalNumColumns; ++i )
265 {
266 cfg->col_order[i] = i;
267 cfg->col_hidden[i] = false;
268 }
269 }
270
271 // Check that all rows are unique to protect against corrupted settings data
272 std::set<int> col_order_set( cfg->col_order.begin(), cfg->col_order.end() );
273
274 if( col_order_set.size() != cfg->col_order.size() )
275 {
276 for( std::size_t i = 0; i < cfg->col_order.size(); ++i )
277 cfg->col_order[i] = static_cast<int>( i );
278 }
279
280 // Add column records for copper layers
281 for( PCB_LAYER_ID layer : m_board->GetEnabledLayers().Seq() )
282 {
283 if( !IsCopperLayer( layer ) )
284 continue;
285
286 m_columns.emplace_back( m_columns.size(), layer, m_board->GetLayerName( layer ), m_board->GetLayerName( layer ),
288 }
289
290 // Add display columns in settings order
291 for( const int i : cfg->col_order )
292 {
293 const int addModelColumn = i;
294
295 if( addModelColumn >= (int) add_col.size() )
296 {
297 m_netsList->AppendTextColumn( m_board->GetLayerName( m_columns[addModelColumn].layer ),
298 m_columns[addModelColumn], wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
299 wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_REORDERABLE|wxDATAVIEW_COL_SORTABLE );
300 }
301 else
302 {
303 add_col.at( i )();
304 }
305 }
306
307 // Set the name column as the expander row
308 if( wxDataViewColumn* col = getDisplayedColumnForModelField( COLUMN_NAME ) )
309 {
310 m_netsList->SetExpanderColumn( col );
311 }
312
314
315 // Delete the temporary config if used
316 if( !m_boardLoaded )
317 delete cfg;
318}
319
320
322{
323 constexpr int margins = 15;
324 constexpr int extra_width = 30;
325
326 int headerWidth = GetTextExtent( m_columns[aModelColumn].display_name ).x;
327 headerWidth += ( aModelColumn == COLUMN_NAME ) ? extra_width : margins;
328
329 return headerWidth;
330}
331
332
333void PCB_NET_INSPECTOR_PANEL::autosizeColumn( wxDataViewColumn* aCol )
334{
335 if( !aCol || aCol->IsHidden() )
336 return;
337
338 const unsigned int modelCol = aCol->GetModelColumn();
339
340 if( modelCol >= m_columns.size() )
341 return;
342
343 constexpr int margins = 15;
344 constexpr int extra_width = 30;
345 const bool isNameCol = ( modelCol == COLUMN_NAME );
346 const int padding = isNameCol ? extra_width : margins;
347 const int indent = m_netsList->GetIndent();
348
349 int maxWidth = getMinColumnWidth( modelCol );
350
351 for( unsigned int row = 0; row < m_dataModel->itemCount(); ++row )
352 {
353 wxVariant value = m_dataModel->valueAt( modelCol, row );
354 int textWidth = GetTextExtent( value.GetString() ).x + padding;
355 maxWidth = std::max( maxWidth, textWidth );
356
357 const LIST_ITEM& item = m_dataModel->itemAt( row );
358
359 if( item.GetIsGroup() )
360 {
361 for( auto it = item.ChildrenBegin(); it != item.ChildrenEnd(); ++it )
362 {
363 wxVariant childValue = m_dataModel->valueForItem( *it, modelCol );
364 int childWidth = GetTextExtent( childValue.GetString() ).x + padding;
365
366 if( isNameCol )
367 childWidth += indent;
368
369 maxWidth = std::max( maxWidth, childWidth );
370 }
371 }
372 }
373
374 aCol->SetWidth( maxWidth );
375}
376
377
379{
380 wxWindowUpdateLocker locker( m_netsList );
381
382 if( cfg->col_widths.size() != m_columns.size() )
383 {
384 int minValueWidth = GetTextExtent( wxT( "00000,000 mm" ) ).x;
385 int minNumberWidth = GetTextExtent( wxT( "000" ) ).x;
386 int minNameWidth = GetTextExtent( wxT( "MMMMMMMMMMMM" ) ).x;
387
388 // Considering left and right margins.
389 // For wxRenderGeneric it is 5px.
390 // Also account for the sorting arrow in the column header.
391 // Column 0 also needs space for any potential expander icons.
392 constexpr int margins = 15;
393 constexpr int extra_width = 30;
394
395 auto getTargetWidth =
396 [&]( int columnID )
397 {
398 switch( columnID )
399 {
400 case COLUMN_NAME: return minNameWidth + extra_width;
401 case COLUMN_NETCLASS: return minNameWidth + margins;
402 case COLUMN_VIA_COUNT: return minNumberWidth + margins;
403 case COLUMN_PAD_COUNT: return minNumberWidth + margins;
404 default: return minValueWidth + margins;
405 }
406 };
407
408 wxASSERT( m_columns.size() == cfg->col_order.size() );
409
410 for( size_t i = 0; i < m_columns.size(); ++i )
411 {
412 const int modelColumn = cfg->col_order[i];
413 int titleSize = GetTextExtent( m_columns[modelColumn].display_name ).x;
414 titleSize = modelColumn == COLUMN_NAME ? titleSize + extra_width : titleSize + margins;
415 const int valSize = getTargetWidth( modelColumn );
416 m_netsList->GetColumn( i )->SetWidth( std::max( titleSize, valSize ) );
417 }
418 }
419 else
420 {
421 wxASSERT( m_columns.size() == cfg->col_hidden.size() );
422 wxASSERT( m_columns.size() == cfg->col_widths.size() );
423
424 for( size_t ii = 0; ii < m_columns.size(); ++ii )
425 {
426 const int modelCol = static_cast<int>( m_netsList->GetColumn( ii )->GetModelColumn() );
427 const int minWidth = getMinColumnWidth( modelCol );
428 const int newWidth = cfg->col_widths[ii];
429
430 m_netsList->GetColumn( ii )->SetWidth( std::max( newWidth, minWidth ) );
431 m_netsList->GetColumn( ii )->SetHidden( cfg->col_hidden[ii] );
432 }
433 }
434
435 m_netsList->Refresh();
436}
437
438
439bool PCB_NET_INSPECTOR_PANEL::restoreSortColumn( const int sortingColumnId, const bool sortOrderAsc ) const
440{
441 if( sortingColumnId != -1 )
442 {
443 if( wxDataViewColumn* col = getDisplayedColumnForModelField( sortingColumnId ) )
444 {
445 col->SetSortOrder( sortOrderAsc );
446 m_dataModel->Resort();
447 return true;
448 }
449 }
450
451 return false;
452}
453
454
455wxDataViewColumn* PCB_NET_INSPECTOR_PANEL::getDisplayedColumnForModelField( const int columnId ) const
456{
457 for( unsigned int i = 0; i < m_netsList->GetColumnCount(); ++i )
458 {
459 wxDataViewColumn* col = m_netsList->GetColumn( i );
460
461 if( static_cast<int>( col->GetModelColumn() ) == columnId )
462 return col;
463 }
464
465 return nullptr;
466}
467
468
469/*****************************************************************************************
470 *
471 * Nets list generation
472 *
473 * ***************************************************************************************/
474
475
476void PCB_NET_INSPECTOR_PANEL::buildNetsList( const bool rebuildColumns )
477{
478 // Only build the list of nets if there is a board present
479 if( !m_board )
480 return;
481
482 m_inBuildNetsList = true;
483
484 m_netsList->Freeze();
485
488
489 // Refresh all filtering / grouping settings
497
498 // Must follow the settings load so the model reflects this board's mode, not the previous one
499 m_dataModel->SetIsTimeDomain( m_showTimeDomainDetails );
500
501 // Attempt to keep any expanded groups open
503 {
504 cfg->expanded_rows.clear();
505 DATA_MODEL* model = static_cast<DATA_MODEL*>( m_netsList->GetModel() );
506
507 for( const auto& [groupName, groupItem] : model->getGroupDataViewItems() )
508 {
509 if( m_netsList->IsExpanded( groupItem ) )
510 cfg->expanded_rows.push_back( groupName );
511 }
512 }
513
514 // When rebuilding the netlist, try to keep the row selection
515 wxDataViewItemArray sel;
516 m_netsList->GetSelections( sel );
517
518 std::vector<int> prev_selected_netcodes;
519 prev_selected_netcodes.reserve( sel.GetCount() );
520
521 for( unsigned int i = 0; i < sel.GetCount(); ++i )
522 {
523 const LIST_ITEM* item = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
524 prev_selected_netcodes.push_back( item->GetNetCode() );
525 }
526
527 int sorting_column_id = cfg->sorting_column;
528 bool sort_order_asc = cfg->sort_order_asc;
529
530 if( wxDataViewColumn* sorting_column = m_netsList->GetSortingColumn() )
531 {
532 if( !m_boardLoading )
533 {
534 sorting_column_id = static_cast<int>( sorting_column->GetModelColumn() );
535 sort_order_asc = sorting_column->IsSortOrderAscending();
536 }
537
538 // On GTK, wxDVC will crash if we rebuild with a sorting column set.
539 sorting_column->UnsetAsSortKey();
540 }
541
542 if( rebuildColumns )
543 {
544 m_netsList->ClearColumns();
545 buildColumns();
546 }
547
548 m_dataModel->deleteAllItems();
549 m_custom_group_rules.clear();
550
551 for( const wxString& rule : cfg->custom_group_rules )
552 m_custom_group_rules.push_back( std::make_unique<EDA_COMBINED_MATCHER>( rule, CTX_NET ) );
553
554 m_dataModel->addCustomGroups();
555
556 std::vector<NETINFO_ITEM*> netCodes;
557
558 for( NETINFO_ITEM* ni : m_board->GetNetInfo() )
559 {
560 if( netFilterMatches( ni, cfg ) )
561 netCodes.emplace_back( ni );
562 }
563
564 std::ranges::sort( netCodes,
565 []( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
566 {
567 return a->GetNetCode() < b->GetNetCode();
568 } );
569
570 std::vector<std::unique_ptr<LIST_ITEM>> lengths = calculateNets( netCodes, m_showZeroPadNets );
571
572 m_dataModel->addItems( lengths );
573
574 // Try to re-enable the sorting column
575 if( !restoreSortColumn( sorting_column_id, sort_order_asc ))
576 {
577 // By default, sort by Name column
579 }
580
581 // Try to restore the expanded groups
582 if( m_boardLoaded )
583 {
584 m_rowExpanding = true;
585
586 std::vector<std::pair<wxString, wxDataViewItem>> groupItems = m_dataModel->getGroupDataViewItems();
587
588 for( wxString& groupName : cfg->expanded_rows )
589 {
590 auto pred =
591 [&groupName]( const std::pair<wxString, wxDataViewItem>& item )
592 {
593 return groupName == item.first;
594 };
595
596 auto tableItem = std::ranges::find_if( groupItems, pred );
597
598 if( tableItem != groupItems.end() )
599 m_netsList->Expand( tableItem->second );
600 }
601
602 m_rowExpanding = false;
603 }
604
605 // Try to restore the selected rows
606 sel.Clear();
607
608 for( const int& nc : prev_selected_netcodes )
609 {
610 if( std::optional<LIST_ITEM_ITER> r = m_dataModel->findItem( nc ) )
611 {
612 const std::unique_ptr<LIST_ITEM>& list_item = *r.value();
613 sel.Add( wxDataViewItem( list_item.get() ) );
614 }
615 }
616
617 m_netsList->Thaw(); // Must thaw before reselecting to avoid windows selection bug
618
619 if( !sel.IsEmpty() )
620 {
621 m_netsList->SetSelections( sel );
622 m_netsList->EnsureVisible( sel.Item( 0 ) );
623 }
624 else
625 {
626 m_netsList->UnselectAll();
627 }
628
629 m_inBuildNetsList = false;
630}
631
632
635{
636 if( cfg == nullptr )
637 {
639 cfg = &localSettings.m_NetInspectorPanel;
640 }
641
642 // Never show an unconnected net
643 if( aNet->GetNetCode() <= 0 )
644 return false;
645
646 const wxString filterString = UnescapeString( m_searchCtrl->GetValue() ).Upper();
647 const wxString netName = UnescapeString( aNet->GetNetname() ).Upper();
648 const NETCLASS* netClass = aNet->GetNetClass();
649 const wxString netClassName = UnescapeString( netClass->GetName() ).Upper();
650
651 bool matched = false;
652
653 // No filter - match all
654 if( filterString.Length() == 0 )
655 matched = true;
656
657 // Search on net class
658 if( !matched && cfg->filter_by_netclass && netClassName.Find( filterString ) != wxNOT_FOUND )
659 matched = true;
660
661 // Search on net name
662 if( !matched && cfg->filter_by_net_name && netName.Find( filterString ) != wxNOT_FOUND )
663 matched = true;
664
665 // Remove unconnected nets if required
666 if( matched )
667 {
669 matched = !netName.StartsWith( wxT( "UNCONNECTED-(" ) );
670 }
671
672 return matched;
673}
674
675
677{
678 bool operator()( const CN_ITEM* a, const CN_ITEM* b ) const { return a->Net() < b->Net(); }
679
680 bool operator()( const CN_ITEM* a, int b ) const { return a->Net() < b; }
681
682 bool operator()( int a, const CN_ITEM* b ) const { return a < b->Net(); }
683};
684
685
687{
688 // Pre-filter the connectivity items and sort them by netcode. This avoids quadratic runtime when building the whole
689 // net list.
690 const auto type_bits = std::bitset<MAX_STRUCT_TYPE_ID>().set( PCB_TRACE_T )
691 .set( PCB_ARC_T )
692 .set( PCB_VIA_T )
693 .set( PCB_PAD_T );
694
695 std::vector<CN_ITEM*> cn_items;
696 cn_items.reserve( 1024 );
697
698 for( CN_ITEM* cn_item : m_board->GetConnectivity()->GetConnectivityAlgo()->ItemList() )
699 {
700 if( cn_item->Valid() && type_bits[cn_item->Parent()->Type()] )
701 cn_items.push_back( cn_item );
702 }
703
704 std::ranges::sort( cn_items, NETCODE_CMP_LESS() );
705
706 return cn_items;
707}
708
709
710std::vector<std::unique_ptr<PCB_NET_INSPECTOR_PANEL::LIST_ITEM>>
711PCB_NET_INSPECTOR_PANEL::calculateNets( const std::vector<NETINFO_ITEM*>& aNetCodes, bool aIncludeZeroPadNets ) const
712{
713 std::vector<std::unique_ptr<LIST_ITEM>> results;
714
715 // A chain total needs every member net, so pull in the siblings.
716 std::vector<NETINFO_ITEM*> netCodes( aNetCodes );
717 std::set<wxString> chains;
718
719 for( NETINFO_ITEM* net : netCodes )
720 {
721 if( !net->GetNetChain().IsEmpty() )
722 chains.insert( net->GetNetChain() );
723 }
724
725 if( !chains.empty() )
726 {
727 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
728 {
729 if( chains.count( net->GetNetChain() ) )
730 netCodes.push_back( net );
731 }
732
733 std::sort( netCodes.begin(), netCodes.end(),
734 []( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
735 {
736 return a->GetNetCode() < b->GetNetCode();
737 } );
738 netCodes.erase( std::unique( netCodes.begin(), netCodes.end() ), netCodes.end() );
739 }
740
741 LENGTH_DELAY_CALCULATION* calc = m_board->GetLengthCalculation();
742 const std::vector<CN_ITEM*> conItems = relevantConnectivityItems();
743
744 // First assemble the LENGTH_CALCULATION_ITEMs for board items which match the nets we need to recompute
745 // Precondition: conItems and aNetCodes are sorted in increasing netcode value
746 // Functionality: This extracts any items from conItems which have a netcode which is present in aNetCodes
747 std::unordered_map<int, std::vector<LENGTH_DELAY_CALCULATION_ITEM>> netItemsMap;
748 std::vector<NETINFO_ITEM*> foundNets;
749
750 auto itemItr = conItems.begin();
751 auto netCodeItr = netCodes.begin();
752
753 while( itemItr != conItems.end() && netCodeItr != netCodes.end() )
754 {
755 const int curNetCode = ( *netCodeItr )->GetNetCode();
756 const int curItemNetCode = ( *itemItr )->Net();
757
758 if( curItemNetCode == curNetCode )
759 {
760 if( foundNets.empty() || foundNets.back() != *netCodeItr )
761 foundNets.emplace_back( *netCodeItr );
762
763 // Take the item
764 LENGTH_DELAY_CALCULATION_ITEM lengthItem = calc->GetLengthCalculationItem( ( *itemItr )->Parent() );
765 netItemsMap[curItemNetCode].emplace_back( std::move( lengthItem ) );
766 ++itemItr;
767 }
768 else if( curItemNetCode < curNetCode )
769 {
770 // Fast-forward through items
771 while( itemItr != conItems.end() && ( *itemItr )->Net() < curNetCode )
772 ++itemItr;
773 }
774 else if( curItemNetCode > curNetCode )
775 {
776 // Fast-forward through required net codes
777 while( netCodeItr != netCodes.end() && curItemNetCode > ( *netCodeItr )->GetNetCode() )
778 ++netCodeItr;
779 }
780 }
781
782 // Now calculate the length statistics for each net. This includes potentially expensive path optimisations, so
783 // parallelize this work.
784 std::mutex resultsMutex;
786
787 auto resultsFuture = tp.submit_loop(
788 0, foundNets.size(),
789 [&, this, calc]( const int i )
790 {
791 int netCode = foundNets[i]->GetNetCode();
792
793 constexpr PATH_OPTIMISATIONS opts = { .OptimiseVias = true,
794 .MergeTracks = true,
795 .OptimiseTracesInPads = true,
796 .InferViaInPad = false };
797
798 LENGTH_DELAY_STATS lengthDetails = calc->CalculateLengthDetails(
799 netItemsMap[netCode],
800 opts,
801 nullptr,
802 nullptr,
806
807 if( aIncludeZeroPadNets || lengthDetails.NumPads > 0 )
808 {
809 std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( foundNets[i] );
810
811 new_item->SetPadCount( lengthDetails.NumPads );
812 new_item->SetLayerCount( m_board->GetCopperLayerCount() );
813 new_item->SetPadDieLength( lengthDetails.PadToDieLength );
814 new_item->SetPadDieDelay( lengthDetails.PadToDieDelay );
815 new_item->SetViaCount( lengthDetails.NumVias );
816 new_item->SetViaLength( lengthDetails.ViaLength );
817 new_item->SetViaDelay( lengthDetails.ViaDelay );
818 new_item->SetLayerWireLengths( *lengthDetails.LayerLengths );
819
820 // Estimated unrouted length: sum of the optimized ratsnest airline edges
821 // for this net, as maintained by the connectivity engine. Uses the
822 // unsorted accessor to avoid the stable sort in RN_NET::GetEdges().
823 int64_t unroutedLength = 0;
824
825 if( RN_NET* rnNet = m_board->GetConnectivity()->GetRatsnestForNet( netCode ) )
826 unroutedLength = rnNet->GetTotalAirlineLength();
827
828 new_item->SetUnroutedLength( unroutedLength );
829
830 if( m_showTimeDomainDetails )
831 new_item->SetLayerWireDelays( *lengthDetails.LayerDelays );
832
833 new_item->SetNetChainName( foundNets[i]->GetNetChain() );
834
835 std::scoped_lock lock( resultsMutex );
836 results.emplace_back( std::move( new_item ) );
837 }
838 } );
839
840 resultsFuture.get();
841
842 std::map<wxString, int64_t> netChainLengths;
843 std::map<wxString, int64_t> netChainDelays;
844
845 for( const std::unique_ptr<LIST_ITEM>& item : results )
846 {
847 if( !item->GetNetChainName().IsEmpty() )
848 {
849 netChainLengths[item->GetNetChainName()] += item->GetTotalLength();
850
851 if( m_showTimeDomainDetails )
852 netChainDelays[item->GetNetChainName()] += item->GetTotalDelay();
853 }
854 }
855
856 // Cache per-chain delay-per-mm so we only sample the chain tracks once.
857 std::map<wxString, double> chainDelayPerIU;
858
859 auto delayPerIUFor = [&]( const wxString& aChain ) -> double
860 {
861 auto it = chainDelayPerIU.find( aChain );
862
863 if( it != chainDelayPerIU.end() )
864 return it->second;
865
866 double perIU = ChainBridgingDelayPerMm( m_board, aChain ) / pcbIUScale.IU_PER_MM;
867 chainDelayPerIU.emplace( aChain, perIU );
868 return perIU;
869 };
870
871 for( FOOTPRINT* fp : m_board->Footprints() )
872 {
873 std::vector<PAD*> chainPads;
874
875 for( PAD* pad : fp->Pads() )
876 {
877 if( pad->GetNet() && !pad->GetNet()->GetNetChain().IsEmpty() )
878 chainPads.push_back( pad );
879 }
880
881 for( size_t i = 0; i < chainPads.size(); ++i )
882 {
883 for( size_t j = i + 1; j < chainPads.size(); ++j )
884 {
885 NETINFO_ITEM* netA = chainPads[i]->GetNet();
886 NETINFO_ITEM* netB = chainPads[j]->GetNet();
887
888 if( netA->GetNetChain() == netB->GetNetChain()
889 && netA->GetNetCode() != netB->GetNetCode() )
890 {
891 VECTOR2I p1 = chainPads[i]->GetPosition();
892 VECTOR2I p2 = chainPads[j]->GetPosition();
893 int64_t dx = p1.x - p2.x;
894 int64_t dy = p1.y - p2.y;
895 int64_t dist = KiROUND( std::hypot( (double) dx, (double) dy ) );
896 const wxString& chain = netA->GetNetChain();
897
898 netChainLengths[chain] += dist;
899
900 if( m_showTimeDomainDetails )
901 netChainDelays[chain] += KiROUND( delayPerIUFor( chain ) * (double) dist );
902 }
903 }
904 }
905 }
906
907 for( std::unique_ptr<LIST_ITEM>& item : results )
908 {
909 if( !item->GetNetChainName().IsEmpty() )
910 {
911 item->SetNetChainLength( netChainLengths[item->GetNetChainName()] );
912
913 if( m_showTimeDomainDetails )
914 item->SetNetChainDelay( netChainDelays[item->GetNetChainName()] );
915 }
916 }
917
918 return results;
919}
920
921
922/*****************************************************************************************
923 *
924 * Formatting helpers
925 *
926 * ***************************************************************************************/
927
928
930{
931 return wxString::Format( wxT( "%.3d" ), aNet->GetNetCode() );
932}
933
934
936{
937 return UnescapeString( aNet->GetNetname() );
938}
939
940
941wxString PCB_NET_INSPECTOR_PANEL::formatCount( const unsigned int aValue )
942{
943 return wxString::Format( wxT( "%u" ), aValue );
944}
945
946
947wxString PCB_NET_INSPECTOR_PANEL::formatLength( const int64_t aValue ) const
948{
949 return m_frame->MessageTextFromValue( aValue,
950 // don't include unit label in the string when reporting
951 !m_inReporting );
952}
953
954wxString PCB_NET_INSPECTOR_PANEL::formatDelay( const int64_t aValue ) const
955{
956 return m_frame->MessageTextFromValue( aValue,
957 // don't include unit label in the string when reporting
959}
960
961
962void PCB_NET_INSPECTOR_PANEL::updateDisplayedRowValues( const std::optional<LIST_ITEM_ITER>& aRow ) const
963{
964 if( !aRow )
965 return;
966
967 wxDataViewItemArray sel;
968 m_netsList->GetSelections( sel );
969
970 m_dataModel->updateItem( aRow );
971
972 if( !sel.IsEmpty() )
973 {
974 m_netsList->SetSelections( sel );
975 m_netsList->EnsureVisible( sel.Item( 0 ) );
976 }
977}
978
979
980/*****************************************************************************************
981 *
982 * BOARD_LISTENER event handling
983 *
984 * ***************************************************************************************/
985
986
988{
989 m_board = m_frame->GetBoard();
990
991 if( m_board )
992 m_board->AddListener( this );
993
994 m_boardLoaded = true;
995 m_boardLoading = true;
996
998 auto& cfg = localSettings.m_NetInspectorPanel;
999 // ChangeValue avoids the wxEVT_TEXT that SetValue fires, which doubled buildNetsList() below
1000 m_searchCtrl->ChangeValue( cfg.filter_text );
1001
1002 // Skip the full net solve while hidden; OnShowPanel() rebuilds from scratch when revealed
1003 if( IsShown() )
1004 {
1005 buildNetsList( true );
1006 m_boardLoading = false;
1007 }
1008}
1009
1010
1012{
1013 const std::vector<BOARD_ITEM*> item{ aBoardItem };
1014 updateBoardItems( item );
1015}
1016
1017
1018void PCB_NET_INSPECTOR_PANEL::OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems )
1019{
1020 updateBoardItems( aBoardItems );
1021}
1022
1023
1024void PCB_NET_INSPECTOR_PANEL::updateBoardItems( const std::vector<BOARD_ITEM*>& aBoardItems )
1025{
1026 if( !IsShownOnScreen() )
1027 return;
1028
1029 // Rebuild full list for large changes
1030 if( aBoardItems.size()
1031 > static_cast<size_t>( ADVANCED_CFG::GetCfg().m_NetInspectorBulkUpdateOptimisationThreshold ) )
1032 {
1033 buildNetsList();
1034 }
1035 else
1036 {
1037 std::vector<NETINFO_ITEM*> changedNets;
1038
1039 for( BOARD_ITEM* boardItem : aBoardItems )
1040 {
1041 if( NETINFO_ITEM* net = dynamic_cast<NETINFO_ITEM*>( boardItem ) )
1042 {
1043 // A new net has been added to the board. Add it to our list if it passes the netname filter test.
1044 if( netFilterMatches( net ) )
1045 changedNets.emplace_back( net );
1046 }
1047 else if( BOARD_CONNECTED_ITEM* i = dynamic_cast<BOARD_CONNECTED_ITEM*>( boardItem ) )
1048 {
1049 changedNets.emplace_back( i->GetNet() );
1050 }
1051 else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( boardItem ) )
1052 {
1053 for( const PAD* pad : footprint->Pads() )
1054 {
1055 if( netFilterMatches( pad->GetNet() ) )
1056 changedNets.emplace_back( pad->GetNet() );
1057 }
1058 }
1059 }
1060
1061 std::ranges::sort( changedNets,
1062 []( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
1063 {
1064 return a->GetNetCode() < b->GetNetCode();
1065 } );
1066
1067 updateNets( changedNets );
1068 }
1069
1070 m_netsList->Refresh();
1071}
1072
1073
1074void PCB_NET_INSPECTOR_PANEL::updateNets( const std::vector<NETINFO_ITEM*>& aNets ) const
1075{
1076 std::vector<NETINFO_ITEM*> netsToUpdate;
1077 std::unordered_set<NETINFO_ITEM*> netsToDelete;
1078
1079 for( NETINFO_ITEM* net : aNets )
1080 {
1081 // Add all nets to the deletion list - we will prune this later to only contain unhandled nets
1082 netsToDelete.insert( net );
1083
1084 // Only calculate nets that match the current filter
1085 if( netFilterMatches( net ) )
1086 netsToUpdate.emplace_back( net );
1087 }
1088
1089 wxWindowUpdateLocker updateLocker( m_netsList );
1090
1091 std::vector<std::unique_ptr<LIST_ITEM>> newListItems = calculateNets( aNets, true );
1092
1093 for( std::unique_ptr<LIST_ITEM>& newListItem : newListItems )
1094 {
1095 // Remove the handled net from the deletion list
1096 netsToDelete.erase( newListItem->GetNet() );
1097
1098 std::optional<LIST_ITEM_ITER> curNetRow = m_dataModel->findItem( newListItem->GetNetCode() );
1099
1100 if( !m_showZeroPadNets && newListItem->GetPadCount() == 0 )
1101 {
1102 m_dataModel->deleteItem( curNetRow );
1103 continue;
1104 }
1105
1106 if( !curNetRow )
1107 {
1108 m_dataModel->addItem( std::move( newListItem ) );
1109 continue;
1110 }
1111
1112 const std::unique_ptr<LIST_ITEM>& curListItem = *curNetRow.value();
1113
1114 if( curListItem->GetNetName() != newListItem->GetNetName() )
1115 {
1116 // If the name has changed, it might require re-grouping. It's easier to remove and re-insert it.
1117 m_dataModel->deleteItem( curNetRow );
1118 m_dataModel->addItem( std::move( newListItem ) );
1119 }
1120 else
1121 {
1122 curListItem->SetPadCount( newListItem->GetPadCount() );
1123 curListItem->SetPadDieLength( newListItem->GetPadDieLength() );
1124 curListItem->SetPadDieDelay( newListItem->GetPadDieDelay() );
1125 curListItem->SetViaCount( newListItem->GetViaCount() );
1126 curListItem->SetViaLength( newListItem->GetViaLength() );
1127 curListItem->SetViaDelay( newListItem->GetViaDelay() );
1128 curListItem->SetUnroutedLength( newListItem->GetUnroutedLength() );
1129 curListItem->SetLayerWireLengths( newListItem->GetLayerWireLengths() );
1130 curListItem->SetNetChainName( newListItem->GetNetChainName() );
1131 curListItem->SetNetChainLength( newListItem->GetNetChainLength() );
1132 curListItem->SetNetChainDelay( newListItem->GetNetChainDelay() );
1133
1135 curListItem->SetLayerWireDelays( newListItem->GetLayerWireDelays() );
1136
1137 updateDisplayedRowValues( curNetRow );
1138 }
1139 }
1140
1141 // Delete any nets we have not yet handled
1142 for( const NETINFO_ITEM* netToDelete : netsToDelete )
1143 m_dataModel->deleteItem( m_dataModel->findItem( netToDelete->GetNetCode() ) );
1144}
1145
1146
1148{
1149 const std::vector<BOARD_ITEM*> item{ aBoardItem };
1150 updateBoardItems( item );
1151}
1152
1153
1154void PCB_NET_INSPECTOR_PANEL::OnBoardItemsRemoved( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems )
1155{
1156 updateBoardItems( aBoardItems );
1157}
1158
1159
1161{
1162 if( !IsShownOnScreen() )
1163 return;
1164
1165 buildNetsList();
1166}
1167
1168
1170{
1171 const std::vector<BOARD_ITEM*> item{ aBoardItem };
1172 updateBoardItems( item );
1173}
1174
1175
1176void PCB_NET_INSPECTOR_PANEL::OnBoardItemsChanged( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems )
1177{
1178 updateBoardItems( aBoardItems );
1179}
1180
1181
1183 std::vector<BOARD_ITEM*>& aAddedItems,
1184 std::vector<BOARD_ITEM*>& aRemovedItems,
1185 std::vector<BOARD_ITEM*>& aChangedItems )
1186{
1187 if( !IsShownOnScreen() )
1188 return;
1189
1190 std::vector<BOARD_ITEM*> allItems{ aAddedItems.begin(), aAddedItems.end() };
1191 allItems.insert( allItems.end(), aRemovedItems.begin(), aRemovedItems.end() );
1192 allItems.insert( allItems.end(), aChangedItems.begin(), aChangedItems.end() );
1193 updateBoardItems( allItems );
1194}
1195
1196
1198{
1199 if( m_highlightingNets || !IsShownOnScreen() )
1200 return;
1201
1202 if( !m_board->IsHighLightNetON() )
1203 {
1204 m_netsList->UnselectAll();
1205 }
1206 else
1207 {
1208 const std::set<int>& selected_codes = m_board->GetHighLightNetCodes();
1209
1210 wxDataViewItemArray new_selection;
1211 new_selection.Alloc( selected_codes.size() );
1212
1213 for( const int code : selected_codes )
1214 {
1215 if( std::optional<LIST_ITEM_ITER> r = m_dataModel->findItem( code ) )
1216 new_selection.Add( wxDataViewItem( &***r ) );
1217 }
1218
1219 m_netsList->SetSelections( new_selection );
1220
1221 if( !new_selection.IsEmpty() )
1222 m_netsList->EnsureVisible( new_selection.Item( 0 ) );
1223 }
1224}
1225
1226
1227/*****************************************************************************************
1228 *
1229 * UI-generated event handling
1230 *
1231 * ***************************************************************************************/
1232
1234{
1235 // A board loaded while hidden still owes its first full build, columns included
1236 if( m_boardLoading )
1237 {
1238 buildNetsList( true );
1239 m_boardLoading = false;
1240 }
1241 else
1242 {
1243 buildNetsList();
1244 }
1245
1247}
1248
1249
1251{
1252 bool multipleSelections = false;
1253 const LIST_ITEM* selItem = nullptr;
1254
1255 if( m_netsList->GetSelectedItemsCount() == 1 )
1256 {
1257 selItem = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1258 }
1259 else
1260 {
1261 if( m_netsList->GetSelectedItemsCount() > 1 )
1262 multipleSelections = true;
1263 }
1264
1265 wxMenu menu;
1266
1267 // Net edit menu items
1268 wxMenuItem* highlightNet = new wxMenuItem( &menu, ID_HIGHLIGHT_SELECTED_NETS, _( "Highlight Selected Net" ),
1269 wxEmptyString, wxITEM_NORMAL );
1270 menu.Append( highlightNet );
1271
1272 wxMenuItem* clearHighlighting = new wxMenuItem( &menu, ID_CLEAR_HIGHLIGHTING, _( "Clear Net Highlighting" ),
1273 wxEmptyString, wxITEM_NORMAL );
1274 menu.Append( clearHighlighting );
1275
1276 RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
1277 const std::set<int>& selected_codes = renderSettings->GetHighlightNetCodes();
1278
1279 if( selected_codes.size() == 0 )
1280 clearHighlighting->Enable( false );
1281
1282 menu.AppendSeparator();
1283
1284 wxMenuItem* renameNet = new wxMenuItem( &menu, ID_RENAME_NET, _( "Rename Selected Net..." ), wxEmptyString,
1285 wxITEM_NORMAL );
1286 menu.Append( renameNet );
1287
1288 wxMenuItem* deleteNet = new wxMenuItem( &menu, ID_DELETE_NET, _( "Delete Selected Net" ), wxEmptyString,
1289 wxITEM_NORMAL );
1290 menu.Append( deleteNet );
1291
1292 menu.AppendSeparator();
1293
1294 wxMenuItem* addNet = new wxMenuItem( &menu, ID_ADD_NET, _( "Add Net..." ), wxEmptyString, wxITEM_NORMAL );
1295 menu.Append( addNet );
1296
1297 if( !selItem && !multipleSelections )
1298 {
1299 highlightNet->Enable( false );
1300 deleteNet->Enable( false );
1301 renameNet->Enable( false );
1302 }
1303 else
1304 {
1305 if( multipleSelections || selItem->GetIsGroup() )
1306 {
1307 highlightNet->SetItemLabel( _( "Highlight Selected Nets" ) );
1308 renameNet->Enable( false );
1309 deleteNet->SetItemLabel( _( "Delete Selected Nets" ) );
1310 }
1311 }
1312
1313 menu.AppendSeparator();
1314
1315 wxMenuItem* removeSelectedGroup = new wxMenuItem( &menu, ID_REMOVE_SELECTED_GROUP,
1316 _( "Remove Selected Custom Group" ),
1317 wxEmptyString, wxITEM_NORMAL );
1318 menu.Append( removeSelectedGroup );
1319
1320 if( !selItem || !selItem->GetIsGroup() )
1321 removeSelectedGroup->Enable( false );
1322
1323 menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &PCB_NET_INSPECTOR_PANEL::onContextMenuSelection, this );
1324
1325 PopupMenu( &menu );
1326}
1327
1328
1330{
1331 SaveSettings();
1332 buildNetsList();
1333}
1334
1335
1337{
1338 wxString newGroupName;
1339 NETNAME_VALIDATOR validator( &newGroupName );
1340
1341 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Group name / pattern:" ), _( "New Group" ), newGroupName );
1342 wxStaticText* help = new wxStaticText( &dlg, wxID_ANY, _( "(Use /.../ to indicate a regular expression.)" ) );
1343 help->SetFont( KIUI::GetInfoFont( this ).Italic() );
1344 dlg.m_ContentSizer->Add( help, 0, wxALL|wxEXPAND, 5 );
1346 dlg.GetSizer()->SetSizeHints( &dlg );
1347
1348 if( dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty() )
1349 return; //Aborted by user
1350
1351 newGroupName = UnescapeString( dlg.GetValue() );
1352
1353 if( newGroupName == "" )
1354 return;
1355
1356 if( std::ranges::find_if( m_custom_group_rules,
1357 [&]( std::unique_ptr<EDA_COMBINED_MATCHER>& rule )
1358 {
1359 return rule->GetPattern() == newGroupName;
1360 } )
1361 == m_custom_group_rules.end() )
1362 {
1363 m_custom_group_rules.push_back( std::make_unique<EDA_COMBINED_MATCHER>( newGroupName, CTX_NET ) );
1364 SaveSettings();
1365 }
1366
1367 buildNetsList();
1368}
1369
1370
1372{
1373 m_highlightingNets = true;
1374
1375 m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( false );
1376 m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
1377 m_frame->GetCanvas()->Refresh();
1378
1379 m_highlightingNets = false;
1380}
1381
1382
1384{
1385 if( !m_rowExpanding )
1386 SaveSettings();
1387}
1388
1389
1391{
1392 m_contextMenuColumn = event.GetDataViewColumn();
1393
1394 wxMenu menu;
1395
1396 menu.Append( ID_AUTOFIT_COLUMN, _( "Auto-fit Column" ) );
1397 menu.Append( ID_AUTOFIT_ALL_COLUMNS, _( "Auto-fit All Columns" ) );
1398 menu.AppendSeparator();
1399
1401 menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &PCB_NET_INSPECTOR_PANEL::onContextMenuSelection, this );
1402 PopupMenu( &menu );
1403}
1404
1405
1406void PCB_NET_INSPECTOR_PANEL::OnConfigButton( wxCommandEvent& event )
1407{
1409 auto& cfg = localSettings.m_NetInspectorPanel;
1410
1411 const LIST_ITEM* selItem = nullptr;
1412
1413 if( m_netsList->GetSelectedItemsCount() == 1 )
1414 {
1415 selItem = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1416 }
1417
1418 wxMenu menu;
1419
1420 // Filtering menu items
1421 wxMenuItem* filterByNetName = new wxMenuItem( &menu, ID_FILTER_BY_NET_NAME, _( "Filter by Net Name" ),
1422 wxEmptyString, wxITEM_CHECK );
1423 menu.Append( filterByNetName );
1424 filterByNetName->Check( cfg.filter_by_net_name );
1425
1426 wxMenuItem* filterByNetclass = new wxMenuItem( &menu, ID_FILTER_BY_NETCLASS, _( "Filter by Netclass" ),
1427 wxEmptyString, wxITEM_CHECK );
1428 menu.Append( filterByNetclass );
1429 filterByNetclass->Check( cfg.filter_by_netclass );
1430
1431 menu.AppendSeparator();
1432
1433 // Grouping menu items
1434 //wxMenuItem* groupConstraint =
1435 // new wxMenuItem( &menu, ID_GROUP_BY_CONSTRAINT, _( "Group by DRC Constraint" ),
1436 // wxEmptyString, wxITEM_CHECK );
1437 //groupConstraint->Check( m_group_by_constraint );
1438 //menu.Append( groupConstraint );
1439
1440 wxMenuItem* groupNetclass = new wxMenuItem( &menu, ID_GROUP_BY_NETCLASS, _( "Group by Netclass" ),
1441 wxEmptyString, wxITEM_CHECK );
1442 menu.Append( groupNetclass );
1443 groupNetclass->Check( m_groupByNetclass );
1444
1445 wxMenuItem* groupSignal = new wxMenuItem( &menu, ID_GROUP_BY_NET_CHAIN,
1446 _( "Group by Net Chain" ),
1447 wxEmptyString, wxITEM_CHECK );
1448 menu.Append( groupSignal );
1449 groupSignal->Check( m_groupByNetChain );
1450
1451 menu.AppendSeparator();
1452
1453 wxMenuItem* addGroup = new wxMenuItem( &menu, ID_ADD_GROUP, _( "Add Custom Group..." ),
1454 wxEmptyString, wxITEM_NORMAL );
1455 menu.Append( addGroup );
1456
1457 wxMenuItem* removeSelectedGroup = new wxMenuItem( &menu, ID_REMOVE_SELECTED_GROUP,
1458 _( "Remove Selected Custom Group" ),
1459 wxEmptyString, wxITEM_NORMAL );
1460 menu.Append( removeSelectedGroup );
1461
1462 if( !selItem || !selItem->GetIsGroup() )
1463 removeSelectedGroup->Enable( false );
1464
1465 wxMenuItem* removeCustomGroups = new wxMenuItem( &menu, ID_REMOVE_GROUPS, _( "Remove All Custom Groups" ),
1466 wxEmptyString, wxITEM_NORMAL );
1467 menu.Append( removeCustomGroups );
1468 removeCustomGroups->Enable( m_custom_group_rules.size() != 0 );
1469
1470 menu.AppendSeparator();
1471
1472 wxMenuItem* showZeroNetPads = new wxMenuItem( &menu, ID_SHOW_ZERO_NET_PADS, _( "Show Zero Pad Nets" ),
1473 wxEmptyString, wxITEM_CHECK );
1474 menu.Append( showZeroNetPads );
1475 showZeroNetPads->Check( m_showZeroPadNets );
1476
1477 wxMenuItem* showUnconnectedNets = new wxMenuItem( &menu, ID_SHOW_UNCONNECTED_NETS, _( "Show Unconnected Nets" ),
1478 wxEmptyString, wxITEM_CHECK );
1479 menu.Append( showUnconnectedNets );
1480 showUnconnectedNets->Check( m_showUnconnectedNets );
1481
1482 menu.AppendSeparator();
1483
1484 wxMenuItem* showTimeDomainDetails = new wxMenuItem( &menu, ID_SHOW_TIME_DOMAIN_DETAILS,
1485 _( "Show Time Domain Details" ),
1486 wxEmptyString, wxITEM_CHECK );
1487 menu.Append( showTimeDomainDetails );
1488 showTimeDomainDetails->Check( m_showTimeDomainDetails );
1489
1490 menu.AppendSeparator();
1491
1492 // Report generation
1493 wxMenuItem* generateReport = new wxMenuItem( &menu, ID_GENERATE_REPORT, _( "Save Net Inspector Report..." ),
1494 wxEmptyString, wxITEM_NORMAL );
1495 menu.Append( generateReport );
1496
1497 menu.AppendSeparator();
1498
1499 // Show / hide columns menu items
1500 wxMenu* colsMenu = new wxMenu();
1501 generateShowHideColumnMenu( colsMenu );
1502 menu.AppendSubMenu( colsMenu, _( "Show / Hide Columns" ) );
1503
1504 menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &PCB_NET_INSPECTOR_PANEL::onContextMenuSelection, this );
1505
1506 PopupMenu( &menu );
1507}
1508
1509
1511{
1512 for( int i = 1; i <= COLUMN_LAST_STATIC_COL; ++i )
1513 {
1514 wxMenuItem* opt = new wxMenuItem( target, ID_HIDE_COLUMN + i, m_columns[i].display_name,
1515 wxEmptyString, wxITEM_CHECK );
1516 wxDataViewColumn* col = getDisplayedColumnForModelField( i );
1517 target->Append( opt );
1518 opt->Check( !col->IsHidden() );
1519 }
1520
1521 target->AppendSeparator();
1522
1523 for( std::size_t i = COLUMN_LAST_STATIC_COL + 1; i < m_columns.size(); ++i )
1524 {
1525 wxMenuItem* opt = new wxMenuItem( target, ID_HIDE_COLUMN + i, m_columns[i].display_name,
1526 wxEmptyString, wxITEM_CHECK );
1527 wxDataViewColumn* col = getDisplayedColumnForModelField( i );
1528 target->Append( opt );
1529 opt->Check( !col->IsHidden() );
1530 }
1531}
1532
1533
1535{
1536 bool saveAndRebuild = true;
1537
1538 switch( event.GetId() )
1539 {
1540 case ID_ADD_NET:
1541 onAddNet();
1542 break;
1543
1544 case ID_RENAME_NET:
1546 break;
1547
1548 case ID_DELETE_NET:
1550 break;
1551
1552 case ID_ADD_GROUP:
1553 onAddGroup();
1554 break;
1555
1558 break;
1559
1562 break;
1563
1566 break;
1567
1570 break;
1571
1574 break;
1575
1578 break;
1579
1580 case ID_REMOVE_GROUPS:
1581 m_custom_group_rules.clear();
1582 break;
1583
1586 break;
1587
1590 break;
1591
1594 break;
1595
1596 case ID_GENERATE_REPORT:
1598 saveAndRebuild = false;
1599 break;
1600
1603 saveAndRebuild = false;
1604 break;
1605
1608 saveAndRebuild = false;
1609 break;
1610
1611 case ID_AUTOFIT_COLUMN:
1614
1615 saveAndRebuild = false;
1616 break;
1617
1619 for( unsigned int i = 0; i < m_netsList->GetColumnCount(); ++i )
1620 autosizeColumn( m_netsList->GetColumn( i ) );
1621
1622 saveAndRebuild = false;
1623 break;
1624
1625 default:
1626 if( event.GetId() >= ID_HIDE_COLUMN )
1627 {
1628 const int columnId = event.GetId() - ID_HIDE_COLUMN;
1629 wxDataViewColumn* col = getDisplayedColumnForModelField( columnId );
1630 // Make sure we end up with something non-zero so we can resize it
1631 col->SetWidth( std::max( col->GetWidth(), 10 ) );
1632 col->SetHidden( !col->IsHidden() );
1633 }
1634 break;
1635 }
1636
1637 if( saveAndRebuild )
1638 {
1639 SaveSettings();
1640 buildNetsList();
1641 }
1642}
1643
1644
1646{
1647 if( m_netsList->GetSelectedItemsCount() == 1 )
1648 {
1649 auto* selItem = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1650
1651 if( selItem->GetIsGroup() )
1652 {
1653 const wxString groupName = selItem->GetGroupName();
1654 const auto groupIter = std::ranges::find_if( m_custom_group_rules,
1655 [&]( std::unique_ptr<EDA_COMBINED_MATCHER>& rule )
1656 {
1657 return rule->GetPattern() == groupName;
1658 } );
1659
1660 if( groupIter != m_custom_group_rules.end() )
1661 {
1662 m_custom_group_rules.erase( groupIter );
1663 SaveSettings();
1664 buildNetsList();
1665 }
1666 }
1667 }
1668}
1669
1670
1672{
1673 wxFileDialog dlg( this, _( "Save Net Inspector Report File" ), "", "",
1674 _( "Report file" ) + AddFileExtListToFilter( { "csv" } ),
1675 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1676
1678
1679 if( dlg.ShowModal() == wxID_CANCEL )
1680 return;
1681
1682 wxTextFile f( dlg.GetPath() );
1683
1684 f.Create();
1685
1686 wxString txt;
1687
1688 m_inReporting = true;
1689
1690 // Print Header:
1691 for( auto&& col : m_columns )
1692 {
1693 txt += '"';
1694
1695 if( col.has_units )
1696 {
1697 txt += wxString::Format( _( "%s (%s)" ),
1698 col.csv_name,
1699 EDA_UNIT_UTILS::GetLabel( m_frame->GetUserUnits() ) );
1700 }
1701 else
1702 {
1703 txt += col.csv_name;
1704 }
1705
1706 txt += wxT( "\";" );
1707 }
1708
1709 f.AddLine( txt );
1710
1711 // Print list of nets:
1712 const unsigned int num_rows = m_dataModel->itemCount();
1713
1714 for( unsigned int row = 0; row < num_rows; row++ )
1715 {
1716 auto& i = m_dataModel->itemAt( row );
1717
1718 if( i.GetIsGroup() || i.GetNetCode() == 0 )
1719 continue;
1720
1721 txt = "";
1722
1723 for( auto&& col : m_columns )
1724 {
1725 if( static_cast<int>( col.csv_flags ) & static_cast<int>( CSV_COLUMN_DESC::CSV_QUOTE ) )
1726 txt += '"' + m_dataModel->valueAt( col.num, row ).GetString() + wxT( "\";" );
1727 else
1728 txt += m_dataModel->valueAt( col.num, row ).GetString() + ';';
1729 }
1730
1731 f.AddLine( txt );
1732 }
1733
1734 m_inReporting = false;
1735
1736 f.Write();
1737 f.Close();
1738}
1739
1740
1742{
1744}
1745
1746
1748{
1749 // ignore selection changes while the whole list is being rebuilt.
1750 if( m_inBuildNetsList )
1751 return;
1752
1753 m_highlightingNets = true;
1754
1755 RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
1756
1757 if( m_netsList->HasSelection() )
1758 {
1759 wxDataViewItemArray sel;
1760 m_netsList->GetSelections( sel );
1761
1762 renderSettings->SetHighlight( false );
1763
1764 for( unsigned int i = 0; i < sel.GetCount(); ++i )
1765 {
1766 const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1767
1768 if( ii->GetIsGroup() )
1769 {
1770 for( auto c = ii->ChildrenBegin(), end = ii->ChildrenEnd(); c != end; ++c )
1771 renderSettings->SetHighlight( true, ( *c )->GetNetCode(), true );
1772 }
1773 else
1774 {
1775 renderSettings->SetHighlight( true, ii->GetNetCode(), true );
1776 }
1777 }
1778 }
1779 else
1780 {
1781 renderSettings->SetHighlight( false );
1782 }
1783
1784 m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
1785 m_frame->GetCanvas()->Refresh();
1786
1787 m_highlightingNets = false;
1788}
1789
1790
1791void PCB_NET_INSPECTOR_PANEL::OnColumnSorted( wxDataViewEvent& event )
1792{
1793 if( !m_inBuildNetsList )
1794 SaveSettings();
1795}
1796
1797
1799{
1800 // Rebuilt the nets list, and force rebuild of columns in case the stackup has changed
1801 buildNetsList( true );
1802}
1803
1804
1806{
1807 wxString newNetName;
1808 NETNAME_VALIDATOR validator( &newNetName );
1809
1810 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "New Net" ), newNetName );
1812
1813 while( true )
1814 {
1815 if( dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty() )
1816 return; //Aborted by user
1817
1818 newNetName = dlg.GetValue();
1819
1820 if( m_board->FindNet( newNetName ) )
1821 {
1822 DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ), newNetName ) );
1823 newNetName = wxEmptyString;
1824 }
1825 else
1826 {
1827 break;
1828 }
1829 }
1830
1831 NETINFO_ITEM* newnet = new NETINFO_ITEM( m_board, dlg.GetValue(), 0 );
1832
1833 m_board->Add( newnet );
1834
1835 // We'll get an OnBoardItemAdded callback from this to update our listbox
1836 m_frame->OnModify();
1837}
1838
1839
1841{
1842 if( m_netsList->GetSelectedItemsCount() == 1 )
1843 {
1844 const LIST_ITEM* sel = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1845
1846 if( sel->GetIsGroup() )
1847 return;
1848
1849 NETINFO_ITEM* net = sel->GetNet();
1850 wxString fullNetName = net->GetNetname();
1851 wxString netPath;
1852 wxString shortNetName;
1853
1854 if( fullNetName.Contains( wxT( "/" ) ) )
1855 {
1856 netPath = fullNetName.BeforeLast( '/' ) + '/';
1857 shortNetName = fullNetName.AfterLast( '/' );
1858 }
1859 else
1860 {
1861 shortNetName = fullNetName;
1862 }
1863
1864 wxString unescapedShortName = UnescapeString( shortNetName );
1865
1866 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "Rename Net" ), unescapedShortName );
1867 NETNAME_VALIDATOR validator( &unescapedShortName );
1869
1870 while( true )
1871 {
1872 if( dlg.ShowModal() != wxID_OK || dlg.GetValue() == unescapedShortName )
1873 return;
1874
1875 unescapedShortName = dlg.GetValue();
1876
1877 if( unescapedShortName.IsEmpty() )
1878 {
1879 DisplayError( this, _( "Net name cannot be empty." ) );
1880 continue;
1881 }
1882
1883 shortNetName = EscapeString( unescapedShortName, CTX_NETNAME );
1884 fullNetName = netPath + shortNetName;
1885
1886 if( m_board->FindNet( shortNetName ) || m_board->FindNet( fullNetName ) )
1887 {
1888 DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ), unescapedShortName ) );
1889 unescapedShortName = wxEmptyString;
1890 }
1891 else
1892 {
1893 break;
1894 }
1895 }
1896
1897 for( BOARD_CONNECTED_ITEM* boardItem : m_frame->GetBoard()->AllConnectedItems() )
1898 {
1899 if( boardItem->GetNet() == net )
1900 boardItem->SetFlags( CANDIDATE );
1901 else
1902 boardItem->ClearFlags( CANDIDATE );
1903 }
1904
1905 // the changed name might require re-grouping. remove/re-insert is easier.
1906 auto removed_item = m_dataModel->deleteItem( m_dataModel->findItem( net ) );
1907
1908 m_board->Remove( net );
1909 net->SetNetname( fullNetName );
1910 m_board->Add( net );
1911
1912 for( BOARD_CONNECTED_ITEM* boardItem : m_frame->GetBoard()->AllConnectedItems() )
1913 {
1914 if( boardItem->GetFlags() & CANDIDATE )
1915 boardItem->SetNet( net );
1916 }
1917
1918 buildNetsList();
1919
1920 if( std::optional<LIST_ITEM_ITER> r = m_dataModel->findItem( net ) )
1921 m_netsList->Select( wxDataViewItem( r.value()->get() ) );
1922
1923 m_frame->OnModify();
1924
1925 // Currently only tracks and pads have netname annotations and need to be redrawn,
1926 // but zones are likely to follow. Since we don't have a way to ask what is current,
1927 // just refresh all items.
1928 m_frame->GetCanvas()->GetView()->UpdateAllItems( KIGFX::REPAINT );
1929 m_frame->GetCanvas()->Refresh();
1930 }
1931}
1932
1933
1935{
1936 if( !m_netsList->HasSelection() )
1937 return;
1938
1939 wxDataViewItemArray sel;
1940 m_netsList->GetSelections( sel );
1941
1942 auto delete_one = [this]( const LIST_ITEM* i )
1943 {
1944 if( i->GetPadCount() == 0
1945 || IsOK( this, wxString::Format( _( "Net '%s' is in use. Delete anyway?" ), i->GetNetName() ) ) )
1946 {
1947 // This is a bit hacky, but it will do for now, since this is the only path
1948 // outside the netlist updater where you can remove a net from a BOARD.
1949 int removedCode = i->GetNetCode();
1950
1951 m_frame->GetCanvas()->GetView()->UpdateAllItemsConditionally(
1952 [removedCode]( KIGFX::VIEW_ITEM* aItem ) -> int
1953 {
1954 auto boardItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem );
1955
1956 if( boardItem && boardItem->GetNetCode() == removedCode )
1957 return KIGFX::REPAINT;
1958
1959 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( aItem );
1960
1961 if( text && text->HasTextVars() )
1962 {
1963 text->ClearRenderCache();
1964 text->ClearBoundingBoxCache();
1966 }
1967
1968 return 0;
1969 } );
1970
1971 m_board->Remove( i->GetNet() );
1972 m_frame->OnModify();
1973
1974 // We'll get an OnBoardItemRemoved callback from this to update our listbox
1975 }
1976 };
1977
1978 for( unsigned int i = 0; i < sel.GetCount(); ++i )
1979 {
1980 const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1981
1982 if( ii->GetIsGroup() )
1983 {
1984 if( ii->ChildrenCount() != 0
1985 && IsOK( this, wxString::Format( _( "Delete all nets in group '%s'?" ), ii->GetGroupName() ) ) )
1986 {
1987 // we can't be iterating the children container and deleting items from
1988 // it at the same time. thus take a copy of it first.
1989 std::vector<const LIST_ITEM*> children;
1990 children.reserve( ii->ChildrenCount() );
1991 std::copy( ii->ChildrenBegin(), ii->ChildrenEnd(), std::back_inserter( children ) );
1992
1993 for( const LIST_ITEM* c : children )
1994 delete_one( c );
1995 }
1996 }
1997 else
1998 {
1999 delete_one( ii );
2000 }
2001 }
2002}
2003
2004/*****************************************************************************************
2005 *
2006 * Application-generated event handling
2007 *
2008 * ***************************************************************************************/
2009
2010
2012{
2013 SaveSettings();
2014 buildNetsList( true );
2015 m_dataModel->updateAllItems();
2016}
2017
2018
2019void PCB_NET_INSPECTOR_PANEL::onUnitsChanged( wxCommandEvent& event )
2020{
2021 m_dataModel->updateAllItems();
2022 event.Skip();
2023}
2024
2025
2026/*****************************************************************************************
2027 *
2028 * Settings persistence
2029 *
2030 * ***************************************************************************************/
2031
2032
2034{
2035 // Don't save settings if a board has not yet been loaded or the panel hasn't been displayed.
2036 // Events fire while we set up the panel which overwrite the settings we haven't yet loaded.
2037 bool displayed = false;
2038
2039 for( unsigned int ii = 0; ii < m_dataModel->columnCount() && !displayed; ++ii )
2040 {
2041 if( m_netsList->GetColumn( ii )->GetWidth() > 0 )
2042 displayed = true;
2043 }
2044
2045 if( !displayed || !m_boardLoaded || m_boardLoading )
2046 return;
2047
2049 auto& cfg = localSettings.m_NetInspectorPanel;
2050
2051 // User-defined filters / grouping
2052 cfg.filter_text = m_searchCtrl->GetValue();
2053 cfg.filter_by_net_name = m_filterByNetName;
2054 cfg.filter_by_netclass = m_filterByNetclass;
2055 cfg.group_by_netclass = m_groupByNetclass;
2056 cfg.group_by_net_chain = m_groupByNetChain;
2057 cfg.group_by_constraint = m_groupByConstraint;
2058 cfg.show_zero_pad_nets = m_showZeroPadNets;
2059 cfg.show_unconnected_nets = m_showUnconnectedNets;
2060 cfg.show_time_domain_details = m_showTimeDomainDetails;
2061
2062 // Grid sorting
2063 wxDataViewColumn* sortingCol = m_netsList->GetSortingColumn();
2064 cfg.sorting_column = sortingCol ? static_cast<int>( sortingCol->GetModelColumn() ) : -1;
2065 cfg.sort_order_asc = sortingCol ? sortingCol->IsSortOrderAscending() : true;
2066
2067 // Column arrangement / sizes
2068 cfg.col_order.resize( m_dataModel->columnCount() );
2069 cfg.col_widths.resize( m_dataModel->columnCount() );
2070 cfg.col_hidden.resize( m_dataModel->columnCount() );
2071
2072 for( unsigned int ii = 0; ii < m_dataModel->columnCount(); ++ii )
2073 {
2074 cfg.col_order[ii] = (int) m_netsList->GetColumn( ii )->GetModelColumn();
2075 cfg.col_widths[ii] = m_netsList->GetColumn( ii )->GetWidth();
2076 cfg.col_hidden[ii] = m_netsList->GetColumn( ii )->IsHidden();
2077 }
2078
2079 // Expanded rows
2080 cfg.expanded_rows.clear();
2081 std::vector<std::pair<wxString, wxDataViewItem>> groupItems = m_dataModel->getGroupDataViewItems();
2082
2083 for( std::pair<wxString, wxDataViewItem>& item : groupItems )
2084 {
2085 if( m_netsList->IsExpanded( item.second ) )
2086 cfg.expanded_rows.push_back( item.first );
2087 }
2088
2089 // Customer group rules
2090 cfg.custom_group_rules.clear();
2091
2092 for( const std::unique_ptr<EDA_COMBINED_MATCHER>& rule : m_custom_group_rules )
2093 cfg.custom_group_rules.push_back( rule->GetPattern() );
2094}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
CN_ITEM represents a BOARD_CONNECTED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
int Net() const
int ShowModal() override
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:82
Lightweight class which holds a pad, via, or a routed trace outline.
Class which calculates lengths (and associated routing statistics) in a BOARD context.
LENGTH_DELAY_STATS CalculateLengthDetails(std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, PATH_OPTIMISATIONS aOptimisations, const PAD *aStartPad=nullptr, const PAD *aEndPad=nullptr, LENGTH_DELAY_LAYER_OPT aLayerOpt=LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL, LENGTH_DELAY_DOMAIN_OPT aDomain=LENGTH_DELAY_DOMAIN_OPT::NO_DELAY_DETAIL, LENGTH_DELAY_ITEM_DETAILS *aPerItemLengthDelays=nullptr) const
Calculates the electrical length of the given items.
LENGTH_DELAY_CALCULATION_ITEM GetLengthCalculationItem(const BOARD_CONNECTED_ITEM *aBoardItem) const
Return a LENGTH_CALCULATION_ITEM constructed from the given BOARD_CONNECTED_ITEM.
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:43
const wxString GetName() const
Gets the name of this (maybe aggregate) netclass in a format for internal usage or for export to exte...
Definition netclass.cpp:368
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:122
void SetNetname(const wxString &aNewName)
Set the long netname to aNetName, the short netname to the last token in the long netname's path,...
const wxString & GetNetname() const
Definition netinfo.h:110
NETCLASS * GetNetClass()
Definition netinfo.h:101
int GetNetCode() const
Definition netinfo.h:104
NET_INSPECTOR_PANEL(wxWindow *parent, EDA_BASE_FRAME *aFrame, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1, -1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
WX_DATAVIEWCTRL * m_netsList
wxSearchCtrl * m_searchCtrl
Definition pad.h:61
The main frame for Pcbnew.
Data model for display in the Net Inspector panel.
Primary data item for entries in the Net Inspector list.
void OnBoardItemRemoved(BOARD &aBoard, BOARD_ITEM *aBoardItem) override
void OnBoardHighlightNetChanged(BOARD &aBoard) override
void generateReport()
Generates a CSV report from currently disaplyed data.
PCB_NET_INSPECTOR_PANEL(wxWindow *parent, PCB_EDIT_FRAME *aFrame)
int getMinColumnWidth(int aModelColumn) const
Computes the minimum display width for a column based on its header text.
void onDeleteSelectedNet()
Deletes a selected net.
bool restoreSortColumn(int sortingColumnId, bool sortOrderAsc) const
Sets the sort column in the grid to that showing the given model ID column.
void OnBoardItemsChanged(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
void adjustListColumnSizes(PANEL_NET_INSPECTOR_SETTINGS *cfg) const
Adjust the sizing of list columns.
void OnSearchTextChanged(wxCommandEvent &event) override
void updateDisplayedRowValues(const std::optional< LIST_ITEM_ITER > &aRow) const
Refreshes displayed data for the given rows.
void onAddNet()
Adds a new user-specified net to the board.
PCB_EDIT_FRAME * m_frame
Owning edit frame.
void OnBoardItemsRemoved(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
void onClearHighlighting()
Clears highlighting from nets.
void onContextMenuSelection(wxCommandEvent &event)
Handle a net row(s) context menu selection.
static wxString formatNetCode(const NETINFO_ITEM *aNet)
void OnBoardChanged() override
Update panel when board is changed.
void generateShowHideColumnMenu(wxMenu *target)
Generates a sub-menu for the show / hide columns submenu.
std::vector< std::unique_ptr< LIST_ITEM > > calculateNets(const std::vector< NETINFO_ITEM * > &aNetCodes, bool aIncludeZeroPadNets) const
Calculates the length statistics for each given netcode.
void autosizeColumn(wxDataViewColumn *aCol)
Auto-sizes a column to fit both its header text and content.
wxDataViewColumn * m_contextMenuColumn
Tracks which column the header context menu was opened for.
static wxString formatCount(unsigned int aValue)
std::vector< std::unique_ptr< EDA_COMBINED_MATCHER > > m_custom_group_rules
Custom net grouping rules.
std::vector< CN_ITEM * > relevantConnectivityItems() const
Fetches an ordered (by NetCode) list of all board connectivity items.
void highlightSelectedNets()
Highlight the currently selected net.
void updateNets(const std::vector< NETINFO_ITEM * > &aNets) const
Updates displayed statistics for the given nets.
void OnHeaderContextMenu(wxDataViewEvent &event)
void OnBoardItemsAdded(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
void OnLanguageChangedImpl() override
Reloads strings on an application language change.
void onAddGroup()
Adds a custom display grouping of nets.
void OnNetsListContextMenu(wxDataViewEvent &event)
void OnBoardNetSettingsChanged(BOARD &aBoard) override
void OnBoardCompositeUpdate(BOARD &aBoard, std::vector< BOARD_ITEM * > &aAddedItems, std::vector< BOARD_ITEM * > &aRemovedItems, std::vector< BOARD_ITEM * > &aChangedItems) override
void OnNetsListItemActivated(wxDataViewEvent &event)
void OnColumnSorted(wxDataViewEvent &event)
void onRemoveSelectedGroup()
Removes a custom display grouping.
void buildColumns()
Build the required columns in the net inspector grid.
void OnBoardItemAdded(BOARD &aBoard, BOARD_ITEM *aBoardItem) override
void OnParentSetupChanged() override
Updates the netlist based on global board changes (e.g.
bool netFilterMatches(NETINFO_ITEM *aNet, PANEL_NET_INSPECTOR_SETTINGS *cfg=nullptr) const
Filter to determine whether a board net should be included in the net inspector.
void OnConfigButton(wxCommandEvent &event) override
void buildNetsList(bool rebuildColumns=false)
Rebuilds the net inspector list, removing all previous entries.
wxString formatLength(int64_t aValue) const
void SaveSettings() override
Persist the net inspector configuration to project / global settings.
void onRenameSelectedNet()
Renames a selected net.
void onUnitsChanged(wxCommandEvent &event)
Handle an application-level change of units.
void OnExpandCollapseRow(wxCommandEvent &event)
void updateBoardItems(const std::vector< BOARD_ITEM * > &aBoardItems)
Unified handling of added / deleted / modified board items.
wxString formatDelay(int64_t aValue) const
wxObjectDataPtr< DATA_MODEL > m_dataModel
The bound data model to display.
wxDataViewColumn * getDisplayedColumnForModelField(int columnId) const
Fetches the displayed grid view column for the given model column ID.
static wxString formatNetName(const NETINFO_ITEM *aNet)
void OnBoardItemChanged(BOARD &aBoard, BOARD_ITEM *aBoardItem) override
std::vector< COLUMN_DESC > m_columns
All displayed (or hidden) columns.
void OnShowPanel() override
Prepare the panel when shown in the editor.
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:123
The project local settings are things that are attached to a particular project, but also might be pa...
PANEL_NET_INSPECTOR_SETTINGS m_NetInspectorPanel
The state of the net inspector panel.
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition project.h:207
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
wxBoxSizer * m_ContentSizer
A KICAD version of wxTextEntryDialog which supports the various improvements/work-arounds from DIALOG...
wxString GetValue() const
void SetTextValidator(const wxTextValidator &validator)
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:274
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
#define _(s)
#define CANDIDATE
flag indicating that the structure is connected
Abstract pattern-matching tool and implementations.
@ CTX_NET
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ UNDEFINED_LAYER
Definition layer_ids.h:57
KICOMMON_API wxString GetLabel(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
@ REPAINT
Item needs to be redrawn.
Definition view_item.h:54
@ GEOMETRY
Position or shape has changed.
Definition view_item.h:51
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:533
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
double ChainBridgingDelayPerMm(const BOARD *aBoard, const wxString &aNetChain, const double aDefaultBridgeUnitDelay)
Pick a single per-IU-per-mm delay for a given chain.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
Class that computes missing connections on a PCB.
static bool highlightNet(TOOL_MANAGER *aToolMgr, const SCH_SHEET_PATH &aSheetPath, const VECTOR2D &aPosition)
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_NETNAME
Holds length measurement result details and statistics.
bool operator()(const CN_ITEM *a, const CN_ITEM *b) const
bool operator()(int a, const CN_ITEM *b) const
bool operator()(const CN_ITEM *a, int b) const
Persisted state for the net inspector panel.
std::vector< wxString > expanded_rows
std::vector< wxString > custom_group_rules
JSON_SCHEMA_VALIDATOR validator(schema)
KIBIS_MODEL * model
const SHAPE_LINE_CHAIN chain
VECTOR2I end
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:27
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
Custom text control validator definitions.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.
Definition of file extensions used in Kicad.