/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Oleg Endo * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * * 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 2 * 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, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct DIALOG_SELECT_NET_FROM_LIST::COLUMN_ID { int col_num; wxString display_name; operator int() const { return col_num; } }; #define def_col( c, num, name ) \ const DIALOG_SELECT_NET_FROM_LIST::COLUMN_ID DIALOG_SELECT_NET_FROM_LIST::c = { num, name } def_col( COLUMN_NET, 0, _( "Net" ) ); def_col( COLUMN_NAME, 1, _( "Name" ) ); def_col( COLUMN_PAD_COUNT, 2, _( "Pad Count" ) ); def_col( COLUMN_VIA_COUNT, 3, _( "Via Count" ) ); def_col( COLUMN_BOARD_LENGTH, 4, _( "Board Length" ) ); def_col( COLUMN_CHIP_LENGTH, 5, _( "Die Length" ) ); def_col( COLUMN_TOTAL_LENGTH, 6, _( "Length" ) ); #undef def_col struct DIALOG_SELECT_NET_FROM_LIST::LIST_ITEM { LIST_ITEM( NETINFO_ITEM* aNet ) : m_net( aNet ) { } NETINFO_ITEM* m_net; int m_pad_count = 0; int m_via_count = 0; int m_board_wire_length = 0; int m_chip_wire_length = 0; int m_total_length = 0; }; struct DIALOG_SELECT_NET_FROM_LIST::LIST_ITEM_NET_CMP_LESS { const LIST_ITEM* m_base_ptr; LIST_ITEM_NET_CMP_LESS( const std::vector& container ) : m_base_ptr( container.data() ) { } bool operator()( unsigned int a, unsigned int b ) const { return m_base_ptr[a].m_net < m_base_ptr[b].m_net; } bool operator()( unsigned int a, NETINFO_ITEM* b ) const { return m_base_ptr[a].m_net < b; } bool operator()( NETINFO_ITEM* a, unsigned int b ) const { return a < m_base_ptr[b].m_net; } }; struct DIALOG_SELECT_NET_FROM_LIST::ROW_DESC { int row_num = -1; decltype( DIALOG_SELECT_NET_FROM_LIST::m_list_items )::iterator by_row; decltype( DIALOG_SELECT_NET_FROM_LIST::m_list_items_by_net )::iterator by_net; bool valid() const { return row_num != -1; } explicit operator bool() const { return valid(); } }; DIALOG_SELECT_NET_FROM_LIST::DIALOG_SELECT_NET_FROM_LIST( PCB_EDIT_FRAME* aParent, const SETTINGS& aSettings ) : DIALOG_SELECT_NET_FROM_LIST_BASE( aParent ), m_frame( aParent ) { m_brd = aParent->GetBoard(); m_wasSelected = false; #define ADD_COL( name, flag, align ) m_netsList->AppendTextColumn( name, flag, 0, align, \ wxDATAVIEW_COL_RESIZABLE ); ADD_COL( COLUMN_NET.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_LEFT ); ADD_COL( COLUMN_NAME.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_LEFT ); ADD_COL( COLUMN_PAD_COUNT.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); ADD_COL( COLUMN_VIA_COUNT.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); ADD_COL( COLUMN_BOARD_LENGTH.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); ADD_COL( COLUMN_CHIP_LENGTH.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); ADD_COL( COLUMN_TOTAL_LENGTH.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); // The fact that we're a list should keep the control from reserving space for the // expander buttons... but it doesn't. Fix by forcing the indent to 0. m_netsList->SetIndent( 0 ); m_textCtrlFilter->SetValue( aSettings.filter_string ); m_cbShowZeroPad->SetValue( aSettings.show_zero_pad_nets ); buildNetsList(); adjustListColumns(); m_addNet->SetBitmap( KiBitmap( small_plus_xpm ) ); m_renameNet->SetBitmap( KiBitmap( small_edit_xpm ) ); m_deleteNet->SetBitmap( KiBitmap( trash_xpm ) ); m_sdbSizerOK->SetDefault(); FinishDialogSettings(); #define connect_event( e, f ) \ m_frame->Connect( e, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST::f ), nullptr, this ) connect_event( wxEVT_CLOSE_WINDOW, onParentWindowClosed ); connect_event( UNITS_CHANGED, onUnitsChanged ); connect_event( BOARD_CHANGED, onBoardChanged ); #undef connect_event if( m_brd != nullptr ) m_brd->AddListener( this ); } DIALOG_SELECT_NET_FROM_LIST::~DIALOG_SELECT_NET_FROM_LIST() { #define disconnect_event( e, f ) \ m_frame->Disconnect( e, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST::f ), nullptr, this ) disconnect_event( wxEVT_CLOSE_WINDOW, onParentWindowClosed ); disconnect_event( UNITS_CHANGED, onUnitsChanged ); disconnect_event( BOARD_CHANGED, onBoardChanged ); #undef disconnect_event if( m_brd != nullptr ) m_brd->RemoveListener( this ); m_frame->GetCanvas()->SetFocus(); } DIALOG_SELECT_NET_FROM_LIST::SETTINGS DIALOG_SELECT_NET_FROM_LIST::Settings() const { return { m_textCtrlFilter->GetValue(), m_cbShowZeroPad->IsChecked() }; } void DIALOG_SELECT_NET_FROM_LIST::onParentWindowClosed( wxCommandEvent& event ) { Close(); event.Skip(); } void DIALOG_SELECT_NET_FROM_LIST::onUnitsChanged( wxCommandEvent& event ) { this->m_units = m_frame->GetUserUnits(); buildNetsList(); m_netsList->Refresh(); event.Skip(); } void DIALOG_SELECT_NET_FROM_LIST::onBoardChanged( wxCommandEvent& event ) { if( m_brd != nullptr ) m_brd->RemoveListener( this ); m_brd = m_frame->GetBoard(); if( m_brd != nullptr ) m_brd->AddListener( this ); m_wasSelected = false; buildNetsList(); m_netsList->Refresh(); event.Skip(); } bool DIALOG_SELECT_NET_FROM_LIST::netFilterMatches( NETINFO_ITEM* aNet ) const { // Note: the filtering is case insensitive. if( m_netFilter.GetPattern().IsEmpty() ) return true; return m_netFilter.Find( UnescapeString( aNet->GetNetname() ).Upper() ) != EDA_PATTERN_NOT_FOUND; } 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 DIALOG_SELECT_NET_FROM_LIST::relevantConnectivityItems() const { // pre-filter the connectivity items and sort them by netcode. // this avoids quadratic runtime when building the whole net list and // calculating the total length for each net. const auto type_bits = std::bitset() .set( PCB_TRACE_T ) .set( PCB_VIA_T ) .set( PCB_PAD_T ); std::vector cn_items; cn_items.reserve( 1024 ); for( CN_ITEM* cn_item : m_brd->GetConnectivity()->GetConnectivityAlgo()->ItemList() ) { if( cn_item->Valid() && type_bits[cn_item->Parent()->Type()] ) cn_items.push_back( cn_item ); } std::sort( cn_items.begin(), cn_items.end(), NETCODE_CMP_LESS() ); return cn_items; } DIALOG_SELECT_NET_FROM_LIST::ROW_DESC DIALOG_SELECT_NET_FROM_LIST::findRow( int aNetCode ) { return findRow( m_brd->FindNet( aNetCode ) ); } DIALOG_SELECT_NET_FROM_LIST::ROW_DESC DIALOG_SELECT_NET_FROM_LIST::findRow( NETINFO_ITEM* aNet ) { auto i = std::lower_bound( m_list_items_by_net.begin(), m_list_items_by_net.end(), aNet, LIST_ITEM_NET_CMP_LESS( m_list_items ) ); if( i != m_list_items_by_net.end() && m_list_items[*i].m_net == aNet ) return { static_cast( *i ), m_list_items.begin() + *i, i }; else return {}; } void DIALOG_SELECT_NET_FROM_LIST::deleteRow( const ROW_DESC& aRow ) { if( !aRow ) return; m_netsList->DeleteItem( aRow.row_num ); m_list_items.erase( aRow.by_row ); std::iter_swap( aRow.by_net, m_list_items_by_net.end() - 1 ); m_list_items_by_net.pop_back(); std::sort( m_list_items_by_net.begin(), m_list_items_by_net.end(), LIST_ITEM_NET_CMP_LESS( m_list_items ) ); } void DIALOG_SELECT_NET_FROM_LIST::setValue( const ROW_DESC& aRow, const COLUMN_ID& aCol, wxString aVal ) { if( aRow ) m_netsList->SetValue( aVal, aRow.row_num, aCol.col_num ); } wxString DIALOG_SELECT_NET_FROM_LIST::formatNetCode( const NETINFO_ITEM* aNet ) const { return wxString::Format( "%.3d", aNet->GetNet() ); } wxString DIALOG_SELECT_NET_FROM_LIST::formatNetName( const NETINFO_ITEM* aNet ) const { return UnescapeString( aNet->GetNetname() ); } wxString DIALOG_SELECT_NET_FROM_LIST::formatCount( unsigned int aValue ) const { return wxString::Format( "%u", aValue ); } wxString DIALOG_SELECT_NET_FROM_LIST::formatLength( int aValue ) const { return MessageTextFromValue( GetUserUnits(), aValue ); } void DIALOG_SELECT_NET_FROM_LIST::OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* aBoardItem ) { if( NETINFO_ITEM* net = dynamic_cast( aBoardItem ) ) { // a new net has been added to the board. add it to our list if it // passes the netname filter test. if( netFilterMatches( net ) ) { m_list_items.emplace_back( net ); m_list_items_by_net.push_back( m_list_items.size() - 1 ); std::sort( m_list_items_by_net.begin(), m_list_items_by_net.end(), LIST_ITEM_NET_CMP_LESS( m_list_items ) ); LIST_ITEM& new_i = m_list_items.back(); new_i.m_pad_count = m_brd->GetNodesCount( net->GetNet() ); wxVector new_row( 7 ); new_row[COLUMN_NET] = formatNetCode( net ); new_row[COLUMN_NAME] = formatNetName( net ); new_row[COLUMN_PAD_COUNT] = formatCount( new_i.m_pad_count ); new_row[COLUMN_VIA_COUNT] = formatCount( new_i.m_via_count ); new_row[COLUMN_BOARD_LENGTH] = formatLength( new_i.m_board_wire_length ); new_row[COLUMN_CHIP_LENGTH] = formatLength( new_i.m_chip_wire_length ); new_row[COLUMN_TOTAL_LENGTH] = formatLength( new_i.m_total_length ); m_netsList->AppendItem( new_row ); } return; } else if( BOARD_CONNECTED_ITEM* i = dynamic_cast( aBoardItem ) ) { const ROW_DESC& r = findRow( i->GetNet() ); if( r ) { // try to handle frequent operations quickly. if( TRACK* track = dynamic_cast( i ) ) { int len = track->GetLength(); r.by_row->m_board_wire_length += len; r.by_row->m_total_length += len; setValue( r, COLUMN_BOARD_LENGTH, formatLength( r.by_row->m_board_wire_length ) ); setValue( r, COLUMN_TOTAL_LENGTH, formatLength( r.by_row->m_total_length ) ); if( track->Type() == PCB_VIA_T ) { r.by_row->m_via_count += 1; setValue( r, COLUMN_VIA_COUNT, formatCount( r.by_row->m_via_count ) ); } return; } } // resort to generic slower net update otherwise. updateNet( i->GetNet() ); } } void DIALOG_SELECT_NET_FROM_LIST::OnBoardItemRemoved( BOARD& aBoard, BOARD_ITEM* aBoardItem ) { if( NETINFO_ITEM* net = dynamic_cast( aBoardItem ) ) { deleteRow( findRow( net ) ); return; } else if( MODULE* mod = dynamic_cast( aBoardItem ) ) { for( const D_PAD* pad : mod->Pads() ) { const ROW_DESC& r = findRow( pad->GetNet() ); if( r ) { r.by_row->m_pad_count -= 1; if( r.by_row->m_pad_count == 0 && !m_cbShowZeroPad->IsChecked() ) deleteRow( r ); else setValue( r, COLUMN_PAD_COUNT, formatCount( r.by_row->m_pad_count ) ); } } } else if( BOARD_CONNECTED_ITEM* i = dynamic_cast( aBoardItem ) ) { const ROW_DESC& r = findRow( i->GetNet() ); if( r ) { // try to handle frequent operations quickly. if( TRACK* track = dynamic_cast( i ) ) { int len = track->GetLength(); r.by_row->m_board_wire_length -= len; r.by_row->m_total_length -= len; setValue( r, COLUMN_BOARD_LENGTH, formatLength( r.by_row->m_board_wire_length ) ); setValue( r, COLUMN_TOTAL_LENGTH, formatLength( r.by_row->m_total_length ) ); if( track->Type() == PCB_VIA_T ) { r.by_row->m_via_count -= 1; setValue( r, COLUMN_VIA_COUNT, formatCount( r.by_row->m_via_count ) ); } return; } // resort to generic slower net update otherwise. updateNet( i->GetNet() ); } } } void DIALOG_SELECT_NET_FROM_LIST::OnBoardItemChanged( BOARD& aBoard, BOARD_ITEM* aBoardItem ) { if( dynamic_cast( aBoardItem ) != nullptr || dynamic_cast( aBoardItem ) != nullptr ) { buildNetsList(); m_netsList->Refresh(); } } void DIALOG_SELECT_NET_FROM_LIST::OnBoardHighlightNetChanged( BOARD& aBoard ) { if( !m_brd->IsHighLightNetON() ) m_netsList->UnselectAll(); else if( !m_brd->GetHighLightNetCodes().empty() ) HighlightNet( m_brd->FindNet( *m_brd->GetHighLightNetCodes().begin() ) ); } void DIALOG_SELECT_NET_FROM_LIST::OnBoardNetSettingsChanged( BOARD& aBoard ) { buildNetsList(); m_netsList->Refresh(); } void DIALOG_SELECT_NET_FROM_LIST::updateNet( NETINFO_ITEM* aNet ) { // something for the specified net has changed, update that row. if( !netFilterMatches( aNet ) ) return; // if the net had no pads before, it might not be in the displayed list yet. // if it had pads and now doesn't anymore, we might need to remove it from the list. auto cur_net_row = findRow( aNet ); const unsigned int node_count = m_brd->GetNodesCount( aNet->GetNet() ); if( node_count == 0 && !m_cbShowZeroPad->IsChecked() ) { deleteRow( cur_net_row ); return; } std::vector all_cn_items = relevantConnectivityItems(); LIST_ITEM list_item( aNet ); list_item.m_pad_count = node_count; const auto cn_items = std::equal_range( all_cn_items.begin(), all_cn_items.end(), aNet->GetNet(), NETCODE_CMP_LESS() ); for( auto i = cn_items.first; i != cn_items.second; ++i ) { BOARD_CONNECTED_ITEM* item = ( *i )->Parent(); if( item->Type() == PCB_PAD_T ) list_item.m_chip_wire_length += static_cast( item )->GetPadToDieLength(); else if( TRACK* track = dynamic_cast( item ) ) { list_item.m_board_wire_length += track->GetLength(); if( item->Type() == PCB_VIA_T ) list_item.m_via_count += 1; } } list_item.m_total_length = list_item.m_board_wire_length + list_item.m_chip_wire_length; if( !cur_net_row ) { m_list_items.push_back( list_item ); m_list_items_by_net.push_back( m_list_items.size() - 1 ); std::sort( m_list_items_by_net.begin(), m_list_items_by_net.end(), LIST_ITEM_NET_CMP_LESS( m_list_items ) ); wxVector new_row( 7 ); new_row[COLUMN_NET] = formatNetCode( aNet ); new_row[COLUMN_NAME] = formatNetName( aNet ); new_row[COLUMN_PAD_COUNT] = formatCount( list_item.m_pad_count ); new_row[COLUMN_VIA_COUNT] = formatCount( list_item.m_via_count ); new_row[COLUMN_BOARD_LENGTH] = formatLength( list_item.m_board_wire_length ); new_row[COLUMN_CHIP_LENGTH] = formatLength( list_item.m_chip_wire_length ); new_row[COLUMN_TOTAL_LENGTH] = formatLength( list_item.m_total_length ); m_netsList->AppendItem( new_row ); } else { *cur_net_row.by_row = list_item; setValue( cur_net_row, COLUMN_PAD_COUNT, formatCount( list_item.m_pad_count ) ); setValue( cur_net_row, COLUMN_VIA_COUNT, formatCount( list_item.m_via_count ) ); setValue( cur_net_row, COLUMN_BOARD_LENGTH, formatLength( list_item.m_board_wire_length ) ); setValue( cur_net_row, COLUMN_CHIP_LENGTH, formatLength( list_item.m_chip_wire_length ) ); setValue( cur_net_row, COLUMN_TOTAL_LENGTH, formatLength( list_item.m_total_length ) ); } } void DIALOG_SELECT_NET_FROM_LIST::buildNetsList() { // when rebuilding the netlist, try to keep the row selection const int prev_selected_row = m_netsList->GetSelectedRow(); const int prev_selected_netcode = prev_selected_row >= 0 ? m_list_items[prev_selected_row].m_net->GetNet() : -1; m_netsList->DeleteAllItems(); m_list_items.clear(); std::vector prefiltered_cn_items = relevantConnectivityItems(); // collect all nets which pass the filter string. struct NET_INFO { int netcode; NETINFO_ITEM* net; unsigned int pad_count; }; struct NET_INFO_CMP_LESS { bool operator()( const NET_INFO& a, const NET_INFO& b ) const { return a.netcode < b.netcode; } bool operator()( const NET_INFO& a, int b ) const { return a.netcode < b; } bool operator()( int a, const NET_INFO& b ) const { return a < b.netcode; } }; std::vector nets; nets.reserve( m_brd->GetNetInfo().NetsByNetcode().size() ); for( const std::pair& ni : m_brd->GetNetInfo().NetsByNetcode() ) { if( netFilterMatches( ni.second ) ) nets.emplace_back( NET_INFO{ ni.first, ni.second, 0 } ); } // count the pads for each net. since the nets are sorted by netcode // this way around is faster than using counting pads for each net. for( MODULE* mod : m_brd->Modules() ) { for( D_PAD* pad : mod->Pads() ) { auto i = std::lower_bound( nets.begin(), nets.end(), pad->GetNetCode(), NET_INFO_CMP_LESS() ); if( i != nets.end() && i->netcode == pad->GetNetCode() ) i->pad_count += 1; } } for( NET_INFO& ni : nets ) { if( !m_cbShowZeroPad->IsChecked() && ni.pad_count == 0 ) continue; m_list_items.emplace_back( ni.net ); LIST_ITEM& list_item = m_list_items.back(); const auto cn_items = std::equal_range( prefiltered_cn_items.begin(), prefiltered_cn_items.end(), ni.netcode, NETCODE_CMP_LESS() ); for( auto i = cn_items.first; i != cn_items.second; ++i ) { BOARD_CONNECTED_ITEM* item = ( *i )->Parent(); if( item->Type() == PCB_PAD_T ) list_item.m_chip_wire_length += static_cast( item )->GetPadToDieLength(); else if( TRACK* track = dynamic_cast( item ) ) { list_item.m_board_wire_length += track->GetLength(); if( track->Type() == PCB_VIA_T ) list_item.m_via_count += 1; } } list_item.m_pad_count = ni.pad_count; list_item.m_total_length = list_item.m_board_wire_length + list_item.m_chip_wire_length; } wxVector dataLine; dataLine.resize( 7 ); for( LIST_ITEM& i : m_list_items ) { dataLine[COLUMN_NET] = formatNetCode( i.m_net ); dataLine[COLUMN_NAME] = formatNetName( i.m_net ); dataLine[COLUMN_PAD_COUNT] = formatCount( i.m_pad_count ); dataLine[COLUMN_VIA_COUNT] = formatCount( i.m_via_count ); dataLine[COLUMN_BOARD_LENGTH] = formatLength( i.m_board_wire_length ); dataLine[COLUMN_CHIP_LENGTH] = formatLength( i.m_chip_wire_length ); dataLine[COLUMN_TOTAL_LENGTH] = formatLength( i.m_total_length ); m_netsList->AppendItem( dataLine ); } m_list_items_by_net.clear(); m_list_items_by_net.reserve( m_list_items.size() ); for( unsigned int i = 0; i < m_list_items.size(); ++i ) m_list_items_by_net.push_back( i ); std::sort( m_list_items_by_net.begin(), m_list_items_by_net.end(), LIST_ITEM_NET_CMP_LESS( m_list_items ) ); if( prev_selected_netcode == -1 ) m_wasSelected = false; else { const ROW_DESC& r = findRow( prev_selected_netcode ); if( r ) { m_selection = r.by_row->m_net->GetNetname(); m_wasSelected = true; wxDataViewItem i = m_netsList->RowToItem( r.row_num ); m_netsList->Select( i ); m_netsList->EnsureVisible( i ); } } } void DIALOG_SELECT_NET_FROM_LIST::HighlightNet( NETINFO_ITEM* aNet ) { const ROW_DESC& r = findRow( aNet ); if( r ) { wxDataViewItem i = m_netsList->RowToItem( r.row_num ); m_netsList->Select( i ); m_netsList->EnsureVisible( i ); } else m_netsList->UnselectAll(); } void DIALOG_SELECT_NET_FROM_LIST::highlightNetOnBoard( NETINFO_ITEM* aNet ) const { int netCode = aNet != nullptr ? aNet->GetNet() : -1; KIGFX::RENDER_SETTINGS *render = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings(); render->SetHighlight( netCode >= 0, netCode ); m_frame->GetCanvas()->GetView()->UpdateAllLayersColor(); m_frame->GetCanvas()->Refresh(); } void DIALOG_SELECT_NET_FROM_LIST::onFilterChange( wxCommandEvent& event ) { m_netFilter.SetPattern( m_textCtrlFilter->GetValue().Upper() ); buildNetsList(); } void DIALOG_SELECT_NET_FROM_LIST::onSelChanged( wxDataViewEvent& ) { int selected_row = m_netsList->GetSelectedRow(); if( selected_row >= 0 && selected_row < static_cast( m_list_items.size() ) ) { NETINFO_ITEM* net = m_list_items[selected_row].m_net; m_selection = net->GetNetname(); m_wasSelected = true; highlightNetOnBoard( net ); return; } highlightNetOnBoard( nullptr ); m_wasSelected = false; } void DIALOG_SELECT_NET_FROM_LIST::adjustListColumns() { int w0, w1, w2, w3, w4, w5, w6; /** * Calculating optimal width of the first (Net) and the last (Pad Count) columns. * That width must be enough to fit column header label and be not less than width of * four chars (0000). */ wxClientDC dc( GetParent() ); int h, minw, minw_col0; dc.GetTextExtent( COLUMN_NET.display_name, &w0, &h ); dc.GetTextExtent( COLUMN_PAD_COUNT.display_name, &w2, &h ); dc.GetTextExtent( COLUMN_VIA_COUNT.display_name, &w3, &h ); dc.GetTextExtent( COLUMN_BOARD_LENGTH.display_name, &w4, &h ); dc.GetTextExtent( COLUMN_CHIP_LENGTH.display_name, &w5, &h ); dc.GetTextExtent( COLUMN_TOTAL_LENGTH.display_name, &w6, &h ); dc.GetTextExtent( "00000,000 mm", &minw, &h ); dc.GetTextExtent( "00000", &minw_col0, &h ); // Considering left and right margins. // For wxRenderGeneric it is 5px. w0 = std::max( w0 + 10, minw_col0 ); w2 = w2 + 10; w3 = w3 + 10; w4 = std::max( w4 + 10, minw ); w5 = std::max( w5 + 10, minw ); w6 = std::max( w6 + 10, minw ); m_netsList->GetColumn( 0 )->SetWidth( w0 ); m_netsList->GetColumn( 2 )->SetWidth( w2 ); m_netsList->GetColumn( 3 )->SetWidth( w3 ); m_netsList->GetColumn( 4 )->SetWidth( w4 ); m_netsList->GetColumn( 5 )->SetWidth( w5 ); m_netsList->GetColumn( 6 )->SetWidth( w6 ); // At resizing of the list the width of middle column (Net Names) changes only. int width = m_netsList->GetClientSize().x - 24; w1 = width - w0 - w2 - w3 - w4 - w5 - w6; // Column 1 (net names) need a minimal width to display net names dc.GetTextExtent( "MMMMMMMMMMMMMMMM", &minw, &h ); w1 = std::max( w1, minw ); m_netsList->GetColumn( 1 )->SetWidth( w1 ); m_netsList->Refresh(); } void DIALOG_SELECT_NET_FROM_LIST::onListSize( wxSizeEvent& aEvent ) { aEvent.Skip(); adjustListColumns(); } bool DIALOG_SELECT_NET_FROM_LIST::GetNetName( wxString& aName ) const { aName = m_selection; return m_wasSelected; } void DIALOG_SELECT_NET_FROM_LIST::onAddNet( wxCommandEvent& aEvent ) { 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(); if( m_brd->FindNet( newNetName ) ) { DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ), newNetName ) ); newNetName = wxEmptyString; } else { break; } } NETINFO_ITEM *newnet = new NETINFO_ITEM( m_brd, dlg.GetValue(), 0 ); m_brd->Add( newnet ); m_frame->OnModify(); // We'll get an OnBoardItemChanged callback from this to update our listbox } void DIALOG_SELECT_NET_FROM_LIST::onRenameNet( wxCommandEvent& aEvent ) { int selected_row = m_netsList->GetSelectedRow(); if( selected_row >= 0 && selected_row < static_cast( m_list_items.size() ) ) { NETINFO_ITEM* net = m_list_items[selected_row].m_net; wxString fullNetName = net->GetNetname(); wxString netPath; wxString shortNetName; if( fullNetName.Contains( "/" ) ) { 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; if( m_brd->FindNet( shortNetName ) || m_brd->FindNet( fullNetName ) ) { DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ), unescapedShortName ) ); unescapedShortName = wxEmptyString; } else { break; } } net->SetNetname( fullNetName ); m_frame->OnModify(); buildNetsList(); m_netsList->Refresh(); // 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 DIALOG_SELECT_NET_FROM_LIST::onDeleteNet( wxCommandEvent& aEvent ) { int selected_row = m_netsList->GetSelectedRow(); if( selected_row >= 0 && selected_row < static_cast( m_list_items.size() ) ) { NETINFO_ITEM* net = m_list_items[selected_row].m_net; if( m_list_items[selected_row].m_pad_count > 0 ) { if( !IsOK( this, _( "Net is in use. Delete anyway?" ) ) ) return; } m_brd->Remove( net ); m_frame->OnModify(); // We'll get an OnBoardItemChanged callback from this to update our listbox } } void DIALOG_SELECT_NET_FROM_LIST::onReport( wxCommandEvent& aEvent ) { wxFileDialog dlg( this, _( "Report file" ), "", "", _( "Report file" ) + AddFileExtListToFilter( { "csv" } ), wxFD_SAVE ); if( dlg.ShowModal() == wxID_CANCEL ) return; wxTextFile f( dlg.GetPath() ); f.Create(); int rows = m_netsList->GetItemCount(); wxString txt; // Print Header: txt.Printf( "\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";", _( "Net Id" ), _( "Net name" ), _( "Pad count" ), _( "Via count" ), _( "Board length" ), _( "Die length" ), _( "Net length" ) ); f.AddLine( txt ); // Print list of nets: for( int row = 1; row < rows; row++ ) { txt.Printf( "%s;\"%s\";%s;%s;%s;%s;%s;", m_netsList->GetTextValue( row, COLUMN_NET ), m_netsList->GetTextValue( row, COLUMN_NAME ), m_netsList->GetTextValue( row, COLUMN_PAD_COUNT ), m_netsList->GetTextValue( row, COLUMN_VIA_COUNT ), m_netsList->GetTextValue( row, COLUMN_BOARD_LENGTH ), m_netsList->GetTextValue( row, COLUMN_CHIP_LENGTH ), m_netsList->GetTextValue( row, COLUMN_TOTAL_LENGTH ) ); f.AddLine( txt ); } f.Write(); f.Close(); }