530 lines
19 KiB
C++
530 lines
19 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@gmail.com>
|
|
* Copyright (C) 2017-2023 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_base.h>
|
|
#include <project.h>
|
|
#include <confirm.h>
|
|
#include <reporter.h>
|
|
#include <wildcards_and_files_ext.h>
|
|
#include <wx_filename.h>
|
|
#include "widgets/wx_html_report_panel.h"
|
|
|
|
#include <symbol_library.h>
|
|
#include <core/kicad_algo.h>
|
|
#include <symbol_viewer_frame.h>
|
|
#include <project_rescue.h>
|
|
#include <sch_io/sch_io_mgr.h>
|
|
#include <sch_symbol.h>
|
|
#include <sch_screen.h>
|
|
#include <sch_edit_frame.h>
|
|
#include <schematic.h>
|
|
#include <settings/settings_manager.h>
|
|
#include <symbol_lib_table.h>
|
|
#include <env_paths.h>
|
|
#include <project_sch.h>
|
|
#include <wx/msgdlg.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;
|
|
|
|
wxString projectPath = Prj().GetProjectPath();
|
|
|
|
if( !wxFileName::IsDirWritable( projectPath ) )
|
|
{
|
|
wxString msg =
|
|
wxString::Format( _( "Remapping is not possible because you have insufficient "
|
|
"privileges to the project folder '%s'." ),
|
|
projectPath );
|
|
|
|
DisplayInfoMessage( this, msg );
|
|
|
|
// 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 \"rescue-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 );
|
|
|
|
m_messagePanel->SetFileName( Prj().GetProjectPath() + wxT( "report.txt" ) );
|
|
}
|
|
|
|
|
|
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." );
|
|
|
|
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 ) )
|
|
{
|
|
wxBusyCursor busy;
|
|
|
|
auto viewer = (SYMBOL_VIEWER_FRAME*) parent->Kiway().Player( FRAME_SCH_VIEWER, false );
|
|
|
|
if( viewer )
|
|
viewer->ReCreateLibList();
|
|
|
|
parent->ClearUndoORRedoList( EDA_BASE_FRAME::UNDO_LIST, 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, nullptr );
|
|
PROJECT_SCH::SchSymbolLibTable( &Prj() );
|
|
|
|
remapSymbolsToLibTable( m_messagePanel->Reporter() );
|
|
|
|
// Remove all of the libraries from the legacy library list.
|
|
wxString paths;
|
|
wxArrayString libNames;
|
|
|
|
SYMBOL_LIBS::SetLibNamesAndPaths( &Prj(), paths, libNames );
|
|
|
|
// Reload the cache symbol library.
|
|
Prj().SetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS, nullptr );
|
|
PROJECT_SCH::SchLibs( &Prj() );
|
|
|
|
Raise();
|
|
m_remapped = true;
|
|
}
|
|
|
|
|
|
size_t DIALOG_SYMBOL_REMAP::getLibsNotInGlobalSymbolLibTable( std::vector< SYMBOL_LIB* >& aLibs )
|
|
{
|
|
SYMBOL_LIBS* libs = PROJECT_SCH::SchLibs( &Prj() );
|
|
|
|
for( SYMBOL_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 )
|
|
{
|
|
std::vector<SYMBOL_LIB*> libs;
|
|
|
|
if( getLibsNotInGlobalSymbolLibTable( libs ) )
|
|
{
|
|
wxBusyCursor busy;
|
|
SYMBOL_LIB_TABLE libTable;
|
|
std::vector<wxString> libNames = SYMBOL_LIB_TABLE::GetGlobalLibTable().GetLogicalLibs();
|
|
wxString msg;
|
|
|
|
for( SYMBOL_LIB* 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( wxS( " " ), wxS( "-" ) );
|
|
|
|
// Don't create duplicate table entries.
|
|
while( alg::contains( libNames, libName ) )
|
|
{
|
|
libName = libName.Left( libNameLen );
|
|
libName << libNameInc;
|
|
libNameInc++;
|
|
}
|
|
|
|
wxString type = 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 normalizedPath = NormalizePath( fn, &Pgm().GetLocalEnvVariables(), &Prj() );
|
|
wxFileName normalizedFn( normalizedPath );
|
|
|
|
// Don't add symbol libraries that do not exist.
|
|
if( normalizedFn.Normalize(FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS )
|
|
&& normalizedFn.FileExists() )
|
|
{
|
|
msg.Printf( _( "Adding library '%s', file '%s' to project symbol library table." ),
|
|
libName,
|
|
normalizedPath );
|
|
aReporter.Report( msg, RPT_SEVERITY_INFO );
|
|
|
|
libTable.InsertRow( new SYMBOL_LIB_TABLE_ROW( libName, normalizedPath, type ) );
|
|
}
|
|
else
|
|
{
|
|
msg.Printf( _( "Library '%s' not found." ), normalizedPath );
|
|
aReporter.Report( msg, RPT_SEVERITY_WARNING );
|
|
}
|
|
}
|
|
|
|
// Don't save empty project symbol library table.
|
|
if( !libTable.IsEmpty() )
|
|
{
|
|
wxFileName fn( Prj().GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
|
|
|
|
try
|
|
{
|
|
FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
|
|
libTable.Format( &formatter, 0 );
|
|
}
|
|
catch( const IO_ERROR& ioe )
|
|
{
|
|
msg.Printf( _( "Error writing project symbol library table.\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 )
|
|
{
|
|
wxBusyCursor busy;
|
|
wxString msg;
|
|
SCH_SCREENS schematic( m_frame->Schematic().Root() );
|
|
SCH_SYMBOL* symbol;
|
|
SCH_SCREEN* screen;
|
|
|
|
for( screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
|
|
{
|
|
for( EDA_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
|
|
{
|
|
symbol = dynamic_cast<SCH_SYMBOL*>( item );
|
|
|
|
wxCHECK2( symbol, continue );
|
|
|
|
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->SetContentModified();
|
|
}
|
|
}
|
|
}
|
|
|
|
aReporter.Report( _( "Symbol library table mapping complete!" ), RPT_SEVERITY_INFO );
|
|
schematic.UpdateSymbolLinks();
|
|
}
|
|
|
|
|
|
bool DIALOG_SYMBOL_REMAP::remapSymbolToLibTable( SCH_SYMBOL* aSymbol )
|
|
{
|
|
wxCHECK_MSG( aSymbol != nullptr, 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." );
|
|
|
|
SYMBOL_LIBS* libs = PROJECT_SCH::SchLibs( &Prj() );
|
|
|
|
for( SYMBOL_LIBS_BASE::iterator it = libs->begin(); it != libs->end(); ++it )
|
|
{
|
|
// Ignore the cache library.
|
|
if( it->IsCache() )
|
|
continue;
|
|
|
|
LIB_SYMBOL* alias = it->FindSymbol( 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 = PROJECT_SCH::SchSymbolLibTable( &Prj() )->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;
|
|
}
|
|
}
|
|
|
|
{
|
|
wxBusyCursor busy;
|
|
|
|
// 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 '%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 '%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 '%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() + wxS( "-cache" ) );
|
|
srcFileName.SetExt( FILEEXT::LegacySymbolLibFileExtension );
|
|
|
|
destFileName = srcFileName;
|
|
destFileName.SetName( destFileName.GetName() + timeStamp );
|
|
destFileName.AppendDir( backupFolder );
|
|
|
|
tmp.Printf( _( "Backing up file '%s' to '%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() + wxS( "-rescue" ) );
|
|
destFileName.SetName( srcFileName.GetName() + timeStamp );
|
|
|
|
tmp.Printf( _( "Backing up file '%s' to '%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( FILEEXT::LegacySymbolDocumentFileExtension );
|
|
destFileName.SetExt( srcFileName.GetExt() );
|
|
|
|
tmp.Printf( _( "Backing up file '%s' to '%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 );
|
|
}
|