kicad-source/eeschema/symbol_library.cpp
Jeff Young 50ccc4e6da Fix issue converting legacy SPICE models.
1) if a legacy model references a library then we need to see if said
   libraray exists and read model from it if so
2) legacy node ordering is by index, not pin name
3) we can't auto-generate a pin map when we don't know the pin names,
   so don't try
2022-12-14 13:36:28 +00:00

573 lines
15 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 2022 CERN
* Copyright (C) 2004-2022 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 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 <algorithm>
#include <kiface_base.h>
#include <eda_base_frame.h>
#include <string_utils.h>
#include <macros.h>
#include <richio.h>
#include <config_params.h>
#include <wildcards_and_files_ext.h>
#include <project/project_file.h>
#include <project_rescue.h>
#include <string_utf8_map.h>
#include <widgets/app_progress_dialog.h>
#include <general.h>
#include <symbol_library.h>
#include <sch_plugins/legacy/sch_legacy_plugin.h>
#include <wx/log.h>
#include <wx/progdlg.h>
#include <wx/tokenzr.h>
#include "sim/sim_model.h"
SYMBOL_LIB::SYMBOL_LIB( SCH_LIB_TYPE aType, const wxString& aFileName,
SCH_IO_MGR::SCH_FILE_T aPluginType ) :
m_pluginType( aPluginType )
{
type = aType;
isModified = false;
timeStamp = 0;
timeStamp = wxDateTime::Now();
versionMajor = 0; // Will be updated after reading the lib file
versionMinor = 0; // Will be updated after reading the lib file
fileName = aFileName;
if( !fileName.IsOk() )
fileName = "unnamed.lib";
m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
m_properties = std::make_unique<STRING_UTF8_MAP>();
m_mod_hash = 0;
}
SYMBOL_LIB::~SYMBOL_LIB()
{
}
void SYMBOL_LIB::Save( bool aSaveDocFile )
{
wxCHECK_RET( m_plugin != nullptr,
wxString::Format( wxT( "no plugin defined for library `%s`." ),
fileName.GetFullPath() ) );
STRING_UTF8_MAP props;
if( !aSaveDocFile )
props[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = "";
m_plugin->SaveLibrary( fileName.GetFullPath(), &props );
isModified = false;
}
void SYMBOL_LIB::Create( const wxString& aFileName )
{
wxString tmpFileName = fileName.GetFullPath();
if( !aFileName.IsEmpty() )
tmpFileName = aFileName;
m_plugin->CreateSymbolLib( tmpFileName, m_properties.get() );
}
void SYMBOL_LIB::SetPluginType( SCH_IO_MGR::SCH_FILE_T aPluginType )
{
if( m_pluginType != aPluginType )
{
m_pluginType = aPluginType;
m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
}
}
bool SYMBOL_LIB::IsCache() const
{
return m_properties->Exists( SCH_LEGACY_PLUGIN::PropNoDocFile );
}
void SYMBOL_LIB::SetCache()
{
(*m_properties)[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = "";
}
bool SYMBOL_LIB::IsBuffering() const
{
return m_properties->Exists( SCH_LEGACY_PLUGIN::PropBuffering );
}
void SYMBOL_LIB::EnableBuffering( bool aEnable )
{
if( aEnable )
(*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
else
m_properties->Clear( SCH_LEGACY_PLUGIN::PropBuffering );
}
void SYMBOL_LIB::GetSymbolNames( wxArrayString& aNames ) const
{
m_plugin->EnumerateSymbolLib( aNames, fileName.GetFullPath(), m_properties.get() );
aNames.Sort();
}
void SYMBOL_LIB::GetSymbols( std::vector<LIB_SYMBOL*>& aSymbols ) const
{
m_plugin->EnumerateSymbolLib( aSymbols, fileName.GetFullPath(), m_properties.get() );
std::sort( aSymbols.begin(), aSymbols.end(),
[](LIB_SYMBOL *lhs, LIB_SYMBOL *rhs) -> bool
{
return lhs->GetName() < rhs->GetName();
} );
}
LIB_SYMBOL* SYMBOL_LIB::FindSymbol( const wxString& aName ) const
{
LIB_SYMBOL* symbol = m_plugin->LoadSymbol( fileName.GetFullPath(), aName, m_properties.get() );
if( symbol )
{
// Set the library to this even though technically the legacy cache plugin owns the
// symbols. This allows the symbol library table conversion tool to determine the
// correct library where the symbol was found.
if( !symbol->GetLib() )
symbol->SetLib( const_cast<SYMBOL_LIB*>( this ) );
SIM_MODEL::MigrateSimModel<LIB_SYMBOL, LIB_FIELD>( *symbol, nullptr );
}
return symbol;
}
LIB_SYMBOL* SYMBOL_LIB::FindSymbol( const LIB_ID& aLibId ) const
{
return FindSymbol( aLibId.Format().wx_str() );
}
void SYMBOL_LIB::AddSymbol( LIB_SYMBOL* aSymbol )
{
// add a clone, not the caller's copy, the plugin take ownership of the new symbol.
m_plugin->SaveSymbol( fileName.GetFullPath(),
new LIB_SYMBOL( *aSymbol->SharedPtr().get(), this ),
m_properties.get() );
// If we are not buffering, the library file is updated immediately when the plugin
// SaveSymbol() function is called.
if( IsBuffering() )
isModified = true;
++m_mod_hash;
}
LIB_SYMBOL* SYMBOL_LIB::RemoveSymbol( LIB_SYMBOL* aEntry )
{
wxCHECK_MSG( aEntry != nullptr, nullptr, "NULL pointer cannot be removed from library." );
m_plugin->DeleteSymbol( fileName.GetFullPath(), aEntry->GetName(), m_properties.get() );
// If we are not buffering, the library file is updated immediately when the plugin
// SaveSymbol() function is called.
if( IsBuffering() )
isModified = true;
++m_mod_hash;
return nullptr;
}
LIB_SYMBOL* SYMBOL_LIB::ReplaceSymbol( LIB_SYMBOL* aOldSymbol, LIB_SYMBOL* aNewSymbol )
{
wxASSERT( aOldSymbol != nullptr );
wxASSERT( aNewSymbol != nullptr );
m_plugin->DeleteSymbol( fileName.GetFullPath(), aOldSymbol->GetName(), m_properties.get() );
LIB_SYMBOL* my_part = new LIB_SYMBOL( *aNewSymbol, this );
m_plugin->SaveSymbol( fileName.GetFullPath(), my_part, m_properties.get() );
// If we are not buffering, the library file is updated immediately when the plugin
// SaveSymbol() function is called.
if( IsBuffering() )
isModified = true;
++m_mod_hash;
return my_part;
}
SYMBOL_LIB* SYMBOL_LIB::LoadSymbolLibrary( const wxString& aFileName )
{
std::unique_ptr<SYMBOL_LIB> lib = std::make_unique<SYMBOL_LIB>( SCH_LIB_TYPE::LT_EESCHEMA,
aFileName );
std::vector<LIB_SYMBOL*> parts;
// This loads the library.
lib->GetSymbols( parts );
// Now, set the LIB_SYMBOL m_library member but it will only be used
// when loading legacy libraries in the future. Once the symbols in the
// schematic have a full #LIB_ID, this will not get called.
for( size_t ii = 0; ii < parts.size(); ii++ )
{
LIB_SYMBOL* part = parts[ii];
part->SetLib( lib.get() );
}
SYMBOL_LIB* ret = lib.release();
return ret;
}
SYMBOL_LIB* SYMBOL_LIBS::AddLibrary( const wxString& aFileName )
{
SYMBOL_LIB* lib;
wxFileName fn = aFileName;
// Don't reload the library if it is already loaded.
lib = FindLibrary( fn.GetName() );
if( lib )
return lib;
try
{
lib = SYMBOL_LIB::LoadSymbolLibrary( aFileName );
push_back( lib );
return lib;
}
catch( ... )
{
return nullptr;
}
}
SYMBOL_LIB* SYMBOL_LIBS::AddLibrary( const wxString& aFileName, SYMBOL_LIBS::iterator& aIterator )
{
// Don't reload the library if it is already loaded.
wxFileName fn( aFileName );
SYMBOL_LIB* lib = FindLibrary( fn.GetName() );
if( lib )
return lib;
try
{
lib = SYMBOL_LIB::LoadSymbolLibrary( aFileName );
if( aIterator >= begin() && aIterator < end() )
insert( aIterator, lib );
else
push_back( lib );
return lib;
}
catch( ... )
{
return nullptr;
}
}
SYMBOL_LIB* SYMBOL_LIBS::FindLibrary( const wxString& aName )
{
for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
{
if( it->GetName() == aName )
return &*it;
}
return nullptr;
}
SYMBOL_LIB* SYMBOL_LIBS::GetCacheLibrary()
{
for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
{
if( it->IsCache() )
return &*it;
}
return nullptr;
}
SYMBOL_LIB* SYMBOL_LIBS::FindLibraryByFullFileName( const wxString& aFullFileName )
{
for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
{
if( it->GetFullFileName() == aFullFileName )
return &*it;
}
return nullptr;
}
wxArrayString SYMBOL_LIBS::GetLibraryNames( bool aSorted )
{
wxArrayString cacheNames;
wxArrayString names;
for( SYMBOL_LIB& lib : *this )
{
if( lib.IsCache() && aSorted )
cacheNames.Add( lib.GetName() );
else
names.Add( lib.GetName() );
}
// Even sorted, the cache library is always at the end of the list.
if( aSorted )
names.Sort();
for( unsigned int i = 0; i<cacheNames.Count(); i++ )
names.Add( cacheNames.Item( i ) );
return names;
}
LIB_SYMBOL* SYMBOL_LIBS::FindLibSymbol( const LIB_ID& aLibId, const wxString& aLibraryName )
{
LIB_SYMBOL* part = nullptr;
for( SYMBOL_LIB& lib : *this )
{
if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
continue;
part = lib.FindSymbol( aLibId.GetLibItemName().wx_str() );
if( part )
break;
}
return part;
}
void SYMBOL_LIBS::FindLibraryNearEntries( std::vector<LIB_SYMBOL*>& aCandidates,
const wxString& aEntryName,
const wxString& aLibraryName )
{
for( SYMBOL_LIB& lib : *this )
{
if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
continue;
wxArrayString partNames;
lib.GetSymbolNames( partNames );
if( partNames.IsEmpty() )
continue;
for( size_t i = 0; i < partNames.size(); i++ )
{
if( partNames[i].CmpNoCase( aEntryName ) == 0 )
aCandidates.push_back( lib.FindSymbol( partNames[i] ) );
}
}
}
void SYMBOL_LIBS::GetLibNamesAndPaths( PROJECT* aProject, wxString* aPaths, wxArrayString* aNames )
{
wxCHECK_RET( aProject, "Null PROJECT in GetLibNamesAndPaths" );
PROJECT_FILE& project = aProject->GetProjectFile();
if( aPaths )
*aPaths = project.m_LegacyLibDir;
if( aNames )
*aNames = project.m_LegacyLibNames;
}
void SYMBOL_LIBS::SetLibNamesAndPaths( PROJECT* aProject, const wxString& aPaths,
const wxArrayString& aNames )
{
wxCHECK_RET( aProject, "Null PROJECT in SetLibNamesAndPaths" );
PROJECT_FILE& project = aProject->GetProjectFile();
project.m_LegacyLibDir = aPaths;
project.m_LegacyLibNames = aNames;
}
const wxString SYMBOL_LIBS::CacheName( const wxString& aFullProjectFilename )
{
wxFileName name = aFullProjectFilename;
name.SetName( name.GetName() + "-cache" );
name.SetExt( LegacySymbolLibFileExtension );
if( name.FileExists() )
return name.GetFullPath();
return wxEmptyString;
}
void SYMBOL_LIBS::LoadAllLibraries( PROJECT* aProject, bool aShowProgress )
{
wxString filename;
wxString libs_not_found;
SEARCH_STACK* lib_search = aProject->SchSearchS();
#if defined(DEBUG) && 0
lib_search->Show( __func__ );
#endif
wxArrayString lib_names;
GetLibNamesAndPaths( aProject, nullptr, &lib_names );
// Post symbol library table, this should be empty. Only the cache library should get loaded.
if( !lib_names.empty() )
{
APP_PROGRESS_DIALOG lib_dialog( _( "Loading Symbol Libraries" ),
wxEmptyString,
lib_names.GetCount(),
nullptr,
false,
wxPD_APP_MODAL );
if( aShowProgress )
{
lib_dialog.Show();
}
for( unsigned i = 0; i < lib_names.GetCount(); ++i )
{
if( aShowProgress )
{
lib_dialog.Update( i, wxString::Format( _( "Loading %s..." ), lib_names[i] ) );
}
// lib_names[] does not store the file extension. Set it.
// Remember lib_names[i] can contain a '.' in name, so using a wxFileName
// before adding the extension can create incorrect full filename
wxString fullname = lib_names[i] + "." + LegacySymbolLibFileExtension;
// Now the full name is set, we can use a wxFileName.
wxFileName fn( fullname );
// Skip if the file name is not valid..
if( !fn.IsOk() )
continue;
if( !fn.FileExists() )
{
filename = lib_search->FindValidPath( fn.GetFullPath() );
if( !filename )
{
libs_not_found += fn.GetFullPath();
libs_not_found += '\n';
continue;
}
}
else
{ // ensure the lib filename has a absolute path.
// If the lib has no absolute path, and is found in the cwd by fn.FileExists(),
// make a full absolute path, to avoid issues with load library functions which
// expects an absolute path.
if( !fn.IsAbsolute() )
fn.MakeAbsolute();
filename = fn.GetFullPath();
}
try
{
AddLibrary( filename );
}
catch( const IO_ERROR& ioe )
{
wxString msg;
msg.Printf( _( "Symbol library '%s' failed to load." ), filename );
wxLogError( msg + wxS( "\n" ) + ioe.What() );
}
}
}
// add the special cache library.
wxString cache_name = CacheName( aProject->GetProjectFullName() );
SYMBOL_LIB* cache_lib;
if( !aProject->IsNullProject() && !cache_name.IsEmpty() )
{
try
{
cache_lib = AddLibrary( cache_name );
if( cache_lib )
cache_lib->SetCache();
}
catch( const IO_ERROR& ioe )
{
wxString msg = wxString::Format( _( "Error loading symbol library '%s'." )
+ wxS( "\n%s" ),
cache_name,
ioe.What() );
THROW_IO_ERROR( msg );
}
}
// Print the libraries not found
if( !libs_not_found.IsEmpty() )
{
// Use a different exception type so catch()er can route to proper use
// of the HTML_MESSAGE_BOX.
THROW_PARSE_ERROR( wxEmptyString, __func__, TO_UTF8( libs_not_found ), 0, 0 );
}
}