mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 18:23:15 +02:00
* Write SCH_LEGACY_PLUGIN_CACHE object for handling the legacy symbol library file format. * Write legacy symbol library file parser. * Write code to transfer cache to PART_LIB object so existing library save code can be used for round trip testing. This is temporary until Eeschema is updated to use the plugin for library management rather than PART_LIB. * Add LIB_XXXX object helper functions where there was no way to set the member variables of an object. * Give the cache object friend status to some object where there are incredibly byzantine ways of setting text in LIB_XXXX objects.
1107 lines
28 KiB
C++
1107 lines
28 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
|
* Copyright (C) 2008-2015 Wayne Stambaugh <stambaughw@verizon.net>
|
|
* Copyright (C) 2004-2016 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
|
|
*/
|
|
|
|
/**
|
|
* @file class_library.cpp
|
|
*/
|
|
|
|
#include <fctsys.h>
|
|
#include <kiface_i.h>
|
|
#include <gr_basic.h>
|
|
#include <macros.h>
|
|
#include <kicad_string.h>
|
|
#include <gestfich.h>
|
|
#include <eda_doc.h>
|
|
#include <wxstruct.h>
|
|
#include <richio.h>
|
|
#include <config_params.h>
|
|
#include <wildcards_and_files_ext.h>
|
|
#include <project_rescue.h>
|
|
|
|
#include <general.h>
|
|
#include <class_library.h>
|
|
#include <sch_legacy_plugin.h>
|
|
|
|
#include <wx/tokenzr.h>
|
|
#include <wx/regex.h>
|
|
|
|
#define DUPLICATE_NAME_MSG \
|
|
_( "Library '%s' has duplicate entry name '%s'.\n" \
|
|
"This may cause some unexpected behavior when loading components into a schematic." )
|
|
|
|
|
|
PART_LIB::PART_LIB( int aType, const wxString& aFileName ) :
|
|
// start @ != 0 so each additional library added
|
|
// is immediately detectable, zero would not be.
|
|
m_mod_hash( PART_LIBS::s_modify_generation )
|
|
{
|
|
type = aType;
|
|
isModified = false;
|
|
timeStamp = 0;
|
|
isCache = false;
|
|
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 = wxT( "unnamed.lib" );
|
|
}
|
|
|
|
|
|
PART_LIB::~PART_LIB()
|
|
{
|
|
// When the library is destroyed, all of the alias objects on the heap should be deleted.
|
|
for( LIB_ALIAS_MAP::iterator it = m_amap.begin(); it != m_amap.end(); ++it )
|
|
{
|
|
wxLogTrace( traceSchLibMem, wxT( "Removing alias %s from library %s." ),
|
|
GetChars( it->second->GetName() ), GetChars( GetLogicalName() ) );
|
|
LIB_PART* part = it->second->GetPart();
|
|
LIB_ALIAS* alias = it->second;
|
|
delete alias;
|
|
|
|
// When the last alias of a part is destroyed, the part is no longer required and it
|
|
// too is destroyed.
|
|
if( part && part->GetAliasCount() == 0 )
|
|
delete part;
|
|
}
|
|
|
|
m_amap.clear();
|
|
}
|
|
|
|
|
|
void PART_LIB::GetEntryNames( wxArrayString& aNames )
|
|
{
|
|
for( LIB_ALIAS_MAP::iterator it = m_amap.begin(); it!=m_amap.end(); it++ )
|
|
{
|
|
aNames.Add( (*it).first );
|
|
}
|
|
|
|
aNames.Sort();
|
|
}
|
|
|
|
|
|
void PART_LIB::GetEntryTypePowerNames( wxArrayString& aNames )
|
|
{
|
|
for( LIB_ALIAS_MAP::iterator it = m_amap.begin(); it!=m_amap.end(); it++ )
|
|
{
|
|
LIB_ALIAS* alias = it->second;
|
|
LIB_PART* root = alias->GetPart();
|
|
|
|
if( !root || !root->IsPower() )
|
|
continue;
|
|
|
|
aNames.Add( (*it).first );
|
|
}
|
|
|
|
aNames.Sort();
|
|
}
|
|
|
|
|
|
bool PART_LIB::Conflicts( LIB_PART* aPart )
|
|
{
|
|
wxCHECK_MSG( aPart != NULL, false,
|
|
wxT( "Cannot test NULL component for conflicts in library " ) + GetName() );
|
|
|
|
for( size_t i=0; i<aPart->m_aliases.size(); i++ )
|
|
{
|
|
LIB_ALIAS_MAP::iterator it = m_amap.find( aPart->m_aliases[i]->GetName() );
|
|
|
|
if( it != m_amap.end() )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
LIB_ALIAS* PART_LIB::FindEntry( const wxString& aName )
|
|
{
|
|
LIB_ALIAS_MAP::iterator it = m_amap.find( aName );
|
|
|
|
if( it != m_amap.end() )
|
|
return it->second;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
LIB_ALIAS* PART_LIB::GetFirstEntry()
|
|
{
|
|
if( m_amap.size() )
|
|
return m_amap.begin()->second;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
LIB_PART* PART_LIB::FindPart( const wxString& aName )
|
|
{
|
|
#if 0 && defined(DEBUG)
|
|
if( !aName.Cmp( wxT( "TI_STELLARIS_BOOSTERPACK" ) ) )
|
|
{
|
|
int breakhere = 1;
|
|
(void) breakhere;
|
|
}
|
|
#endif
|
|
|
|
if( LIB_ALIAS* alias = FindEntry( aName ) )
|
|
{
|
|
return alias->GetPart();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool PART_LIB::HasPowerParts()
|
|
{
|
|
// return true if at least one power part is found in lib
|
|
for( LIB_ALIAS_MAP::iterator it = m_amap.begin(); it!=m_amap.end(); it++ )
|
|
{
|
|
LIB_ALIAS* alias = it->second;
|
|
LIB_PART* root = alias->GetPart();
|
|
|
|
if( root && root->IsPower() )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PART_LIB::AddAlias( LIB_ALIAS* aAlias )
|
|
{
|
|
wxASSERT( aAlias );
|
|
|
|
#if defined(DEBUG) && 0
|
|
if( !aAlias->GetName().Cmp( wxT( "TI_STELLARIS_BOOSTERPACK" ) ) )
|
|
{
|
|
int breakhere = 1;
|
|
(void) breakhere;
|
|
}
|
|
#endif
|
|
|
|
LIB_ALIAS_MAP::iterator it = m_amap.find( aAlias->GetName() );
|
|
|
|
if( it != m_amap.end() )
|
|
{
|
|
wxString msg;
|
|
|
|
msg.Printf( _( "Cannot add duplicate alias '%s' to library '%s'." ),
|
|
GetChars( aAlias->GetName() ),
|
|
GetChars( fileName.GetName() ) );
|
|
return false;
|
|
}
|
|
|
|
wxString name = aAlias->GetName();
|
|
|
|
m_amap[ name ] = aAlias;
|
|
isModified = true;
|
|
++m_mod_hash;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool PART_LIB::AddPart( LIB_PART* aPart )
|
|
{
|
|
// Conflict detection: See if already existing aliases exist,
|
|
// and if yes, ask user for continue or abort
|
|
// Special case: if the library is the library cache of the project,
|
|
// old aliases are always removed to avoid conflict,
|
|
// and user is not prompted )
|
|
if( Conflicts( aPart ) && !IsCache() )
|
|
{
|
|
wxFAIL_MSG( wxT( "Cannot add component <" ) + aPart->GetName() +
|
|
wxT( "> to library <" ) + GetName() + wxT( "> due to name conflict." ) );
|
|
return false;
|
|
}
|
|
|
|
// add a clone, not the caller's copy
|
|
LIB_PART* my_part = new LIB_PART( *aPart, this );
|
|
|
|
for( size_t i = 0; i < my_part->m_aliases.size(); i++ )
|
|
{
|
|
wxString aliasname = my_part->m_aliases[i]->GetName();
|
|
|
|
if( LIB_ALIAS* alias = FindAlias( aliasname ) )
|
|
RemoveEntry( alias );
|
|
|
|
m_amap[ aliasname ] = my_part->m_aliases[i];
|
|
}
|
|
|
|
isModified = true;
|
|
++m_mod_hash;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
LIB_ALIAS* PART_LIB::RemoveEntry( LIB_ALIAS* aEntry )
|
|
{
|
|
wxCHECK_MSG( aEntry != NULL, NULL, wxT( "NULL pointer cannot be removed from library." ) );
|
|
|
|
LIB_ALIAS_MAP::iterator it = m_amap.find( aEntry->GetName() );
|
|
|
|
if( it == m_amap.end() )
|
|
return NULL;
|
|
|
|
// If the entry pointer doesn't match the name it is mapped to in the library, we
|
|
// have done something terribly wrong.
|
|
wxCHECK_MSG( *it->second == aEntry, NULL,
|
|
wxT( "Pointer mismatch while attempting to remove entry <" ) +
|
|
aEntry->GetName() + wxT( "> from library <" ) + GetName() + wxT( ">." ) );
|
|
|
|
LIB_ALIAS* alias = aEntry;
|
|
LIB_PART* part = alias->GetPart();
|
|
|
|
alias = part->RemoveAlias( alias );
|
|
|
|
if( !alias )
|
|
{
|
|
delete part;
|
|
|
|
if( m_amap.size() > 1 )
|
|
{
|
|
LIB_ALIAS_MAP::iterator next = it;
|
|
next++;
|
|
|
|
if( next == m_amap.end() )
|
|
next = m_amap.begin();
|
|
|
|
alias = next->second;
|
|
}
|
|
}
|
|
|
|
m_amap.erase( it );
|
|
isModified = true;
|
|
++m_mod_hash;
|
|
return alias;
|
|
}
|
|
|
|
|
|
LIB_PART* PART_LIB::ReplacePart( LIB_PART* aOldPart, LIB_PART* aNewPart )
|
|
{
|
|
wxASSERT( aOldPart != NULL );
|
|
wxASSERT( aNewPart != NULL );
|
|
|
|
/* Remove the old root component. The component will automatically be deleted
|
|
* when all it's aliases are deleted. Do not place any code that accesses
|
|
* aOldPart inside this loop that gets evaluated after the last alias is
|
|
* removed in RemoveEntry(). Failure to heed this warning will result in a
|
|
* segfault.
|
|
*/
|
|
size_t i = aOldPart->m_aliases.size();
|
|
|
|
while( i > 0 )
|
|
{
|
|
i -= 1;
|
|
RemoveEntry( aOldPart->m_aliases[ i ] );
|
|
}
|
|
|
|
LIB_PART* my_part = new LIB_PART( *aNewPart, this );
|
|
|
|
// Add new aliases to library alias map.
|
|
for( i = 0; i < my_part->m_aliases.size(); i++ )
|
|
{
|
|
wxString aname = my_part->m_aliases[ i ]->GetName();
|
|
m_amap[ aname ] = my_part->m_aliases[ i ];
|
|
}
|
|
|
|
isModified = true;
|
|
++m_mod_hash;
|
|
return my_part;
|
|
}
|
|
|
|
|
|
LIB_ALIAS* PART_LIB::GetNextEntry( const wxString& aName )
|
|
{
|
|
if( m_amap.empty() )
|
|
return NULL;
|
|
|
|
LIB_ALIAS_MAP::iterator it = m_amap.find( aName );
|
|
|
|
it++;
|
|
|
|
if( it == m_amap.end() )
|
|
it = m_amap.begin();
|
|
|
|
return it->second;
|
|
}
|
|
|
|
|
|
LIB_ALIAS* PART_LIB::GetPreviousEntry( const wxString& aName )
|
|
{
|
|
if( m_amap.empty() )
|
|
return NULL;
|
|
|
|
LIB_ALIAS_MAP::iterator it = m_amap.find( aName );
|
|
|
|
if( it == m_amap.begin() )
|
|
it = m_amap.end();
|
|
|
|
it--;
|
|
|
|
return it->second;
|
|
}
|
|
|
|
|
|
bool PART_LIB::Load( wxString& aErrorMsg )
|
|
{
|
|
if( fileName.GetFullPath().IsEmpty() )
|
|
{
|
|
aErrorMsg = _( "The component library file name is not set." );
|
|
return false;
|
|
}
|
|
|
|
FILE* file = wxFopen( fileName.GetFullPath(), wxT( "rt" ) );
|
|
|
|
if( file == NULL )
|
|
{
|
|
aErrorMsg = _( "The file could not be opened." );
|
|
return false;
|
|
}
|
|
|
|
FILE_LINE_READER reader( file, fileName.GetFullPath() );
|
|
|
|
if( !reader.ReadLine() )
|
|
{
|
|
aErrorMsg = _( "The file is empty!" );
|
|
return false;
|
|
}
|
|
|
|
// There is no header if this is a symbol library.
|
|
if( type == LIBRARY_TYPE_EESCHEMA )
|
|
{
|
|
char* line = reader.Line();
|
|
|
|
header = FROM_UTF8( line );
|
|
|
|
wxStringTokenizer tkn( header );
|
|
|
|
/*
|
|
* The file header (first line) in library versions 2.0 and lower
|
|
* apparently started with EESchema-LIB. Sometime after 2.0, it
|
|
* was changed to EESchema-LIBRARY. Therefore, the test for
|
|
* EESchema-LIB will work in both cases. Don't change this unless
|
|
* backwards compatibility is no longer required.
|
|
*/
|
|
if( !tkn.HasMoreTokens()
|
|
|| !tkn.GetNextToken().Upper().StartsWith(wxT( "EESCHEMA-LIB" ) ) )
|
|
{
|
|
aErrorMsg = _( "The file is NOT an Eeschema library!" );
|
|
return false;
|
|
}
|
|
|
|
if( !tkn.HasMoreTokens() )
|
|
{
|
|
aErrorMsg = _( "The file header is missing version and time stamp information." );
|
|
return false;
|
|
}
|
|
|
|
if( tkn.GetNextToken() != wxT( "Version" ) || !tkn.HasMoreTokens() )
|
|
{
|
|
aErrorMsg = wxT( "The file header version information is invalid." );
|
|
return false;
|
|
}
|
|
|
|
long major, minor;
|
|
wxStringTokenizer vers( tkn.GetNextToken(), wxT( "." ) );
|
|
|
|
if( !vers.HasMoreTokens() || !vers.GetNextToken().ToLong( &major )
|
|
|| major < 1L || !vers.HasMoreTokens()
|
|
|| !vers.GetNextToken().ToLong( & minor ) || minor < 0L
|
|
|| minor > 99 )
|
|
{
|
|
#if 0 // Note for developers:
|
|
// Not sure this warning is very useful: old designs *must* be always loadable
|
|
wxLogWarning( wxT(
|
|
"The component library '%s' header version "
|
|
"number is invalid.\n\nIn future versions of Eeschema this library may not "
|
|
"load correctly. To resolve this problem open the library in the library "
|
|
"editor and save it. If this library is the project cache library, save "
|
|
"the current schematic." ),
|
|
GetChars( GetName() ) );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
versionMajor = (int) major;
|
|
versionMinor = (int) minor;
|
|
}
|
|
}
|
|
|
|
while( reader.ReadLine() )
|
|
{
|
|
char * line = reader.Line();
|
|
|
|
if( type == LIBRARY_TYPE_EESCHEMA && strncasecmp( line, "$HEADER", 7 ) == 0 )
|
|
{
|
|
if( !LoadHeader( reader ) )
|
|
{
|
|
aErrorMsg = _( "An error occurred attempting to read the header." );
|
|
return false;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
wxString msg;
|
|
|
|
if( strncasecmp( line, "DEF", 3 ) == 0 )
|
|
{
|
|
// Read one DEF/ENDDEF part entry from library:
|
|
LIB_PART* part = new LIB_PART( wxEmptyString, this );
|
|
|
|
if( part->Load( reader, msg ) )
|
|
{
|
|
// Check for duplicate entry names and warn the user about
|
|
// the potential conflict.
|
|
if( FindEntry( part->GetName() ) != NULL )
|
|
{
|
|
wxLogWarning( DUPLICATE_NAME_MSG,
|
|
GetChars( fileName.GetName() ),
|
|
GetChars( part->GetName() ) );
|
|
}
|
|
|
|
LoadAliases( part );
|
|
}
|
|
else
|
|
{
|
|
wxLogWarning( _( "Library '%s' component load error %s." ),
|
|
GetChars( fileName.GetName() ),
|
|
GetChars( msg ) );
|
|
msg.Clear();
|
|
delete part;
|
|
}
|
|
}
|
|
}
|
|
|
|
++m_mod_hash;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void PART_LIB::LoadAliases( LIB_PART* aPart )
|
|
{
|
|
wxCHECK_RET( aPart, wxT( "Cannot load aliases of NULL part. Bad programmer!" ) );
|
|
|
|
for( size_t i = 0; i < aPart->m_aliases.size(); i++ )
|
|
{
|
|
if( FindEntry( aPart->m_aliases[i]->GetName() ) != NULL )
|
|
{
|
|
wxLogError( DUPLICATE_NAME_MSG,
|
|
GetChars( fileName.GetName() ),
|
|
GetChars( aPart->m_aliases[i]->GetName() ) );
|
|
}
|
|
|
|
wxString aname = aPart->m_aliases[i]->GetName();
|
|
m_amap[ aname ] = aPart->m_aliases[i];
|
|
}
|
|
}
|
|
|
|
|
|
bool PART_LIB::LoadHeader( LINE_READER& aLineReader )
|
|
{
|
|
char* line, * text, * data;
|
|
|
|
while( aLineReader.ReadLine() )
|
|
{
|
|
line = (char*) aLineReader;
|
|
|
|
text = strtok( line, " \t\r\n" );
|
|
data = strtok( NULL, " \t\r\n" );
|
|
|
|
if( strcasecmp( text, "TimeStamp" ) == 0 )
|
|
timeStamp = atol( data );
|
|
|
|
if( strcasecmp( text, "$ENDHEADER" ) == 0 )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PART_LIB::LoadDocs( wxString& aErrorMsg )
|
|
{
|
|
int lineNumber = 0;
|
|
char line[8000], * name, * text;
|
|
LIB_ALIAS* entry;
|
|
FILE* file;
|
|
wxFileName fn = fileName;
|
|
|
|
fn.SetExt( DOC_EXT );
|
|
|
|
file = wxFopen( fn.GetFullPath(), wxT( "rt" ) );
|
|
|
|
if( file == NULL )
|
|
{
|
|
aErrorMsg.Printf( _( "Could not open component document library file '%s'." ),
|
|
GetChars( fn.GetFullPath() ) );
|
|
return false;
|
|
}
|
|
|
|
if( GetLine( file, line, &lineNumber, sizeof(line) ) == NULL )
|
|
{
|
|
aErrorMsg.Printf( _( "Part document library file '%s' is empty." ),
|
|
GetChars( fn.GetFullPath() ) );
|
|
fclose( file );
|
|
return false;
|
|
}
|
|
|
|
if( strncasecmp( line, DOCFILE_IDENT, 10 ) != 0 )
|
|
{
|
|
aErrorMsg.Printf( _( "File '%s' is not a valid component library document file." ),
|
|
GetChars( fn.GetFullPath() ) );
|
|
fclose( file );
|
|
return false;
|
|
}
|
|
|
|
while( GetLine( file, line, &lineNumber, sizeof(line) ) )
|
|
{
|
|
if( strncmp( line, "$CMP", 4 ) != 0 )
|
|
{
|
|
aErrorMsg.Printf( wxT( "$CMP command expected in line %d, aborted." ), lineNumber );
|
|
fclose( file );
|
|
return false;
|
|
}
|
|
|
|
// Read one $CMP/$ENDCMP part entry from library:
|
|
name = strtok( line + 5, "\n\r" );
|
|
|
|
wxString cmpname = FROM_UTF8( name );
|
|
|
|
entry = FindEntry( cmpname );
|
|
|
|
while( GetLine( file, line, &lineNumber, sizeof(line) ) )
|
|
{
|
|
if( strncmp( line, "$ENDCMP", 7 ) == 0 )
|
|
break;
|
|
|
|
text = strtok( line + 2, "\n\r" );
|
|
|
|
if( entry )
|
|
{
|
|
switch( line[0] )
|
|
{
|
|
case 'D':
|
|
entry->SetDescription( FROM_UTF8( text ) );
|
|
break;
|
|
|
|
case 'K':
|
|
entry->SetKeyWords( FROM_UTF8( text ) );
|
|
break;
|
|
|
|
case 'F':
|
|
entry->SetDocFileName( FROM_UTF8( text ) );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose( file );
|
|
return true;
|
|
}
|
|
|
|
|
|
bool PART_LIB::Save( OUTPUTFORMATTER& aFormatter )
|
|
{
|
|
if( isModified )
|
|
{
|
|
timeStamp = GetNewTimeStamp();
|
|
isModified = false;
|
|
}
|
|
|
|
bool success = true;
|
|
|
|
try
|
|
{
|
|
SaveHeader( aFormatter );
|
|
|
|
for( LIB_ALIAS_MAP::iterator it=m_amap.begin(); it!=m_amap.end(); it++ )
|
|
{
|
|
if( !it->second->IsRoot() )
|
|
continue;
|
|
|
|
it->second->GetPart()->Save( aFormatter );
|
|
}
|
|
|
|
aFormatter.Print( 0, "#\n#End Library\n" );
|
|
}
|
|
catch( const IO_ERROR& ioe )
|
|
{
|
|
success = false;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
bool PART_LIB::SaveDocs( OUTPUTFORMATTER& aFormatter )
|
|
{
|
|
bool success = true;
|
|
|
|
try
|
|
{
|
|
aFormatter.Print( 0, "%s\n", DOCFILE_IDENT );
|
|
|
|
for( LIB_ALIAS_MAP::iterator it=m_amap.begin(); it!=m_amap.end(); it++ )
|
|
{
|
|
if( !it->second->SaveDoc( aFormatter ) )
|
|
success = false;
|
|
}
|
|
|
|
aFormatter.Print( 0, "#\n#End Doc Library\n" );
|
|
}
|
|
catch( const IO_ERROR& ioe )
|
|
{
|
|
success = false;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
bool PART_LIB::SaveHeader( OUTPUTFORMATTER& aFormatter )
|
|
{
|
|
aFormatter.Print( 0, "%s %d.%d\n", LIBFILE_IDENT,
|
|
LIB_VERSION_MAJOR, LIB_VERSION_MINOR );
|
|
|
|
aFormatter.Print( 0, "#encoding utf-8\n");
|
|
|
|
#if 0
|
|
aFormatter.Print( 0, "$HEADER\n" );
|
|
aFormatter.Print( 0, "TimeStamp %8.8lX\n", m_TimeStamp );
|
|
aFormatter.Print( 0, "Parts %d\n", m_amap.size() );
|
|
aFormatter.Print( 0, "$ENDHEADER\n" ) != 1 );
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
PART_LIB* PART_LIB::LoadLibrary( const wxString& aFileName ) throw( IO_ERROR, boost::bad_pointer )
|
|
{
|
|
std::unique_ptr<PART_LIB> lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, aFileName ) );
|
|
|
|
wxBusyCursor ShowWait; // Do we want UI elements in PART_LIB?
|
|
|
|
wxString errorMsg;
|
|
|
|
#ifdef USE_SCH_IO_MANAGER
|
|
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
|
|
|
|
wxArrayString tmp;
|
|
|
|
pi->EnumerateSymbolLib( tmp, aFileName );
|
|
pi->TransferCache( *lib.get() );
|
|
#else
|
|
if( !lib->Load( errorMsg ) )
|
|
THROW_IO_ERROR( errorMsg );
|
|
|
|
if( USE_OLD_DOC_FILE_FORMAT( lib->versionMajor, lib->versionMinor ) )
|
|
{
|
|
#if 1
|
|
// not fatal if error here.
|
|
lib->LoadDocs( errorMsg );
|
|
#else
|
|
if( !lib->LoadDocs( errorMsg ) )
|
|
THROW_IO_ERROR( errorMsg );
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
PART_LIB* ret = lib.release();
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
PART_LIB* PART_LIBS::AddLibrary( const wxString& aFileName ) throw( IO_ERROR, boost::bad_pointer )
|
|
{
|
|
PART_LIB* lib;
|
|
|
|
#if 1
|
|
wxFileName fn = aFileName;
|
|
// Don't reload the library if it is already loaded.
|
|
lib = FindLibrary( fn.GetName() );
|
|
|
|
if( lib )
|
|
return lib;
|
|
#endif
|
|
|
|
lib = PART_LIB::LoadLibrary( aFileName );
|
|
|
|
push_back( lib );
|
|
|
|
return lib;
|
|
}
|
|
|
|
|
|
PART_LIB* PART_LIBS::AddLibrary( const wxString& aFileName, PART_LIBS::iterator& aIterator )
|
|
throw( IO_ERROR, boost::bad_pointer )
|
|
{
|
|
#if 1
|
|
// Don't reload the library if it is already loaded.
|
|
wxFileName fn( aFileName );
|
|
PART_LIB* lib = FindLibrary( fn.GetName() );
|
|
|
|
if( lib )
|
|
return lib;
|
|
#endif
|
|
|
|
lib = PART_LIB::LoadLibrary( aFileName );
|
|
|
|
if( aIterator >= begin() && aIterator < end() )
|
|
insert( aIterator, lib );
|
|
else
|
|
push_back( lib );
|
|
|
|
return lib;
|
|
}
|
|
|
|
|
|
void PART_LIBS::RemoveLibrary( const wxString& aName )
|
|
{
|
|
if( aName.IsEmpty() )
|
|
return;
|
|
|
|
for( PART_LIBS::iterator it = begin(); it < end(); ++it )
|
|
{
|
|
if( it->GetName().CmpNoCase( aName ) == 0 )
|
|
{
|
|
erase( it );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PART_LIB* PART_LIBS::FindLibrary( const wxString& aName )
|
|
{
|
|
for( PART_LIBS::iterator it = begin(); it!=end(); ++it )
|
|
{
|
|
if( it->GetName() == aName )
|
|
return &*it;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
wxArrayString PART_LIBS::GetLibraryNames( bool aSorted )
|
|
{
|
|
wxArrayString cacheNames;
|
|
wxArrayString names;
|
|
|
|
for( PART_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_PART* PART_LIBS::FindLibPart( const wxString& aPartName, const wxString& aLibraryName )
|
|
{
|
|
LIB_PART* part = NULL;
|
|
|
|
for( PART_LIB& lib : *this )
|
|
{
|
|
if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
|
|
continue;
|
|
|
|
part = lib.FindPart( aPartName );
|
|
|
|
if( part )
|
|
break;
|
|
}
|
|
|
|
return part;
|
|
}
|
|
|
|
|
|
LIB_ALIAS* PART_LIBS::FindLibraryEntry( const wxString& aEntryName, const wxString& aLibraryName )
|
|
{
|
|
LIB_ALIAS* entry = NULL;
|
|
|
|
for( PART_LIB& lib : *this )
|
|
{
|
|
if( !!aLibraryName && lib.GetName() != aLibraryName )
|
|
continue;
|
|
|
|
entry = lib.FindEntry( aEntryName );
|
|
|
|
if( entry )
|
|
break;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
|
|
/* searches all libraries in the list for an entry, using a case insensitive comparison.
|
|
* Used to find an entry, when the normal (case sensitive) search fails.
|
|
*/
|
|
void PART_LIBS::FindLibraryNearEntries( std::vector<LIB_ALIAS*>& aCandidates,
|
|
const wxString& aEntryName,
|
|
const wxString& aLibraryName )
|
|
{
|
|
for( PART_LIB& lib : *this )
|
|
{
|
|
if( !!aLibraryName && lib.GetName() != aLibraryName )
|
|
continue;
|
|
|
|
LIB_ALIAS* entry = lib.GetFirstEntry();
|
|
|
|
if( ! entry )
|
|
continue;
|
|
|
|
wxString first_entry_name = entry->GetName();
|
|
wxString entry_name = first_entry_name;
|
|
|
|
for( ;; )
|
|
{
|
|
if( entry_name.CmpNoCase( aEntryName ) == 0 )
|
|
aCandidates.push_back( entry );
|
|
|
|
entry = lib.GetNextEntry( entry_name );
|
|
entry_name = entry->GetName();
|
|
|
|
if( first_entry_name == entry_name )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int PART_LIBS::s_modify_generation = 1; // starts at 1 and goes up
|
|
|
|
|
|
int PART_LIBS::GetModifyHash()
|
|
{
|
|
int hash = 0;
|
|
|
|
for( PART_LIBS::const_iterator it = begin(); it != end(); ++it )
|
|
{
|
|
hash += it->m_mod_hash;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
void PART_LIBS::LibNamesAndPaths( PROJECT* aProject, bool doSave,
|
|
wxString* aPaths, wxArrayString* aNames )
|
|
throw( IO_ERROR, boost::bad_pointer )
|
|
{
|
|
wxString pro = aProject->GetProjectFullName();
|
|
|
|
PARAM_CFG_ARRAY ca;
|
|
|
|
if( aPaths )
|
|
ca.push_back( new PARAM_CFG_FILENAME( wxT( "LibDir" ), aPaths ) );
|
|
|
|
if( aNames )
|
|
ca.push_back( new PARAM_CFG_LIBNAME_LIST( wxT( "LibName" ), aNames, GROUP_SCH_LIBS ) );
|
|
|
|
if( doSave )
|
|
{
|
|
aProject->ConfigSave( Kiface().KifaceSearch(), GROUP_SCH, ca );
|
|
|
|
/*
|
|
{
|
|
wxString msg = wxString::Format( _(
|
|
"Unable save project's '%s' file" ),
|
|
GetChars( pro )
|
|
);
|
|
THROW_IO_ERROR( msg );
|
|
}
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
if( !aProject->ConfigLoad( Kiface().KifaceSearch(), GROUP_SCH, ca ) )
|
|
{
|
|
wxString msg = wxString::Format( _(
|
|
"Unable to load project's '%s' file" ),
|
|
GetChars( pro )
|
|
);
|
|
THROW_IO_ERROR( msg );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
const wxString PART_LIBS::CacheName( const wxString& aFullProjectFilename )
|
|
{
|
|
/* until apr 2009 the project cache lib was named: <root_name>.cache.lib,
|
|
* and after: <root_name>-cache.lib. So if the <name>-cache.lib is not found,
|
|
* the old file will be renamed and returned.
|
|
*/
|
|
wxFileName new_name = aFullProjectFilename;
|
|
|
|
new_name.SetName( new_name.GetName() + wxT( "-cache" ) );
|
|
new_name.SetExt( SchematicLibraryFileExtension );
|
|
|
|
if( new_name.FileExists() )
|
|
return new_name.GetFullPath();
|
|
else
|
|
{
|
|
wxFileName old_name = aFullProjectFilename;
|
|
old_name.SetExt( wxT( "cache.lib" ) );
|
|
|
|
if( old_name.FileExists() )
|
|
{
|
|
wxRenameFile( old_name.GetFullPath(), new_name.GetFullPath() );
|
|
return new_name.GetFullPath();
|
|
}
|
|
}
|
|
return wxEmptyString;
|
|
}
|
|
|
|
|
|
void PART_LIBS::LoadAllLibraries( PROJECT* aProject ) throw( IO_ERROR, boost::bad_pointer )
|
|
{
|
|
wxFileName fn;
|
|
wxString filename;
|
|
wxString libs_not_found;
|
|
SEARCH_STACK* lib_search = aProject->SchSearchS();
|
|
|
|
#if defined(DEBUG) && 1
|
|
lib_search->Show( __func__ );
|
|
#endif
|
|
|
|
wxArrayString lib_names;
|
|
|
|
LibNamesAndPaths( aProject, false, NULL, &lib_names );
|
|
|
|
// If the list is empty, force loading the standard power symbol library.
|
|
if( !lib_names.GetCount() )
|
|
lib_names.Add( wxT( "power" ) );
|
|
|
|
wxASSERT( !size() ); // expect to load into "this" empty container.
|
|
|
|
for( unsigned i = 0; i < lib_names.GetCount(); ++i )
|
|
{
|
|
fn.Clear();
|
|
fn.SetName( lib_names[i] );
|
|
fn.SetExt( SchematicLibraryFileExtension );
|
|
|
|
// 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.GetName();
|
|
libs_not_found += wxT( '\n' );
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
filename = fn.GetFullPath();
|
|
}
|
|
|
|
try
|
|
{
|
|
AddLibrary( filename );
|
|
}
|
|
catch( const IO_ERROR& ioe )
|
|
{
|
|
wxString msg = wxString::Format( _(
|
|
"Part library '%s' failed to load. Error:\n"
|
|
"%s" ),
|
|
GetChars( filename ),
|
|
GetChars( ioe.errorText )
|
|
);
|
|
|
|
THROW_IO_ERROR( msg );
|
|
}
|
|
}
|
|
|
|
// add the special cache library.
|
|
wxString cache_name = CacheName( aProject->GetProjectFullName() );
|
|
PART_LIB* cache_lib;
|
|
|
|
if( !!cache_name )
|
|
{
|
|
try
|
|
{
|
|
cache_lib = AddLibrary( cache_name );
|
|
|
|
if( cache_lib )
|
|
cache_lib->SetCache();
|
|
}
|
|
catch( const IO_ERROR& ioe )
|
|
{
|
|
wxString msg = wxString::Format( _(
|
|
"Part library '%s' failed to load.\nError: %s" ),
|
|
GetChars( cache_name ),
|
|
GetChars( ioe.errorText )
|
|
);
|
|
|
|
THROW_IO_ERROR( msg );
|
|
}
|
|
}
|
|
|
|
// Print the libraries not found
|
|
if( !!libs_not_found )
|
|
{
|
|
// Use a different exception type so catch()er can route to proper use
|
|
// of the HTML_MESSAGE_BOX.
|
|
THROW_PARSE_ERROR( wxEmptyString, UTF8( __func__ ),
|
|
UTF8( libs_not_found ), 0, 0 );
|
|
}
|
|
|
|
#if defined(DEBUG) && 1
|
|
printf( "%s: lib_names:\n", __func__ );
|
|
|
|
for( PART_LIBS::const_iterator it = begin(); it < end(); ++it )
|
|
printf( " %s\n", TO_UTF8( it->GetName() ) );
|
|
#endif
|
|
}
|