/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 CERN * @author Jon Evans * * 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, see . */ #include #include #include #include #include #include /** * Migrates buses using legacy multi-label joining behavior. * * In KiCad versions before 6.0, you were allowed to place multiple labels * on a given bus subgraph, and that would have the effect of making those * bus descriptions equivalent according to the bus vector number. * * For example, if the labels PCA[0..15], ADR[0.7], and BUS[5..10] are all * attached to the same subgraph, the intention is that there is connectivity * between PCA0 and ADR0, between PCA10 and BUS10, and between PCA5, ADR5, * and BUS5 (basically connect all the prefix names where the vector numbers * line up). * * This is no longer allowed, because it doesn't map well onto the new * bus groups feature and because it is confusing (the netlist will take on * one of the possible names and it's impossible to control which one is * used). * * This dialog identifies all of the subgraphs that have this behavior, * and corrects them by determining a new name for the subgraph and removing * all but one label. The name is determined by choosing a prefix and bus * vector notation that can capture all the attached buses. In the above * example, the result would need to have the vector notation [0..15] to * capture all of the attached buses, and the name could be any of PCA, ADR, * or BUS. We present a dialog to the user for them to select which name * they want to use. */ DIALOG_MIGRATE_BUSES::DIALOG_MIGRATE_BUSES( SCH_EDIT_FRAME* aParent ) : DIALOG_MIGRATE_BUSES_BASE( aParent ), m_frame( aParent ), m_selected_index( 0 ) { m_migration_list->Bind( wxEVT_LIST_ITEM_SELECTED, &DIALOG_MIGRATE_BUSES::onItemSelected, this ); m_btn_accept->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_MIGRATE_BUSES::onAcceptClicked, this ); loadGraphData(); updateUi(); aParent->GetToolManager()->RunAction( ACTIONS::zoomFitScreen, true ); } void DIALOG_MIGRATE_BUSES::loadGraphData() { m_items.clear(); auto subgraphs = m_frame->Schematic().ConnectionGraph()->GetBusesNeedingMigration(); for( auto subgraph : subgraphs ) { BUS_MIGRATION_STATUS status; status.subgraph = subgraph; status.approved = false; auto labels = subgraph->GetBusLabels(); wxASSERT( labels.size() > 1 ); for( auto label : labels ) status.labels.push_back( static_cast( label )->GetText() ); status.possible_labels = getProposedLabels( status.labels ); m_items.push_back( status ); } } void DIALOG_MIGRATE_BUSES::updateUi() { m_migration_list->DeleteAllItems(); m_migration_list->InsertColumn( 0, _( "Sheet" ) ); m_migration_list->InsertColumn( 1, _( "Conflicting Labels" ) ); m_migration_list->InsertColumn( 2, _( "New Label" ) ); m_migration_list->InsertColumn( 3, _( "Status" ) ); for( auto item : m_items ) { wxString old = item.labels[0]; for( unsigned j = 1; j < item.labels.size(); j++ ) old << wxT( ", " ) << item.labels[j]; auto i = m_migration_list->InsertItem( m_migration_list->GetItemCount(), wxEmptyString ); m_migration_list->SetItem( i, 0, item.subgraph->m_sheet.PathHumanReadable() ); m_migration_list->SetItem( i, 1, old ); m_migration_list->SetItem( i, 2, item.possible_labels[0] ); m_migration_list->SetItem( i, 3, "" ); } m_migration_list->Select( 0 ); m_migration_list->SetColumnWidth( 1, -1 ); } std::vector DIALOG_MIGRATE_BUSES::getProposedLabels( const std::vector& aLabelList ) { int lowest_start = INT_MAX; int highest_end = -1; int widest_bus = -1; SCH_CONNECTION conn( m_frame->Schematic().ConnectionGraph() ); for( const wxString& label : aLabelList ) { conn.ConfigureFromLabel( label ); int start = conn.VectorStart(); int end = conn.VectorEnd(); if( start < lowest_start ) lowest_start = start; if( end > highest_end ) highest_end = end; if( end - start + 1 > widest_bus ) widest_bus = end - start + 1; } std::vector proposals; for( const wxString& label : aLabelList ) { conn.ConfigureFromLabel( label ); wxString proposal = conn.VectorPrefix(); proposal << wxT( "[" ) << highest_end << wxT( ".." ) << lowest_start << wxT( "]" ); proposals.push_back( proposal ); } return proposals; } void DIALOG_MIGRATE_BUSES::onItemSelected( wxListEvent& aEvent ) { unsigned sel = aEvent.GetIndex(); wxASSERT( sel < m_items.size() ); m_selected_index = sel; auto subgraph = m_items[sel].subgraph; auto sheet = subgraph->m_sheet; auto driver = subgraph->m_driver; const SCH_SHEET_PATH& current = m_frame->GetCurrentSheet(); if( sheet != current ) { sheet.UpdateAllScreenReferences(); m_frame->Schematic().SetCurrentSheet( sheet ); m_frame->TestDanglingEnds(); } auto pos = driver->GetPosition(); m_frame->GetCanvas()->GetViewControls()->SetCrossHairCursorPosition( pos, false ); m_frame->RedrawScreen( pos, false ); m_cb_new_name->Clear(); for( const wxString& option : m_items[sel].possible_labels ) m_cb_new_name->Append( option ); m_cb_new_name->Select( 0 ); } void DIALOG_MIGRATE_BUSES::onAcceptClicked( wxCommandEvent& aEvent ) { wxASSERT( m_selected_index < m_items.size() ); auto sel = m_selected_index; m_items[sel].approved_label = m_cb_new_name->GetStringSelection(); m_items[sel].approved = true; auto labels = m_items[sel].subgraph->GetBusLabels(); for( auto label : labels ) static_cast( label )->SetText( m_items[sel].approved_label ); m_migration_list->SetItem( sel, 2, m_items[sel].approved_label ); m_migration_list->SetItem( sel, 3, _( "Updated" ) ); if( sel < m_items.size() - 1 ) { m_migration_list->Select( sel + 1 ); } m_frame->GetCanvas()->Refresh(); }