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
486 m_dataModel->SetIsTimeDomain( m_showTimeDomainDetails );
487
490
491 // Refresh all filtering / grouping settings
499
500 // Attempt to keep any expanded groups open
502 {
503 cfg->expanded_rows.clear();
504 DATA_MODEL* model = static_cast<DATA_MODEL*>( m_netsList->GetModel() );
505
506 for( const auto& [groupName, groupItem] : model->getGroupDataViewItems() )
507 {
508 if( m_netsList->IsExpanded( groupItem ) )
509 cfg->expanded_rows.push_back( groupName );
510 }
511 }
512
513 // When rebuilding the netlist, try to keep the row selection
514 wxDataViewItemArray sel;
515 m_netsList->GetSelections( sel );
516
517 std::vector<int> prev_selected_netcodes;
518 prev_selected_netcodes.reserve( sel.GetCount() );
519
520 for( unsigned int i = 0; i < sel.GetCount(); ++i )
521 {
522 const LIST_ITEM* item = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
523 prev_selected_netcodes.push_back( item->GetNetCode() );
524 }
525
526 int sorting_column_id = cfg->sorting_column;
527 bool sort_order_asc = cfg->sort_order_asc;
528
529 if( wxDataViewColumn* sorting_column = m_netsList->GetSortingColumn() )
530 {
531 if( !m_boardLoading )
532 {
533 sorting_column_id = static_cast<int>( sorting_column->GetModelColumn() );
534 sort_order_asc = sorting_column->IsSortOrderAscending();
535 }
536
537 // On GTK, wxDVC will crash if we rebuild with a sorting column set.
538 sorting_column->UnsetAsSortKey();
539 }
540
541 if( rebuildColumns )
542 {
543 m_netsList->ClearColumns();
544 buildColumns();
545 }
546
547 m_dataModel->deleteAllItems();
548 m_custom_group_rules.clear();
549
550 for( const wxString& rule : cfg->custom_group_rules )
551 m_custom_group_rules.push_back( std::make_unique<EDA_COMBINED_MATCHER>( rule, CTX_NET ) );
552
553 m_dataModel->addCustomGroups();
554
555 std::vector<NETINFO_ITEM*> netCodes;
556
557 for( NETINFO_ITEM* ni : m_board->GetNetInfo() )
558 {
559 if( netFilterMatches( ni, cfg ) )
560 netCodes.emplace_back( ni );
561 }
562
563 std::ranges::sort( netCodes,
564 []( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
565 {
566 return a->GetNetCode() < b->GetNetCode();
567 } );
568
569 std::vector<std::unique_ptr<LIST_ITEM>> lengths = calculateNets( netCodes, m_showZeroPadNets );
570
571 m_dataModel->addItems( lengths );
572
573 // Try to re-enable the sorting column
574 if( !restoreSortColumn( sorting_column_id, sort_order_asc ))
575 {
576 // By default, sort by Name column
578 }
579
580 // Try to restore the expanded groups
581 if( m_boardLoaded )
582 {
583 m_rowExpanding = true;
584
585 std::vector<std::pair<wxString, wxDataViewItem>> groupItems = m_dataModel->getGroupDataViewItems();
586
587 for( wxString& groupName : cfg->expanded_rows )
588 {
589 auto pred =
590 [&groupName]( const std::pair<wxString, wxDataViewItem>& item )
591 {
592 return groupName == item.first;
593 };
594
595 auto tableItem = std::ranges::find_if( groupItems, pred );
596
597 if( tableItem != groupItems.end() )
598 m_netsList->Expand( tableItem->second );
599 }
600
601 m_rowExpanding = false;
602 }
603
604 // Try to restore the selected rows
605 sel.Clear();
606
607 for( const int& nc : prev_selected_netcodes )
608 {
609 if( std::optional<LIST_ITEM_ITER> r = m_dataModel->findItem( nc ) )
610 {
611 const std::unique_ptr<LIST_ITEM>& list_item = *r.value();
612 sel.Add( wxDataViewItem( list_item.get() ) );
613 }
614 }
615
616 m_netsList->Thaw(); // Must thaw before reselecting to avoid windows selection bug
617
618 if( !sel.IsEmpty() )
619 {
620 m_netsList->SetSelections( sel );
621 m_netsList->EnsureVisible( sel.Item( 0 ) );
622 }
623 else
624 {
625 m_netsList->UnselectAll();
626 }
627
628 m_inBuildNetsList = false;
629}
630
631
634{
635 if( cfg == nullptr )
636 {
638 cfg = &localSettings.m_NetInspectorPanel;
639 }
640
641 // Never show an unconnected net
642 if( aNet->GetNetCode() <= 0 )
643 return false;
644
645 const wxString filterString = UnescapeString( m_searchCtrl->GetValue() ).Upper();
646 const wxString netName = UnescapeString( aNet->GetNetname() ).Upper();
647 const NETCLASS* netClass = aNet->GetNetClass();
648 const wxString netClassName = UnescapeString( netClass->GetName() ).Upper();
649
650 bool matched = false;
651
652 // No filter - match all
653 if( filterString.Length() == 0 )
654 matched = true;
655
656 // Search on net class
657 if( !matched && cfg->filter_by_netclass && netClassName.Find( filterString ) != wxNOT_FOUND )
658 matched = true;
659
660 // Search on net name
661 if( !matched && cfg->filter_by_net_name && netName.Find( filterString ) != wxNOT_FOUND )
662 matched = true;
663
664 // Remove unconnected nets if required
665 if( matched )
666 {
668 matched = !netName.StartsWith( wxT( "UNCONNECTED-(" ) );
669 }
670
671 return matched;
672}
673
674
676{
677 bool operator()( const CN_ITEM* a, const CN_ITEM* b ) const { return a->Net() < b->Net(); }
678
679 bool operator()( const CN_ITEM* a, int b ) const { return a->Net() < b; }
680
681 bool operator()( int a, const CN_ITEM* b ) const { return a < b->Net(); }
682};
683
684
686{
687 // Pre-filter the connectivity items and sort them by netcode. This avoids quadratic runtime when building the whole
688 // net list.
689 const auto type_bits = std::bitset<MAX_STRUCT_TYPE_ID>().set( PCB_TRACE_T )
690 .set( PCB_ARC_T )
691 .set( PCB_VIA_T )
692 .set( PCB_PAD_T );
693
694 std::vector<CN_ITEM*> cn_items;
695 cn_items.reserve( 1024 );
696
697 for( CN_ITEM* cn_item : m_board->GetConnectivity()->GetConnectivityAlgo()->ItemList() )
698 {
699 if( cn_item->Valid() && type_bits[cn_item->Parent()->Type()] )
700 cn_items.push_back( cn_item );
701 }
702
703 std::ranges::sort( cn_items, NETCODE_CMP_LESS() );
704
705 return cn_items;
706}
707
708
709std::vector<std::unique_ptr<PCB_NET_INSPECTOR_PANEL::LIST_ITEM>>
710PCB_NET_INSPECTOR_PANEL::calculateNets( const std::vector<NETINFO_ITEM*>& aNetCodes, bool aIncludeZeroPadNets ) const
711{
712 std::vector<std::unique_ptr<LIST_ITEM>> results;
713
714 // A chain total needs every member net, so pull in the siblings.
715 std::vector<NETINFO_ITEM*> netCodes( aNetCodes );
716 std::set<wxString> chains;
717
718 for( NETINFO_ITEM* net : netCodes )
719 {
720 if( !net->GetNetChain().IsEmpty() )
721 chains.insert( net->GetNetChain() );
722 }
723
724 if( !chains.empty() )
725 {
726 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
727 {
728 if( chains.count( net->GetNetChain() ) )
729 netCodes.push_back( net );
730 }
731
732 std::sort( netCodes.begin(), netCodes.end(),
733 []( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
734 {
735 return a->GetNetCode() < b->GetNetCode();
736 } );
737 netCodes.erase( std::unique( netCodes.begin(), netCodes.end() ), netCodes.end() );
738 }
739
740 LENGTH_DELAY_CALCULATION* calc = m_board->GetLengthCalculation();
741 const std::vector<CN_ITEM*> conItems = relevantConnectivityItems();
742
743 // First assemble the LENGTH_CALCULATION_ITEMs for board items which match the nets we need to recompute
744 // Precondition: conItems and aNetCodes are sorted in increasing netcode value
745 // Functionality: This extracts any items from conItems which have a netcode which is present in aNetCodes
746 std::unordered_map<int, std::vector<LENGTH_DELAY_CALCULATION_ITEM>> netItemsMap;
747 std::vector<NETINFO_ITEM*> foundNets;
748
749 auto itemItr = conItems.begin();
750 auto netCodeItr = netCodes.begin();
751
752 while( itemItr != conItems.end() && netCodeItr != netCodes.end() )
753 {
754 const int curNetCode = ( *netCodeItr )->GetNetCode();
755 const int curItemNetCode = ( *itemItr )->Net();
756
757 if( curItemNetCode == curNetCode )
758 {
759 if( foundNets.empty() || foundNets.back() != *netCodeItr )
760 foundNets.emplace_back( *netCodeItr );
761
762 // Take the item
763 LENGTH_DELAY_CALCULATION_ITEM lengthItem = calc->GetLengthCalculationItem( ( *itemItr )->Parent() );
764 netItemsMap[curItemNetCode].emplace_back( std::move( lengthItem ) );
765 ++itemItr;
766 }
767 else if( curItemNetCode < curNetCode )
768 {
769 // Fast-forward through items
770 while( itemItr != conItems.end() && ( *itemItr )->Net() < curNetCode )
771 ++itemItr;
772 }
773 else if( curItemNetCode > curNetCode )
774 {
775 // Fast-forward through required net codes
776 while( netCodeItr != netCodes.end() && curItemNetCode > ( *netCodeItr )->GetNetCode() )
777 ++netCodeItr;
778 }
779 }
780
781 // Now calculate the length statistics for each net. This includes potentially expensive path optimisations, so
782 // parallelize this work.
783 std::mutex resultsMutex;
785
786 auto resultsFuture = tp.submit_loop(
787 0, foundNets.size(),
788 [&, this, calc]( const int i )
789 {
790 int netCode = foundNets[i]->GetNetCode();
791
792 constexpr PATH_OPTIMISATIONS opts = { .OptimiseVias = true,
793 .MergeTracks = true,
794 .OptimiseTracesInPads = true,
795 .InferViaInPad = false };
796
797 LENGTH_DELAY_STATS lengthDetails = calc->CalculateLengthDetails(
798 netItemsMap[netCode],
799 opts,
800 nullptr,
801 nullptr,
805
806 if( aIncludeZeroPadNets || lengthDetails.NumPads > 0 )
807 {
808 std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( foundNets[i] );
809
810 new_item->SetPadCount( lengthDetails.NumPads );
811 new_item->SetLayerCount( m_board->GetCopperLayerCount() );
812 new_item->SetPadDieLength( lengthDetails.PadToDieLength );
813 new_item->SetPadDieDelay( lengthDetails.PadToDieDelay );
814 new_item->SetViaCount( lengthDetails.NumVias );
815 new_item->SetViaLength( lengthDetails.ViaLength );
816 new_item->SetViaDelay( lengthDetails.ViaDelay );
817 new_item->SetLayerWireLengths( *lengthDetails.LayerLengths );
818
819 // Estimated unrouted length: sum of the optimized ratsnest airline edges
820 // for this net, as maintained by the connectivity engine. Uses the
821 // unsorted accessor to avoid the stable sort in RN_NET::GetEdges().
822 int64_t unroutedLength = 0;
823
824 if( RN_NET* rnNet = m_board->GetConnectivity()->GetRatsnestForNet( netCode ) )
825 unroutedLength = rnNet->GetTotalAirlineLength();
826
827 new_item->SetUnroutedLength( unroutedLength );
828
829 if( m_showTimeDomainDetails )
830 new_item->SetLayerWireDelays( *lengthDetails.LayerDelays );
831
832 new_item->SetNetChainName( foundNets[i]->GetNetChain() );
833
834 std::scoped_lock lock( resultsMutex );
835 results.emplace_back( std::move( new_item ) );
836 }
837 } );
838
839 resultsFuture.get();
840
841 std::map<wxString, int64_t> netChainLengths;
842 std::map<wxString, int64_t> netChainDelays;
843
844 for( const std::unique_ptr<LIST_ITEM>& item : results )
845 {
846 if( !item->GetNetChainName().IsEmpty() )
847 {
848 netChainLengths[item->GetNetChainName()] += item->GetTotalLength();
849
850 if( m_showTimeDomainDetails )
851 netChainDelays[item->GetNetChainName()] += item->GetTotalDelay();
852 }
853 }
854
855 // Cache per-chain delay-per-mm so we only sample the chain tracks once.
856 std::map<wxString, double> chainDelayPerIU;
857
858 auto delayPerIUFor = [&]( const wxString& aChain ) -> double
859 {
860 auto it = chainDelayPerIU.find( aChain );
861
862 if( it != chainDelayPerIU.end() )
863 return it->second;
864
865 double perIU = ChainBridgingDelayPerMm( m_board, aChain ) / pcbIUScale.IU_PER_MM;
866 chainDelayPerIU.emplace( aChain, perIU );
867 return perIU;
868 };
869
870 for( FOOTPRINT* fp : m_board->Footprints() )
871 {
872 std::vector<PAD*> chainPads;
873
874 for( PAD* pad : fp->Pads() )
875 {
876 if( pad->GetNet() && !pad->GetNet()->GetNetChain().IsEmpty() )
877 chainPads.push_back( pad );
878 }
879
880 for( size_t i = 0; i < chainPads.size(); ++i )
881 {
882 for( size_t j = i + 1; j < chainPads.size(); ++j )
883 {
884 NETINFO_ITEM* netA = chainPads[i]->GetNet();
885 NETINFO_ITEM* netB = chainPads[j]->GetNet();
886
887 if( netA->GetNetChain() == netB->GetNetChain()
888 && netA->GetNetCode() != netB->GetNetCode() )
889 {
890 VECTOR2I p1 = chainPads[i]->GetPosition();
891 VECTOR2I p2 = chainPads[j]->GetPosition();
892 int64_t dx = p1.x - p2.x;
893 int64_t dy = p1.y - p2.y;
894 int64_t dist = KiROUND( std::hypot( (double) dx, (double) dy ) );
895 const wxString& chain = netA->GetNetChain();
896
897 netChainLengths[chain] += dist;
898
899 if( m_showTimeDomainDetails )
900 netChainDelays[chain] += KiROUND( delayPerIUFor( chain ) * (double) dist );
901 }
902 }
903 }
904 }
905
906 for( std::unique_ptr<LIST_ITEM>& item : results )
907 {
908 if( !item->GetNetChainName().IsEmpty() )
909 {
910 item->SetNetChainLength( netChainLengths[item->GetNetChainName()] );
911
912 if( m_showTimeDomainDetails )
913 item->SetNetChainDelay( netChainDelays[item->GetNetChainName()] );
914 }
915 }
916
917 return results;
918}
919
920
921/*****************************************************************************************
922 *
923 * Formatting helpers
924 *
925 * ***************************************************************************************/
926
927
929{
930 return wxString::Format( wxT( "%.3d" ), aNet->GetNetCode() );
931}
932
933
935{
936 return UnescapeString( aNet->GetNetname() );
937}
938
939
940wxString PCB_NET_INSPECTOR_PANEL::formatCount( const unsigned int aValue )
941{
942 return wxString::Format( wxT( "%u" ), aValue );
943}
944
945
946wxString PCB_NET_INSPECTOR_PANEL::formatLength( const int64_t aValue ) const
947{
948 return m_frame->MessageTextFromValue( aValue,
949 // don't include unit label in the string when reporting
950 !m_inReporting );
951}
952
953wxString PCB_NET_INSPECTOR_PANEL::formatDelay( const int64_t aValue ) const
954{
955 return m_frame->MessageTextFromValue( aValue,
956 // don't include unit label in the string when reporting
958}
959
960
961void PCB_NET_INSPECTOR_PANEL::updateDisplayedRowValues( const std::optional<LIST_ITEM_ITER>& aRow ) const
962{
963 if( !aRow )
964 return;
965
966 wxDataViewItemArray sel;
967 m_netsList->GetSelections( sel );
968
969 m_dataModel->updateItem( aRow );
970
971 if( !sel.IsEmpty() )
972 {
973 m_netsList->SetSelections( sel );
974 m_netsList->EnsureVisible( sel.Item( 0 ) );
975 }
976}
977
978
979/*****************************************************************************************
980 *
981 * BOARD_LISTENER event handling
982 *
983 * ***************************************************************************************/
984
985
987{
988 m_board = m_frame->GetBoard();
989
990 if( m_board )
991 m_board->AddListener( this );
992
993 m_boardLoaded = true;
994 m_boardLoading = true;
995
997 auto& cfg = localSettings.m_NetInspectorPanel;
998 m_searchCtrl->SetValue( cfg.filter_text );
999
1000 buildNetsList( true );
1001
1002 m_boardLoading = false;
1003}
1004
1005
1007{
1008 const std::vector<BOARD_ITEM*> item{ aBoardItem };
1009 updateBoardItems( item );
1010}
1011
1012
1013void PCB_NET_INSPECTOR_PANEL::OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems )
1014{
1015 updateBoardItems( aBoardItems );
1016}
1017
1018
1019void PCB_NET_INSPECTOR_PANEL::updateBoardItems( const std::vector<BOARD_ITEM*>& aBoardItems )
1020{
1021 if( !IsShownOnScreen() )
1022 return;
1023
1024 // Rebuild full list for large changes
1025 if( aBoardItems.size()
1026 > static_cast<size_t>( ADVANCED_CFG::GetCfg().m_NetInspectorBulkUpdateOptimisationThreshold ) )
1027 {
1028 buildNetsList();
1029 }
1030 else
1031 {
1032 std::vector<NETINFO_ITEM*> changedNets;
1033
1034 for( BOARD_ITEM* boardItem : aBoardItems )
1035 {
1036 if( NETINFO_ITEM* net = dynamic_cast<NETINFO_ITEM*>( boardItem ) )
1037 {
1038 // A new net has been added to the board. Add it to our list if it passes the netname filter test.
1039 if( netFilterMatches( net ) )
1040 changedNets.emplace_back( net );
1041 }
1042 else if( BOARD_CONNECTED_ITEM* i = dynamic_cast<BOARD_CONNECTED_ITEM*>( boardItem ) )
1043 {
1044 changedNets.emplace_back( i->GetNet() );
1045 }
1046 else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( boardItem ) )
1047 {
1048 for( const PAD* pad : footprint->Pads() )
1049 {
1050 if( netFilterMatches( pad->GetNet() ) )
1051 changedNets.emplace_back( pad->GetNet() );
1052 }
1053 }
1054 }
1055
1056 std::ranges::sort( changedNets,
1057 []( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
1058 {
1059 return a->GetNetCode() < b->GetNetCode();
1060 } );
1061
1062 updateNets( changedNets );
1063 }
1064
1065 m_netsList->Refresh();
1066}
1067
1068
1069void PCB_NET_INSPECTOR_PANEL::updateNets( const std::vector<NETINFO_ITEM*>& aNets ) const
1070{
1071 std::vector<NETINFO_ITEM*> netsToUpdate;
1072 std::unordered_set<NETINFO_ITEM*> netsToDelete;
1073
1074 for( NETINFO_ITEM* net : aNets )
1075 {
1076 // Add all nets to the deletion list - we will prune this later to only contain unhandled nets
1077 netsToDelete.insert( net );
1078
1079 // Only calculate nets that match the current filter
1080 if( netFilterMatches( net ) )
1081 netsToUpdate.emplace_back( net );
1082 }
1083
1084 wxWindowUpdateLocker updateLocker( m_netsList );
1085
1086 std::vector<std::unique_ptr<LIST_ITEM>> newListItems = calculateNets( aNets, true );
1087
1088 for( std::unique_ptr<LIST_ITEM>& newListItem : newListItems )
1089 {
1090 // Remove the handled net from the deletion list
1091 netsToDelete.erase( newListItem->GetNet() );
1092
1093 std::optional<LIST_ITEM_ITER> curNetRow = m_dataModel->findItem( newListItem->GetNetCode() );
1094
1095 if( !m_showZeroPadNets && newListItem->GetPadCount() == 0 )
1096 {
1097 m_dataModel->deleteItem( curNetRow );
1098 continue;
1099 }
1100
1101 if( !curNetRow )
1102 {
1103 m_dataModel->addItem( std::move( newListItem ) );
1104 continue;
1105 }
1106
1107 const std::unique_ptr<LIST_ITEM>& curListItem = *curNetRow.value();
1108
1109 if( curListItem->GetNetName() != newListItem->GetNetName() )
1110 {
1111 // If the name has changed, it might require re-grouping. It's easier to remove and re-insert it.
1112 m_dataModel->deleteItem( curNetRow );
1113 m_dataModel->addItem( std::move( newListItem ) );
1114 }
1115 else
1116 {
1117 curListItem->SetPadCount( newListItem->GetPadCount() );
1118 curListItem->SetPadDieLength( newListItem->GetPadDieLength() );
1119 curListItem->SetPadDieDelay( newListItem->GetPadDieDelay() );
1120 curListItem->SetViaCount( newListItem->GetViaCount() );
1121 curListItem->SetViaLength( newListItem->GetViaLength() );
1122 curListItem->SetViaDelay( newListItem->GetViaDelay() );
1123 curListItem->SetUnroutedLength( newListItem->GetUnroutedLength() );
1124 curListItem->SetLayerWireLengths( newListItem->GetLayerWireLengths() );
1125 curListItem->SetNetChainName( newListItem->GetNetChainName() );
1126 curListItem->SetNetChainLength( newListItem->GetNetChainLength() );
1127 curListItem->SetNetChainDelay( newListItem->GetNetChainDelay() );
1128
1130 curListItem->SetLayerWireDelays( newListItem->GetLayerWireDelays() );
1131
1132 updateDisplayedRowValues( curNetRow );
1133 }
1134 }
1135
1136 // Delete any nets we have not yet handled
1137 for( const NETINFO_ITEM* netToDelete : netsToDelete )
1138 m_dataModel->deleteItem( m_dataModel->findItem( netToDelete->GetNetCode() ) );
1139}
1140
1141
1143{
1144 const std::vector<BOARD_ITEM*> item{ aBoardItem };
1145 updateBoardItems( item );
1146}
1147
1148
1149void PCB_NET_INSPECTOR_PANEL::OnBoardItemsRemoved( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems )
1150{
1151 updateBoardItems( aBoardItems );
1152}
1153
1154
1156{
1157 if( !IsShownOnScreen() )
1158 return;
1159
1160 buildNetsList();
1161}
1162
1163
1165{
1166 const std::vector<BOARD_ITEM*> item{ aBoardItem };
1167 updateBoardItems( item );
1168}
1169
1170
1171void PCB_NET_INSPECTOR_PANEL::OnBoardItemsChanged( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems )
1172{
1173 updateBoardItems( aBoardItems );
1174}
1175
1176
1178 std::vector<BOARD_ITEM*>& aAddedItems,
1179 std::vector<BOARD_ITEM*>& aRemovedItems,
1180 std::vector<BOARD_ITEM*>& aChangedItems )
1181{
1182 if( !IsShownOnScreen() )
1183 return;
1184
1185 std::vector<BOARD_ITEM*> allItems{ aAddedItems.begin(), aAddedItems.end() };
1186 allItems.insert( allItems.end(), aRemovedItems.begin(), aRemovedItems.end() );
1187 allItems.insert( allItems.end(), aChangedItems.begin(), aChangedItems.end() );
1188 updateBoardItems( allItems );
1189}
1190
1191
1193{
1194 if( m_highlightingNets || !IsShownOnScreen() )
1195 return;
1196
1197 if( !m_board->IsHighLightNetON() )
1198 {
1199 m_netsList->UnselectAll();
1200 }
1201 else
1202 {
1203 const std::set<int>& selected_codes = m_board->GetHighLightNetCodes();
1204
1205 wxDataViewItemArray new_selection;
1206 new_selection.Alloc( selected_codes.size() );
1207
1208 for( const int code : selected_codes )
1209 {
1210 if( std::optional<LIST_ITEM_ITER> r = m_dataModel->findItem( code ) )
1211 new_selection.Add( wxDataViewItem( &***r ) );
1212 }
1213
1214 m_netsList->SetSelections( new_selection );
1215
1216 if( !new_selection.IsEmpty() )
1217 m_netsList->EnsureVisible( new_selection.Item( 0 ) );
1218 }
1219}
1220
1221
1222/*****************************************************************************************
1223 *
1224 * UI-generated event handling
1225 *
1226 * ***************************************************************************************/
1227
1233
1234
1236{
1237 bool multipleSelections = false;
1238 const LIST_ITEM* selItem = nullptr;
1239
1240 if( m_netsList->GetSelectedItemsCount() == 1 )
1241 {
1242 selItem = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1243 }
1244 else
1245 {
1246 if( m_netsList->GetSelectedItemsCount() > 1 )
1247 multipleSelections = true;
1248 }
1249
1250 wxMenu menu;
1251
1252 // Net edit menu items
1253 wxMenuItem* highlightNet = new wxMenuItem( &menu, ID_HIGHLIGHT_SELECTED_NETS, _( "Highlight Selected Net" ),
1254 wxEmptyString, wxITEM_NORMAL );
1255 menu.Append( highlightNet );
1256
1257 wxMenuItem* clearHighlighting = new wxMenuItem( &menu, ID_CLEAR_HIGHLIGHTING, _( "Clear Net Highlighting" ),
1258 wxEmptyString, wxITEM_NORMAL );
1259 menu.Append( clearHighlighting );
1260
1261 RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
1262 const std::set<int>& selected_codes = renderSettings->GetHighlightNetCodes();
1263
1264 if( selected_codes.size() == 0 )
1265 clearHighlighting->Enable( false );
1266
1267 menu.AppendSeparator();
1268
1269 wxMenuItem* renameNet = new wxMenuItem( &menu, ID_RENAME_NET, _( "Rename Selected Net..." ), wxEmptyString,
1270 wxITEM_NORMAL );
1271 menu.Append( renameNet );
1272
1273 wxMenuItem* deleteNet = new wxMenuItem( &menu, ID_DELETE_NET, _( "Delete Selected Net" ), wxEmptyString,
1274 wxITEM_NORMAL );
1275 menu.Append( deleteNet );
1276
1277 menu.AppendSeparator();
1278
1279 wxMenuItem* addNet = new wxMenuItem( &menu, ID_ADD_NET, _( "Add Net..." ), wxEmptyString, wxITEM_NORMAL );
1280 menu.Append( addNet );
1281
1282 if( !selItem && !multipleSelections )
1283 {
1284 highlightNet->Enable( false );
1285 deleteNet->Enable( false );
1286 renameNet->Enable( false );
1287 }
1288 else
1289 {
1290 if( multipleSelections || selItem->GetIsGroup() )
1291 {
1292 highlightNet->SetItemLabel( _( "Highlight Selected Nets" ) );
1293 renameNet->Enable( false );
1294 deleteNet->SetItemLabel( _( "Delete Selected Nets" ) );
1295 }
1296 }
1297
1298 menu.AppendSeparator();
1299
1300 wxMenuItem* removeSelectedGroup = new wxMenuItem( &menu, ID_REMOVE_SELECTED_GROUP,
1301 _( "Remove Selected Custom Group" ),
1302 wxEmptyString, wxITEM_NORMAL );
1303 menu.Append( removeSelectedGroup );
1304
1305 if( !selItem || !selItem->GetIsGroup() )
1306 removeSelectedGroup->Enable( false );
1307
1308 menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &PCB_NET_INSPECTOR_PANEL::onContextMenuSelection, this );
1309
1310 PopupMenu( &menu );
1311}
1312
1313
1315{
1316 SaveSettings();
1317 buildNetsList();
1318}
1319
1320
1322{
1323 wxString newGroupName;
1324 NETNAME_VALIDATOR validator( &newGroupName );
1325
1326 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Group name / pattern:" ), _( "New Group" ), newGroupName );
1327 wxStaticText* help = new wxStaticText( &dlg, wxID_ANY, _( "(Use /.../ to indicate a regular expression.)" ) );
1328 help->SetFont( KIUI::GetInfoFont( this ).Italic() );
1329 dlg.m_ContentSizer->Add( help, 0, wxALL|wxEXPAND, 5 );
1331 dlg.GetSizer()->SetSizeHints( &dlg );
1332
1333 if( dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty() )
1334 return; //Aborted by user
1335
1336 newGroupName = UnescapeString( dlg.GetValue() );
1337
1338 if( newGroupName == "" )
1339 return;
1340
1341 if( std::ranges::find_if( m_custom_group_rules,
1342 [&]( std::unique_ptr<EDA_COMBINED_MATCHER>& rule )
1343 {
1344 return rule->GetPattern() == newGroupName;
1345 } )
1346 == m_custom_group_rules.end() )
1347 {
1348 m_custom_group_rules.push_back( std::make_unique<EDA_COMBINED_MATCHER>( newGroupName, CTX_NET ) );
1349 SaveSettings();
1350 }
1351
1352 buildNetsList();
1353}
1354
1355
1357{
1358 m_highlightingNets = true;
1359
1360 m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( false );
1361 m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
1362 m_frame->GetCanvas()->Refresh();
1363
1364 m_highlightingNets = false;
1365}
1366
1367
1369{
1370 if( !m_rowExpanding )
1371 SaveSettings();
1372}
1373
1374
1376{
1377 m_contextMenuColumn = event.GetDataViewColumn();
1378
1379 wxMenu menu;
1380
1381 menu.Append( ID_AUTOFIT_COLUMN, _( "Auto-fit Column" ) );
1382 menu.Append( ID_AUTOFIT_ALL_COLUMNS, _( "Auto-fit All Columns" ) );
1383 menu.AppendSeparator();
1384
1386 menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &PCB_NET_INSPECTOR_PANEL::onContextMenuSelection, this );
1387 PopupMenu( &menu );
1388}
1389
1390
1391void PCB_NET_INSPECTOR_PANEL::OnConfigButton( wxCommandEvent& event )
1392{
1394 auto& cfg = localSettings.m_NetInspectorPanel;
1395
1396 const LIST_ITEM* selItem = nullptr;
1397
1398 if( m_netsList->GetSelectedItemsCount() == 1 )
1399 {
1400 selItem = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1401 }
1402
1403 wxMenu menu;
1404
1405 // Filtering menu items
1406 wxMenuItem* filterByNetName = new wxMenuItem( &menu, ID_FILTER_BY_NET_NAME, _( "Filter by Net Name" ),
1407 wxEmptyString, wxITEM_CHECK );
1408 menu.Append( filterByNetName );
1409 filterByNetName->Check( cfg.filter_by_net_name );
1410
1411 wxMenuItem* filterByNetclass = new wxMenuItem( &menu, ID_FILTER_BY_NETCLASS, _( "Filter by Netclass" ),
1412 wxEmptyString, wxITEM_CHECK );
1413 menu.Append( filterByNetclass );
1414 filterByNetclass->Check( cfg.filter_by_netclass );
1415
1416 menu.AppendSeparator();
1417
1418 // Grouping menu items
1419 //wxMenuItem* groupConstraint =
1420 // new wxMenuItem( &menu, ID_GROUP_BY_CONSTRAINT, _( "Group by DRC Constraint" ),
1421 // wxEmptyString, wxITEM_CHECK );
1422 //groupConstraint->Check( m_group_by_constraint );
1423 //menu.Append( groupConstraint );
1424
1425 wxMenuItem* groupNetclass = new wxMenuItem( &menu, ID_GROUP_BY_NETCLASS, _( "Group by Netclass" ),
1426 wxEmptyString, wxITEM_CHECK );
1427 menu.Append( groupNetclass );
1428 groupNetclass->Check( m_groupByNetclass );
1429
1430 wxMenuItem* groupSignal = new wxMenuItem( &menu, ID_GROUP_BY_NET_CHAIN,
1431 _( "Group by Net Chain" ),
1432 wxEmptyString, wxITEM_CHECK );
1433 menu.Append( groupSignal );
1434 groupSignal->Check( m_groupByNetChain );
1435
1436 menu.AppendSeparator();
1437
1438 wxMenuItem* addGroup = new wxMenuItem( &menu, ID_ADD_GROUP, _( "Add Custom Group..." ),
1439 wxEmptyString, wxITEM_NORMAL );
1440 menu.Append( addGroup );
1441
1442 wxMenuItem* removeSelectedGroup = new wxMenuItem( &menu, ID_REMOVE_SELECTED_GROUP,
1443 _( "Remove Selected Custom Group" ),
1444 wxEmptyString, wxITEM_NORMAL );
1445 menu.Append( removeSelectedGroup );
1446
1447 if( !selItem || !selItem->GetIsGroup() )
1448 removeSelectedGroup->Enable( false );
1449
1450 wxMenuItem* removeCustomGroups = new wxMenuItem( &menu, ID_REMOVE_GROUPS, _( "Remove All Custom Groups" ),
1451 wxEmptyString, wxITEM_NORMAL );
1452 menu.Append( removeCustomGroups );
1453 removeCustomGroups->Enable( m_custom_group_rules.size() != 0 );
1454
1455 menu.AppendSeparator();
1456
1457 wxMenuItem* showZeroNetPads = new wxMenuItem( &menu, ID_SHOW_ZERO_NET_PADS, _( "Show Zero Pad Nets" ),
1458 wxEmptyString, wxITEM_CHECK );
1459 menu.Append( showZeroNetPads );
1460 showZeroNetPads->Check( m_showZeroPadNets );
1461
1462 wxMenuItem* showUnconnectedNets = new wxMenuItem( &menu, ID_SHOW_UNCONNECTED_NETS, _( "Show Unconnected Nets" ),
1463 wxEmptyString, wxITEM_CHECK );
1464 menu.Append( showUnconnectedNets );
1465 showUnconnectedNets->Check( m_showUnconnectedNets );
1466
1467 menu.AppendSeparator();
1468
1469 wxMenuItem* showTimeDomainDetails = new wxMenuItem( &menu, ID_SHOW_TIME_DOMAIN_DETAILS,
1470 _( "Show Time Domain Details" ),
1471 wxEmptyString, wxITEM_CHECK );
1472 menu.Append( showTimeDomainDetails );
1473 showTimeDomainDetails->Check( m_showTimeDomainDetails );
1474
1475 menu.AppendSeparator();
1476
1477 // Report generation
1478 wxMenuItem* generateReport = new wxMenuItem( &menu, ID_GENERATE_REPORT, _( "Save Net Inspector Report..." ),
1479 wxEmptyString, wxITEM_NORMAL );
1480 menu.Append( generateReport );
1481
1482 menu.AppendSeparator();
1483
1484 // Show / hide columns menu items
1485 wxMenu* colsMenu = new wxMenu();
1486 generateShowHideColumnMenu( colsMenu );
1487 menu.AppendSubMenu( colsMenu, _( "Show / Hide Columns" ) );
1488
1489 menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &PCB_NET_INSPECTOR_PANEL::onContextMenuSelection, this );
1490
1491 PopupMenu( &menu );
1492}
1493
1494
1496{
1497 for( int i = 1; i <= COLUMN_LAST_STATIC_COL; ++i )
1498 {
1499 wxMenuItem* opt = new wxMenuItem( target, ID_HIDE_COLUMN + i, m_columns[i].display_name,
1500 wxEmptyString, wxITEM_CHECK );
1501 wxDataViewColumn* col = getDisplayedColumnForModelField( i );
1502 target->Append( opt );
1503 opt->Check( !col->IsHidden() );
1504 }
1505
1506 target->AppendSeparator();
1507
1508 for( std::size_t i = COLUMN_LAST_STATIC_COL + 1; i < m_columns.size(); ++i )
1509 {
1510 wxMenuItem* opt = new wxMenuItem( target, ID_HIDE_COLUMN + i, m_columns[i].display_name,
1511 wxEmptyString, wxITEM_CHECK );
1512 wxDataViewColumn* col = getDisplayedColumnForModelField( i );
1513 target->Append( opt );
1514 opt->Check( !col->IsHidden() );
1515 }
1516}
1517
1518
1520{
1521 bool saveAndRebuild = true;
1522
1523 switch( event.GetId() )
1524 {
1525 case ID_ADD_NET:
1526 onAddNet();
1527 break;
1528
1529 case ID_RENAME_NET:
1531 break;
1532
1533 case ID_DELETE_NET:
1535 break;
1536
1537 case ID_ADD_GROUP:
1538 onAddGroup();
1539 break;
1540
1543 break;
1544
1547 break;
1548
1551 break;
1552
1555 break;
1556
1559 break;
1560
1563 break;
1564
1565 case ID_REMOVE_GROUPS:
1566 m_custom_group_rules.clear();
1567 break;
1568
1571 break;
1572
1575 break;
1576
1579 break;
1580
1581 case ID_GENERATE_REPORT:
1583 saveAndRebuild = false;
1584 break;
1585
1588 saveAndRebuild = false;
1589 break;
1590
1593 saveAndRebuild = false;
1594 break;
1595
1596 case ID_AUTOFIT_COLUMN:
1599
1600 saveAndRebuild = false;
1601 break;
1602
1604 for( unsigned int i = 0; i < m_netsList->GetColumnCount(); ++i )
1605 autosizeColumn( m_netsList->GetColumn( i ) );
1606
1607 saveAndRebuild = false;
1608 break;
1609
1610 default:
1611 if( event.GetId() >= ID_HIDE_COLUMN )
1612 {
1613 const int columnId = event.GetId() - ID_HIDE_COLUMN;
1614 wxDataViewColumn* col = getDisplayedColumnForModelField( columnId );
1615 // Make sure we end up with something non-zero so we can resize it
1616 col->SetWidth( std::max( col->GetWidth(), 10 ) );
1617 col->SetHidden( !col->IsHidden() );
1618 }
1619 break;
1620 }
1621
1622 if( saveAndRebuild )
1623 {
1624 SaveSettings();
1625 buildNetsList();
1626 }
1627}
1628
1629
1631{
1632 if( m_netsList->GetSelectedItemsCount() == 1 )
1633 {
1634 auto* selItem = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1635
1636 if( selItem->GetIsGroup() )
1637 {
1638 const wxString groupName = selItem->GetGroupName();
1639 const auto groupIter = std::ranges::find_if( m_custom_group_rules,
1640 [&]( std::unique_ptr<EDA_COMBINED_MATCHER>& rule )
1641 {
1642 return rule->GetPattern() == groupName;
1643 } );
1644
1645 if( groupIter != m_custom_group_rules.end() )
1646 {
1647 m_custom_group_rules.erase( groupIter );
1648 SaveSettings();
1649 buildNetsList();
1650 }
1651 }
1652 }
1653}
1654
1655
1657{
1658 wxFileDialog dlg( this, _( "Save Net Inspector Report File" ), "", "",
1659 _( "Report file" ) + AddFileExtListToFilter( { "csv" } ),
1660 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1661
1663
1664 if( dlg.ShowModal() == wxID_CANCEL )
1665 return;
1666
1667 wxTextFile f( dlg.GetPath() );
1668
1669 f.Create();
1670
1671 wxString txt;
1672
1673 m_inReporting = true;
1674
1675 // Print Header:
1676 for( auto&& col : m_columns )
1677 {
1678 txt += '"';
1679
1680 if( col.has_units )
1681 {
1682 txt += wxString::Format( _( "%s (%s)" ),
1683 col.csv_name,
1684 EDA_UNIT_UTILS::GetLabel( m_frame->GetUserUnits() ) );
1685 }
1686 else
1687 {
1688 txt += col.csv_name;
1689 }
1690
1691 txt += wxT( "\";" );
1692 }
1693
1694 f.AddLine( txt );
1695
1696 // Print list of nets:
1697 const unsigned int num_rows = m_dataModel->itemCount();
1698
1699 for( unsigned int row = 0; row < num_rows; row++ )
1700 {
1701 auto& i = m_dataModel->itemAt( row );
1702
1703 if( i.GetIsGroup() || i.GetNetCode() == 0 )
1704 continue;
1705
1706 txt = "";
1707
1708 for( auto&& col : m_columns )
1709 {
1710 if( static_cast<int>( col.csv_flags ) & static_cast<int>( CSV_COLUMN_DESC::CSV_QUOTE ) )
1711 txt += '"' + m_dataModel->valueAt( col.num, row ).GetString() + wxT( "\";" );
1712 else
1713 txt += m_dataModel->valueAt( col.num, row ).GetString() + ';';
1714 }
1715
1716 f.AddLine( txt );
1717 }
1718
1719 m_inReporting = false;
1720
1721 f.Write();
1722 f.Close();
1723}
1724
1725
1727{
1729}
1730
1731
1733{
1734 // ignore selection changes while the whole list is being rebuilt.
1735 if( m_inBuildNetsList )
1736 return;
1737
1738 m_highlightingNets = true;
1739
1740 RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
1741
1742 if( m_netsList->HasSelection() )
1743 {
1744 wxDataViewItemArray sel;
1745 m_netsList->GetSelections( sel );
1746
1747 renderSettings->SetHighlight( false );
1748
1749 for( unsigned int i = 0; i < sel.GetCount(); ++i )
1750 {
1751 const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1752
1753 if( ii->GetIsGroup() )
1754 {
1755 for( auto c = ii->ChildrenBegin(), end = ii->ChildrenEnd(); c != end; ++c )
1756 renderSettings->SetHighlight( true, ( *c )->GetNetCode(), true );
1757 }
1758 else
1759 {
1760 renderSettings->SetHighlight( true, ii->GetNetCode(), true );
1761 }
1762 }
1763 }
1764 else
1765 {
1766 renderSettings->SetHighlight( false );
1767 }
1768
1769 m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
1770 m_frame->GetCanvas()->Refresh();
1771
1772 m_highlightingNets = false;
1773}
1774
1775
1776void PCB_NET_INSPECTOR_PANEL::OnColumnSorted( wxDataViewEvent& event )
1777{
1778 if( !m_inBuildNetsList )
1779 SaveSettings();
1780}
1781
1782
1784{
1785 // Rebuilt the nets list, and force rebuild of columns in case the stackup has changed
1786 buildNetsList( true );
1787}
1788
1789
1791{
1792 wxString newNetName;
1793 NETNAME_VALIDATOR validator( &newNetName );
1794
1795 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "New Net" ), newNetName );
1797
1798 while( true )
1799 {
1800 if( dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty() )
1801 return; //Aborted by user
1802
1803 newNetName = dlg.GetValue();
1804
1805 if( m_board->FindNet( newNetName ) )
1806 {
1807 DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ), newNetName ) );
1808 newNetName = wxEmptyString;
1809 }
1810 else
1811 {
1812 break;
1813 }
1814 }
1815
1816 NETINFO_ITEM* newnet = new NETINFO_ITEM( m_board, dlg.GetValue(), 0 );
1817
1818 m_board->Add( newnet );
1819
1820 // We'll get an OnBoardItemAdded callback from this to update our listbox
1821 m_frame->OnModify();
1822}
1823
1824
1826{
1827 if( m_netsList->GetSelectedItemsCount() == 1 )
1828 {
1829 const LIST_ITEM* sel = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1830
1831 if( sel->GetIsGroup() )
1832 return;
1833
1834 NETINFO_ITEM* net = sel->GetNet();
1835 wxString fullNetName = net->GetNetname();
1836 wxString netPath;
1837 wxString shortNetName;
1838
1839 if( fullNetName.Contains( wxT( "/" ) ) )
1840 {
1841 netPath = fullNetName.BeforeLast( '/' ) + '/';
1842 shortNetName = fullNetName.AfterLast( '/' );
1843 }
1844 else
1845 {
1846 shortNetName = fullNetName;
1847 }
1848
1849 wxString unescapedShortName = UnescapeString( shortNetName );
1850
1851 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "Rename Net" ), unescapedShortName );
1852 NETNAME_VALIDATOR validator( &unescapedShortName );
1854
1855 while( true )
1856 {
1857 if( dlg.ShowModal() != wxID_OK || dlg.GetValue() == unescapedShortName )
1858 return;
1859
1860 unescapedShortName = dlg.GetValue();
1861
1862 if( unescapedShortName.IsEmpty() )
1863 {
1864 DisplayError( this, _( "Net name cannot be empty." ) );
1865 continue;
1866 }
1867
1868 shortNetName = EscapeString( unescapedShortName, CTX_NETNAME );
1869 fullNetName = netPath + shortNetName;
1870
1871 if( m_board->FindNet( shortNetName ) || m_board->FindNet( fullNetName ) )
1872 {
1873 DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ), unescapedShortName ) );
1874 unescapedShortName = wxEmptyString;
1875 }
1876 else
1877 {
1878 break;
1879 }
1880 }
1881
1882 for( BOARD_CONNECTED_ITEM* boardItem : m_frame->GetBoard()->AllConnectedItems() )
1883 {
1884 if( boardItem->GetNet() == net )
1885 boardItem->SetFlags( CANDIDATE );
1886 else
1887 boardItem->ClearFlags( CANDIDATE );
1888 }
1889
1890 // the changed name might require re-grouping. remove/re-insert is easier.
1891 auto removed_item = m_dataModel->deleteItem( m_dataModel->findItem( net ) );
1892
1893 m_board->Remove( net );
1894 net->SetNetname( fullNetName );
1895 m_board->Add( net );
1896
1897 for( BOARD_CONNECTED_ITEM* boardItem : m_frame->GetBoard()->AllConnectedItems() )
1898 {
1899 if( boardItem->GetFlags() & CANDIDATE )
1900 boardItem->SetNet( net );
1901 }
1902
1903 buildNetsList();
1904
1905 if( std::optional<LIST_ITEM_ITER> r = m_dataModel->findItem( net ) )
1906 m_netsList->Select( wxDataViewItem( r.value()->get() ) );
1907
1908 m_frame->OnModify();
1909
1910 // Currently only tracks and pads have netname annotations and need to be redrawn,
1911 // but zones are likely to follow. Since we don't have a way to ask what is current,
1912 // just refresh all items.
1913 m_frame->GetCanvas()->GetView()->UpdateAllItems( KIGFX::REPAINT );
1914 m_frame->GetCanvas()->Refresh();
1915 }
1916}
1917
1918
1920{
1921 if( !m_netsList->HasSelection() )
1922 return;
1923
1924 wxDataViewItemArray sel;
1925 m_netsList->GetSelections( sel );
1926
1927 auto delete_one = [this]( const LIST_ITEM* i )
1928 {
1929 if( i->GetPadCount() == 0
1930 || IsOK( this, wxString::Format( _( "Net '%s' is in use. Delete anyway?" ), i->GetNetName() ) ) )
1931 {
1932 // This is a bit hacky, but it will do for now, since this is the only path
1933 // outside the netlist updater where you can remove a net from a BOARD.
1934 int removedCode = i->GetNetCode();
1935
1936 m_frame->GetCanvas()->GetView()->UpdateAllItemsConditionally(
1937 [removedCode]( KIGFX::VIEW_ITEM* aItem ) -> int
1938 {
1939 auto boardItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem );
1940
1941 if( boardItem && boardItem->GetNetCode() == removedCode )
1942 return KIGFX::REPAINT;
1943
1944 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( aItem );
1945
1946 if( text && text->HasTextVars() )
1947 {
1948 text->ClearRenderCache();
1949 text->ClearBoundingBoxCache();
1951 }
1952
1953 return 0;
1954 } );
1955
1956 m_board->Remove( i->GetNet() );
1957 m_frame->OnModify();
1958
1959 // We'll get an OnBoardItemRemoved callback from this to update our listbox
1960 }
1961 };
1962
1963 for( unsigned int i = 0; i < sel.GetCount(); ++i )
1964 {
1965 const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1966
1967 if( ii->GetIsGroup() )
1968 {
1969 if( ii->ChildrenCount() != 0
1970 && IsOK( this, wxString::Format( _( "Delete all nets in group '%s'?" ), ii->GetGroupName() ) ) )
1971 {
1972 // we can't be iterating the children container and deleting items from
1973 // it at the same time. thus take a copy of it first.
1974 std::vector<const LIST_ITEM*> children;
1975 children.reserve( ii->ChildrenCount() );
1976 std::copy( ii->ChildrenBegin(), ii->ChildrenEnd(), std::back_inserter( children ) );
1977
1978 for( const LIST_ITEM* c : children )
1979 delete_one( c );
1980 }
1981 }
1982 else
1983 {
1984 delete_one( ii );
1985 }
1986 }
1987}
1988
1989/*****************************************************************************************
1990 *
1991 * Application-generated event handling
1992 *
1993 * ***************************************************************************************/
1994
1995
1997{
1998 SaveSettings();
1999 buildNetsList( true );
2000 m_dataModel->updateAllItems();
2001}
2002
2003
2004void PCB_NET_INSPECTOR_PANEL::onUnitsChanged( wxCommandEvent& event )
2005{
2006 m_dataModel->updateAllItems();
2007 event.Skip();
2008}
2009
2010
2011/*****************************************************************************************
2012 *
2013 * Settings persistence
2014 *
2015 * ***************************************************************************************/
2016
2017
2019{
2020 // Don't save settings if a board has not yet been loaded or the panel hasn't been displayed.
2021 // Events fire while we set up the panel which overwrite the settings we haven't yet loaded.
2022 bool displayed = false;
2023
2024 for( unsigned int ii = 0; ii < m_dataModel->columnCount() && !displayed; ++ii )
2025 {
2026 if( m_netsList->GetColumn( ii )->GetWidth() > 0 )
2027 displayed = true;
2028 }
2029
2030 if( !displayed || !m_boardLoaded || m_boardLoading )
2031 return;
2032
2034 auto& cfg = localSettings.m_NetInspectorPanel;
2035
2036 // User-defined filters / grouping
2037 cfg.filter_text = m_searchCtrl->GetValue();
2038 cfg.filter_by_net_name = m_filterByNetName;
2039 cfg.filter_by_netclass = m_filterByNetclass;
2040 cfg.group_by_netclass = m_groupByNetclass;
2041 cfg.group_by_net_chain = m_groupByNetChain;
2042 cfg.group_by_constraint = m_groupByConstraint;
2043 cfg.show_zero_pad_nets = m_showZeroPadNets;
2044 cfg.show_unconnected_nets = m_showUnconnectedNets;
2045 cfg.show_time_domain_details = m_showTimeDomainDetails;
2046
2047 // Grid sorting
2048 wxDataViewColumn* sortingCol = m_netsList->GetSortingColumn();
2049 cfg.sorting_column = sortingCol ? static_cast<int>( sortingCol->GetModelColumn() ) : -1;
2050 cfg.sort_order_asc = sortingCol ? sortingCol->IsSortOrderAscending() : true;
2051
2052 // Column arrangement / sizes
2053 cfg.col_order.resize( m_dataModel->columnCount() );
2054 cfg.col_widths.resize( m_dataModel->columnCount() );
2055 cfg.col_hidden.resize( m_dataModel->columnCount() );
2056
2057 for( unsigned int ii = 0; ii < m_dataModel->columnCount(); ++ii )
2058 {
2059 cfg.col_order[ii] = (int) m_netsList->GetColumn( ii )->GetModelColumn();
2060 cfg.col_widths[ii] = m_netsList->GetColumn( ii )->GetWidth();
2061 cfg.col_hidden[ii] = m_netsList->GetColumn( ii )->IsHidden();
2062 }
2063
2064 // Expanded rows
2065 cfg.expanded_rows.clear();
2066 std::vector<std::pair<wxString, wxDataViewItem>> groupItems = m_dataModel->getGroupDataViewItems();
2067
2068 for( std::pair<wxString, wxDataViewItem>& item : groupItems )
2069 {
2070 if( m_netsList->IsExpanded( item.second ) )
2071 cfg.expanded_rows.push_back( item.first );
2072 }
2073
2074 // Customer group rules
2075 cfg.custom_group_rules.clear();
2076
2077 for( const std::unique_ptr<EDA_COMBINED_MATCHER>& rule : m_custom_group_rules )
2078 cfg.custom_group_rules.push_back( rule->GetPattern() );
2079}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
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:409
CN_ITEM represents a BOARD_CONNETED_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:521
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 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:683
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.