mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2024-11-24 00:34:47 +00:00
a6923b3de3
The sheet list is returned as a copy of the cached list rather than a reference to prevent external code from changing the list. While not as performant, it eliminates the risk of the sheet list being altered in ways that could break the schematic. The sheet list should only be updated by calling SCHEMATIC::RefreshHierarchy() when any appropriate sheet changes are made. Note to developers: there is something inherently different about how the QA tests are loading and handling schematics versus the schematic editor. Using the cached sheet list for the SCHEMATIC object will cause some QA test to fail. This is why SCHEMATIC::Hierarchy() has not replaced SCHEMATIC::BuildSheetListSortedByPageNumbers() everywhere.
850 lines
28 KiB
C++
850 lines
28 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2020-2021 CERN
|
|
* Copyright (C) 2021-2023, 2024 KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* @author Wayne Stambaugh <stambaughw@gmail.com>
|
|
*
|
|
* 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 <algorithm>
|
|
|
|
#include <bitmaps.h>
|
|
#include <connection_graph.h>
|
|
#include <string_utils.h> // WildCompareString
|
|
#include <kiway.h>
|
|
#include <refdes_utils.h>
|
|
#include <core/kicad_algo.h>
|
|
#include <dialog_change_symbols.h>
|
|
#include <sch_symbol.h>
|
|
#include <sch_edit_frame.h>
|
|
#include <sch_screen.h>
|
|
#include <schematic.h>
|
|
#include <template_fieldnames.h>
|
|
#include <widgets/wx_html_report_panel.h>
|
|
#include <widgets/std_bitmap_button.h>
|
|
#include <sch_commit.h>
|
|
|
|
bool g_selectRefDes = false;
|
|
bool g_selectValue = false;
|
|
// { change, update }
|
|
bool g_removeExtraFields[2] = { false, false };
|
|
bool g_resetEmptyFields[2] = { false, false };
|
|
bool g_resetFieldText[2] = { true, true };
|
|
bool g_resetFieldVisibilities[2] = { true, false };
|
|
bool g_resetFieldEffects[2] = { true, false };
|
|
bool g_resetFieldPositions[2] = { true, false };
|
|
bool g_resetAttributes[2] = { true, false };
|
|
bool g_resetCustomPower[2] = { false, false };
|
|
|
|
|
|
DIALOG_CHANGE_SYMBOLS::DIALOG_CHANGE_SYMBOLS( SCH_EDIT_FRAME* aParent, SCH_SYMBOL* aSymbol,
|
|
MODE aMode ) :
|
|
DIALOG_CHANGE_SYMBOLS_BASE( aParent ),
|
|
m_symbol( aSymbol),
|
|
m_mode( aMode )
|
|
{
|
|
wxASSERT( aParent );
|
|
|
|
if( m_mode == MODE::UPDATE )
|
|
{
|
|
m_newIdSizer->Show( false );
|
|
}
|
|
else
|
|
{
|
|
m_matchAll->SetLabel( _( "Change all symbols in schematic" ) );
|
|
SetTitle( _( "Change Symbols" ) );
|
|
m_matchSizer->FindItem( m_matchAll )->Show( false );
|
|
}
|
|
|
|
if( m_symbol )
|
|
{
|
|
SCH_SHEET_PATH* currentSheet = &aParent->Schematic().CurrentSheet();
|
|
|
|
if( m_mode == MODE::CHANGE )
|
|
m_matchBySelection->SetLabel( _( "Change selected symbol(s)" ) );
|
|
|
|
m_newId->ChangeValue( UnescapeString( m_symbol->GetLibId().Format() ) );
|
|
m_specifiedReference->ChangeValue( m_symbol->GetRef( currentSheet ) );
|
|
m_specifiedValue->ChangeValue( UnescapeString( m_symbol->GetField( VALUE_FIELD )->GetText() ) );
|
|
m_specifiedId->ChangeValue( UnescapeString( m_symbol->GetLibId().Format() ) );
|
|
}
|
|
else
|
|
{
|
|
m_matchSizer->FindItem( m_matchBySelection )->Show( false );
|
|
}
|
|
|
|
m_matchIdBrowserButton->SetBitmap( KiBitmapBundle( BITMAPS::small_library ) );
|
|
m_newIdBrowserButton->SetBitmap( KiBitmapBundle( BITMAPS::small_library ) );
|
|
|
|
if( m_mode == MODE::CHANGE )
|
|
{
|
|
m_matchByReference->SetLabel( _( "Change symbols matching reference designator:" ) );
|
|
m_matchByValue->SetLabel( _( "Change symbols matching value:" ) );
|
|
m_matchById->SetLabel( _( "Change symbols matching library identifier:" ) );
|
|
}
|
|
|
|
m_matchSizer->SetEmptyCellSize( wxSize( 0, 0 ) );
|
|
m_matchSizer->Layout();
|
|
|
|
for( int i = 0; i < MANDATORY_FIELDS; ++i )
|
|
{
|
|
m_fieldsBox->Append( TEMPLATE_FIELDNAME::GetDefaultFieldName( i, DO_TRANSLATE ) );
|
|
|
|
if( i == REFERENCE_FIELD )
|
|
m_fieldsBox->Check( i, g_selectRefDes );
|
|
else if( i == VALUE_FIELD )
|
|
m_fieldsBox->Check( i, g_selectValue );
|
|
else
|
|
m_fieldsBox->Check( i, true );
|
|
}
|
|
|
|
m_messagePanel->SetLazyUpdate( true );
|
|
m_messagePanel->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
|
|
|
|
if( aSymbol && aSymbol->IsSelected() )
|
|
{
|
|
m_matchBySelection->SetValue( true );
|
|
}
|
|
else
|
|
{
|
|
if( aMode == MODE::UPDATE )
|
|
m_matchAll->SetValue( true );
|
|
else
|
|
m_matchByReference->SetValue( true );
|
|
}
|
|
|
|
updateFieldsList();
|
|
|
|
if( m_mode == MODE::CHANGE )
|
|
{
|
|
m_updateFieldsSizer->GetStaticBox()->SetLabel( _( "Update Fields" ) );
|
|
m_removeExtraBox->SetLabel( _( "Remove fields if not in new symbol" ) );
|
|
m_resetEmptyFields->SetLabel( _( "Reset fields if empty in new symbol" ) );
|
|
m_resetFieldText->SetLabel( _( "Update field text" ) );
|
|
m_resetFieldVisibilities->SetLabel( _( "Update field visibilities" ) );
|
|
m_resetFieldEffects->SetLabel( _( "Update field sizes and styles" ) );
|
|
m_resetFieldPositions->SetLabel( _( "Update field positions" ) );
|
|
m_resetAttributes->SetLabel( _( "Update symbol attributes" ) );
|
|
}
|
|
|
|
m_removeExtraBox->SetValue( g_removeExtraFields[ (int) m_mode ] );
|
|
m_resetEmptyFields->SetValue( g_resetEmptyFields[ (int) m_mode ] );
|
|
m_resetFieldText->SetValue( g_resetFieldText[ (int) m_mode ] );
|
|
m_resetFieldVisibilities->SetValue( g_resetFieldVisibilities[ (int) m_mode ] );
|
|
m_resetFieldEffects->SetValue( g_resetFieldEffects[ (int) m_mode ] );
|
|
m_resetFieldPositions->SetValue( g_resetFieldPositions[ (int) m_mode ] );
|
|
m_resetAttributes->SetValue( g_resetAttributes[ (int) m_mode ] );
|
|
m_resetCustomPower->SetValue( g_resetCustomPower[ (int) m_mode ] );
|
|
|
|
// DIALOG_SHIM needs a unique hash_key because classname is not sufficient
|
|
// because the update and change versions of this dialog have different controls.
|
|
m_hash_key = TO_UTF8( GetTitle() );
|
|
|
|
wxString okLabel = m_mode == MODE::CHANGE ? _( "Change" ) : _( "Update" );
|
|
|
|
SetupStandardButtons( { { wxID_OK, okLabel },
|
|
{ wxID_CANCEL, _( "Close" ) } } );
|
|
|
|
// Now all widgets have the size fixed, call FinishDialogSettings
|
|
finishDialogSettings();
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::onMatchByAll( wxCommandEvent& aEvent )
|
|
{
|
|
updateFieldsList();
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::onMatchBySelected( wxCommandEvent& aEvent )
|
|
{
|
|
updateFieldsList();
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::onMatchByReference( wxCommandEvent& aEvent )
|
|
{
|
|
updateFieldsList();
|
|
m_specifiedReference->SetFocus();
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::onMatchByValue( wxCommandEvent& aEvent )
|
|
{
|
|
updateFieldsList();
|
|
m_specifiedValue->SetFocus();
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::onMatchById( wxCommandEvent& aEvent )
|
|
{
|
|
updateFieldsList();
|
|
m_specifiedId->SetFocus();
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::onMatchTextKillFocus( wxFocusEvent& event )
|
|
{
|
|
updateFieldsList();
|
|
event.Skip(); // Mandatory in wxFocusEvent
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::onMatchIDKillFocus( wxFocusEvent& event )
|
|
{
|
|
updateFieldsList();
|
|
event.Skip(); // Mandatory in wxFocusEvent
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::onNewLibIDKillFocus( wxFocusEvent& event )
|
|
{
|
|
updateFieldsList();
|
|
event.Skip(); // Mandatory in wxFocusEvent
|
|
}
|
|
|
|
|
|
DIALOG_CHANGE_SYMBOLS::~DIALOG_CHANGE_SYMBOLS()
|
|
{
|
|
g_selectRefDes = m_fieldsBox->IsChecked( REFERENCE_FIELD );
|
|
g_selectValue = m_fieldsBox->IsChecked( VALUE_FIELD );
|
|
|
|
g_removeExtraFields[ (int) m_mode ] = m_removeExtraBox->GetValue();
|
|
g_resetEmptyFields[ (int) m_mode ] = m_resetEmptyFields->GetValue();
|
|
g_resetFieldText[ (int) m_mode ] = m_resetFieldText->GetValue();
|
|
g_resetFieldVisibilities[ (int) m_mode ] = m_resetFieldVisibilities->GetValue();
|
|
g_resetFieldEffects[ (int) m_mode ] = m_resetFieldEffects->GetValue();
|
|
g_resetFieldPositions[ (int) m_mode ] = m_resetFieldPositions->GetValue();
|
|
g_resetAttributes[ (int) m_mode ] = m_resetAttributes->GetValue();
|
|
g_resetCustomPower[ (int) m_mode ] = m_resetCustomPower->GetValue();
|
|
}
|
|
|
|
|
|
wxString getLibIdValue( const wxTextCtrl* aCtrl )
|
|
{
|
|
wxString rawValue = aCtrl->GetValue();
|
|
wxString itemName;
|
|
wxString libName = rawValue.BeforeFirst( ':', &itemName );
|
|
|
|
return EscapeString( libName, CTX_LIBID ) + ':' + EscapeString( itemName, CTX_LIBID );
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::launchMatchIdSymbolBrowser( wxCommandEvent& aEvent )
|
|
{
|
|
wxString newName = getLibIdValue( m_specifiedId );
|
|
|
|
if( KIWAY_PLAYER* frame = Kiway().Player( FRAME_SYMBOL_CHOOSER, true, this ) )
|
|
{
|
|
if( frame->ShowModal( &newName, this ) )
|
|
{
|
|
m_specifiedId->SetValue( UnescapeString( newName ) );
|
|
updateFieldsList();
|
|
}
|
|
|
|
frame->Destroy();
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::launchNewIdSymbolBrowser( wxCommandEvent& aEvent )
|
|
{
|
|
wxString newName = getLibIdValue( m_newId );
|
|
|
|
if( KIWAY_PLAYER* frame = Kiway().Player( FRAME_SYMBOL_CHOOSER, true, this ) )
|
|
{
|
|
if( frame->ShowModal( &newName, this ) )
|
|
{
|
|
m_newId->SetValue( UnescapeString( newName ) );
|
|
updateFieldsList();
|
|
}
|
|
|
|
frame->Destroy();
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::updateFieldsList()
|
|
{
|
|
SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
|
|
|
|
wxCHECK( frame, /* void */ );
|
|
|
|
// Load non-mandatory fields from all matching symbols and their library symbols
|
|
std::vector<SCH_FIELD*> fields;
|
|
std::vector<SCH_FIELD*> libFields;
|
|
std::set<wxString> fieldNames;
|
|
|
|
for( SCH_SHEET_PATH& instance : frame->Schematic().Hierarchy() )
|
|
{
|
|
SCH_SCREEN* screen = instance.LastScreen();
|
|
|
|
wxCHECK2( screen, continue );
|
|
|
|
for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
|
|
{
|
|
SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
|
|
|
|
wxCHECK2( symbol, continue );
|
|
|
|
if( !isMatch( symbol, &instance ) )
|
|
continue;
|
|
|
|
fields.clear();
|
|
symbol->GetFields( fields, false );
|
|
|
|
for( unsigned i = MANDATORY_FIELDS; i < fields.size(); ++i )
|
|
fieldNames.insert( fields[i]->GetName() );
|
|
|
|
if( m_mode == MODE::UPDATE && symbol->GetLibId().IsValid() )
|
|
{
|
|
LIB_SYMBOL* libSymbol = frame->GetLibSymbol( symbol->GetLibId() );
|
|
|
|
if( libSymbol )
|
|
{
|
|
std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
|
|
|
|
flattenedSymbol->GetFields( libFields );
|
|
|
|
for( unsigned i = MANDATORY_FIELDS; i < libFields.size(); ++i )
|
|
fieldNames.insert( libFields[i]->GetName() );
|
|
|
|
libFields.clear(); // flattenedSymbol is about to go out of scope...
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load non-mandatory fields from the change-to library symbol
|
|
if( m_mode == MODE::CHANGE )
|
|
{
|
|
LIB_ID newId;
|
|
|
|
newId.Parse( getLibIdValue( m_newId ) );
|
|
|
|
if( newId.IsValid() )
|
|
{
|
|
LIB_SYMBOL* libSymbol = frame->GetLibSymbol( newId );
|
|
|
|
if( libSymbol )
|
|
{
|
|
std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
|
|
|
|
flattenedSymbol->GetFields( libFields );
|
|
|
|
for( unsigned i = MANDATORY_FIELDS; i < libFields.size(); ++i )
|
|
fieldNames.insert( libFields[i]->GetName() );
|
|
|
|
libFields.clear(); // flattenedSymbol is about to go out of scope...
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the listbox widget
|
|
wxArrayInt checkedItems;
|
|
wxArrayString checkedNames;
|
|
|
|
m_fieldsBox->GetCheckedItems( checkedItems );
|
|
|
|
for( int ii : checkedItems )
|
|
checkedNames.push_back( m_fieldsBox->GetString( ii ) );
|
|
|
|
bool allChecked = true;
|
|
|
|
for( unsigned ii = 0; ii < m_fieldsBox->GetCount(); ++ii )
|
|
{
|
|
if( ii == REFERENCE_FIELD || ii == VALUE_FIELD )
|
|
continue;
|
|
|
|
if( !m_fieldsBox->IsChecked( ii ) )
|
|
allChecked = false;
|
|
}
|
|
|
|
for( unsigned ii = m_fieldsBox->GetCount() - 1; ii >= MANDATORY_FIELDS; --ii )
|
|
m_fieldsBox->Delete( ii );
|
|
|
|
for( const wxString& fieldName : fieldNames )
|
|
{
|
|
m_fieldsBox->Append( fieldName );
|
|
|
|
if( allChecked || alg::contains( checkedNames, fieldName ) )
|
|
m_fieldsBox->Check( m_fieldsBox->GetCount() - 1, true );
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::checkAll( bool aCheck )
|
|
{
|
|
for( unsigned i = 0; i < m_fieldsBox->GetCount(); ++i )
|
|
m_fieldsBox->Check( i, aCheck );
|
|
}
|
|
|
|
|
|
void DIALOG_CHANGE_SYMBOLS::onOkButtonClicked( wxCommandEvent& aEvent )
|
|
{
|
|
SCH_EDIT_FRAME* parent = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
|
|
|
|
wxCHECK( parent, /* void */ );
|
|
|
|
wxBusyCursor dummy;
|
|
SCH_COMMIT commit( parent );
|
|
|
|
m_messagePanel->Clear();
|
|
m_messagePanel->Flush( false );
|
|
|
|
// Create the set of fields to be updated. Use non translated (canonical) names
|
|
// for mandatory fields
|
|
m_updateFields.clear();
|
|
|
|
for( unsigned i = 0; i < m_fieldsBox->GetCount(); ++i )
|
|
{
|
|
if( m_fieldsBox->IsChecked( i ) )
|
|
{
|
|
if( i < MANDATORY_FIELDS )
|
|
{
|
|
SCH_FIELD dummy_field( nullptr, i );
|
|
m_updateFields.insert( dummy_field.GetCanonicalName() );
|
|
}
|
|
else
|
|
{
|
|
m_updateFields.insert( m_fieldsBox->GetString( i ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( processMatchingSymbols( &commit) )
|
|
commit.Push( m_mode == MODE::CHANGE ? _( "Change Symbols" ) : _( "Update Symbols" ) );
|
|
|
|
m_messagePanel->Flush( false );
|
|
}
|
|
|
|
|
|
bool DIALOG_CHANGE_SYMBOLS::isMatch( SCH_SYMBOL* aSymbol, SCH_SHEET_PATH* aInstance )
|
|
{
|
|
SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
|
|
|
|
wxCHECK( frame, false );
|
|
|
|
if( !aSymbol )
|
|
return false;
|
|
|
|
if( m_matchAll->GetValue() )
|
|
{
|
|
return true;
|
|
}
|
|
else if( m_matchBySelection->GetValue() )
|
|
{
|
|
return aSymbol == m_symbol || aSymbol->IsSelected();
|
|
}
|
|
else if( m_matchByReference->GetValue() )
|
|
{
|
|
return WildCompareString( m_specifiedReference->GetValue(),
|
|
UnescapeString( aSymbol->GetRef( aInstance, false ) ),
|
|
false );
|
|
}
|
|
else if( m_matchByValue->GetValue() )
|
|
{
|
|
return WildCompareString( m_specifiedValue->GetValue(),
|
|
UnescapeString( aSymbol->GetField( VALUE_FIELD )->GetText() ),
|
|
false );
|
|
}
|
|
else if( m_matchById )
|
|
{
|
|
LIB_ID id;
|
|
|
|
id.Parse( getLibIdValue( m_specifiedId ) );
|
|
return aSymbol->GetLibId() == id;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
int DIALOG_CHANGE_SYMBOLS::processMatchingSymbols( SCH_COMMIT* aCommit )
|
|
{
|
|
SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
|
|
|
|
wxCHECK( frame, false );
|
|
|
|
LIB_ID newId;
|
|
wxString msg;
|
|
int matchesProcessed = 0;
|
|
SCH_SYMBOL* symbol = nullptr;
|
|
|
|
if( m_mode == MODE::CHANGE )
|
|
{
|
|
newId.Parse( getLibIdValue( m_newId ) );
|
|
|
|
if( !newId.IsValid() )
|
|
return false;
|
|
}
|
|
|
|
std::map<SCH_SYMBOL*, SYMBOL_CHANGE_INFO> symbols;
|
|
|
|
for( SCH_SHEET_PATH& instance : frame->Schematic().Hierarchy() )
|
|
{
|
|
SCH_SCREEN* screen = instance.LastScreen();
|
|
|
|
wxCHECK2( screen, continue );
|
|
|
|
// Fetch all the symbols that meet the change criteria.
|
|
for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
|
|
{
|
|
symbol = static_cast<SCH_SYMBOL*>( item );
|
|
|
|
wxCHECK2( symbol, continue );
|
|
|
|
if( !isMatch( symbol, &instance ) )
|
|
continue;
|
|
|
|
if( m_mode == MODE::UPDATE )
|
|
newId = symbol->GetLibId();
|
|
|
|
auto it = symbols.find( symbol );
|
|
|
|
if( it == symbols.end() )
|
|
{
|
|
SYMBOL_CHANGE_INFO info;
|
|
|
|
info.m_Instances.emplace_back( instance );
|
|
info.m_LibId = newId;
|
|
symbols.insert( { symbol, info } );
|
|
}
|
|
else
|
|
{
|
|
it->second.m_Instances.emplace_back( instance );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( symbols.size() > 0 )
|
|
matchesProcessed += processSymbols( aCommit, symbols );
|
|
else
|
|
m_messagePanel->Report( _( "*** No symbols matching criteria found ***" ),
|
|
RPT_SEVERITY_ERROR );
|
|
|
|
frame->GetCurrentSheet().UpdateAllScreenReferences();
|
|
|
|
return matchesProcessed;
|
|
}
|
|
|
|
|
|
int DIALOG_CHANGE_SYMBOLS::processSymbols( SCH_COMMIT* aCommit,
|
|
const std::map<SCH_SYMBOL*,
|
|
SYMBOL_CHANGE_INFO>& aSymbols )
|
|
{
|
|
wxCHECK( !aSymbols.empty(), 0 );
|
|
|
|
int matchesProcessed = 0;
|
|
SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( GetParent() );
|
|
wxString msg;
|
|
|
|
wxCHECK( frame, 0 );
|
|
|
|
std::map<SCH_SYMBOL*, SYMBOL_CHANGE_INFO> symbols = aSymbols;
|
|
std::map<SCH_SYMBOL*, SYMBOL_CHANGE_INFO>::iterator it = symbols.begin();
|
|
|
|
// Remove all symbols that don't have a valid library symbol link or enough units to
|
|
// satify the library symbol update.
|
|
while( it != symbols.end() )
|
|
{
|
|
SCH_SYMBOL* symbol = it->first;
|
|
|
|
wxCHECK2( symbol && it->second.m_LibId.IsValid(), continue );
|
|
|
|
LIB_SYMBOL* libSymbol = frame->GetLibSymbol( it->second.m_LibId );
|
|
|
|
if( !libSymbol )
|
|
{
|
|
msg = getSymbolReferences( *symbol, it->second.m_LibId );
|
|
msg << wxT( ": " ) << _( "*** symbol not found ***" );
|
|
m_messagePanel->Report( msg, RPT_SEVERITY_ERROR );
|
|
it = symbols.erase( it );
|
|
continue;
|
|
}
|
|
|
|
std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
|
|
|
|
if( flattenedSymbol->GetUnitCount() < symbol->GetUnit() )
|
|
{
|
|
msg = getSymbolReferences( *symbol, it->second.m_LibId );
|
|
msg << wxT( ": " ) << _( "*** new symbol has too few units ***" );
|
|
m_messagePanel->Report( msg, RPT_SEVERITY_ERROR );
|
|
it = symbols.erase( it );
|
|
}
|
|
else
|
|
{
|
|
++it;
|
|
}
|
|
}
|
|
|
|
// Removing the symbol needs to be done before the LIB_SYMBOL is changed to prevent stale
|
|
// library symbols in the schematic file.
|
|
for( const auto& [ symbol, symbol_change_info ] : symbols )
|
|
{
|
|
wxCHECK( symbol && !symbol_change_info.m_Instances.empty(), 0 );
|
|
|
|
SCH_SCREEN* screen = symbol_change_info.m_Instances[0].LastScreen();
|
|
|
|
wxCHECK( screen, 0 );
|
|
|
|
screen->Remove( symbol );
|
|
SCH_SYMBOL* symbol_copy = static_cast<SCH_SYMBOL*>( symbol->Clone() );
|
|
aCommit->Modified( symbol, symbol_copy, screen );
|
|
|
|
CONNECTION_GRAPH* connectionGraph = screen->Schematic()->ConnectionGraph();
|
|
|
|
// When we replace the lib symbol below, we free the associated pins if the new symbol has
|
|
// fewer than the original. This will cause the connection graph to be out of date unless
|
|
// we replace references in the graph to the old symbol/pins with references to the ones stored
|
|
// in the undo stack.
|
|
if( connectionGraph )
|
|
connectionGraph->ExchangeItem( symbol, symbol_copy );
|
|
}
|
|
|
|
for( const auto& [ symbol, symbol_change_info ] : symbols )
|
|
{
|
|
// Remember initial link before changing for diags purpose
|
|
wxString initialLibLinkName = UnescapeString( symbol->GetLibId().Format() );
|
|
|
|
if( symbol_change_info.m_LibId != symbol->GetLibId() )
|
|
symbol->SetLibId( symbol_change_info.m_LibId );
|
|
|
|
LIB_SYMBOL* libSymbol = frame->GetLibSymbol( symbol_change_info.m_LibId );
|
|
std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
|
|
SCH_SCREEN* screen = symbol_change_info.m_Instances[0].LastScreen();
|
|
|
|
symbol->SetLibSymbol( flattenedSymbol.release() );
|
|
|
|
if( m_resetAttributes->GetValue() )
|
|
{
|
|
// Fetch the attributes from the *flattened* library symbol. They are not supported
|
|
// in derived symbols.
|
|
symbol->SetExcludedFromSim( symbol->GetLibSymbolRef()->GetExcludedFromSim() );
|
|
symbol->SetExcludedFromBOM( symbol->GetLibSymbolRef()->GetExcludedFromBOM() );
|
|
symbol->SetExcludedFromBoard( symbol->GetLibSymbolRef()->GetExcludedFromBoard() );
|
|
}
|
|
|
|
if( m_resetPinTextVisibility->GetValue() )
|
|
{
|
|
symbol->SetShowPinNames( symbol->GetLibSymbolRef()->GetShowPinNames() );
|
|
symbol->SetShowPinNumbers( symbol->GetLibSymbolRef()->GetShowPinNumbers() );
|
|
}
|
|
|
|
bool removeExtras = m_removeExtraBox->GetValue();
|
|
bool resetVis = m_resetFieldVisibilities->GetValue();
|
|
bool resetEffects = m_resetFieldEffects->GetValue();
|
|
bool resetPositions = m_resetFieldPositions->GetValue();
|
|
|
|
for( unsigned i = 0; i < symbol->GetFields().size(); ++i )
|
|
{
|
|
SCH_FIELD& field = symbol->GetFields()[i];
|
|
SCH_FIELD* libField = nullptr;
|
|
|
|
// Mandatory fields always exist in m_updateFields, but these names can be translated.
|
|
// so use GetCanonicalName().
|
|
if( !alg::contains( m_updateFields, field.GetCanonicalName() ) )
|
|
continue;
|
|
|
|
if( i < MANDATORY_FIELDS )
|
|
libField = symbol->GetLibSymbolRef()->GetFieldById( (int) i );
|
|
else
|
|
libField = symbol->GetLibSymbolRef()->FindField( field.GetName() );
|
|
|
|
if( libField )
|
|
{
|
|
bool resetText = libField->GetText().IsEmpty() ? m_resetEmptyFields->GetValue()
|
|
: m_resetFieldText->GetValue();
|
|
|
|
if( resetText )
|
|
{
|
|
if( i == REFERENCE_FIELD )
|
|
{
|
|
wxString prefix = UTIL::GetRefDesPrefix( libField->GetText() );
|
|
|
|
for( const SCH_SHEET_PATH& instance : symbol_change_info.m_Instances )
|
|
{
|
|
wxString ref = symbol->GetRef( &instance );
|
|
int number = UTIL::GetRefDesNumber( ref );
|
|
|
|
if( number >= 0 )
|
|
ref.Printf( wxS( "%s%d" ), prefix, number );
|
|
else
|
|
ref = UTIL::GetRefDesUnannotated( prefix );
|
|
|
|
symbol->SetRef( &instance, ref );
|
|
}
|
|
}
|
|
else if( i == VALUE_FIELD )
|
|
{
|
|
if( ( symbol->IsPower() && m_resetCustomPower->IsChecked() )
|
|
|| !symbol->IsPower() )
|
|
symbol->SetValueFieldText( UnescapeString( libField->GetText() ) );
|
|
}
|
|
else if( i == FOOTPRINT_FIELD )
|
|
{
|
|
symbol->SetFootprintFieldText( libField->GetText() );
|
|
}
|
|
else
|
|
{
|
|
field.SetText( libField->GetText() );
|
|
}
|
|
}
|
|
|
|
if( resetVis )
|
|
field.SetVisible( libField->IsVisible() );
|
|
|
|
if( resetEffects )
|
|
{
|
|
// Careful: the visible bit and position are also set by SetAttributes()
|
|
bool visible = field.IsVisible();
|
|
VECTOR2I pos = field.GetPosition();
|
|
|
|
field.SetAttributes( *libField );
|
|
|
|
field.SetVisible( visible );
|
|
field.SetPosition( pos );
|
|
field.SetNameShown( libField->IsNameShown() );
|
|
field.SetCanAutoplace( libField->CanAutoplace() );
|
|
}
|
|
|
|
if( resetPositions )
|
|
field.SetTextPos( symbol->GetPosition() + libField->GetTextPos() );
|
|
}
|
|
else if( i >= MANDATORY_FIELDS && removeExtras )
|
|
{
|
|
symbol->RemoveField( field.GetName() );
|
|
i--;
|
|
}
|
|
}
|
|
|
|
std::vector<SCH_FIELD*> libFields;
|
|
symbol->GetLibSymbolRef()->GetFields( libFields );
|
|
|
|
for( unsigned i = MANDATORY_FIELDS; i < libFields.size(); ++i )
|
|
{
|
|
const SCH_FIELD& libField = *libFields[i];
|
|
|
|
if( !alg::contains( m_updateFields, libField.GetCanonicalName() ) )
|
|
continue;
|
|
|
|
if( !symbol->FindField( libField.GetName(), false ) )
|
|
{
|
|
wxString fieldName = libField.GetCanonicalName();
|
|
SCH_FIELD newField( VECTOR2I( 0, 0 ), symbol->GetFieldCount(), symbol,
|
|
fieldName );
|
|
SCH_FIELD* schField = symbol->AddField( newField );
|
|
|
|
// Careful: the visible bit and position are also set by SetAttributes()
|
|
schField->SetAttributes( libField );
|
|
schField->SetText( libField.GetText() );
|
|
schField->SetTextPos( symbol->GetPosition() + libField.GetTextPos() );
|
|
}
|
|
|
|
if( resetPositions && frame->eeconfig()->m_AutoplaceFields.enable )
|
|
symbol->AutoAutoplaceFields( screen );
|
|
}
|
|
|
|
symbol->SetSchSymbolLibraryName( wxEmptyString );
|
|
screen->Append( symbol );
|
|
|
|
if( resetPositions )
|
|
symbol->AutoAutoplaceFields( screen );
|
|
|
|
frame->GetCanvas()->GetView()->Update( symbol );
|
|
|
|
msg = getSymbolReferences( *symbol, symbol_change_info.m_LibId, &initialLibLinkName );
|
|
msg += wxS( ": OK" );
|
|
m_messagePanel->Report( msg, RPT_SEVERITY_ACTION );
|
|
matchesProcessed +=1;
|
|
}
|
|
|
|
return matchesProcessed;
|
|
}
|
|
|
|
|
|
wxString DIALOG_CHANGE_SYMBOLS::getSymbolReferences( SCH_SYMBOL& aSymbol,
|
|
const LIB_ID& aNewId,
|
|
const wxString* aOldLibLinkName )
|
|
{
|
|
wxString msg;
|
|
wxString references;
|
|
LIB_ID oldId = aSymbol.GetLibId();
|
|
|
|
wxString oldLibLinkName; // For report
|
|
|
|
if( aOldLibLinkName )
|
|
oldLibLinkName = *aOldLibLinkName;
|
|
else
|
|
oldLibLinkName = UnescapeString( oldId.Format() );
|
|
|
|
SCH_EDIT_FRAME* parent = dynamic_cast< SCH_EDIT_FRAME* >( GetParent() );
|
|
|
|
wxCHECK( parent, msg );
|
|
|
|
SCH_SHEET_LIST sheets = parent->Schematic().Hierarchy();
|
|
|
|
for( const SCH_SYMBOL_INSTANCE& instance : aSymbol.GetInstances() )
|
|
{
|
|
// Only include the symbol instances for the current project.
|
|
if( !sheets.HasPath( instance.m_Path ) )
|
|
continue;
|
|
|
|
if( references.IsEmpty() )
|
|
references = instance.m_Reference;
|
|
else
|
|
references += wxT( " " ) + instance.m_Reference;
|
|
}
|
|
|
|
if( m_mode == MODE::UPDATE )
|
|
{
|
|
if( aSymbol.GetInstances().size() == 1 )
|
|
{
|
|
msg.Printf( _( "Update symbol %s from '%s' to '%s'" ),
|
|
references,
|
|
oldLibLinkName,
|
|
UnescapeString( aNewId.Format() ) );
|
|
}
|
|
else
|
|
{
|
|
msg.Printf( _( "Update symbols %s from '%s' to '%s'" ),
|
|
references,
|
|
oldLibLinkName,
|
|
UnescapeString( aNewId.Format() ) );
|
|
}
|
|
}
|
|
else // mode is MODE::CHANGE
|
|
{
|
|
if( aSymbol.GetInstances().size() == 1 )
|
|
{
|
|
msg.Printf( _( "Change symbol %s from '%s' to '%s'" ),
|
|
references,
|
|
oldLibLinkName,
|
|
UnescapeString( aNewId.Format() ) );
|
|
}
|
|
else
|
|
{
|
|
msg.Printf( _( "Change symbols %s from '%s' to '%s'" ),
|
|
references,
|
|
oldLibLinkName,
|
|
UnescapeString( aNewId.Format() ) );
|
|
}
|
|
}
|
|
|
|
return msg;
|
|
}
|