2024-03-04 23:00:26 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2025-01-01 13:30:11 -08:00
|
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
2024-03-04 23:00:26 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <widgets/pcb_net_inspector_panel.h>
|
|
|
|
#include <widgets/pcb_net_inspector_panel_data_model.h>
|
|
|
|
|
2025-02-09 12:57:20 +00:00
|
|
|
#include <advanced_config.h>
|
2024-03-04 23:00:26 +00:00
|
|
|
#include <board_design_settings.h>
|
|
|
|
#include <confirm.h>
|
|
|
|
#include <connectivity/connectivity_algo.h>
|
|
|
|
#include <dialogs/dialog_text_entry.h>
|
|
|
|
#include <footprint.h>
|
2025-06-14 13:27:42 -04:00
|
|
|
#include <length_delay_calculation/length_delay_calculation.h>
|
|
|
|
#include <length_delay_calculation/length_delay_calculation_item.h>
|
2024-03-04 23:00:26 +00:00
|
|
|
#include <pad.h>
|
|
|
|
#include <pcb_edit_frame.h>
|
|
|
|
#include <pcb_painter.h>
|
|
|
|
#include <pgm_base.h>
|
|
|
|
#include <settings/settings_manager.h>
|
|
|
|
#include <validators.h>
|
|
|
|
#include <wildcards_and_files_ext.h>
|
2024-05-24 12:23:27 +01:00
|
|
|
#include <eda_pattern_match.h>
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
#include <wx/wupdlock.h>
|
|
|
|
#include <wx/filedlg.h>
|
|
|
|
|
|
|
|
#include <algorithm>
|
2025-03-27 21:26:37 +00:00
|
|
|
#include <thread_pool.h>
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
PCB_NET_INSPECTOR_PANEL::PCB_NET_INSPECTOR_PANEL( wxWindow* parent, PCB_EDIT_FRAME* aFrame ) :
|
2025-03-27 21:26:37 +00:00
|
|
|
NET_INSPECTOR_PANEL( parent, aFrame ), m_frame( aFrame ), m_dataModel( new DATA_MODEL( *this ) )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
m_board = m_frame->GetBoard();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_netsList->AssociateModel( &*m_dataModel );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
// Rebuild nets list
|
|
|
|
buildNetsList( true );
|
|
|
|
|
|
|
|
// Register the panel to receive board change notifications
|
2025-03-27 21:26:37 +00:00
|
|
|
if( m_board != nullptr )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
PCB_NET_INSPECTOR_PANEL::OnBoardHighlightNetChanged( *m_board );
|
|
|
|
m_board->AddListener( this );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Connect to board events
|
|
|
|
m_frame->Bind( EDA_EVT_UNITS_CHANGED, &PCB_NET_INSPECTOR_PANEL::onUnitsChanged, this );
|
|
|
|
|
|
|
|
// Connect to wxDataViewCtrl events
|
|
|
|
m_netsList->Bind( wxEVT_DATAVIEW_ITEM_EXPANDED, &PCB_NET_INSPECTOR_PANEL::OnExpandCollapseRow,
|
|
|
|
this );
|
|
|
|
m_netsList->Bind( wxEVT_DATAVIEW_ITEM_COLLAPSED, &PCB_NET_INSPECTOR_PANEL::OnExpandCollapseRow,
|
|
|
|
this );
|
|
|
|
m_netsList->Bind( wxEVT_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
|
|
|
|
&PCB_NET_INSPECTOR_PANEL::OnHeaderContextMenu, this );
|
|
|
|
m_netsList->Bind( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU,
|
|
|
|
&PCB_NET_INSPECTOR_PANEL::OnNetsListContextMenu, this );
|
|
|
|
m_netsList->Bind( wxEVT_DATAVIEW_ITEM_ACTIVATED,
|
|
|
|
&PCB_NET_INSPECTOR_PANEL::OnNetsListItemActivated, this );
|
2024-03-21 20:48:09 +00:00
|
|
|
m_netsList->Bind( wxEVT_DATAVIEW_COLUMN_SORTED,
|
|
|
|
&PCB_NET_INSPECTOR_PANEL::OnColumnSorted, this );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PCB_NET_INSPECTOR_PANEL::~PCB_NET_INSPECTOR_PANEL()
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
PCB_NET_INSPECTOR_PANEL::SaveSettings();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
m_netsList->AssociateModel( nullptr );
|
|
|
|
|
|
|
|
// Disconnect from board events
|
|
|
|
m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &PCB_NET_INSPECTOR_PANEL::onUnitsChanged, this );
|
|
|
|
|
|
|
|
// Connect to wxDataViewCtrl events
|
|
|
|
m_netsList->Unbind( wxEVT_DATAVIEW_ITEM_EXPANDED, &PCB_NET_INSPECTOR_PANEL::OnExpandCollapseRow,
|
|
|
|
this );
|
|
|
|
m_netsList->Unbind( wxEVT_DATAVIEW_ITEM_COLLAPSED,
|
|
|
|
&PCB_NET_INSPECTOR_PANEL::OnExpandCollapseRow, this );
|
|
|
|
m_netsList->Unbind( wxEVT_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK,
|
|
|
|
&PCB_NET_INSPECTOR_PANEL::OnHeaderContextMenu, this );
|
|
|
|
m_netsList->Unbind( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU,
|
|
|
|
&PCB_NET_INSPECTOR_PANEL::OnNetsListContextMenu, this );
|
|
|
|
m_netsList->Unbind( wxEVT_DATAVIEW_ITEM_ACTIVATED,
|
|
|
|
&PCB_NET_INSPECTOR_PANEL::OnNetsListItemActivated, this );
|
2024-03-21 20:48:09 +00:00
|
|
|
m_netsList->Unbind( wxEVT_DATAVIEW_COLUMN_SORTED,
|
2024-05-25 11:31:54 +01:00
|
|
|
&PCB_NET_INSPECTOR_PANEL::OnColumnSorted, this );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************************
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* Grid / model columns configuration
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* ***************************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::buildColumns()
|
|
|
|
{
|
|
|
|
m_columns.clear();
|
|
|
|
|
|
|
|
// Set up the column display vector
|
|
|
|
m_columns.emplace_back( 0u, UNDEFINED_LAYER, _( "Name" ), _( "Net Name" ),
|
|
|
|
CSV_COLUMN_DESC::CSV_QUOTE, false );
|
|
|
|
m_columns.emplace_back( 1u, UNDEFINED_LAYER, _( "Netclass" ), _( "Netclass" ),
|
|
|
|
CSV_COLUMN_DESC::CSV_QUOTE, false );
|
2025-02-10 20:57:51 +00:00
|
|
|
|
|
|
|
if( m_showTimeDomainDetails )
|
|
|
|
{
|
|
|
|
m_columns.emplace_back( 2u, UNDEFINED_LAYER, _( "Total Delay" ), _( "Net Delay" ), CSV_COLUMN_DESC::CSV_NONE,
|
|
|
|
true );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_columns.emplace_back( 2u, UNDEFINED_LAYER, _( "Total Length" ), _( "Net Length" ), CSV_COLUMN_DESC::CSV_NONE,
|
|
|
|
true );
|
|
|
|
}
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
m_columns.emplace_back( 3u, UNDEFINED_LAYER, _( "Via Count" ), _( "Via Count" ),
|
|
|
|
CSV_COLUMN_DESC::CSV_NONE, false );
|
2025-02-10 20:57:51 +00:00
|
|
|
|
|
|
|
if( m_showTimeDomainDetails )
|
|
|
|
{
|
|
|
|
m_columns.emplace_back( 4u, UNDEFINED_LAYER, _( "Via Delay" ), _( "Via Delay" ), CSV_COLUMN_DESC::CSV_NONE,
|
|
|
|
true );
|
|
|
|
m_columns.emplace_back( 5u, UNDEFINED_LAYER, _( "Track Delay" ), _( "Track Delay" ), CSV_COLUMN_DESC::CSV_NONE,
|
|
|
|
true );
|
|
|
|
m_columns.emplace_back( 6u, UNDEFINED_LAYER, _( "Die Delay" ), _( "Die Delay" ), CSV_COLUMN_DESC::CSV_NONE,
|
|
|
|
true );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_columns.emplace_back( 4u, UNDEFINED_LAYER, _( "Via Length" ), _( "Via Length" ), CSV_COLUMN_DESC::CSV_NONE,
|
|
|
|
true );
|
|
|
|
m_columns.emplace_back( 5u, UNDEFINED_LAYER, _( "Track Length" ), _( "Track Length" ),
|
|
|
|
CSV_COLUMN_DESC::CSV_NONE, true );
|
|
|
|
m_columns.emplace_back( 6u, UNDEFINED_LAYER, _( "Die Length" ), _( "Die Length" ), CSV_COLUMN_DESC::CSV_NONE,
|
|
|
|
true );
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_columns.emplace_back( 7u, UNDEFINED_LAYER, _( "Pad Count" ), _( "Pad Count" ), CSV_COLUMN_DESC::CSV_NONE, false );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
const std::vector<std::function<void( void )>> add_col{
|
2024-03-04 23:00:26 +00:00
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_netsList->AppendTextColumn( m_columns[COLUMN_NAME].display_name,
|
|
|
|
m_columns[COLUMN_NAME], wxDATAVIEW_CELL_INERT, -1,
|
|
|
|
wxALIGN_LEFT,
|
|
|
|
wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE );
|
|
|
|
},
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_netsList->AppendTextColumn( m_columns[COLUMN_NETCLASS].display_name,
|
|
|
|
m_columns[COLUMN_NETCLASS], wxDATAVIEW_CELL_INERT, -1,
|
|
|
|
wxALIGN_LEFT,
|
|
|
|
wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE
|
|
|
|
| wxDATAVIEW_COL_SORTABLE );
|
|
|
|
},
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_netsList->AppendTextColumn( m_columns[COLUMN_TOTAL_LENGTH].display_name,
|
|
|
|
m_columns[COLUMN_TOTAL_LENGTH], wxDATAVIEW_CELL_INERT, -1,
|
|
|
|
wxALIGN_CENTER,
|
|
|
|
wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE
|
|
|
|
| wxDATAVIEW_COL_SORTABLE );
|
|
|
|
},
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_COUNT].display_name,
|
|
|
|
m_columns[COLUMN_VIA_COUNT], wxDATAVIEW_CELL_INERT, -1,
|
|
|
|
wxALIGN_CENTER,
|
|
|
|
wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE
|
|
|
|
| wxDATAVIEW_COL_SORTABLE );
|
|
|
|
},
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_LENGTH].display_name,
|
|
|
|
m_columns[COLUMN_VIA_LENGTH], wxDATAVIEW_CELL_INERT, -1,
|
|
|
|
wxALIGN_CENTER,
|
|
|
|
wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE
|
|
|
|
| wxDATAVIEW_COL_SORTABLE );
|
|
|
|
},
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_netsList->AppendTextColumn( m_columns[COLUMN_BOARD_LENGTH].display_name,
|
|
|
|
m_columns[COLUMN_BOARD_LENGTH], wxDATAVIEW_CELL_INERT, -1,
|
|
|
|
wxALIGN_CENTER,
|
|
|
|
wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE
|
|
|
|
| wxDATAVIEW_COL_SORTABLE );
|
|
|
|
},
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_netsList->AppendTextColumn( m_columns[COLUMN_PAD_DIE_LENGTH].display_name,
|
|
|
|
m_columns[COLUMN_PAD_DIE_LENGTH], wxDATAVIEW_CELL_INERT,
|
|
|
|
-1, wxALIGN_CENTER,
|
|
|
|
wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE
|
|
|
|
| wxDATAVIEW_COL_SORTABLE );
|
|
|
|
},
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_netsList->AppendTextColumn( m_columns[COLUMN_PAD_COUNT].display_name,
|
|
|
|
m_columns[COLUMN_PAD_COUNT], wxDATAVIEW_CELL_INERT, -1,
|
|
|
|
wxALIGN_CENTER,
|
|
|
|
wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE
|
|
|
|
| wxDATAVIEW_COL_SORTABLE );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// If we have not yet loaded the first board, use a dummy local settings object to ensure we
|
|
|
|
// don't over-write existing board settings (note that PCB_EDIT_FRAME loads the local settings
|
|
|
|
// object prior to loading the board; the two are not synced and we need to account for that)
|
|
|
|
PANEL_NET_INSPECTOR_SETTINGS* cfg = nullptr;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( m_boardLoaded )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
PROJECT_LOCAL_SETTINGS& localSettings = Pgm().GetSettingsManager().Prj().GetLocalSettings();
|
|
|
|
cfg = &localSettings.m_NetInspectorPanel;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cfg = new PANEL_NET_INSPECTOR_SETTINGS();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the column display settings if column count doesn't match
|
2025-03-27 21:26:37 +00:00
|
|
|
const int totalNumColumns = add_col.size() + m_board->GetCopperLayerCount();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
if( (int) cfg->col_order.size() != totalNumColumns
|
|
|
|
|| (int) cfg->col_hidden.size() != totalNumColumns )
|
|
|
|
{
|
|
|
|
cfg->col_order.resize( totalNumColumns );
|
|
|
|
cfg->col_hidden.resize( totalNumColumns );
|
|
|
|
|
|
|
|
for( int i = 0; i < totalNumColumns; ++i )
|
|
|
|
{
|
|
|
|
cfg->col_order[i] = i;
|
|
|
|
cfg->col_hidden[i] = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that all rows are unique to protect against corrupted settings data
|
|
|
|
std::set<int> col_order_set( cfg->col_order.begin(), cfg->col_order.end() );
|
|
|
|
if( col_order_set.size() != cfg->col_order.size() )
|
|
|
|
{
|
|
|
|
for( std::size_t i = 0; i < cfg->col_order.size(); ++i )
|
2025-03-27 21:26:37 +00:00
|
|
|
cfg->col_order[i] = static_cast<int>( i );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add column records for copper layers
|
2025-03-27 21:26:37 +00:00
|
|
|
for( PCB_LAYER_ID layer : m_board->GetEnabledLayers().Seq() )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
if( !IsCopperLayer( layer ) )
|
|
|
|
continue;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_columns.emplace_back( m_columns.size(), layer, m_board->GetLayerName( layer ), m_board->GetLayerName( layer ),
|
|
|
|
CSV_COLUMN_DESC::CSV_NONE, true );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add display columns in settings order
|
2025-03-27 21:26:37 +00:00
|
|
|
for( const int i : cfg->col_order )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
const int addModelColumn = i;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
if( addModelColumn >= (int) add_col.size() )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
m_netsList->AppendTextColumn( m_board->GetLayerName( m_columns[addModelColumn].layer ),
|
|
|
|
m_columns[addModelColumn], wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
|
2024-03-04 23:00:26 +00:00
|
|
|
wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE
|
|
|
|
| wxDATAVIEW_COL_SORTABLE );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
add_col.at( i )();
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the name column as the expander row
|
|
|
|
if( wxDataViewColumn* col = getDisplayedColumnForModelField( COLUMN_NAME ) )
|
|
|
|
{
|
|
|
|
m_netsList->SetExpanderColumn( col );
|
|
|
|
}
|
|
|
|
|
|
|
|
adjustListColumnSizes( cfg );
|
|
|
|
|
|
|
|
// Delete the temporary config if used
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !m_boardLoaded )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
delete cfg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::adjustListColumnSizes( PANEL_NET_INSPECTOR_SETTINGS* cfg ) const
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
wxWindowUpdateLocker locker( m_netsList );
|
|
|
|
|
|
|
|
if( cfg->col_widths.size() != m_columns.size() )
|
|
|
|
{
|
|
|
|
int minValueWidth = GetTextExtent( wxT( "00000,000 mm" ) ).x;
|
|
|
|
int minNumberWidth = GetTextExtent( wxT( "000" ) ).x;
|
|
|
|
int minNameWidth = GetTextExtent( wxT( "MMMMMMMMMMMM" ) ).x;
|
|
|
|
|
|
|
|
// Considering left and right margins.
|
|
|
|
// For wxRenderGeneric it is 5px.
|
|
|
|
// Also account for the sorting arrow in the column header.
|
|
|
|
// Column 0 also needs space for any potential expander icons.
|
2025-03-27 21:26:37 +00:00
|
|
|
constexpr int margins = 15;
|
|
|
|
constexpr int extra_width = 30;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-01-13 16:44:48 +00:00
|
|
|
auto getTargetWidth =
|
|
|
|
[&]( int columnID )
|
|
|
|
{
|
|
|
|
switch( columnID )
|
|
|
|
{
|
|
|
|
case COLUMN_NAME: return minNameWidth + extra_width;
|
|
|
|
case COLUMN_NETCLASS: return minNameWidth + margins;
|
|
|
|
case COLUMN_VIA_COUNT: return minNumberWidth + margins;
|
|
|
|
case COLUMN_PAD_COUNT: return minNumberWidth + margins;
|
|
|
|
default: return minValueWidth + margins;
|
|
|
|
}
|
|
|
|
};
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
wxASSERT( m_columns.size() == cfg->col_order.size() );
|
|
|
|
|
|
|
|
for( size_t i = 0; i < m_columns.size(); ++i )
|
|
|
|
{
|
|
|
|
const int modelColumn = cfg->col_order[i];
|
|
|
|
int titleSize = GetTextExtent( m_columns[modelColumn].display_name ).x;
|
|
|
|
titleSize = modelColumn == COLUMN_NAME ? titleSize + extra_width : titleSize + margins;
|
|
|
|
const int valSize = getTargetWidth( modelColumn );
|
|
|
|
m_netsList->GetColumn( i )->SetWidth( std::max( titleSize, valSize ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wxASSERT( m_columns.size() == cfg->col_hidden.size() );
|
|
|
|
wxASSERT( m_columns.size() == cfg->col_widths.size() );
|
|
|
|
|
|
|
|
for( size_t ii = 0; ii < m_columns.size(); ++ii )
|
|
|
|
{
|
|
|
|
const int newWidth = cfg->col_widths[ii];
|
2024-04-03 12:38:31 -04:00
|
|
|
// Make sure we end up with something non-zero so we can resize it
|
|
|
|
m_netsList->GetColumn( ii )->SetWidth( std::max( newWidth, 10 ) );
|
2024-03-04 23:00:26 +00:00
|
|
|
m_netsList->GetColumn( ii )->SetHidden( cfg->col_hidden[ii] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_netsList->Refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
bool PCB_NET_INSPECTOR_PANEL::restoreSortColumn( const int sortingColumnId, const bool sortOrderAsc ) const
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
if( sortingColumnId != -1 )
|
|
|
|
{
|
|
|
|
if( wxDataViewColumn* col = getDisplayedColumnForModelField( sortingColumnId ) )
|
|
|
|
{
|
|
|
|
col->SetSortOrder( sortOrderAsc );
|
2025-03-27 21:26:37 +00:00
|
|
|
m_dataModel->Resort();
|
2024-03-04 23:00:26 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
wxDataViewColumn* PCB_NET_INSPECTOR_PANEL::getDisplayedColumnForModelField( const int columnId ) const
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
for( unsigned int i = 0; i < m_netsList->GetColumnCount(); ++i )
|
|
|
|
{
|
|
|
|
wxDataViewColumn* col = m_netsList->GetColumn( i );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( static_cast<int>( col->GetModelColumn() ) == columnId )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
return col;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************************
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* Nets list generation
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* ***************************************************************************************/
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::buildNetsList( const bool rebuildColumns )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
// Only build the list of nets if there is a board present
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !m_board )
|
2024-03-04 23:00:26 +00:00
|
|
|
return;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_inBuildNetsList = true;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-31 20:47:03 +01:00
|
|
|
m_netsList->Freeze();
|
|
|
|
|
2025-02-10 20:57:51 +00:00
|
|
|
m_dataModel->SetIsTimeDomain( m_showTimeDomainDetails );
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
PROJECT_LOCAL_SETTINGS& localSettings = Pgm().GetSettingsManager().Prj().GetLocalSettings();
|
|
|
|
PANEL_NET_INSPECTOR_SETTINGS* cfg = &localSettings.m_NetInspectorPanel;
|
|
|
|
|
|
|
|
// Refresh all filtering / grouping settings
|
2025-03-27 21:26:37 +00:00
|
|
|
m_filterByNetName = cfg->filter_by_net_name;
|
|
|
|
m_filterByNetclass = cfg->filter_by_netclass;
|
|
|
|
m_showZeroPadNets = cfg->show_zero_pad_nets;
|
2025-02-10 20:57:51 +00:00
|
|
|
m_showTimeDomainDetails = cfg->show_time_domain_details;
|
2025-03-27 21:26:37 +00:00
|
|
|
m_groupByNetclass = cfg->group_by_netclass;
|
|
|
|
m_groupByConstraint = cfg->group_by_constraint;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-02-08 18:08:39 +00:00
|
|
|
// Attempt to keep any expanded groups open
|
2025-03-27 21:26:37 +00:00
|
|
|
if( m_boardLoaded && !m_boardLoading )
|
2025-02-08 18:08:39 +00:00
|
|
|
{
|
|
|
|
cfg->expanded_rows.clear();
|
|
|
|
DATA_MODEL* model = static_cast<DATA_MODEL*>( m_netsList->GetModel() );
|
|
|
|
|
|
|
|
for( const auto& groupItems = model->getGroupDataViewItems();
|
|
|
|
auto& [groupName, groupItem] : groupItems )
|
|
|
|
{
|
|
|
|
if( m_netsList->IsExpanded( groupItem ) )
|
|
|
|
cfg->expanded_rows.push_back( groupName );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// When rebuilding the netlist, try to keep the row selection
|
2024-03-04 23:00:26 +00:00
|
|
|
wxDataViewItemArray sel;
|
|
|
|
m_netsList->GetSelections( sel );
|
|
|
|
|
|
|
|
std::vector<int> prev_selected_netcodes;
|
|
|
|
prev_selected_netcodes.reserve( sel.GetCount() );
|
|
|
|
|
|
|
|
for( unsigned int i = 0; i < sel.GetCount(); ++i )
|
|
|
|
{
|
|
|
|
const LIST_ITEM* item = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
|
|
|
|
prev_selected_netcodes.push_back( item->GetNetCode() );
|
|
|
|
}
|
|
|
|
|
|
|
|
int sorting_column_id = cfg->sorting_column;
|
|
|
|
bool sort_order_asc = cfg->sort_order_asc;
|
|
|
|
|
2024-03-21 20:48:09 +00:00
|
|
|
if( wxDataViewColumn* sorting_column = m_netsList->GetSortingColumn() )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !m_boardLoading )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
sorting_column_id = static_cast<int>( sorting_column->GetModelColumn() );
|
|
|
|
sort_order_asc = sorting_column->IsSortOrderAscending();
|
|
|
|
}
|
2024-03-21 20:48:09 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// On GTK, wxDVC will crash if we rebuild with a sorting column set.
|
2024-03-21 20:48:09 +00:00
|
|
|
sorting_column->UnsetAsSortKey();
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if( rebuildColumns )
|
|
|
|
{
|
|
|
|
m_netsList->ClearColumns();
|
|
|
|
buildColumns();
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_dataModel->deleteAllItems();
|
2024-05-24 12:23:27 +01:00
|
|
|
m_custom_group_rules.clear();
|
|
|
|
|
|
|
|
for( const wxString& rule : cfg->custom_group_rules )
|
|
|
|
m_custom_group_rules.push_back( std::make_unique<EDA_COMBINED_MATCHER>( rule, CTX_NET ) );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_dataModel->addCustomGroups();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<NETINFO_ITEM*> netCodes;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
for( NETINFO_ITEM* ni : m_board->GetNetInfo() )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( netFilterMatches( ni, cfg ) )
|
|
|
|
netCodes.emplace_back( ni );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::ranges::sort( netCodes,
|
|
|
|
[]( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
|
|
|
|
{
|
|
|
|
return a->GetNetCode() < b->GetNetCode();
|
|
|
|
} );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<std::unique_ptr<LIST_ITEM>> lengths = calculateNets( netCodes, m_showZeroPadNets );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_dataModel->addItems( lengths );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// Try to re-enable the sorting column
|
2024-03-21 20:48:09 +00:00
|
|
|
if( !restoreSortColumn( sorting_column_id, sort_order_asc ))
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
// By default, sort by Name column
|
2024-03-21 20:48:09 +00:00
|
|
|
restoreSortColumn( COLUMN_NAME, true );
|
|
|
|
}
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
// Try to restore the expanded groups
|
2025-03-27 21:26:37 +00:00
|
|
|
if( m_boardLoaded )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
m_rowExpanding = true;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<std::pair<wxString, wxDataViewItem>> groupItems = m_dataModel->getGroupDataViewItems();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
for( wxString& groupName : cfg->expanded_rows )
|
|
|
|
{
|
2024-05-25 11:31:54 +01:00
|
|
|
auto pred =
|
|
|
|
[&groupName]( const std::pair<wxString, wxDataViewItem>& item )
|
|
|
|
{
|
|
|
|
return groupName == item.first;
|
|
|
|
};
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
auto tableItem = std::ranges::find_if( groupItems, pred );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
if( tableItem != groupItems.end() )
|
|
|
|
m_netsList->Expand( tableItem->second );
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_rowExpanding = false;
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// Try to restore the selected rows
|
2024-03-04 23:00:26 +00:00
|
|
|
sel.Clear();
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
for( const int& nc : prev_selected_netcodes )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( std::optional<LIST_ITEM_ITER> r = m_dataModel->findItem( nc ) )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
const std::unique_ptr<LIST_ITEM>& list_item = *r.value();
|
|
|
|
sel.Add( wxDataViewItem( list_item.get() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-31 21:06:52 +01:00
|
|
|
m_netsList->Thaw(); // Must thaw before reselecting to avoid windows selection bug
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
if( !sel.IsEmpty() )
|
|
|
|
{
|
|
|
|
m_netsList->SetSelections( sel );
|
|
|
|
m_netsList->EnsureVisible( sel.Item( 0 ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_netsList->UnselectAll();
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_inBuildNetsList = false;
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PCB_NET_INSPECTOR_PANEL::netFilterMatches( NETINFO_ITEM* aNet,
|
|
|
|
PANEL_NET_INSPECTOR_SETTINGS* cfg ) const
|
|
|
|
{
|
|
|
|
if( cfg == nullptr )
|
|
|
|
{
|
|
|
|
PROJECT_LOCAL_SETTINGS& localSettings = Pgm().GetSettingsManager().Prj().GetLocalSettings();
|
|
|
|
cfg = &localSettings.m_NetInspectorPanel;
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// Never show an unconnected net
|
2024-03-04 23:00:26 +00:00
|
|
|
if( aNet->GetNetCode() <= 0 )
|
|
|
|
return false;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
const wxString filterString = UnescapeString( m_searchCtrl->GetValue() ).Upper();
|
|
|
|
const wxString netName = UnescapeString( aNet->GetNetname() ).Upper();
|
|
|
|
const NETCLASS* netClass = aNet->GetNetClass();
|
|
|
|
const wxString netClassName = UnescapeString( netClass->GetName() ).Upper();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
bool matched = false;
|
|
|
|
|
|
|
|
// No filter - match all
|
|
|
|
if( filterString.Length() == 0 )
|
|
|
|
matched = true;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// Search on net class
|
2024-03-04 23:00:26 +00:00
|
|
|
if( !matched && cfg->filter_by_netclass && netClassName.Find( filterString ) != wxNOT_FOUND )
|
|
|
|
matched = true;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// Search on net name
|
2024-03-04 23:00:26 +00:00
|
|
|
if( !matched && cfg->filter_by_net_name && netName.Find( filterString ) != wxNOT_FOUND )
|
|
|
|
matched = true;
|
|
|
|
|
|
|
|
// Remove unconnected nets if required
|
|
|
|
if( matched )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !m_showUnconnectedNets )
|
2024-03-04 23:00:26 +00:00
|
|
|
matched = !netName.StartsWith( wxT( "UNCONNECTED-(" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return matched;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct NETCODE_CMP_LESS
|
|
|
|
{
|
|
|
|
bool operator()( const CN_ITEM* a, const CN_ITEM* b ) const { return a->Net() < b->Net(); }
|
|
|
|
|
|
|
|
bool operator()( const CN_ITEM* a, int b ) const { return a->Net() < b; }
|
|
|
|
|
|
|
|
bool operator()( int a, const CN_ITEM* b ) const { return a < b->Net(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<CN_ITEM*> PCB_NET_INSPECTOR_PANEL::relevantConnectivityItems() const
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
// Pre-filter the connectivity items and sort them by netcode. This avoids quadratic runtime when building the whole
|
|
|
|
// net list.
|
2024-03-04 23:00:26 +00:00
|
|
|
const auto type_bits = std::bitset<MAX_STRUCT_TYPE_ID>()
|
|
|
|
.set( PCB_TRACE_T )
|
|
|
|
.set( PCB_ARC_T )
|
|
|
|
.set( PCB_VIA_T )
|
|
|
|
.set( PCB_PAD_T );
|
|
|
|
|
|
|
|
std::vector<CN_ITEM*> cn_items;
|
|
|
|
cn_items.reserve( 1024 );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
for( CN_ITEM* cn_item : m_board->GetConnectivity()->GetConnectivityAlgo()->ItemList() )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
if( cn_item->Valid() && type_bits[cn_item->Parent()->Type()] )
|
|
|
|
cn_items.push_back( cn_item );
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::ranges::sort( cn_items, NETCODE_CMP_LESS() );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
return cn_items;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<std::unique_ptr<PCB_NET_INSPECTOR_PANEL::LIST_ITEM>>
|
|
|
|
PCB_NET_INSPECTOR_PANEL::calculateNets( const std::vector<NETINFO_ITEM*>& aNetCodes, bool aIncludeZeroPadNets ) const
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<std::unique_ptr<LIST_ITEM>> results;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-02-10 20:57:51 +00:00
|
|
|
LENGTH_DELAY_CALCULATION* calc = m_board->GetLengthCalculation();
|
2025-03-27 21:26:37 +00:00
|
|
|
const std::vector<CN_ITEM*> conItems = relevantConnectivityItems();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// First assemble the LENGTH_CALCULATION_ITEMs for board items which match the nets we need to recompute
|
|
|
|
// Precondition: conItems and aNetCodes are sorted in increasing netcode value
|
|
|
|
// Functionality: This extracts any items from conItems which have a netcode which is present in aNetCodes
|
2025-02-10 20:57:51 +00:00
|
|
|
std::unordered_map<int, std::vector<LENGTH_DELAY_CALCULATION_ITEM>> netItemsMap;
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<NETINFO_ITEM*> foundNets;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
auto itemItr = conItems.begin();
|
|
|
|
auto netCodeItr = aNetCodes.begin();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
while( itemItr != conItems.end() && netCodeItr != aNetCodes.end() )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
const int curNetCode = ( *netCodeItr )->GetNetCode();
|
|
|
|
const int curItemNetCode = ( *itemItr )->Net();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( curItemNetCode == curNetCode )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( foundNets.empty() || foundNets.back() != *netCodeItr )
|
|
|
|
foundNets.emplace_back( *netCodeItr );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// Take the item
|
2025-02-10 20:57:51 +00:00
|
|
|
LENGTH_DELAY_CALCULATION_ITEM lengthItem = calc->GetLengthCalculationItem( ( *itemItr )->Parent() );
|
2025-03-27 21:26:37 +00:00
|
|
|
netItemsMap[curItemNetCode].emplace_back( std::move( lengthItem ) );
|
|
|
|
++itemItr;
|
|
|
|
}
|
|
|
|
else if( curItemNetCode < curNetCode )
|
|
|
|
{
|
|
|
|
// Fast-forward through items
|
|
|
|
while( itemItr != conItems.end() && ( *itemItr )->Net() < curNetCode )
|
|
|
|
++itemItr;
|
|
|
|
}
|
|
|
|
else if( curItemNetCode > curNetCode )
|
|
|
|
{
|
|
|
|
// Fast-forward through required net codes
|
|
|
|
while( netCodeItr != aNetCodes.end() && curItemNetCode > ( *netCodeItr )->GetNetCode() )
|
|
|
|
++netCodeItr;
|
|
|
|
}
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
// Now calculate the length statistics for each net. This includes potentially expensive path optimisations, so
|
|
|
|
// parallelize this work.
|
|
|
|
std::mutex resultsMutex;
|
|
|
|
thread_pool& tp = GetKiCadThreadPool();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
auto resultsFuture = tp.parallelize_loop(
|
|
|
|
0, foundNets.size(),
|
|
|
|
[&, this, calc]( const int start, const int end )
|
|
|
|
{
|
|
|
|
for( int i = start; i < end; ++i )
|
|
|
|
{
|
|
|
|
int netCode = foundNets[i]->GetNetCode();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
constexpr PATH_OPTIMISATIONS opts = { .OptimiseViaLayers = true,
|
|
|
|
.MergeTracks = true,
|
|
|
|
.OptimiseTracesInPads = true,
|
|
|
|
.InferViaInPad = false };
|
2025-02-10 20:57:51 +00:00
|
|
|
LENGTH_DELAY_STATS lengthDetails = calc->CalculateLengthDetails(
|
|
|
|
netItemsMap[netCode], opts, nullptr, nullptr, LENGTH_DELAY_LAYER_OPT::WITH_LAYER_DETAIL,
|
|
|
|
m_showTimeDomainDetails ? LENGTH_DELAY_DOMAIN_OPT::WITH_DELAY_DETAIL
|
|
|
|
: LENGTH_DELAY_DOMAIN_OPT::NO_DELAY_DETAIL );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( aIncludeZeroPadNets || lengthDetails.NumPads > 0 )
|
|
|
|
{
|
|
|
|
std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( foundNets[i] );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
new_item->SetPadCount( lengthDetails.NumPads );
|
|
|
|
new_item->SetLayerCount( m_board->GetCopperLayerCount() );
|
|
|
|
new_item->SetPadDieLength( lengthDetails.PadToDieLength );
|
2025-02-10 20:57:51 +00:00
|
|
|
new_item->SetPadDieDelay( lengthDetails.PadToDieDelay );
|
2025-03-27 21:26:37 +00:00
|
|
|
new_item->SetViaCount( lengthDetails.NumVias );
|
|
|
|
new_item->SetViaLength( lengthDetails.ViaLength );
|
2025-02-10 20:57:51 +00:00
|
|
|
new_item->SetViaDelay( lengthDetails.ViaDelay );
|
2025-03-27 21:26:37 +00:00
|
|
|
new_item->SetLayerWireLengths( *lengthDetails.LayerLengths );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-02-10 20:57:51 +00:00
|
|
|
if( m_showTimeDomainDetails )
|
|
|
|
new_item->SetLayerWireDelays( *lengthDetails.LayerDelays );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::scoped_lock lock( resultsMutex );
|
|
|
|
results.emplace_back( std::move( new_item ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
resultsFuture.get();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
return results;
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************************
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* Formatting helpers
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* ***************************************************************************************/
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
wxString PCB_NET_INSPECTOR_PANEL::formatNetCode( const NETINFO_ITEM* aNet )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
return wxString::Format( wxT( "%.3d" ), aNet->GetNetCode() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
wxString PCB_NET_INSPECTOR_PANEL::formatNetName( const NETINFO_ITEM* aNet )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
return UnescapeString( aNet->GetNetname() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
wxString PCB_NET_INSPECTOR_PANEL::formatCount( const unsigned int aValue )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
return wxString::Format( wxT( "%u" ), aValue );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
wxString PCB_NET_INSPECTOR_PANEL::formatLength( const int64_t aValue ) const
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
return m_frame->MessageTextFromValue( aValue,
|
2024-05-25 11:31:54 +01:00
|
|
|
// don't include unit label in the string when reporting
|
2025-03-27 21:26:37 +00:00
|
|
|
!m_inReporting );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
2025-02-10 20:57:51 +00:00
|
|
|
wxString PCB_NET_INSPECTOR_PANEL::formatDelay( const int64_t aValue ) const
|
|
|
|
{
|
|
|
|
return m_frame->MessageTextFromValue( aValue,
|
|
|
|
// don't include unit label in the string when reporting
|
|
|
|
!m_inReporting, EDA_DATA_TYPE::TIME );
|
|
|
|
}
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::updateDisplayedRowValues( const std::optional<LIST_ITEM_ITER>& aRow ) const
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
if( !aRow )
|
|
|
|
return;
|
|
|
|
|
|
|
|
wxDataViewItemArray sel;
|
|
|
|
m_netsList->GetSelections( sel );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_dataModel->updateItem( aRow );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
if( !sel.IsEmpty() )
|
|
|
|
{
|
|
|
|
m_netsList->SetSelections( sel );
|
|
|
|
m_netsList->EnsureVisible( sel.Item( 0 ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************************
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* BOARD_LISTENER event handling
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* ***************************************************************************************/
|
|
|
|
|
|
|
|
|
2024-04-01 20:56:10 +01:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardChanged()
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
m_board = m_frame->GetBoard();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( m_board )
|
|
|
|
m_board->AddListener( this );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_boardLoaded = true;
|
|
|
|
m_boardLoading = true;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
const PROJECT_LOCAL_SETTINGS& localSettings = Pgm().GetSettingsManager().Prj().GetLocalSettings();
|
2024-03-04 23:00:26 +00:00
|
|
|
auto& cfg = localSettings.m_NetInspectorPanel;
|
|
|
|
m_searchCtrl->SetValue( cfg.filter_text );
|
|
|
|
|
|
|
|
buildNetsList( true );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_boardLoading = false;
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* aBoardItem )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
const std::vector<BOARD_ITEM*> item{ aBoardItem };
|
|
|
|
updateBoardItems( item );
|
|
|
|
}
|
2024-03-24 07:56:53 +00:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems )
|
|
|
|
{
|
|
|
|
updateBoardItems( aBoardItems );
|
|
|
|
}
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::updateBoardItems( const std::vector<BOARD_ITEM*>& aBoardItems )
|
|
|
|
{
|
|
|
|
if( !IsShownOnScreen() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Rebuild full list for large changes
|
|
|
|
if( aBoardItems.size()
|
|
|
|
> static_cast<size_t>( ADVANCED_CFG::GetCfg().m_NetInspectorBulkUpdateOptimisationThreshold ) )
|
|
|
|
{
|
|
|
|
buildNetsList();
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
2025-03-27 21:26:37 +00:00
|
|
|
else
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<NETINFO_ITEM*> changedNets;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
for( BOARD_ITEM* boardItem : aBoardItems )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( NETINFO_ITEM* net = dynamic_cast<NETINFO_ITEM*>( boardItem ) )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
// A new net has been added to the board. Add it to our list if it passes the netname filter test.
|
|
|
|
if( netFilterMatches( net ) )
|
|
|
|
changedNets.emplace_back( net );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
2025-03-27 21:26:37 +00:00
|
|
|
else if( BOARD_CONNECTED_ITEM* i = dynamic_cast<BOARD_CONNECTED_ITEM*>( boardItem ) )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
changedNets.emplace_back( i->GetNet() );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
2025-03-27 21:26:37 +00:00
|
|
|
else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( boardItem ) )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
for( const PAD* pad : footprint->Pads() )
|
|
|
|
{
|
|
|
|
if( netFilterMatches( pad->GetNet() ) )
|
|
|
|
changedNets.emplace_back( pad->GetNet() );
|
|
|
|
}
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
}
|
2025-03-27 21:26:37 +00:00
|
|
|
|
|
|
|
std::ranges::sort( changedNets,
|
|
|
|
[]( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
|
|
|
|
{
|
|
|
|
return a->GetNetCode() < b->GetNetCode();
|
|
|
|
} );
|
|
|
|
|
|
|
|
updateNets( changedNets );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
2025-03-27 21:26:37 +00:00
|
|
|
|
|
|
|
m_netsList->Refresh();
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::updateNets( const std::vector<NETINFO_ITEM*>& aNets ) const
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<NETINFO_ITEM*> netsToUpdate;
|
|
|
|
std::unordered_set<NETINFO_ITEM*> netsToDelete;
|
2025-02-10 17:52:55 -05:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
for( NETINFO_ITEM* net : aNets )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
// Add all nets to the deletion list - we will prune this later to only contain unhandled nets
|
|
|
|
netsToDelete.insert( net );
|
|
|
|
|
|
|
|
// Only calculate nets that match the current filter
|
|
|
|
if( netFilterMatches( net ) )
|
|
|
|
netsToUpdate.emplace_back( net );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
2025-07-11 13:06:09 +01:00
|
|
|
wxWindowUpdateLocker updateLocker( m_netsList );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<std::unique_ptr<LIST_ITEM>> newListItems = calculateNets( aNets, true );
|
2024-03-24 07:56:53 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
for( std::unique_ptr<LIST_ITEM>& newListItem : newListItems )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
// Remove the handled net from the deletion list
|
|
|
|
netsToDelete.erase( newListItem->GetNet() );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::optional<LIST_ITEM_ITER> curNetRow = m_dataModel->findItem( newListItem->GetNetCode() );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !m_showZeroPadNets && newListItem->GetPadCount() == 0 )
|
|
|
|
{
|
|
|
|
m_dataModel->deleteItem( curNetRow );
|
|
|
|
continue;
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !curNetRow )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
m_dataModel->addItem( std::move( newListItem ) );
|
|
|
|
continue;
|
|
|
|
}
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
const std::unique_ptr<LIST_ITEM>& curListItem = *curNetRow.value();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( curListItem->GetNetName() != newListItem->GetNetName() )
|
|
|
|
{
|
|
|
|
// If the name has changed, it might require re-grouping. It's easier to remove and re-insert it.
|
|
|
|
m_dataModel->deleteItem( curNetRow );
|
|
|
|
m_dataModel->addItem( std::move( newListItem ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
curListItem->SetPadCount( newListItem->GetPadCount() );
|
|
|
|
curListItem->SetPadDieLength( newListItem->GetPadDieLength() );
|
2025-02-10 20:57:51 +00:00
|
|
|
curListItem->SetPadDieDelay( newListItem->GetPadDieDelay() );
|
2025-03-27 21:26:37 +00:00
|
|
|
curListItem->SetViaCount( newListItem->GetViaCount() );
|
|
|
|
curListItem->SetViaLength( newListItem->GetViaLength() );
|
2025-02-10 20:57:51 +00:00
|
|
|
curListItem->SetViaDelay( newListItem->GetViaDelay() );
|
2025-03-27 21:26:37 +00:00
|
|
|
curListItem->SetLayerWireLengths( newListItem->GetLayerWireLengths() );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-02-10 20:57:51 +00:00
|
|
|
if( m_showTimeDomainDetails )
|
|
|
|
curListItem->SetLayerWireDelays( newListItem->GetLayerWireDelays() );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
updateDisplayedRowValues( curNetRow );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
}
|
2025-03-27 21:26:37 +00:00
|
|
|
|
|
|
|
// Delete any nets we have not yet handled
|
|
|
|
for( const NETINFO_ITEM* netToDelete : netsToDelete )
|
|
|
|
m_dataModel->deleteItem( m_dataModel->findItem( netToDelete->GetNetCode() ) );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardItemRemoved( BOARD& aBoard, BOARD_ITEM* aBoardItem )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
const std::vector<BOARD_ITEM*> item{ aBoardItem };
|
|
|
|
updateBoardItems( item );
|
|
|
|
}
|
2024-03-24 07:56:53 +00:00
|
|
|
|
2025-02-10 17:52:55 -05:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardItemsRemoved( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems )
|
|
|
|
{
|
|
|
|
updateBoardItems( aBoardItems );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardNetSettingsChanged( BOARD& aBoard )
|
|
|
|
{
|
2024-03-24 07:56:53 +00:00
|
|
|
if( !IsShownOnScreen() )
|
|
|
|
return;
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
buildNetsList();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardItemChanged( BOARD& aBoard, BOARD_ITEM* aBoardItem )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
const std::vector<BOARD_ITEM*> item{ aBoardItem };
|
|
|
|
updateBoardItems( item );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardItemsChanged( BOARD& aBoard,
|
|
|
|
std::vector<BOARD_ITEM*>& aBoardItems )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
updateBoardItems( aBoardItems );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-27 10:26:05 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardCompositeUpdate( BOARD& aBoard,
|
|
|
|
std::vector<BOARD_ITEM*>& aAddedItems,
|
|
|
|
std::vector<BOARD_ITEM*>& aRemovedItems,
|
2025-02-09 12:46:56 +00:00
|
|
|
std::vector<BOARD_ITEM*>& aChangedItems )
|
2024-03-27 10:26:05 +00:00
|
|
|
{
|
|
|
|
if( !IsShownOnScreen() )
|
|
|
|
return;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<BOARD_ITEM*> allItems{ aAddedItems.begin(), aAddedItems.end() };
|
|
|
|
allItems.insert( allItems.end(), aRemovedItems.begin(), aRemovedItems.end() );
|
|
|
|
allItems.insert( allItems.end(), aChangedItems.begin(), aChangedItems.end() );
|
|
|
|
updateBoardItems( allItems );
|
2024-03-27 10:26:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnBoardHighlightNetChanged( BOARD& aBoard )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( m_highlightingNets || !IsShownOnScreen() )
|
2024-03-04 23:00:26 +00:00
|
|
|
return;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !m_board->IsHighLightNetON() )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
m_netsList->UnselectAll();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
const std::set<int>& selected_codes = m_board->GetHighLightNetCodes();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
wxDataViewItemArray new_selection;
|
|
|
|
new_selection.Alloc( selected_codes.size() );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
for( const int code : selected_codes )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( std::optional<LIST_ITEM_ITER> r = m_dataModel->findItem( code ) )
|
2024-03-04 23:00:26 +00:00
|
|
|
new_selection.Add( wxDataViewItem( &***r ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_netsList->SetSelections( new_selection );
|
|
|
|
|
|
|
|
if( !new_selection.IsEmpty() )
|
|
|
|
m_netsList->EnsureVisible( new_selection.Item( 0 ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************************
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* UI-generated event handling
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* ***************************************************************************************/
|
|
|
|
|
2024-03-24 07:56:53 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnShowPanel()
|
|
|
|
{
|
|
|
|
buildNetsList();
|
2025-03-27 21:26:37 +00:00
|
|
|
OnBoardHighlightNetChanged( *m_board );
|
2024-03-24 07:56:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnNetsListContextMenu( wxDataViewEvent& event )
|
|
|
|
{
|
|
|
|
bool multipleSelections = false;
|
|
|
|
const LIST_ITEM* selItem = nullptr;
|
|
|
|
|
|
|
|
if( m_netsList->GetSelectedItemsCount() == 1 )
|
|
|
|
{
|
|
|
|
selItem = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( m_netsList->GetSelectedItemsCount() > 1 )
|
|
|
|
multipleSelections = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxMenu menu;
|
|
|
|
|
|
|
|
// Net edit menu items
|
2024-05-24 12:23:27 +01:00
|
|
|
wxMenuItem* highlightNet = new wxMenuItem( &menu, ID_HIGHLIGHT_SELECTED_NETS,
|
|
|
|
_( "Highlight Selected Net" ),
|
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( highlightNet );
|
|
|
|
|
2024-05-24 12:23:27 +01:00
|
|
|
wxMenuItem* clearHighlighting = new wxMenuItem( &menu, ID_CLEAR_HIGHLIGHTING,
|
|
|
|
_( "Clear Net Highlighting" ),
|
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( clearHighlighting );
|
|
|
|
|
|
|
|
RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
|
|
|
|
const std::set<int>& selected_codes = renderSettings->GetHighlightNetCodes();
|
|
|
|
|
|
|
|
if( selected_codes.size() == 0 )
|
|
|
|
clearHighlighting->Enable( false );
|
|
|
|
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
2025-02-22 15:00:14 -05:00
|
|
|
wxMenuItem* renameNet = new wxMenuItem( &menu, ID_RENAME_NET, _( "Rename Selected Net..." ),
|
2024-03-04 23:00:26 +00:00
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
|
|
|
menu.Append( renameNet );
|
|
|
|
|
|
|
|
wxMenuItem* deleteNet = new wxMenuItem( &menu, ID_DELETE_NET, _( "Delete Selected Net" ),
|
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
|
|
|
menu.Append( deleteNet );
|
|
|
|
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
2025-02-22 15:00:14 -05:00
|
|
|
wxMenuItem* addNet = new wxMenuItem( &menu, ID_ADD_NET, _( "Add Net..." ),
|
2024-05-25 11:31:54 +01:00
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( addNet );
|
|
|
|
|
|
|
|
if( !selItem && !multipleSelections )
|
|
|
|
{
|
|
|
|
highlightNet->Enable( false );
|
|
|
|
deleteNet->Enable( false );
|
|
|
|
renameNet->Enable( false );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( multipleSelections || selItem->GetIsGroup() )
|
|
|
|
{
|
|
|
|
highlightNet->SetItemLabel( _( "Highlight Selected Nets" ) );
|
|
|
|
renameNet->Enable( false );
|
|
|
|
deleteNet->SetItemLabel( _( "Delete Selected Nets" ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
2024-05-25 11:31:54 +01:00
|
|
|
wxMenuItem* removeSelectedGroup = new wxMenuItem( &menu, ID_REMOVE_SELECTED_GROUP,
|
|
|
|
_( "Remove Selected Custom Group" ),
|
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( removeSelectedGroup );
|
|
|
|
|
|
|
|
if( !selItem || !selItem->GetIsGroup() )
|
|
|
|
removeSelectedGroup->Enable( false );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &PCB_NET_INSPECTOR_PANEL::onContextMenuSelection, this );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
PopupMenu( &menu );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnSearchTextChanged( wxCommandEvent& event )
|
|
|
|
{
|
|
|
|
SaveSettings();
|
|
|
|
buildNetsList();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::onAddGroup()
|
|
|
|
{
|
|
|
|
wxString newGroupName;
|
|
|
|
NETNAME_VALIDATOR validator( &newGroupName );
|
|
|
|
|
2024-05-24 12:23:27 +01:00
|
|
|
WX_TEXT_ENTRY_DIALOG dlg( this, _( "Group name / pattern:" ), _( "New Group" ), newGroupName );
|
|
|
|
wxStaticText* help = new wxStaticText( &dlg, wxID_ANY,
|
|
|
|
_( "(Use /.../ to indicate a regular expression.)" ) );
|
|
|
|
help->SetFont( KIUI::GetInfoFont( this ).Italic() );
|
|
|
|
dlg.m_ContentSizer->Add( help, 0, wxALL|wxEXPAND, 5 );
|
2024-03-04 23:00:26 +00:00
|
|
|
dlg.SetTextValidator( validator );
|
2024-05-24 12:23:27 +01:00
|
|
|
dlg.GetSizer()->SetSizeHints( &dlg );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
if( dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty() )
|
|
|
|
return; //Aborted by user
|
|
|
|
|
|
|
|
newGroupName = UnescapeString( dlg.GetValue() );
|
|
|
|
|
|
|
|
if( newGroupName == "" )
|
|
|
|
return;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( std::ranges::find_if( m_custom_group_rules,
|
|
|
|
[&]( std::unique_ptr<EDA_COMBINED_MATCHER>& rule )
|
|
|
|
{
|
|
|
|
return rule->GetPattern() == newGroupName;
|
|
|
|
} )
|
2025-03-30 00:04:37 +00:00
|
|
|
== m_custom_group_rules.end() )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
2024-05-24 12:23:27 +01:00
|
|
|
m_custom_group_rules.push_back( std::make_unique<EDA_COMBINED_MATCHER>( newGroupName,
|
|
|
|
CTX_NET ) );
|
2024-03-04 23:00:26 +00:00
|
|
|
SaveSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
buildNetsList();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::onClearHighlighting()
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
m_highlightingNets = true;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( false );
|
|
|
|
m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
|
|
|
|
m_frame->GetCanvas()->Refresh();
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_highlightingNets = false;
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnExpandCollapseRow( wxCommandEvent& event )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !m_rowExpanding )
|
2024-03-04 23:00:26 +00:00
|
|
|
SaveSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnHeaderContextMenu( wxCommandEvent& event )
|
|
|
|
{
|
|
|
|
wxMenu menu;
|
|
|
|
generateShowHideColumnMenu( &menu );
|
2025-03-27 21:26:37 +00:00
|
|
|
menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &PCB_NET_INSPECTOR_PANEL::onContextMenuSelection, this );
|
2024-03-04 23:00:26 +00:00
|
|
|
PopupMenu( &menu );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnConfigButton( wxCommandEvent& event )
|
|
|
|
{
|
|
|
|
PROJECT_LOCAL_SETTINGS& localSettings = Pgm().GetSettingsManager().Prj().GetLocalSettings();
|
|
|
|
auto& cfg = localSettings.m_NetInspectorPanel;
|
|
|
|
|
|
|
|
const LIST_ITEM* selItem = nullptr;
|
|
|
|
|
|
|
|
if( m_netsList->GetSelectedItemsCount() == 1 )
|
|
|
|
{
|
|
|
|
selItem = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
|
|
|
|
}
|
|
|
|
|
|
|
|
wxMenu menu;
|
|
|
|
|
|
|
|
// Filtering menu items
|
2024-05-24 12:23:27 +01:00
|
|
|
wxMenuItem* filterByNetName = new wxMenuItem( &menu, ID_FILTER_BY_NET_NAME,
|
|
|
|
_( "Filter by Net Name" ),
|
|
|
|
wxEmptyString, wxITEM_CHECK );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( filterByNetName );
|
2024-11-23 14:03:02 +03:00
|
|
|
filterByNetName->Check( cfg.filter_by_net_name );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2024-05-24 12:23:27 +01:00
|
|
|
wxMenuItem* filterByNetclass = new wxMenuItem( &menu, ID_FILTER_BY_NETCLASS,
|
|
|
|
_( "Filter by Netclass" ),
|
|
|
|
wxEmptyString, wxITEM_CHECK );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( filterByNetclass );
|
2024-11-23 14:03:02 +03:00
|
|
|
filterByNetclass->Check( cfg.filter_by_netclass );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
|
|
|
// Grouping menu items
|
|
|
|
//wxMenuItem* groupConstraint =
|
|
|
|
// new wxMenuItem( &menu, ID_GROUP_BY_CONSTRAINT, _( "Group by DRC Constraint" ),
|
|
|
|
// wxEmptyString, wxITEM_CHECK );
|
|
|
|
//groupConstraint->Check( m_group_by_constraint );
|
|
|
|
//menu.Append( groupConstraint );
|
|
|
|
|
2024-05-25 11:31:54 +01:00
|
|
|
wxMenuItem* groupNetclass = new wxMenuItem( &menu, ID_GROUP_BY_NETCLASS,
|
|
|
|
_( "Group by Netclass" ),
|
|
|
|
wxEmptyString, wxITEM_CHECK );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( groupNetclass );
|
2025-03-27 21:26:37 +00:00
|
|
|
groupNetclass->Check( m_groupByNetclass );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
2025-02-22 15:00:14 -05:00
|
|
|
wxMenuItem* addGroup = new wxMenuItem( &menu, ID_ADD_GROUP, _( "Add Custom Group..." ),
|
2024-03-04 23:00:26 +00:00
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
|
|
|
menu.Append( addGroup );
|
|
|
|
|
2024-05-24 12:23:27 +01:00
|
|
|
wxMenuItem* removeSelectedGroup = new wxMenuItem( &menu, ID_REMOVE_SELECTED_GROUP,
|
|
|
|
_( "Remove Selected Custom Group" ),
|
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( removeSelectedGroup );
|
|
|
|
|
|
|
|
if( !selItem || !selItem->GetIsGroup() )
|
|
|
|
removeSelectedGroup->Enable( false );
|
|
|
|
|
2024-05-24 12:23:27 +01:00
|
|
|
wxMenuItem* removeCustomGroups = new wxMenuItem( &menu, ID_REMOVE_GROUPS,
|
|
|
|
_( "Remove All Custom Groups" ),
|
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( removeCustomGroups );
|
|
|
|
removeCustomGroups->Enable( m_custom_group_rules.size() != 0 );
|
|
|
|
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
2024-05-24 12:23:27 +01:00
|
|
|
wxMenuItem* showZeroNetPads = new wxMenuItem( &menu, ID_SHOW_ZERO_NET_PADS,
|
|
|
|
_( "Show Zero Pad Nets" ),
|
|
|
|
wxEmptyString, wxITEM_CHECK );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( showZeroNetPads );
|
2025-03-27 21:26:37 +00:00
|
|
|
showZeroNetPads->Check( m_showZeroPadNets );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2024-05-24 12:23:27 +01:00
|
|
|
wxMenuItem* showUnconnectedNets = new wxMenuItem( &menu, ID_SHOW_UNCONNECTED_NETS,
|
|
|
|
_( "Show Unconnected Nets" ),
|
|
|
|
wxEmptyString, wxITEM_CHECK );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( showUnconnectedNets );
|
2025-03-27 21:26:37 +00:00
|
|
|
showUnconnectedNets->Check( m_showUnconnectedNets );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
2025-02-10 20:57:51 +00:00
|
|
|
wxMenuItem* showTimeDomainDetails = new wxMenuItem( &menu, ID_SHOW_TIME_DOMAIN_DETAILS,
|
|
|
|
_( "Show Time Domain Details" ), wxEmptyString, wxITEM_CHECK );
|
|
|
|
menu.Append( showTimeDomainDetails );
|
|
|
|
showTimeDomainDetails->Check( m_showTimeDomainDetails );
|
|
|
|
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
// Report generation
|
2024-05-24 12:23:27 +01:00
|
|
|
wxMenuItem* generateReport = new wxMenuItem( &menu, ID_GENERATE_REPORT,
|
2025-02-22 15:00:14 -05:00
|
|
|
_( "Save Net Inspector Report..." ),
|
2024-05-24 12:23:27 +01:00
|
|
|
wxEmptyString, wxITEM_NORMAL );
|
2024-03-04 23:00:26 +00:00
|
|
|
menu.Append( generateReport );
|
|
|
|
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
|
|
|
// Show / hide columns menu items
|
|
|
|
wxMenu* colsMenu = new wxMenu();
|
|
|
|
generateShowHideColumnMenu( colsMenu );
|
|
|
|
menu.AppendSubMenu( colsMenu, _( "Show / Hide Columns" ) );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &PCB_NET_INSPECTOR_PANEL::onContextMenuSelection, this );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
PopupMenu( &menu );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::generateShowHideColumnMenu( wxMenu* target )
|
|
|
|
{
|
|
|
|
for( int i = 1; i <= COLUMN_LAST_STATIC_COL; ++i )
|
|
|
|
{
|
|
|
|
wxMenuItem* opt = new wxMenuItem( target, ID_HIDE_COLUMN + i, m_columns[i].display_name,
|
|
|
|
wxEmptyString, wxITEM_CHECK );
|
|
|
|
wxDataViewColumn* col = getDisplayedColumnForModelField( i );
|
|
|
|
target->Append( opt );
|
2024-04-03 12:38:31 -04:00
|
|
|
opt->Check( !col->IsHidden() );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
target->AppendSeparator();
|
|
|
|
|
|
|
|
for( std::size_t i = COLUMN_LAST_STATIC_COL + 1; i < m_columns.size(); ++i )
|
|
|
|
{
|
|
|
|
wxMenuItem* opt = new wxMenuItem( target, ID_HIDE_COLUMN + i, m_columns[i].display_name,
|
|
|
|
wxEmptyString, wxITEM_CHECK );
|
|
|
|
wxDataViewColumn* col = getDisplayedColumnForModelField( i );
|
|
|
|
target->Append( opt );
|
2024-04-03 12:38:31 -04:00
|
|
|
opt->Check( !col->IsHidden() );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::onContextMenuSelection( wxCommandEvent& event )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
bool saveAndRebuild = true;
|
|
|
|
|
|
|
|
switch( event.GetId() )
|
|
|
|
{
|
|
|
|
case ID_ADD_NET:
|
|
|
|
onAddNet();
|
|
|
|
break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
case ID_RENAME_NET:
|
|
|
|
onRenameSelectedNet();
|
|
|
|
break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
case ID_DELETE_NET:
|
|
|
|
onDeleteSelectedNet();
|
|
|
|
break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
case ID_ADD_GROUP:
|
|
|
|
onAddGroup();
|
|
|
|
break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
case ID_GROUP_BY_CONSTRAINT: m_groupByConstraint = !m_groupByConstraint; break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
case ID_GROUP_BY_NETCLASS: m_groupByNetclass = !m_groupByNetclass; break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
case ID_FILTER_BY_NET_NAME: m_filterByNetName = !m_filterByNetName; break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
case ID_FILTER_BY_NETCLASS: m_filterByNetclass = !m_filterByNetclass; break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
case ID_REMOVE_SELECTED_GROUP:
|
|
|
|
onRemoveSelectedGroup();
|
|
|
|
break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
case ID_REMOVE_GROUPS:
|
|
|
|
m_custom_group_rules.clear();
|
|
|
|
break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
case ID_SHOW_ZERO_NET_PADS: m_showZeroPadNets = !m_showZeroPadNets; break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
case ID_SHOW_UNCONNECTED_NETS: m_showUnconnectedNets = !m_showUnconnectedNets; break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2025-02-10 20:57:51 +00:00
|
|
|
case ID_SHOW_TIME_DOMAIN_DETAILS: m_showTimeDomainDetails = !m_showTimeDomainDetails; break;
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
case ID_GENERATE_REPORT:
|
|
|
|
generateReport();
|
|
|
|
saveAndRebuild = false;
|
|
|
|
break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
case ID_HIGHLIGHT_SELECTED_NETS:
|
|
|
|
highlightSelectedNets();
|
|
|
|
saveAndRebuild = false;
|
|
|
|
break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
case ID_CLEAR_HIGHLIGHTING:
|
|
|
|
onClearHighlighting();
|
|
|
|
saveAndRebuild = false;
|
|
|
|
break;
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
default:
|
|
|
|
if( event.GetId() >= ID_HIDE_COLUMN )
|
|
|
|
{
|
|
|
|
const int columnId = event.GetId() - ID_HIDE_COLUMN;
|
|
|
|
wxDataViewColumn* col = getDisplayedColumnForModelField( columnId );
|
2024-04-03 12:38:31 -04:00
|
|
|
// Make sure we end up with something non-zero so we can resize it
|
|
|
|
col->SetWidth( std::max( col->GetWidth(), 10 ) );
|
2024-03-04 23:00:26 +00:00
|
|
|
col->SetHidden( !col->IsHidden() );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( saveAndRebuild )
|
|
|
|
{
|
|
|
|
SaveSettings();
|
|
|
|
buildNetsList();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::onRemoveSelectedGroup()
|
|
|
|
{
|
|
|
|
if( m_netsList->GetSelectedItemsCount() == 1 )
|
|
|
|
{
|
2024-05-24 12:23:27 +01:00
|
|
|
auto* selItem = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
if( selItem->GetIsGroup() )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
const wxString groupName = selItem->GetGroupName();
|
|
|
|
const auto groupIter = std::ranges::find_if( m_custom_group_rules,
|
|
|
|
[&]( std::unique_ptr<EDA_COMBINED_MATCHER>& rule )
|
|
|
|
{
|
|
|
|
return rule->GetPattern() == groupName;
|
|
|
|
} );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
if( groupIter != m_custom_group_rules.end() )
|
|
|
|
{
|
|
|
|
m_custom_group_rules.erase( groupIter );
|
|
|
|
SaveSettings();
|
|
|
|
buildNetsList();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::generateReport()
|
|
|
|
{
|
|
|
|
wxFileDialog dlg( this, _( "Save Net Inspector Report File" ), "", "",
|
|
|
|
_( "Report file" ) + AddFileExtListToFilter( { "csv" } ),
|
|
|
|
wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
|
|
|
|
|
|
|
|
if( dlg.ShowModal() == wxID_CANCEL )
|
|
|
|
return;
|
|
|
|
|
|
|
|
wxTextFile f( dlg.GetPath() );
|
|
|
|
|
|
|
|
f.Create();
|
|
|
|
|
|
|
|
wxString txt;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_inReporting = true;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
// Print Header:
|
|
|
|
for( auto&& col : m_columns )
|
|
|
|
{
|
|
|
|
txt += '"';
|
|
|
|
|
|
|
|
if( col.has_units )
|
|
|
|
{
|
2024-05-24 12:23:27 +01:00
|
|
|
txt += wxString::Format( _( "%s (%s)" ),
|
|
|
|
col.csv_name,
|
2024-03-04 23:00:26 +00:00
|
|
|
EDA_UNIT_UTILS::GetLabel( m_frame->GetUserUnits() ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
txt += col.csv_name;
|
|
|
|
}
|
2024-05-24 12:23:27 +01:00
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
txt += wxT( "\";" );
|
|
|
|
}
|
|
|
|
|
|
|
|
f.AddLine( txt );
|
|
|
|
|
|
|
|
// Print list of nets:
|
2025-03-27 21:26:37 +00:00
|
|
|
const unsigned int num_rows = m_dataModel->itemCount();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
for( unsigned int row = 0; row < num_rows; row++ )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
auto& i = m_dataModel->itemAt( row );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
if( i.GetIsGroup() || i.GetNetCode() == 0 )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
txt = "";
|
|
|
|
|
|
|
|
for( auto&& col : m_columns )
|
|
|
|
{
|
|
|
|
if( static_cast<int>( col.csv_flags ) & static_cast<int>( CSV_COLUMN_DESC::CSV_QUOTE ) )
|
2025-03-27 21:26:37 +00:00
|
|
|
txt += '"' + m_dataModel->valueAt( col.num, row ).GetString() + wxT( "\";" );
|
2024-03-04 23:00:26 +00:00
|
|
|
else
|
2025-03-27 21:26:37 +00:00
|
|
|
txt += m_dataModel->valueAt( col.num, row ).GetString() + ';';
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
f.AddLine( txt );
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_inReporting = false;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
f.Write();
|
|
|
|
f.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnNetsListItemActivated( wxDataViewEvent& event )
|
|
|
|
{
|
|
|
|
highlightSelectedNets();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::highlightSelectedNets()
|
|
|
|
{
|
|
|
|
// ignore selection changes while the whole list is being rebuilt.
|
2025-03-27 21:26:37 +00:00
|
|
|
if( m_inBuildNetsList )
|
2024-03-04 23:00:26 +00:00
|
|
|
return;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_highlightingNets = true;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
|
|
|
|
|
|
|
|
if( m_netsList->HasSelection() )
|
|
|
|
{
|
|
|
|
wxDataViewItemArray sel;
|
|
|
|
m_netsList->GetSelections( sel );
|
|
|
|
|
|
|
|
renderSettings->SetHighlight( false );
|
|
|
|
|
|
|
|
for( unsigned int i = 0; i < sel.GetCount(); ++i )
|
|
|
|
{
|
|
|
|
const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
|
|
|
|
|
|
|
|
if( ii->GetIsGroup() )
|
|
|
|
{
|
|
|
|
for( auto c = ii->ChildrenBegin(), end = ii->ChildrenEnd(); c != end; ++c )
|
|
|
|
renderSettings->SetHighlight( true, ( *c )->GetNetCode(), true );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
renderSettings->SetHighlight( true, ii->GetNetCode(), true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
renderSettings->SetHighlight( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
|
|
|
|
m_frame->GetCanvas()->Refresh();
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_highlightingNets = false;
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-21 20:48:09 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnColumnSorted( wxDataViewEvent& event )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !m_inBuildNetsList )
|
2025-02-08 18:08:39 +00:00
|
|
|
SaveSettings();
|
2024-03-21 20:48:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-04 23:00:26 +00:00
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnParentSetupChanged()
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
// Rebuilt the nets list, and force rebuild of columns in case the stackup has changed
|
2024-03-04 23:00:26 +00:00
|
|
|
buildNetsList( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::onAddNet()
|
|
|
|
{
|
|
|
|
wxString newNetName;
|
|
|
|
NETNAME_VALIDATOR validator( &newNetName );
|
|
|
|
|
|
|
|
WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "New Net" ), newNetName );
|
|
|
|
dlg.SetTextValidator( validator );
|
|
|
|
|
|
|
|
while( true )
|
|
|
|
{
|
|
|
|
if( dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty() )
|
|
|
|
return; //Aborted by user
|
|
|
|
|
|
|
|
newNetName = dlg.GetValue();
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( m_board->FindNet( newNetName ) )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
DisplayError( this,
|
|
|
|
wxString::Format( _( "Net name '%s' is already in use." ), newNetName ) );
|
|
|
|
newNetName = wxEmptyString;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
NETINFO_ITEM* newnet = new NETINFO_ITEM( m_board, dlg.GetValue(), 0 );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_board->Add( newnet );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
// We'll get an OnBoardItemAdded callback from this to update our listbox
|
|
|
|
m_frame->OnModify();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::onRenameSelectedNet()
|
|
|
|
{
|
|
|
|
if( m_netsList->GetSelectedItemsCount() == 1 )
|
|
|
|
{
|
|
|
|
const LIST_ITEM* sel = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
|
|
|
|
|
|
|
|
if( sel->GetIsGroup() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
NETINFO_ITEM* net = sel->GetNet();
|
|
|
|
wxString fullNetName = net->GetNetname();
|
|
|
|
wxString netPath;
|
|
|
|
wxString shortNetName;
|
|
|
|
|
|
|
|
if( fullNetName.Contains( wxT( "/" ) ) )
|
|
|
|
{
|
|
|
|
netPath = fullNetName.BeforeLast( '/' ) + '/';
|
|
|
|
shortNetName = fullNetName.AfterLast( '/' );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
shortNetName = fullNetName;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString unescapedShortName = UnescapeString( shortNetName );
|
|
|
|
|
|
|
|
WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "Rename Net" ), unescapedShortName );
|
|
|
|
NETNAME_VALIDATOR validator( &unescapedShortName );
|
|
|
|
dlg.SetTextValidator( validator );
|
|
|
|
|
|
|
|
while( true )
|
|
|
|
{
|
|
|
|
if( dlg.ShowModal() != wxID_OK || dlg.GetValue() == unescapedShortName )
|
|
|
|
return;
|
|
|
|
|
|
|
|
unescapedShortName = dlg.GetValue();
|
|
|
|
|
|
|
|
if( unescapedShortName.IsEmpty() )
|
|
|
|
{
|
|
|
|
DisplayError( this, wxString::Format( _( "Net name cannot be empty." ),
|
|
|
|
unescapedShortName ) );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
shortNetName = EscapeString( unescapedShortName, CTX_NETNAME );
|
|
|
|
fullNetName = netPath + shortNetName;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( m_board->FindNet( shortNetName ) || m_board->FindNet( fullNetName ) )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ),
|
|
|
|
unescapedShortName ) );
|
|
|
|
unescapedShortName = wxEmptyString;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( BOARD_CONNECTED_ITEM* boardItem : m_frame->GetBoard()->AllConnectedItems() )
|
|
|
|
{
|
|
|
|
if( boardItem->GetNet() == net )
|
|
|
|
boardItem->SetFlags( CANDIDATE );
|
|
|
|
else
|
|
|
|
boardItem->ClearFlags( CANDIDATE );
|
|
|
|
}
|
|
|
|
|
|
|
|
// the changed name might require re-grouping. remove/re-insert is easier.
|
2025-03-27 21:26:37 +00:00
|
|
|
auto removed_item = m_dataModel->deleteItem( m_dataModel->findItem( net ) );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_board->Remove( net );
|
2024-03-04 23:00:26 +00:00
|
|
|
net->SetNetname( fullNetName );
|
2025-03-27 21:26:37 +00:00
|
|
|
m_board->Add( net );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
for( BOARD_CONNECTED_ITEM* boardItem : m_frame->GetBoard()->AllConnectedItems() )
|
|
|
|
{
|
|
|
|
if( boardItem->GetFlags() & CANDIDATE )
|
|
|
|
boardItem->SetNet( net );
|
|
|
|
}
|
|
|
|
|
|
|
|
buildNetsList();
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( std::optional<LIST_ITEM_ITER> r = m_dataModel->findItem( net ) )
|
2024-03-04 23:00:26 +00:00
|
|
|
m_netsList->Select( wxDataViewItem( r.value()->get() ) );
|
|
|
|
|
|
|
|
m_frame->OnModify();
|
|
|
|
|
|
|
|
// Currently only tracks and pads have netname annotations and need to be redrawn,
|
|
|
|
// but zones are likely to follow. Since we don't have a way to ask what is current,
|
|
|
|
// just refresh all items.
|
|
|
|
m_frame->GetCanvas()->GetView()->UpdateAllItems( KIGFX::REPAINT );
|
|
|
|
m_frame->GetCanvas()->Refresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::onDeleteSelectedNet()
|
|
|
|
{
|
|
|
|
if( !m_netsList->HasSelection() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
wxDataViewItemArray sel;
|
|
|
|
m_netsList->GetSelections( sel );
|
|
|
|
|
|
|
|
auto delete_one = [this]( const LIST_ITEM* i )
|
|
|
|
{
|
|
|
|
if( i->GetPadCount() == 0
|
|
|
|
|| IsOK( this, wxString::Format( _( "Net '%s' is in use. Delete anyway?" ),
|
|
|
|
i->GetNetName() ) ) )
|
|
|
|
{
|
|
|
|
// This is a bit hacky, but it will do for now, since this is the only path
|
|
|
|
// outside the netlist updater where you can remove a net from a BOARD.
|
|
|
|
int removedCode = i->GetNetCode();
|
|
|
|
|
|
|
|
m_frame->GetCanvas()->GetView()->UpdateAllItemsConditionally(
|
|
|
|
[removedCode]( KIGFX::VIEW_ITEM* aItem ) -> int
|
|
|
|
{
|
|
|
|
auto boardItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem );
|
|
|
|
|
|
|
|
if( boardItem && boardItem->GetNetCode() == removedCode )
|
|
|
|
return KIGFX::REPAINT;
|
|
|
|
|
|
|
|
EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( aItem );
|
|
|
|
|
|
|
|
if( text && text->HasTextVars() )
|
|
|
|
{
|
|
|
|
text->ClearRenderCache();
|
|
|
|
text->ClearBoundingBoxCache();
|
|
|
|
return KIGFX::GEOMETRY | KIGFX::REPAINT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
} );
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
m_board->Remove( i->GetNet() );
|
2024-03-04 23:00:26 +00:00
|
|
|
m_frame->OnModify();
|
|
|
|
|
|
|
|
// We'll get an OnBoardItemRemoved callback from this to update our listbox
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for( unsigned int i = 0; i < sel.GetCount(); ++i )
|
|
|
|
{
|
|
|
|
const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
|
|
|
|
|
|
|
|
if( ii->GetIsGroup() )
|
|
|
|
{
|
|
|
|
if( ii->ChildrenCount() != 0
|
|
|
|
&& IsOK( this, wxString::Format( _( "Delete all nets in group '%s'?" ),
|
|
|
|
ii->GetGroupName() ) ) )
|
|
|
|
{
|
|
|
|
// we can't be iterating the children container and deleting items from
|
|
|
|
// it at the same time. thus take a copy of it first.
|
|
|
|
std::vector<const LIST_ITEM*> children;
|
|
|
|
children.reserve( ii->ChildrenCount() );
|
|
|
|
std::copy( ii->ChildrenBegin(), ii->ChildrenEnd(), std::back_inserter( children ) );
|
|
|
|
|
|
|
|
for( const LIST_ITEM* c : children )
|
|
|
|
delete_one( c );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete_one( ii );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************************
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* Application-generated event handling
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* ***************************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::OnLanguageChangedImpl()
|
|
|
|
{
|
|
|
|
SaveSettings();
|
|
|
|
buildNetsList( true );
|
2025-03-27 21:26:37 +00:00
|
|
|
m_dataModel->updateAllItems();
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::onUnitsChanged( wxCommandEvent& event )
|
|
|
|
{
|
2025-03-27 21:26:37 +00:00
|
|
|
m_dataModel->updateAllItems();
|
2024-03-04 23:00:26 +00:00
|
|
|
event.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************************
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* Settings persistence
|
2024-07-21 10:49:18 -07:00
|
|
|
*
|
2024-03-04 23:00:26 +00:00
|
|
|
* ***************************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
void PCB_NET_INSPECTOR_PANEL::SaveSettings()
|
|
|
|
{
|
2025-03-17 11:04:27 +00:00
|
|
|
// Don't save settings if a board has not yet been loaded or the panel hasn't been displayed.
|
|
|
|
// Events fire while we set up the panel which overwrite the settings we haven't yet loaded.
|
|
|
|
bool displayed = false;
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
for( unsigned int ii = 0; ii < m_dataModel->columnCount() && !displayed; ++ii )
|
2025-03-17 11:04:27 +00:00
|
|
|
{
|
|
|
|
if( m_netsList->GetColumn( ii )->GetWidth() > 0 )
|
|
|
|
displayed = true;
|
|
|
|
}
|
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
if( !displayed || !m_boardLoaded || m_boardLoading )
|
2024-03-04 23:00:26 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
PROJECT_LOCAL_SETTINGS& localSettings = Pgm().GetSettingsManager().Prj().GetLocalSettings();
|
|
|
|
auto& cfg = localSettings.m_NetInspectorPanel;
|
|
|
|
|
|
|
|
// User-defined filters / grouping
|
|
|
|
cfg.filter_text = m_searchCtrl->GetValue();
|
2025-03-27 21:26:37 +00:00
|
|
|
cfg.filter_by_net_name = m_filterByNetName;
|
|
|
|
cfg.filter_by_netclass = m_filterByNetclass;
|
|
|
|
cfg.group_by_netclass = m_groupByNetclass;
|
|
|
|
cfg.group_by_constraint = m_groupByConstraint;
|
|
|
|
cfg.show_zero_pad_nets = m_showZeroPadNets;
|
|
|
|
cfg.show_unconnected_nets = m_showUnconnectedNets;
|
2025-02-10 20:57:51 +00:00
|
|
|
cfg.show_time_domain_details = m_showTimeDomainDetails;
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
// Grid sorting
|
|
|
|
wxDataViewColumn* sortingCol = m_netsList->GetSortingColumn();
|
|
|
|
cfg.sorting_column = sortingCol ? static_cast<int>( sortingCol->GetModelColumn() ) : -1;
|
|
|
|
cfg.sort_order_asc = sortingCol ? sortingCol->IsSortOrderAscending() : true;
|
|
|
|
|
|
|
|
// Column arrangement / sizes
|
2025-03-27 21:26:37 +00:00
|
|
|
cfg.col_order.resize( m_dataModel->columnCount() );
|
|
|
|
cfg.col_widths.resize( m_dataModel->columnCount() );
|
|
|
|
cfg.col_hidden.resize( m_dataModel->columnCount() );
|
2024-03-04 23:00:26 +00:00
|
|
|
|
2025-03-27 21:26:37 +00:00
|
|
|
for( unsigned int ii = 0; ii < m_dataModel->columnCount(); ++ii )
|
2024-03-04 23:00:26 +00:00
|
|
|
{
|
|
|
|
cfg.col_order[ii] = (int) m_netsList->GetColumn( ii )->GetModelColumn();
|
|
|
|
cfg.col_widths[ii] = m_netsList->GetColumn( ii )->GetWidth();
|
|
|
|
cfg.col_hidden[ii] = m_netsList->GetColumn( ii )->IsHidden();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expanded rows
|
|
|
|
cfg.expanded_rows.clear();
|
2025-03-27 21:26:37 +00:00
|
|
|
std::vector<std::pair<wxString, wxDataViewItem>> groupItems = m_dataModel->getGroupDataViewItems();
|
2024-03-04 23:00:26 +00:00
|
|
|
|
|
|
|
for( std::pair<wxString, wxDataViewItem>& item : groupItems )
|
|
|
|
{
|
|
|
|
if( m_netsList->IsExpanded( item.second ) )
|
|
|
|
cfg.expanded_rows.push_back( item.first );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Customer group rules
|
|
|
|
cfg.custom_group_rules.clear();
|
|
|
|
|
2024-05-24 12:23:27 +01:00
|
|
|
for( const std::unique_ptr<EDA_COMBINED_MATCHER>& rule : m_custom_group_rules )
|
|
|
|
cfg.custom_group_rules.push_back( rule->GetPattern() );
|
2024-03-04 23:00:26 +00:00
|
|
|
}
|