922 lines
29 KiB
C++
922 lines
29 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2015 Chris Pavlina <pavlina.chris@gmail.com>
|
|
* Copyright (C) 2015-2023 KiCad Developers, see change_log.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 <sch_draw_panel.h>
|
|
#include <symbol_library.h>
|
|
#include <confirm.h>
|
|
#include <connection_graph.h>
|
|
#include <invoke_sch_dialog.h>
|
|
#include <kiway.h>
|
|
#include <symbol_viewer_frame.h>
|
|
#include <project_rescue.h>
|
|
#include <project_sch.h>
|
|
#include <sch_symbol.h>
|
|
#include <sch_sheet.h>
|
|
#include <sch_edit_frame.h>
|
|
#include <schematic.h>
|
|
#include <string_utils.h>
|
|
#include <symbol_lib_table.h>
|
|
#include <wildcards_and_files_ext.h>
|
|
#include <project_sch.h>
|
|
|
|
#include <cctype>
|
|
#include <map>
|
|
|
|
|
|
typedef std::pair<SCH_SYMBOL*, wxString> SYMBOL_NAME_PAIR;
|
|
|
|
|
|
// Helper sort function, used in getSymbols, to sort a symbol list by lib_id
|
|
static bool sort_by_libid( const SCH_SYMBOL* ref, SCH_SYMBOL* cmp )
|
|
{
|
|
return ref->GetLibId() < cmp->GetLibId();
|
|
}
|
|
|
|
|
|
/**
|
|
* Fill a vector with all of the project's symbols, to ease iterating over them.
|
|
*
|
|
* The list is sorted by #LIB_ID, therefore symbols using the same library
|
|
* symbol are grouped, allowing later faster calculations (one library search by group
|
|
* of symbols)
|
|
*
|
|
* @param aSymbols is a vector that will take the symbols.
|
|
*/
|
|
static void getSymbols( SCHEMATIC* aSchematic, std::vector<SCH_SYMBOL*>& aSymbols )
|
|
{
|
|
SCH_SCREENS screens( aSchematic->Root() );
|
|
|
|
// Get the full list
|
|
for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
|
|
{
|
|
for( EDA_ITEM* aItem : screen->Items().OfType( SCH_SYMBOL_T ) )
|
|
aSymbols.push_back( static_cast<SCH_SYMBOL*>( aItem ) );
|
|
}
|
|
|
|
if( aSymbols.empty() )
|
|
return;
|
|
|
|
// sort aSymbols by lib symbol. symbols will be grouped by same lib symbol.
|
|
std::sort( aSymbols.begin(), aSymbols.end(), sort_by_libid );
|
|
}
|
|
|
|
|
|
/**
|
|
* Search the libraries for the first symbol with a given name.
|
|
*
|
|
* @param aName - name to search for
|
|
* @param aLibs - the loaded SYMBOL_LIBS
|
|
* @param aCached - whether we are looking for the cached symbol
|
|
*/
|
|
static LIB_SYMBOL* findSymbol( const wxString& aName, SYMBOL_LIBS* aLibs, bool aCached )
|
|
{
|
|
LIB_SYMBOL *symbol = nullptr;
|
|
|
|
for( SYMBOL_LIB& each_lib : *aLibs )
|
|
{
|
|
if( aCached && !each_lib.IsCache() )
|
|
continue;
|
|
|
|
if( !aCached && each_lib.IsCache() )
|
|
continue;
|
|
|
|
symbol = each_lib.FindSymbol( aName );
|
|
|
|
if( symbol )
|
|
break;
|
|
}
|
|
|
|
return symbol;
|
|
}
|
|
|
|
|
|
static wxFileName GetRescueLibraryFileName( SCHEMATIC* aSchematic )
|
|
{
|
|
wxFileName fn = aSchematic->GetFileName();
|
|
fn.SetName( fn.GetName() + wxT( "-rescue" ) );
|
|
fn.SetExt( LegacySymbolLibFileExtension );
|
|
return fn;
|
|
}
|
|
|
|
|
|
RESCUE_CASE_CANDIDATE::RESCUE_CASE_CANDIDATE( const wxString& aRequestedName,
|
|
const wxString& aNewName,
|
|
LIB_SYMBOL* aLibCandidate,
|
|
int aUnit,
|
|
int aConvert )
|
|
{
|
|
m_requested_name = aRequestedName;
|
|
m_new_name = aNewName;
|
|
m_lib_candidate = aLibCandidate;
|
|
m_unit = aUnit;
|
|
m_convert = aConvert;
|
|
}
|
|
|
|
|
|
void RESCUE_CASE_CANDIDATE::FindRescues( RESCUER& aRescuer,
|
|
boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
|
|
{
|
|
typedef std::map<wxString, RESCUE_CASE_CANDIDATE> candidate_map_t;
|
|
candidate_map_t candidate_map;
|
|
|
|
// Remember the list of symbols is sorted by symbol name.
|
|
// So a search in libraries is made only once by group
|
|
LIB_SYMBOL* case_sensitive_match = nullptr;
|
|
std::vector<LIB_SYMBOL*> case_insensitive_matches;
|
|
|
|
wxString symbol_name;
|
|
wxString last_symbol_name;
|
|
|
|
for( SCH_SYMBOL* eachSymbol : *( aRescuer.GetSymbols() ) )
|
|
{
|
|
symbol_name = eachSymbol->GetLibId().GetUniStringLibItemName();
|
|
|
|
if( last_symbol_name != symbol_name )
|
|
{
|
|
// A new symbol name is found (a new group starts here).
|
|
// Search the symbol names candidates only once for this group:
|
|
last_symbol_name = symbol_name;
|
|
case_insensitive_matches.clear();
|
|
|
|
LIB_ID id( wxEmptyString, symbol_name );
|
|
|
|
case_sensitive_match = PROJECT_SCH::SchLibs( aRescuer.GetPrj() )->FindLibSymbol( id );
|
|
|
|
if( case_sensitive_match )
|
|
continue;
|
|
|
|
// If the case sensitive match failed, try a case insensitive match.
|
|
PROJECT_SCH::SchLibs( aRescuer.GetPrj() )
|
|
->FindLibraryNearEntries( case_insensitive_matches,
|
|
symbol_name );
|
|
|
|
// If there are not case insensitive matches either, the symbol cannot be rescued.
|
|
if( !case_insensitive_matches.size() )
|
|
continue;
|
|
|
|
RESCUE_CASE_CANDIDATE candidate( symbol_name, case_insensitive_matches[0]->GetName(),
|
|
case_insensitive_matches[0],
|
|
eachSymbol->GetUnit(),
|
|
eachSymbol->GetConvert() );
|
|
|
|
candidate_map[symbol_name] = candidate;
|
|
}
|
|
}
|
|
|
|
// Now, dump the map into aCandidates
|
|
for( const candidate_map_t::value_type& each_pair : candidate_map )
|
|
{
|
|
aCandidates.push_back( new RESCUE_CASE_CANDIDATE( each_pair.second ) );
|
|
}
|
|
}
|
|
|
|
|
|
wxString RESCUE_CASE_CANDIDATE::GetActionDescription() const
|
|
{
|
|
wxString action;
|
|
action.Printf( _( "Rename %s to %s" ), m_requested_name, m_new_name );
|
|
return action;
|
|
}
|
|
|
|
|
|
bool RESCUE_CASE_CANDIDATE::PerformAction( RESCUER* aRescuer )
|
|
{
|
|
wxCHECK( m_lib_candidate, true );
|
|
|
|
std::unique_ptr<LIB_SYMBOL> new_symbol = m_lib_candidate->Flatten();
|
|
new_symbol->SetName( m_new_name );
|
|
aRescuer->AddSymbol( new_symbol.get() );
|
|
|
|
for( SCH_SYMBOL* eachSymbol : *aRescuer->GetSymbols() )
|
|
{
|
|
if( eachSymbol->GetLibId().GetLibItemName() != UTF8( m_requested_name ) )
|
|
continue;
|
|
|
|
LIB_ID libId;
|
|
|
|
libId.SetLibItemName( m_new_name );
|
|
eachSymbol->SetLibId( libId );
|
|
eachSymbol->ClearFlags();
|
|
aRescuer->LogRescue( eachSymbol, m_requested_name, m_new_name );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
RESCUE_CACHE_CANDIDATE::RESCUE_CACHE_CANDIDATE( const wxString& aRequestedName,
|
|
const wxString& aNewName,
|
|
LIB_SYMBOL* aCacheCandidate,
|
|
LIB_SYMBOL* aLibCandidate,
|
|
int aUnit,
|
|
int aConvert )
|
|
{
|
|
m_requested_name = aRequestedName;
|
|
m_new_name = aNewName;
|
|
m_cache_candidate = aCacheCandidate;
|
|
m_lib_candidate = aLibCandidate;
|
|
m_unit = aUnit;
|
|
m_convert = aConvert;
|
|
}
|
|
|
|
|
|
RESCUE_CACHE_CANDIDATE::RESCUE_CACHE_CANDIDATE()
|
|
{
|
|
m_cache_candidate = nullptr;
|
|
m_lib_candidate = nullptr;
|
|
}
|
|
|
|
|
|
void RESCUE_CACHE_CANDIDATE::FindRescues( RESCUER& aRescuer,
|
|
boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
|
|
{
|
|
typedef std::map<wxString, RESCUE_CACHE_CANDIDATE> candidate_map_t;
|
|
candidate_map_t candidate_map;
|
|
|
|
// Remember the list of symbols is sorted by symbol name.
|
|
// So a search in libraries is made only once by group
|
|
LIB_SYMBOL* cache_match = nullptr;
|
|
LIB_SYMBOL* lib_match = nullptr;
|
|
wxString symbol_name;
|
|
wxString old_symbol_name;
|
|
|
|
for( SCH_SYMBOL* eachSymbol : *( aRescuer.GetSymbols() ) )
|
|
{
|
|
symbol_name = eachSymbol->GetLibId().GetUniStringLibItemName();
|
|
|
|
if( old_symbol_name != symbol_name )
|
|
{
|
|
// A new symbol name is found (a new group starts here).
|
|
// Search the symbol names candidates only once for this group:
|
|
old_symbol_name = symbol_name;
|
|
cache_match = findSymbol( symbol_name, PROJECT_SCH::SchLibs( aRescuer.GetPrj() ), true );
|
|
lib_match = findSymbol( symbol_name, PROJECT_SCH::SchLibs( aRescuer.GetPrj() ), false );
|
|
|
|
// At some point during V5 development, the LIB_ID delimiter character ':' was
|
|
// replaced by '_' when writing the symbol cache library so we have to test for
|
|
// the LIB_NICKNAME_LIB_SYMBOL_NAME case.
|
|
if( !cache_match && eachSymbol->GetLibId().IsValid() )
|
|
{
|
|
wxString tmp;
|
|
|
|
tmp = eachSymbol->GetLibId().GetLibNickname().wx_str() + wxT( "_" ) +
|
|
eachSymbol->GetLibId().GetLibItemName().wx_str();
|
|
cache_match = findSymbol( tmp, PROJECT_SCH::SchLibs( aRescuer.GetPrj() ), true );
|
|
}
|
|
|
|
// Test whether there is a conflict or if the symbol can only be found in the cache
|
|
// and the symbol name does not have any illegal characters.
|
|
if( cache_match && lib_match &&
|
|
!cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) )
|
|
continue;
|
|
|
|
if( !cache_match && lib_match )
|
|
continue;
|
|
|
|
// Check if the symbol has already been rescued.
|
|
RESCUE_CACHE_CANDIDATE candidate( symbol_name, symbol_name, cache_match, lib_match,
|
|
eachSymbol->GetUnit(),
|
|
eachSymbol->GetConvert() );
|
|
|
|
candidate_map[symbol_name] = candidate;
|
|
}
|
|
}
|
|
|
|
// Now, dump the map into aCandidates
|
|
for( const candidate_map_t::value_type& each_pair : candidate_map )
|
|
{
|
|
aCandidates.push_back( new RESCUE_CACHE_CANDIDATE( each_pair.second ) );
|
|
}
|
|
}
|
|
|
|
|
|
wxString RESCUE_CACHE_CANDIDATE::GetActionDescription() const
|
|
{
|
|
wxString action;
|
|
|
|
if( !m_cache_candidate && !m_lib_candidate )
|
|
action.Printf( _( "Cannot rescue symbol %s which is not available in any library or "
|
|
"the cache." ), m_requested_name );
|
|
else if( m_cache_candidate && !m_lib_candidate )
|
|
action.Printf( _( "Rescue symbol %s found only in cache library to %s." ),
|
|
m_requested_name, m_new_name );
|
|
else
|
|
action.Printf( _( "Rescue modified symbol %s to %s" ),
|
|
m_requested_name, m_new_name );
|
|
|
|
return action;
|
|
}
|
|
|
|
|
|
bool RESCUE_CACHE_CANDIDATE::PerformAction( RESCUER* aRescuer )
|
|
{
|
|
LIB_SYMBOL* tmp = ( m_cache_candidate ) ? m_cache_candidate : m_lib_candidate;
|
|
|
|
// A symbol that cannot be rescued is a valid condition so just bail out here.
|
|
if( !tmp )
|
|
return true;
|
|
|
|
std::unique_ptr<LIB_SYMBOL> new_symbol = tmp->Flatten();
|
|
new_symbol->SetName( m_new_name );
|
|
aRescuer->AddSymbol( new_symbol.get() );
|
|
|
|
for( SCH_SYMBOL* eachSymbol : *aRescuer->GetSymbols() )
|
|
{
|
|
if( eachSymbol->GetLibId().GetLibItemName() != UTF8( m_requested_name ) )
|
|
continue;
|
|
|
|
LIB_ID libId;
|
|
|
|
libId.SetLibItemName( m_new_name );
|
|
eachSymbol->SetLibId( libId );
|
|
eachSymbol->ClearFlags();
|
|
aRescuer->LogRescue( eachSymbol, m_requested_name, m_new_name );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::RESCUE_SYMBOL_LIB_TABLE_CANDIDATE(
|
|
const LIB_ID& aRequestedId,
|
|
const LIB_ID& aNewId,
|
|
LIB_SYMBOL* aCacheCandidate,
|
|
LIB_SYMBOL* aLibCandidate,
|
|
int aUnit,
|
|
int aConvert ) : RESCUE_CANDIDATE()
|
|
{
|
|
m_requested_id = aRequestedId;
|
|
m_requested_name = aRequestedId.Format().wx_str();
|
|
m_new_id = aNewId;
|
|
m_lib_candidate = aLibCandidate;
|
|
m_cache_candidate = aCacheCandidate;
|
|
m_unit = aUnit;
|
|
m_convert = aConvert;
|
|
}
|
|
|
|
|
|
RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::RESCUE_SYMBOL_LIB_TABLE_CANDIDATE()
|
|
{
|
|
m_cache_candidate = nullptr;
|
|
m_lib_candidate = nullptr;
|
|
}
|
|
|
|
|
|
void RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::FindRescues(
|
|
RESCUER& aRescuer,
|
|
boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
|
|
{
|
|
typedef std::map<LIB_ID, RESCUE_SYMBOL_LIB_TABLE_CANDIDATE> candidate_map_t;
|
|
|
|
candidate_map_t candidate_map;
|
|
|
|
// Remember the list of symbols is sorted by LIB_ID.
|
|
// So a search in libraries is made only once by group
|
|
LIB_SYMBOL* cache_match = nullptr;
|
|
LIB_SYMBOL* lib_match = nullptr;
|
|
LIB_ID old_symbol_id;
|
|
|
|
wxString symbolName;
|
|
|
|
for( SCH_SYMBOL* eachSymbol : *( aRescuer.GetSymbols() ) )
|
|
{
|
|
const LIB_ID& symbol_id = eachSymbol->GetLibId();
|
|
|
|
if( old_symbol_id != symbol_id )
|
|
{
|
|
// A new symbol name is found (a new group starts here).
|
|
// Search the symbol names candidates only once for this group:
|
|
old_symbol_id = symbol_id;
|
|
|
|
symbolName = symbol_id.Format().wx_str();
|
|
|
|
// Get the library symbol from the cache library. It will be a flattened
|
|
// symbol by default (no inheritance).
|
|
cache_match = findSymbol( symbolName, PROJECT_SCH::SchLibs( aRescuer.GetPrj() ), true );
|
|
|
|
// At some point during V5 development, the LIB_ID delimiter character ':' was
|
|
// replaced by '_' when writing the symbol cache library so we have to test for
|
|
// the LIB_NICKNAME_LIB_SYMBOL_NAME case.
|
|
if( !cache_match )
|
|
{
|
|
symbolName = symbol_id.GetLibNickname().wx_str() + wxT( "_" ) +
|
|
symbol_id.GetLibItemName().wx_str();
|
|
cache_match = findSymbol( symbolName, PROJECT_SCH::SchLibs( aRescuer.GetPrj() ), true );
|
|
}
|
|
|
|
// Get the library symbol from the symbol library table.
|
|
lib_match = SchGetLibSymbol( symbol_id, PROJECT_SCH::SchSymbolLibTable( aRescuer.GetPrj() ) );
|
|
|
|
if( !cache_match && !lib_match )
|
|
continue;
|
|
|
|
LIB_SYMBOL_SPTR lib_match_parent;
|
|
|
|
// If it's a derive symbol, use the parent symbol to perform the pin test.
|
|
if( lib_match && lib_match->IsAlias() )
|
|
{
|
|
lib_match_parent = lib_match->GetRootSymbol();
|
|
|
|
if( !lib_match_parent )
|
|
lib_match = nullptr;
|
|
else
|
|
lib_match = lib_match_parent.get();
|
|
}
|
|
|
|
// Test whether there is a conflict or if the symbol can only be found in the cache.
|
|
if( LIB_ID::HasIllegalChars( symbol_id.GetLibItemName() ) == -1 )
|
|
{
|
|
if( cache_match && lib_match &&
|
|
!cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( !cache_match && lib_match )
|
|
continue;
|
|
}
|
|
|
|
// Fix illegal LIB_ID name characters.
|
|
wxString new_name = EscapeString( symbol_id.GetLibItemName(), CTX_LIBID );
|
|
|
|
// Differentiate symbol name in the rescue library by appending the original symbol
|
|
// library table nickname to the symbol name to prevent name clashes in the rescue
|
|
// library.
|
|
wxString libNickname = GetRescueLibraryFileName( aRescuer.Schematic() ).GetName();
|
|
|
|
LIB_ID new_id( libNickname, new_name + wxS( "-" ) + symbol_id.GetLibNickname().wx_str() );
|
|
|
|
RESCUE_SYMBOL_LIB_TABLE_CANDIDATE candidate( symbol_id, new_id, cache_match, lib_match,
|
|
eachSymbol->GetUnit(),
|
|
eachSymbol->GetConvert() );
|
|
|
|
candidate_map[symbol_id] = candidate;
|
|
}
|
|
}
|
|
|
|
// Now, dump the map into aCandidates
|
|
for( const candidate_map_t::value_type& each_pair : candidate_map )
|
|
{
|
|
aCandidates.push_back( new RESCUE_SYMBOL_LIB_TABLE_CANDIDATE( each_pair.second ) );
|
|
}
|
|
}
|
|
|
|
|
|
wxString RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::GetActionDescription() const
|
|
{
|
|
wxString action;
|
|
|
|
if( !m_cache_candidate && !m_lib_candidate )
|
|
{
|
|
action.Printf( _( "Cannot rescue symbol %s which is not available in any library or "
|
|
"the cache." ),
|
|
UnescapeString( m_requested_id.GetLibItemName().wx_str() ) );
|
|
}
|
|
else if( m_cache_candidate && !m_lib_candidate )
|
|
{
|
|
action.Printf( _( "Rescue symbol %s found only in cache library to %s." ),
|
|
UnescapeString( m_requested_id.Format().wx_str() ),
|
|
UnescapeString( m_new_id.Format().wx_str() ) );
|
|
}
|
|
else
|
|
{
|
|
action.Printf( _( "Rescue modified symbol %s to %s" ),
|
|
UnescapeString( m_requested_id.Format().wx_str() ),
|
|
UnescapeString( m_new_id.Format().wx_str() ) );
|
|
}
|
|
|
|
return action;
|
|
}
|
|
|
|
|
|
bool RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::PerformAction( RESCUER* aRescuer )
|
|
{
|
|
LIB_SYMBOL* tmp = ( m_cache_candidate ) ? m_cache_candidate : m_lib_candidate;
|
|
|
|
wxCHECK_MSG( tmp, false, wxS( "Both cache and library symbols undefined." ) );
|
|
|
|
std::unique_ptr<LIB_SYMBOL> new_symbol = tmp->Flatten();
|
|
new_symbol->SetLibId( m_new_id );
|
|
new_symbol->SetName( m_new_id.GetLibItemName() );
|
|
aRescuer->AddSymbol( new_symbol.get() );
|
|
|
|
for( SCH_SYMBOL* eachSymbol : *aRescuer->GetSymbols() )
|
|
{
|
|
if( eachSymbol->GetLibId() != m_requested_id )
|
|
continue;
|
|
|
|
eachSymbol->SetLibId( m_new_id );
|
|
eachSymbol->ClearFlags();
|
|
aRescuer->LogRescue( eachSymbol, m_requested_id.Format(), m_new_id.Format() );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
RESCUER::RESCUER( PROJECT& aProject, SCHEMATIC* aSchematic, SCH_SHEET_PATH* aCurrentSheet,
|
|
EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType )
|
|
{
|
|
m_schematic = aSchematic ? aSchematic : aCurrentSheet->LastScreen()->Schematic();
|
|
|
|
wxASSERT( m_schematic );
|
|
|
|
if( m_schematic )
|
|
getSymbols( m_schematic, m_symbols );
|
|
|
|
m_prj = &aProject;
|
|
m_currentSheet = aCurrentSheet;
|
|
m_galBackEndType = aGalBackEndType;
|
|
}
|
|
|
|
|
|
void RESCUER::LogRescue( SCH_SYMBOL* aSymbol, const wxString &aOldName,
|
|
const wxString &aNewName )
|
|
{
|
|
RESCUE_LOG logitem;
|
|
logitem.symbol = aSymbol;
|
|
logitem.old_name = aOldName;
|
|
logitem.new_name = aNewName;
|
|
m_rescue_log.push_back( logitem );
|
|
}
|
|
|
|
|
|
bool RESCUER::DoRescues()
|
|
{
|
|
for( RESCUE_CANDIDATE* each_candidate : m_chosen_candidates )
|
|
{
|
|
if( ! each_candidate->PerformAction( this ) )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void RESCUER::UndoRescues()
|
|
{
|
|
for( RESCUE_LOG& each_logitem : m_rescue_log )
|
|
{
|
|
LIB_ID libId;
|
|
|
|
libId.SetLibItemName( each_logitem.old_name );
|
|
each_logitem.symbol->SetLibId( libId );
|
|
each_logitem.symbol->ClearFlags();
|
|
}
|
|
}
|
|
|
|
|
|
bool RESCUER::RescueProject( wxWindow* aParent, RESCUER& aRescuer, bool aRunningOnDemand )
|
|
{
|
|
aRescuer.FindCandidates();
|
|
|
|
if( !aRescuer.GetCandidateCount() )
|
|
{
|
|
if( aRunningOnDemand )
|
|
{
|
|
wxMessageDialog dlg( aParent, _( "This project has nothing to rescue." ),
|
|
_( "Project Rescue Helper" ) );
|
|
dlg.ShowModal();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
aRescuer.RemoveDuplicates();
|
|
aRescuer.InvokeDialog( aParent, !aRunningOnDemand );
|
|
|
|
// If no symbols were rescued, let the user know what's going on. He might
|
|
// have clicked cancel by mistake, and should have some indication of that.
|
|
if( !aRescuer.GetChosenCandidateCount() )
|
|
{
|
|
wxMessageDialog dlg( aParent, _( "No symbols were rescued." ),
|
|
_( "Project Rescue Helper" ) );
|
|
dlg.ShowModal();
|
|
|
|
// Set the modified flag even on Cancel. Many users seem to instinctively want to Save at
|
|
// this point, due to the reloading of the symbols, so we'll make the save button active.
|
|
return true;
|
|
}
|
|
|
|
aRescuer.OpenRescueLibrary();
|
|
|
|
if( !aRescuer.DoRescues() )
|
|
{
|
|
aRescuer.UndoRescues();
|
|
return false;
|
|
}
|
|
|
|
aRescuer.WriteRescueLibrary( aParent );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void RESCUER::RemoveDuplicates()
|
|
{
|
|
std::vector<wxString> names_seen;
|
|
|
|
for( boost::ptr_vector<RESCUE_CANDIDATE>::iterator it = m_all_candidates.begin();
|
|
it != m_all_candidates.end(); )
|
|
{
|
|
bool seen_already = false;
|
|
|
|
for( wxString& name_seen : names_seen )
|
|
{
|
|
if( name_seen == it->GetRequestedName() )
|
|
{
|
|
seen_already = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( seen_already )
|
|
{
|
|
it = m_all_candidates.erase( it );
|
|
}
|
|
else
|
|
{
|
|
names_seen.push_back( it->GetRequestedName() );
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void LEGACY_RESCUER::FindCandidates()
|
|
{
|
|
RESCUE_CASE_CANDIDATE::FindRescues( *this, m_all_candidates );
|
|
RESCUE_CACHE_CANDIDATE::FindRescues( *this, m_all_candidates );
|
|
}
|
|
|
|
|
|
void LEGACY_RESCUER::InvokeDialog( wxWindow* aParent, bool aAskShowAgain )
|
|
{
|
|
InvokeDialogRescueEach( aParent, static_cast< RESCUER& >( *this ), m_currentSheet,
|
|
m_galBackEndType, aAskShowAgain );
|
|
}
|
|
|
|
|
|
void LEGACY_RESCUER::OpenRescueLibrary()
|
|
{
|
|
wxFileName fn = GetRescueLibraryFileName( m_schematic );
|
|
|
|
std::unique_ptr<SYMBOL_LIB> rescue_lib = std::make_unique<SYMBOL_LIB>( SCH_LIB_TYPE::LT_EESCHEMA,
|
|
fn.GetFullPath() );
|
|
|
|
m_rescue_lib = std::move( rescue_lib );
|
|
m_rescue_lib->EnableBuffering();
|
|
|
|
// If a rescue library already exists copy the contents of that library so we do not
|
|
// lose any previous rescues.
|
|
SYMBOL_LIB* rescueLib = PROJECT_SCH::SchLibs( m_prj )->FindLibrary( fn.GetName() );
|
|
|
|
if( rescueLib )
|
|
{
|
|
// For items in the rescue library, aliases are the root symbol.
|
|
std::vector< LIB_SYMBOL* > symbols;
|
|
|
|
rescueLib->GetSymbols( symbols );
|
|
|
|
for( LIB_SYMBOL* symbol : symbols )
|
|
{
|
|
// The LIB_SYMBOL copy constructor flattens derived symbols (formerly known as aliases).
|
|
m_rescue_lib->AddSymbol( new LIB_SYMBOL( *symbol, m_rescue_lib.get() ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool LEGACY_RESCUER::WriteRescueLibrary( wxWindow *aParent )
|
|
{
|
|
try
|
|
{
|
|
m_rescue_lib->Save( false );
|
|
}
|
|
catch( ... /* IO_ERROR ioe */ )
|
|
{
|
|
wxString msg;
|
|
|
|
msg.Printf( _( "Failed to create symbol library file '%s'." ),
|
|
m_rescue_lib->GetFullFileName() );
|
|
DisplayError( aParent, msg );
|
|
return false;
|
|
}
|
|
|
|
wxArrayString libNames;
|
|
wxString libPaths;
|
|
|
|
wxString libName = m_rescue_lib->GetName();
|
|
SYMBOL_LIBS *libs = dynamic_cast<SYMBOL_LIBS*>( m_prj->GetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS ) );
|
|
|
|
if( !libs )
|
|
{
|
|
libs = new SYMBOL_LIBS();
|
|
m_prj->SetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS, libs );
|
|
}
|
|
|
|
try
|
|
{
|
|
SYMBOL_LIBS::GetLibNamesAndPaths( m_prj, &libPaths, &libNames );
|
|
|
|
// Make sure the library is not already in the list
|
|
while( libNames.Index( libName ) != wxNOT_FOUND )
|
|
libNames.Remove( libName );
|
|
|
|
// Add the library to the top of the list and save.
|
|
libNames.Insert( libName, 0 );
|
|
SYMBOL_LIBS::SetLibNamesAndPaths( m_prj, libPaths, libNames );
|
|
}
|
|
catch( const IO_ERROR& )
|
|
{
|
|
// Could not get or save the current libraries.
|
|
return false;
|
|
}
|
|
|
|
// Save the old libraries in case there is a problem after clear(). We'll
|
|
// put them back in.
|
|
boost::ptr_vector<SYMBOL_LIB> libsSave;
|
|
libsSave.transfer( libsSave.end(), libs->begin(), libs->end(), *libs );
|
|
|
|
m_prj->SetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS, nullptr );
|
|
|
|
libs = new SYMBOL_LIBS();
|
|
|
|
try
|
|
{
|
|
libs->LoadAllLibraries( m_prj );
|
|
}
|
|
catch( const PARSE_ERROR& )
|
|
{
|
|
// Some libraries were not found. There's no point in showing the error,
|
|
// because it was already shown. Just don't do anything.
|
|
}
|
|
catch( const IO_ERROR& )
|
|
{
|
|
// Restore the old list
|
|
libs->clear();
|
|
libs->transfer( libs->end(), libsSave.begin(), libsSave.end(), libsSave );
|
|
return false;
|
|
}
|
|
|
|
m_prj->SetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS, libs );
|
|
|
|
// Update the schematic symbol library links since the library list has changed.
|
|
SCH_SCREENS schematic( m_schematic->Root() );
|
|
schematic.UpdateSymbolLinks();
|
|
return true;
|
|
}
|
|
|
|
|
|
void LEGACY_RESCUER::AddSymbol( LIB_SYMBOL* aNewSymbol )
|
|
{
|
|
wxCHECK_RET( aNewSymbol, wxS( "Invalid LIB_SYMBOL pointer." ) );
|
|
|
|
aNewSymbol->SetLib( m_rescue_lib.get() );
|
|
m_rescue_lib->AddSymbol( aNewSymbol );
|
|
}
|
|
|
|
|
|
SYMBOL_LIB_TABLE_RESCUER::SYMBOL_LIB_TABLE_RESCUER( PROJECT& aProject, SCHEMATIC* aSchematic,
|
|
SCH_SHEET_PATH* aCurrentSheet,
|
|
EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType ) :
|
|
RESCUER( aProject, aSchematic, aCurrentSheet, aGalBackEndType )
|
|
{
|
|
m_properties = std::make_unique<STRING_UTF8_MAP>();
|
|
}
|
|
|
|
|
|
void SYMBOL_LIB_TABLE_RESCUER::FindCandidates()
|
|
{
|
|
RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::FindRescues( *this, m_all_candidates );
|
|
}
|
|
|
|
|
|
void SYMBOL_LIB_TABLE_RESCUER::InvokeDialog( wxWindow* aParent, bool aAskShowAgain )
|
|
{
|
|
InvokeDialogRescueEach( aParent, static_cast< RESCUER& >( *this ), m_currentSheet,
|
|
m_galBackEndType, aAskShowAgain );
|
|
}
|
|
|
|
|
|
void SYMBOL_LIB_TABLE_RESCUER::OpenRescueLibrary()
|
|
{
|
|
(*m_properties)[ SCH_IO_KICAD_LEGACY::PropBuffering ] = "";
|
|
|
|
wxFileName fn = GetRescueLibraryFileName( m_schematic );
|
|
|
|
SYMBOL_LIB_TABLE_ROW* row = PROJECT_SCH::SchSymbolLibTable( m_prj )->FindRow( fn.GetName() );
|
|
|
|
// If a rescue library already exists copy the contents of that library so we do not
|
|
// lose any previous rescues.
|
|
if( row )
|
|
{
|
|
if( SCH_IO_MGR::EnumFromStr( row->GetType() ) == SCH_IO_MGR::SCH_KICAD )
|
|
fn.SetExt( KiCadSymbolLibFileExtension );
|
|
|
|
std::vector<LIB_SYMBOL*> symbols;
|
|
|
|
try
|
|
{
|
|
PROJECT_SCH::SchSymbolLibTable( m_prj )->LoadSymbolLib( symbols, fn.GetName() );
|
|
}
|
|
catch( ... /* IO_ERROR */ )
|
|
{
|
|
return;
|
|
}
|
|
|
|
for( LIB_SYMBOL* symbol : symbols )
|
|
m_rescueLibSymbols.emplace_back( std::make_unique<LIB_SYMBOL>( *symbol ) );
|
|
}
|
|
}
|
|
|
|
|
|
bool SYMBOL_LIB_TABLE_RESCUER::WriteRescueLibrary( wxWindow *aParent )
|
|
{
|
|
wxString msg;
|
|
wxFileName fn = GetRescueLibraryFileName( m_schematic );
|
|
SYMBOL_LIB_TABLE_ROW* row = PROJECT_SCH::SchSymbolLibTable( m_prj )->FindRow( fn.GetName() );
|
|
|
|
fn.SetExt( KiCadSymbolLibFileExtension );
|
|
|
|
try
|
|
{
|
|
SCH_IO::SCH_IO_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
|
|
|
|
for( const std::unique_ptr<LIB_SYMBOL>& symbol : m_rescueLibSymbols )
|
|
pi->SaveSymbol( fn.GetFullPath(), new LIB_SYMBOL( *symbol.get() ), m_properties.get() );
|
|
|
|
pi->SaveLibrary( fn.GetFullPath() );
|
|
}
|
|
catch( const IO_ERROR& ioe )
|
|
{
|
|
msg.Printf( _( "Failed to save rescue library %s." ), fn.GetFullPath() );
|
|
DisplayErrorMessage( aParent, msg, ioe.What() );
|
|
return false;
|
|
}
|
|
|
|
// If the rescue library already exists in the symbol library table no need save it to add
|
|
// it to the table.
|
|
if( !row || ( SCH_IO_MGR::EnumFromStr( row->GetType() ) == SCH_IO_MGR::SCH_LEGACY ) )
|
|
{
|
|
wxString uri = wxS( "${KIPRJMOD}/" ) + fn.GetFullName();
|
|
wxString libNickname = fn.GetName();
|
|
|
|
row = new SYMBOL_LIB_TABLE_ROW( libNickname, uri, wxT( "KiCad" ) );
|
|
PROJECT_SCH::SchSymbolLibTable( m_prj )->InsertRow( row, true );
|
|
|
|
fn = wxFileName( m_prj->GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
|
|
|
|
try
|
|
{
|
|
PROJECT_SCH::SchSymbolLibTable( m_prj )->Save( fn.GetFullPath() );
|
|
}
|
|
catch( const IO_ERROR& ioe )
|
|
{
|
|
msg.Printf( _( "Error occurred saving project specific symbol library table." ) );
|
|
DisplayErrorMessage( aParent, msg, ioe.What() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_prj->SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, nullptr );
|
|
|
|
// This can only happen if the symbol library table file was corrupted on write.
|
|
if( !PROJECT_SCH::SchSymbolLibTable( m_prj ) )
|
|
return false;
|
|
|
|
// Update the schematic symbol library links since the library list has changed.
|
|
SCH_SCREENS schematic( m_schematic->Root() );
|
|
schematic.UpdateSymbolLinks();
|
|
return true;
|
|
}
|
|
|
|
|
|
void SYMBOL_LIB_TABLE_RESCUER::AddSymbol( LIB_SYMBOL* aNewSymbol )
|
|
{
|
|
wxCHECK_RET( aNewSymbol, wxS( "Invalid LIB_SYMBOL pointer." ) );
|
|
|
|
m_rescueLibSymbols.emplace_back( std::make_unique<LIB_SYMBOL>( *aNewSymbol ) );
|
|
}
|