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