/* * 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-2017 Wayne Stambaugh * Copyright (C) 2004-2017 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, SCH_IO_MGR::SCH_FILE_T aPluginType ) : // start @ != 0 so each additional library added // is immediately detectable, zero would not be. m_mod_hash( PART_LIBS::s_modify_generation ), m_pluginType( aPluginType ) { 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 m_buffering = false; fileName = aFileName; if( !fileName.IsOk() ) fileName = "unnamed.lib"; m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) ); } PART_LIB::~PART_LIB() { } void PART_LIB::Save( bool aSaveDocFile ) { wxCHECK_RET( m_plugin != NULL, wxString::Format( "no plugin defined for library `%s`.", fileName.GetFullPath() ) ); std::unique_ptr< PROPERTIES > props; if( !aSaveDocFile ) { props.reset( new PROPERTIES ); (*props.get())[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = ""; } m_plugin->SaveLibrary( fileName.GetFullPath(), props.get() ); isModified = false; } void PART_LIB::Create( const wxString& aFileName ) { std::unique_ptr< PROPERTIES > props; if( isCache ) { props.reset( new PROPERTIES ); (*props.get())[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = ""; } wxString tmpFileName = fileName.GetFullPath(); if( !aFileName.IsEmpty() ) tmpFileName = aFileName; m_plugin->CreateSymbolLib( tmpFileName, props.get() ); } void PART_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 ) ); } } void PART_LIB::GetAliasNames( wxArrayString& aNames ) { m_plugin->EnumerateSymbolLib( aNames, fileName.GetFullPath() ); aNames.Sort(); } void PART_LIB::GetEntryTypePowerNames( wxArrayString& aNames ) { wxArrayString aliases; m_plugin->EnumerateSymbolLib( aliases, fileName.GetFullPath() ); for( size_t i = 0; i < aliases.GetCount(); i++ ) { LIB_ALIAS* alias = m_plugin->LoadSymbol( fileName.GetFullPath(), aliases[i] ); wxCHECK2_MSG( alias != NULL, continue, wxString::Format( "alias '%s' not found in symbol library '%s'", aliases[i], fileName.GetFullPath() ) ); LIB_PART* root = alias->GetPart(); if( !root || !root->IsPower() ) continue; aNames.Add( aliases[i] ); } aNames.Sort(); } LIB_ALIAS* PART_LIB::FindAlias( const wxString& aName ) { std::unique_ptr< PROPERTIES > props; if( isCache || m_buffering ) { props.reset( new PROPERTIES ); if( isCache ) (*props.get())[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = ""; if( m_buffering ) (*props.get())[ SCH_LEGACY_PLUGIN::PropBuffering ] = ""; } return m_plugin->LoadSymbol( fileName.GetFullPath(), aName, props.get() ); } LIB_PART* PART_LIB::FindPart( const wxString& aName ) { LIB_ALIAS* alias = FindAlias( aName ); if( alias != NULL ) return alias->GetPart(); return NULL; } bool PART_LIB::HasPowerParts() { // return true if at least one power part is found in lib wxArrayString aliases; m_plugin->EnumerateSymbolLib( aliases, fileName.GetFullPath() ); for( size_t i = 0; i < aliases.GetCount(); i++ ) { LIB_ALIAS* alias = m_plugin->LoadSymbol( fileName.GetFullPath(), aliases[i] ); wxCHECK2_MSG( alias != NULL, continue, wxString::Format( "alias '%s' not found in symbol library '%s'", aliases[i], fileName.GetFullPath() ) ); LIB_PART* root = alias->GetPart(); if( !root || root->IsPower() ) return true; } return false; } void PART_LIB::AddPart( LIB_PART* aPart ) { std::unique_ptr< PROPERTIES > props; if( isCache || m_buffering ) { props.reset( new PROPERTIES ); if( isCache ) (*props.get())[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = ""; if( m_buffering ) (*props.get())[ SCH_LEGACY_PLUGIN::PropBuffering ] = ""; } // add a clone, not the caller's copy, the plugin take ownership of the new symbol. m_plugin->SaveSymbol( fileName.GetFullPath(), new LIB_PART( *aPart, this ), props.get() ); // If we are not buffering, the library file is updated immediately when the plugin // SaveSymbol() function is called. if( m_buffering ) isModified = true; ++m_mod_hash; } LIB_ALIAS* PART_LIB::RemoveAlias( LIB_ALIAS* aEntry ) { wxCHECK_MSG( aEntry != NULL, NULL, "NULL pointer cannot be removed from library." ); std::unique_ptr< PROPERTIES > props; if( isCache || m_buffering ) { props.reset( new PROPERTIES ); if( isCache ) (*props.get())[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = ""; if( m_buffering ) (*props.get())[ SCH_LEGACY_PLUGIN::PropBuffering ] = ""; } m_plugin->DeleteAlias( fileName.GetFullPath(), aEntry->GetName(), props.get() ); // If we are not buffering, the library file is updated immediately when the plugin // SaveSymbol() function is called. if( m_buffering ) isModified = true; ++m_mod_hash; return NULL; } LIB_PART* PART_LIB::ReplacePart( LIB_PART* aOldPart, LIB_PART* aNewPart ) { wxASSERT( aOldPart != NULL ); wxASSERT( aNewPart != NULL ); std::unique_ptr< PROPERTIES > props; if( isCache || m_buffering ) { props.reset( new PROPERTIES ); if( isCache ) (*props.get())[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = ""; if( m_buffering ) (*props.get())[ SCH_LEGACY_PLUGIN::PropBuffering ] = ""; } m_plugin->DeleteSymbol( fileName.GetFullPath(), aOldPart->GetName(), props.get() ); LIB_PART* my_part = new LIB_PART( *aNewPart, this ); m_plugin->SaveSymbol( fileName.GetFullPath(), my_part, props.get() ); // If we are not buffering, the library file is updated immediately when the plugin // SaveSymbol() function is called. if( m_buffering ) isModified = true; ++m_mod_hash; return my_part; } PART_LIB* PART_LIB::LoadLibrary( const wxString& aFileName ) throw( IO_ERROR, boost::bad_pointer ) { std::unique_ptr lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, aFileName ) ); wxArrayString tmp; // This loads the library although we could probably do lazy loading. lib->GetAliasNames( tmp ); // This not them most efficient way to set the LIB_PART 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 i = 0; i < tmp.GetCount(); i++ ) { LIB_ALIAS* alias = lib->FindAlias( tmp[i] ); if( alias && alias->GetPart() ) alias->GetPart()->SetLib( lib.get() ); } PART_LIB* ret = lib.release(); return ret; } PART_LIB* PART_LIBS::AddLibrary( const wxString& aFileName ) throw( IO_ERROR, boost::bad_pointer ) { PART_LIB* lib; wxFileName fn = aFileName; // Don't reload the library if it is already loaded. lib = FindLibrary( fn.GetName() ); if( lib ) return lib; 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 ) { // Don't reload the library if it is already loaded. wxFileName fn( aFileName ); PART_LIB* lib = FindLibrary( fn.GetName() ); if( lib ) return lib; lib = PART_LIB::LoadLibrary( aFileName ); if( aIterator >= begin() && aIterator < end() ) insert( aIterator, lib ); else push_back( lib ); return lib; } 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; } PART_LIB* PART_LIBS::FindLibraryByFullFileName( const wxString& aFullFileName ) { for( PART_LIBS::iterator it = begin(); it!=end(); ++it ) { if( it->GetFullFileName() == aFullFileName ) 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& aCandidates, const wxString& aEntryName, const wxString& aLibraryName ) { for( PART_LIB& lib : *this ) { if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName ) continue; wxArrayString aliasNames; lib.GetAliasNames( aliasNames ); if( aliasNames.IsEmpty() ) continue; for( size_t i = 0; i < aliasNames.size(); i++ ) { if( aliasNames[i].CmpNoCase( aEntryName ) == 0 ) aCandidates.push_back( lib.FindAlias( aliasNames[i] ) ); } } } 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->GetModHash(); } 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( "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: .cache.lib, * and after: -cache.lib. So if the -cache.lib is not found, * the old file will be renamed and returned. */ wxFileName new_name = aFullProjectFilename; new_name.SetName( new_name.GetName() + "-cache" ); new_name.SetExt( SchematicLibraryFileExtension ); if( new_name.FileExists() ) return new_name.GetFullPath(); else { wxFileName old_name = aFullProjectFilename; old_name.SetExt( "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, bool aShowProgress ) throw( IO_ERROR, boost::bad_pointer ) { wxString filename; wxString libs_not_found; SEARCH_STACK* lib_search = aProject->SchSearchS(); #if defined(DEBUG) && 0 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( "power" ); wxASSERT( !size() ); // expect to load into "this" empty container. wxProgressDialog lib_dialog( _( "Loading symbol libraries" ), wxEmptyString, lib_names.GetCount(), NULL, wxPD_APP_MODAL ); if( aShowProgress ) { lib_dialog.Show(); } wxString progress_message; for( unsigned i = 0; i < lib_names.GetCount(); ++i ) { if( aShowProgress ) { lib_dialog.Update( i, _( "Loading " + lib_names[i] ) ); } wxFileName fn = lib_names[i]; // lib_names[] does not store the file extension. Set it: 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.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( _( "Part library '%s' failed to load. Error:\n %s" ), GetChars( filename ), GetChars( ioe.What() ) ); wxLogError( msg ); } } if( aShowProgress ) { lib_dialog.Destroy(); } // add the special cache library. wxString cache_name = CacheName( aProject->GetProjectFullName() ); PART_LIB* cache_lib; if( !cache_name.IsEmpty() ) { 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.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, UTF8( __func__ ), UTF8( libs_not_found ), 0, 0 ); } #if defined(DEBUG) && 0 printf( "%s: lib_names:\n", __func__ ); for( PART_LIBS::const_iterator it = begin(); it < end(); ++it ) printf( " %s\n", TO_UTF8( it->GetName() ) ); #endif }