mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 18:23:15 +02:00
Set up a new lineage for SCH_ITEMS to get back to the SCHEMATIC they live on: Items will all be parented to the SCH_SCREEN that they are added to, and each SCH_SCREEN will point back to the SCHEMATIC that it is part of. Note that this hierarchy is not the same as the actual schematic hierarchy, which continues to be managed through SCH_SHEETs and SCH_SHEET_PATHS.
508 lines
18 KiB
C++
508 lines
18 KiB
C++
/**
|
|
* @file dialog_symbol_remap.cpp
|
|
*/
|
|
|
|
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2017 Wayne Stambaugh <stambaughw@verizon.net>
|
|
* Copyright (C) 2017-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 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 <macros.h>
|
|
#include <pgm_base.h>
|
|
#include <kiface_i.h>
|
|
#include <project.h>
|
|
#include <confirm.h>
|
|
#include <reporter.h>
|
|
#include <wildcards_and_files_ext.h>
|
|
#include <wx_html_report_panel.h>
|
|
|
|
#include <class_library.h>
|
|
#include <lib_view_frame.h>
|
|
#include <project_rescue.h>
|
|
#include <sch_io_mgr.h>
|
|
#include <sch_component.h>
|
|
#include <sch_screen.h>
|
|
#include <sch_edit_frame.h>
|
|
#include <schematic.h>
|
|
#include <symbol_lib_table.h>
|
|
#include <env_paths.h>
|
|
|
|
#include <dialog_symbol_remap.h>
|
|
|
|
|
|
DIALOG_SYMBOL_REMAP::DIALOG_SYMBOL_REMAP( SCH_EDIT_FRAME* aParent ) :
|
|
DIALOG_SYMBOL_REMAP_BASE( aParent ),
|
|
m_frame( aParent )
|
|
{
|
|
m_remapped = false;
|
|
|
|
if( !wxFileName::IsDirWritable( Prj().GetProjectPath() ) )
|
|
{
|
|
DisplayInfoMessage( this, _( "Remapping is not possible because you do not have "
|
|
"write privileges to the project folder \"%s\"." ) );
|
|
|
|
// Disable the remap button.
|
|
m_remapped = true;
|
|
}
|
|
|
|
wxString text;
|
|
|
|
text = _( "This schematic currently uses the project symbol library list look up method "
|
|
"for loading library symbols. KiCad will attempt to map the existing symbols "
|
|
"to use the new symbol library table. Remapping will change some project files "
|
|
"and schematics may not be compatible with older versions of KiCad. All files "
|
|
"that are changed will be backed up to the \"remap_backup\" folder in the project "
|
|
"folder should you need to revert any changes. If you choose to skip this step, "
|
|
"you will be responsible for manually remapping the symbols." );
|
|
|
|
m_htmlCtrl->AppendToPage( text );
|
|
}
|
|
|
|
|
|
void DIALOG_SYMBOL_REMAP::OnRemapSymbols( wxCommandEvent& aEvent )
|
|
{
|
|
SCH_EDIT_FRAME* parent = dynamic_cast< SCH_EDIT_FRAME* >( GetParent() );
|
|
|
|
wxCHECK_RET( parent != nullptr, "Parent window is not type SCH_EDIT_FRAME." );
|
|
|
|
wxBusyCursor busy;
|
|
|
|
if( !backupProject( m_messagePanel->Reporter() ) )
|
|
return;
|
|
|
|
// Ignore the never show rescue setting for one last rescue of legacy symbol
|
|
// libraries before remapping to the symbol library table. This ensures the
|
|
// best remapping results.
|
|
LEGACY_RESCUER rescuer( Prj(), &parent->Schematic(), &parent->GetCurrentSheet(),
|
|
parent->GetCanvas()->GetBackend() );
|
|
|
|
if( RESCUER::RescueProject( this, rescuer, false ) )
|
|
{
|
|
LIB_VIEW_FRAME* viewer = (LIB_VIEW_FRAME*) parent->Kiway().Player( FRAME_SCH_VIEWER, false );
|
|
|
|
if( viewer )
|
|
viewer->ReCreateListLib();
|
|
|
|
parent->GetScreen()->ClearUndoORRedoList( parent->GetScreen()->m_UndoList, 1 );
|
|
parent->SyncView();
|
|
parent->GetCanvas()->Refresh();
|
|
parent->OnModify();
|
|
}
|
|
|
|
// The schematic is fully loaded, any legacy library symbols have been rescued. Now
|
|
// check to see if the schematic has not been converted to the symbol library table
|
|
// method for looking up symbols.
|
|
wxFileName prjSymLibTableFileName( Prj().GetProjectPath(),
|
|
SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
|
|
|
|
// Delete the existing project symbol library table.
|
|
if( prjSymLibTableFileName.FileExists() )
|
|
{
|
|
wxRemoveFile( prjSymLibTableFileName.GetFullPath() );
|
|
}
|
|
|
|
createProjectSymbolLibTable( m_messagePanel->Reporter() );
|
|
Prj().SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, NULL );
|
|
Prj().SchSymbolLibTable();
|
|
|
|
remapSymbolsToLibTable( m_messagePanel->Reporter() );
|
|
|
|
// Remove all of the libraries from the legacy library list.
|
|
wxString paths;
|
|
wxArrayString libNames;
|
|
|
|
PART_LIBS::LibNamesAndPaths( &Prj(), true, &paths, &libNames );
|
|
|
|
// Reload the the cache symbol library.
|
|
Prj().SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL );
|
|
Prj().SchLibs();
|
|
|
|
Raise();
|
|
m_remapped = true;
|
|
}
|
|
|
|
|
|
size_t DIALOG_SYMBOL_REMAP::getLibsNotInGlobalSymbolLibTable( std::vector< PART_LIB* >& aLibs )
|
|
{
|
|
PART_LIBS* libs = Prj().SchLibs();
|
|
|
|
for( PART_LIBS_BASE::iterator it = libs->begin(); it != libs->end(); ++it )
|
|
{
|
|
// Ignore the cache library.
|
|
if( it->IsCache() )
|
|
continue;
|
|
|
|
// Check for the obvious library name.
|
|
wxString libFileName = it->GetFullFileName();
|
|
|
|
if( !SYMBOL_LIB_TABLE::GetGlobalLibTable().FindRowByURI( libFileName ) )
|
|
aLibs.push_back( &(*it) );
|
|
}
|
|
|
|
return aLibs.size();
|
|
}
|
|
|
|
|
|
void DIALOG_SYMBOL_REMAP::createProjectSymbolLibTable( REPORTER& aReporter )
|
|
{
|
|
wxString msg;
|
|
std::vector< PART_LIB* > libs;
|
|
|
|
if( getLibsNotInGlobalSymbolLibTable( libs ) )
|
|
{
|
|
SYMBOL_LIB_TABLE prjLibTable;
|
|
std::vector< wxString > libNames = SYMBOL_LIB_TABLE::GetGlobalLibTable().GetLogicalLibs();
|
|
|
|
for( auto lib : libs )
|
|
{
|
|
wxString libName = lib->GetName();
|
|
int libNameInc = 1;
|
|
int libNameLen = libName.Length();
|
|
|
|
// Spaces in the file name will break the symbol name because they are not
|
|
// quoted in the symbol library file format.
|
|
libName.Replace( " ", "-" );
|
|
|
|
// Don't create duplicate table entries.
|
|
while( std::find( libNames.begin(), libNames.end(), libName ) != libNames.end() )
|
|
{
|
|
libName = libName.Left( libNameLen );
|
|
libName << libNameInc;
|
|
libNameInc++;
|
|
}
|
|
|
|
wxString pluginType = SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY );
|
|
wxFileName fn = lib->GetFullFileName();
|
|
|
|
// Use environment variable substitution where possible. This is based solely
|
|
// on the internal user environment variable list. Checking against all of the
|
|
// system wide environment variables is probably not a good idea.
|
|
wxString fullFileName = NormalizePath( fn, &Pgm().GetLocalEnvVariables(), &Prj() );
|
|
|
|
// Fall back to the absolute library path.
|
|
if( fullFileName.IsEmpty() )
|
|
fullFileName = lib->GetFullFileName();
|
|
|
|
wxFileName tmpFn = fullFileName;
|
|
|
|
// Don't add symbol libraries that do not exist.
|
|
if( tmpFn.Normalize() && tmpFn.FileExists() )
|
|
{
|
|
msg.Printf( _( "Adding library \"%s\", file \"%s\" to project symbol library table." ),
|
|
libName, fullFileName );
|
|
aReporter.Report( msg, RPT_SEVERITY_INFO );
|
|
|
|
prjLibTable.InsertRow( new SYMBOL_LIB_TABLE_ROW( libName, fullFileName,
|
|
pluginType ) );
|
|
}
|
|
else
|
|
{
|
|
msg.Printf( _( "Library \"%s\" not found." ), fullFileName );
|
|
aReporter.Report( msg, RPT_SEVERITY_WARNING );
|
|
}
|
|
}
|
|
|
|
// Don't save empty project symbol library table.
|
|
if( !prjLibTable.IsEmpty() )
|
|
{
|
|
wxFileName fn( Prj().GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
|
|
|
|
try
|
|
{
|
|
FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
|
|
prjLibTable.Format( &formatter, 0 );
|
|
}
|
|
catch( const IO_ERROR& ioe )
|
|
{
|
|
msg.Printf( _( "Failed to write project symbol library table. Error:\n %s" ),
|
|
ioe.What() );
|
|
aReporter.ReportTail( msg, RPT_SEVERITY_ERROR );
|
|
}
|
|
|
|
aReporter.ReportTail( _( "Created project symbol library table.\n" ), RPT_SEVERITY_INFO );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_SYMBOL_REMAP::remapSymbolsToLibTable( REPORTER& aReporter )
|
|
{
|
|
wxString msg;
|
|
SCH_SCREENS schematic( m_frame->Schematic().Root() );
|
|
SCH_COMPONENT* symbol;
|
|
SCH_SCREEN* screen;
|
|
|
|
for( screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
|
|
{
|
|
for( auto item : screen->Items().OfType( SCH_COMPONENT_T ) )
|
|
{
|
|
symbol = dynamic_cast<SCH_COMPONENT*>( item );
|
|
|
|
if( !remapSymbolToLibTable( symbol ) )
|
|
{
|
|
msg.Printf( _( "No symbol \"%s\" found in symbol library table." ),
|
|
symbol->GetLibId().GetLibItemName().wx_str() );
|
|
aReporter.Report( msg, RPT_SEVERITY_WARNING );
|
|
}
|
|
else
|
|
{
|
|
msg.Printf( _( "Symbol \"%s\" mapped to symbol library \"%s\"." ),
|
|
symbol->GetLibId().GetLibItemName().wx_str(),
|
|
symbol->GetLibId().GetLibNickname().wx_str() );
|
|
aReporter.Report( msg, RPT_SEVERITY_ACTION );
|
|
screen->SetModify();
|
|
}
|
|
}
|
|
}
|
|
|
|
aReporter.Report( _( "Symbol library table mapping complete!" ), RPT_SEVERITY_INFO );
|
|
schematic.UpdateSymbolLinks();
|
|
}
|
|
|
|
|
|
bool DIALOG_SYMBOL_REMAP::remapSymbolToLibTable( SCH_COMPONENT* aSymbol )
|
|
{
|
|
wxCHECK_MSG( aSymbol != NULL, false, "Null pointer passed to remapSymbolToLibTable." );
|
|
wxCHECK_MSG( aSymbol->GetLibId().GetLibNickname().empty(), false,
|
|
"Cannot remap symbol that is already mapped." );
|
|
wxCHECK_MSG( !aSymbol->GetLibId().GetLibItemName().empty(), false,
|
|
"The symbol LIB_ID name is empty." );
|
|
|
|
PART_LIBS* libs = Prj().SchLibs();
|
|
|
|
for( PART_LIBS_BASE::iterator it = libs->begin(); it != libs->end(); ++it )
|
|
{
|
|
// Ignore the cache library.
|
|
if( it->IsCache() )
|
|
continue;
|
|
|
|
LIB_PART* alias = it->FindPart( aSymbol->GetLibId().GetLibItemName().wx_str() );
|
|
|
|
// Found in the same library as the old look up method assuming the user didn't
|
|
// change the libraries or library ordering since the last time the schematic was
|
|
// loaded.
|
|
if( alias )
|
|
{
|
|
// Find the same library in the symbol library table using the full path and file name.
|
|
wxString libFileName = it->GetFullFileName();
|
|
|
|
const LIB_TABLE_ROW* row = Prj().SchSymbolLibTable()->FindRowByURI( libFileName );
|
|
|
|
if( row )
|
|
{
|
|
LIB_ID id = aSymbol->GetLibId();
|
|
|
|
id.SetLibNickname( row->GetNickName() );
|
|
|
|
// Don't resolve symbol library links now.
|
|
aSymbol->SetLibId( id );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool DIALOG_SYMBOL_REMAP::backupProject( REPORTER& aReporter )
|
|
{
|
|
static wxString backupFolder = "rescue-backup";
|
|
|
|
wxString tmp;
|
|
wxString errorMsg;
|
|
wxFileName srcFileName;
|
|
wxFileName destFileName;
|
|
wxFileName backupPath;
|
|
SCH_SCREENS schematic( m_frame->Schematic().Root() );
|
|
|
|
// Copy backup files to different folder so as not to pollute the project folder.
|
|
destFileName.SetPath( Prj().GetProjectPath() );
|
|
destFileName.AppendDir( backupFolder );
|
|
backupPath = destFileName;
|
|
|
|
if( !destFileName.DirExists() )
|
|
{
|
|
if( !destFileName.Mkdir() )
|
|
{
|
|
errorMsg.Printf( _( "Cannot create project remap back up folder \"%s\"." ),
|
|
destFileName.GetPath() );
|
|
|
|
wxMessageDialog dlg( this, errorMsg, _( "Backup Error" ),
|
|
wxYES_NO | wxCENTRE | wxRESIZE_BORDER | wxICON_QUESTION );
|
|
dlg.SetYesNoLabels( wxMessageDialog::ButtonLabel( _( "Continue with Rescue" ) ),
|
|
wxMessageDialog::ButtonLabel( _( "Abort Rescue" ) ) );
|
|
|
|
if( dlg.ShowModal() == wxID_NO )
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Time stamp to append to file name in case multiple remappings are performed.
|
|
wxString timeStamp = wxDateTime::Now().Format( "-%Y-%m-%d-%H-%M-%S" );
|
|
|
|
// Back up symbol library table.
|
|
srcFileName.SetPath( Prj().GetProjectPath() );
|
|
srcFileName.SetName( SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
|
|
destFileName = srcFileName;
|
|
destFileName.AppendDir( backupFolder );
|
|
destFileName.SetName( destFileName.GetName() + timeStamp );
|
|
|
|
tmp.Printf( _( "Backing up file \"%s\" to file \"%s\"." ),
|
|
srcFileName.GetFullPath(), destFileName.GetFullPath() );
|
|
aReporter.Report( tmp, RPT_SEVERITY_INFO );
|
|
|
|
if( wxFileName::Exists( srcFileName.GetFullPath() )
|
|
&& !wxCopyFile( srcFileName.GetFullPath(), destFileName.GetFullPath() ) )
|
|
{
|
|
tmp.Printf( _( "Failed to back up file \"%s\".\n" ), srcFileName.GetFullPath() );
|
|
errorMsg += tmp;
|
|
}
|
|
|
|
// Back up the schematic files.
|
|
for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
|
|
{
|
|
destFileName = screen->GetFileName();
|
|
destFileName.SetName( destFileName.GetName() + timeStamp );
|
|
|
|
// Check for nest hierarchical schematic paths.
|
|
if( destFileName.GetPath() != backupPath.GetPath() )
|
|
{
|
|
destFileName.SetPath( backupPath.GetPath() );
|
|
|
|
wxArrayString srcDirs = wxFileName( screen->GetFileName() ).GetDirs();
|
|
wxArrayString destDirs = wxFileName( Prj().GetProjectPath() ).GetDirs();
|
|
|
|
for( size_t i = destDirs.GetCount(); i < srcDirs.GetCount(); i++ )
|
|
destFileName.AppendDir( srcDirs[i] );
|
|
}
|
|
else
|
|
{
|
|
destFileName.AppendDir( backupFolder );
|
|
}
|
|
|
|
tmp.Printf( _( "Backing up file \"%s\" to file \"%s\"." ),
|
|
screen->GetFileName(), destFileName.GetFullPath() );
|
|
aReporter.Report( tmp, RPT_SEVERITY_INFO );
|
|
|
|
if( !destFileName.DirExists() && !destFileName.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
|
|
{
|
|
tmp.Printf( _( "Failed to create backup folder \"%s\"\n" ), destFileName.GetPath() );
|
|
errorMsg += tmp;
|
|
continue;
|
|
}
|
|
|
|
if( wxFileName::Exists( screen->GetFileName() )
|
|
&& !wxCopyFile( screen->GetFileName(), destFileName.GetFullPath() ) )
|
|
{
|
|
tmp.Printf( _( "Failed to back up file \"%s\".\n" ), screen->GetFileName() );
|
|
errorMsg += tmp;
|
|
}
|
|
}
|
|
|
|
// Back up the project file.
|
|
destFileName = Prj().GetProjectFullName();
|
|
destFileName.SetName( destFileName.GetName() + timeStamp );
|
|
destFileName.AppendDir( backupFolder );
|
|
|
|
tmp.Printf( _( "Backing up file \"%s\" to file \"%s\"." ),
|
|
Prj().GetProjectFullName(), destFileName.GetFullPath() );
|
|
aReporter.Report( tmp, RPT_SEVERITY_INFO );
|
|
|
|
if( wxFileName::Exists( Prj().GetProjectFullName() )
|
|
&& !wxCopyFile( Prj().GetProjectFullName(), destFileName.GetFullPath() ) )
|
|
{
|
|
tmp.Printf( _( "Failed to back up file \"%s\".\n" ), Prj().GetProjectFullName() );
|
|
errorMsg += tmp;
|
|
}
|
|
|
|
// Back up the cache library.
|
|
srcFileName.SetPath( Prj().GetProjectPath() );
|
|
srcFileName.SetName( Prj().GetProjectName() + "-cache" );
|
|
srcFileName.SetExt( SchematicLibraryFileExtension );
|
|
|
|
destFileName = srcFileName;
|
|
destFileName.SetName( destFileName.GetName() + timeStamp );
|
|
destFileName.AppendDir( backupFolder );
|
|
|
|
tmp.Printf( _( "Backing up file \"%s\" to file \"%s\"." ),
|
|
srcFileName.GetFullPath(), destFileName.GetFullPath() );
|
|
aReporter.Report( tmp, RPT_SEVERITY_INFO );
|
|
|
|
if( srcFileName.Exists()
|
|
&& !wxCopyFile( srcFileName.GetFullPath(), destFileName.GetFullPath() ) )
|
|
{
|
|
tmp.Printf( _( "Failed to back up file \"%s\".\n" ), srcFileName.GetFullPath() );
|
|
errorMsg += tmp;
|
|
}
|
|
|
|
// Back up the rescue symbol library if it exists.
|
|
srcFileName.SetName( Prj().GetProjectName() + "-rescue" );
|
|
destFileName.SetName( srcFileName.GetName() + timeStamp );
|
|
|
|
tmp.Printf( _( "Backing up file \"%s\" to file \"%s\"." ),
|
|
srcFileName.GetFullPath(),
|
|
destFileName.GetFullPath() );
|
|
aReporter.Report( tmp, RPT_SEVERITY_INFO );
|
|
|
|
if( srcFileName.Exists()
|
|
&& !wxCopyFile( srcFileName.GetFullPath(), destFileName.GetFullPath() ) )
|
|
{
|
|
tmp.Printf( _( "Failed to back up file \"%s\".\n" ), srcFileName.GetFullPath() );
|
|
errorMsg += tmp;
|
|
}
|
|
|
|
// Back up the rescue symbol library document file if it exists.
|
|
srcFileName.SetExt( "dcm" );
|
|
destFileName.SetExt( srcFileName.GetExt() );
|
|
|
|
tmp.Printf( _( "Backing up file \"%s\" to file \"%s\"." ),
|
|
srcFileName.GetFullPath(),
|
|
destFileName.GetFullPath() );
|
|
aReporter.Report( tmp, RPT_SEVERITY_INFO );
|
|
|
|
if( srcFileName.Exists()
|
|
&& !wxCopyFile( srcFileName.GetFullPath(), destFileName.GetFullPath() ) )
|
|
{
|
|
tmp.Printf( _( "Failed to back up file \"%s\".\n" ), srcFileName.GetFullPath() );
|
|
errorMsg += tmp;
|
|
}
|
|
|
|
if( !errorMsg.IsEmpty() )
|
|
{
|
|
wxMessageDialog dlg( this, _( "Some of the project files could not be backed up." ),
|
|
_( "Backup Error" ),
|
|
wxYES_NO | wxCENTRE | wxRESIZE_BORDER | wxICON_QUESTION );
|
|
errorMsg.Trim();
|
|
dlg.SetExtendedMessage( errorMsg );
|
|
dlg.SetYesNoLabels( wxMessageDialog::ButtonLabel( _( "Continue with Rescue" ) ),
|
|
wxMessageDialog::ButtonLabel( _( "Abort Rescue" ) ) );
|
|
|
|
if( dlg.ShowModal() == wxID_NO )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void DIALOG_SYMBOL_REMAP::OnUpdateUIRemapButton( wxUpdateUIEvent& aEvent )
|
|
{
|
|
aEvent.Enable( !m_remapped );
|
|
}
|