KiCad PCB EDA Suite
symbol_library.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2004-2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <algorithm>
27 #include <kiface_base.h>
28 #include <eda_base_frame.h>
29 #include <string_utils.h>
30 #include <macros.h>
31 #include <richio.h>
32 #include <config_params.h>
34 #include <project/project_file.h>
35 #include <project_rescue.h>
36 #include <properties.h>
38 
39 #include <general.h>
40 #include <symbol_library.h>
42 
43 #include <wx/log.h>
44 #include <wx/progdlg.h>
45 #include <wx/tokenzr.h>
46 
47 SYMBOL_LIB::SYMBOL_LIB( SCH_LIB_TYPE aType, const wxString& aFileName,
48  SCH_IO_MGR::SCH_FILE_T aPluginType ) :
49  // start @ != 0 so each additional library added
50  // is immediately detectable, zero would not be.
51  m_mod_hash( SYMBOL_LIBS::GetModifyGeneration() ),
52  m_pluginType( aPluginType )
53 {
54  type = aType;
55  isModified = false;
56  timeStamp = 0;
57  timeStamp = wxDateTime::Now();
58  versionMajor = 0; // Will be updated after reading the lib file
59  versionMinor = 0; // Will be updated after reading the lib file
60 
61  fileName = aFileName;
62 
63  if( !fileName.IsOk() )
64  fileName = "unnamed.lib";
65 
66  m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
67  m_properties = std::make_unique<PROPERTIES>();
68 }
69 
70 
72 {
73 }
74 
75 
76 void SYMBOL_LIB::Save( bool aSaveDocFile )
77 {
78  wxCHECK_RET( m_plugin != nullptr, wxString::Format( "no plugin defined for library `%s`.",
79  fileName.GetFullPath() ) );
80 
81  PROPERTIES props;
82 
83  if( !aSaveDocFile )
85 
86  m_plugin->SaveLibrary( fileName.GetFullPath(), &props );
87  isModified = false;
88 }
89 
90 
91 void SYMBOL_LIB::Create( const wxString& aFileName )
92 {
93  wxString tmpFileName = fileName.GetFullPath();
94 
95  if( !aFileName.IsEmpty() )
96  tmpFileName = aFileName;
97 
98  m_plugin->CreateSymbolLib( tmpFileName, m_properties.get() );
99 }
100 
101 
102 void SYMBOL_LIB::SetPluginType( SCH_IO_MGR::SCH_FILE_T aPluginType )
103 {
104  if( m_pluginType != aPluginType )
105  {
106  m_pluginType = aPluginType;
107  m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
108  }
109 }
110 
111 
113 {
115 }
116 
117 
119 {
120  (*m_properties)[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = "";
121 }
122 
123 
125 {
127 }
128 
129 
130 void SYMBOL_LIB::EnableBuffering( bool aEnable )
131 {
132  if( aEnable )
133  (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
134  else
136 }
137 
138 
139 void SYMBOL_LIB::GetSymbolNames( wxArrayString& aNames ) const
140 {
141  m_plugin->EnumerateSymbolLib( aNames, fileName.GetFullPath(), m_properties.get() );
142 
143  aNames.Sort();
144 }
145 
146 
147 void SYMBOL_LIB::GetSymbols( std::vector<LIB_SYMBOL*>& aSymbols ) const
148 {
149  m_plugin->EnumerateSymbolLib( aSymbols, fileName.GetFullPath(), m_properties.get() );
150 
151  std::sort( aSymbols.begin(), aSymbols.end(),
152  [](LIB_SYMBOL *lhs, LIB_SYMBOL *rhs) -> bool
153  { return lhs->GetName() < rhs->GetName(); });
154 }
155 
156 
157 LIB_SYMBOL* SYMBOL_LIB::FindSymbol( const wxString& aName ) const
158 {
159  LIB_SYMBOL* symbol = m_plugin->LoadSymbol( fileName.GetFullPath(), aName, m_properties.get() );
160 
161  // Set the library to this even though technically the legacy cache plugin owns the
162  // symbols. This allows the symbol library table conversion tool to determine the
163  // correct library where the symbol was found.
164  if( symbol && !symbol->GetLib() )
165  symbol->SetLib( const_cast<SYMBOL_LIB*>( this ) );
166 
167  return symbol;
168 }
169 
170 
171 LIB_SYMBOL* SYMBOL_LIB::FindSymbol( const LIB_ID& aLibId ) const
172 {
173  return FindSymbol( aLibId.Format().wx_str() );
174 }
175 
176 
178 {
179  // add a clone, not the caller's copy, the plugin take ownership of the new symbol.
180  m_plugin->SaveSymbol( fileName.GetFullPath(),
181  new LIB_SYMBOL( *aSymbol->SharedPtr().get(), this ),
182  m_properties.get() );
183 
184  // If we are not buffering, the library file is updated immediately when the plugin
185  // SaveSymbol() function is called.
186  if( IsBuffering() )
187  isModified = true;
188 
189  ++m_mod_hash;
190 }
191 
192 
194 {
195  wxCHECK_MSG( aEntry != nullptr, nullptr, "NULL pointer cannot be removed from library." );
196 
197  m_plugin->DeleteSymbol( fileName.GetFullPath(), aEntry->GetName(), m_properties.get() );
198 
199  // If we are not buffering, the library file is updated immediately when the plugin
200  // SaveSymbol() function is called.
201  if( IsBuffering() )
202  isModified = true;
203 
204  ++m_mod_hash;
205  return nullptr;
206 }
207 
208 
210 {
211  wxASSERT( aOldSymbol != nullptr );
212  wxASSERT( aNewSymbol != nullptr );
213 
214  m_plugin->DeleteSymbol( fileName.GetFullPath(), aOldSymbol->GetName(), m_properties.get() );
215 
216  LIB_SYMBOL* my_part = new LIB_SYMBOL( *aNewSymbol, this );
217 
218  m_plugin->SaveSymbol( fileName.GetFullPath(), my_part, m_properties.get() );
219 
220  // If we are not buffering, the library file is updated immediately when the plugin
221  // SaveSymbol() function is called.
222  if( IsBuffering() )
223  isModified = true;
224 
225  ++m_mod_hash;
226  return my_part;
227 }
228 
229 
230 SYMBOL_LIB* SYMBOL_LIB::LoadLibrary( const wxString& aFileName )
231 {
232  std::unique_ptr<SYMBOL_LIB> lib = std::make_unique<SYMBOL_LIB>( SCH_LIB_TYPE::LT_EESCHEMA,
233  aFileName );
234 
235  std::vector<LIB_SYMBOL*> parts;
236  // This loads the library.
237  lib->GetSymbols( parts );
238 
239  // Now, set the LIB_SYMBOL m_library member but it will only be used
240  // when loading legacy libraries in the future. Once the symbols in the
241  // schematic have a full #LIB_ID, this will not get called.
242  for( size_t ii = 0; ii < parts.size(); ii++ )
243  {
244  LIB_SYMBOL* part = parts[ii];
245 
246  part->SetLib( lib.get() );
247  }
248 
249  SYMBOL_LIB* ret = lib.release();
250  return ret;
251 }
252 
253 
254 SYMBOL_LIB* SYMBOL_LIBS::AddLibrary( const wxString& aFileName )
255 {
256  SYMBOL_LIB* lib;
257 
258  wxFileName fn = aFileName;
259  // Don't reload the library if it is already loaded.
260  lib = FindLibrary( fn.GetName() );
261 
262  if( lib )
263  return lib;
264 
265  try
266  {
267  lib = SYMBOL_LIB::LoadLibrary( aFileName );
268  push_back( lib );
269 
270  return lib;
271  }
272  catch( ... )
273  {
274  return nullptr;
275  }
276 }
277 
278 
279 SYMBOL_LIB* SYMBOL_LIBS::AddLibrary( const wxString& aFileName, SYMBOL_LIBS::iterator& aIterator )
280 {
281  // Don't reload the library if it is already loaded.
282  wxFileName fn( aFileName );
283  SYMBOL_LIB* lib = FindLibrary( fn.GetName() );
284 
285  if( lib )
286  return lib;
287 
288  try
289  {
290  lib = SYMBOL_LIB::LoadLibrary( aFileName );
291 
292  if( aIterator >= begin() && aIterator < end() )
293  insert( aIterator, lib );
294  else
295  push_back( lib );
296 
297  return lib;
298  }
299  catch( ... )
300  {
301  return nullptr;
302  }
303 }
304 
305 
306 SYMBOL_LIB* SYMBOL_LIBS::FindLibrary( const wxString& aName )
307 {
308  for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
309  {
310  if( it->GetName() == aName )
311  return &*it;
312  }
313 
314  return nullptr;
315 }
316 
317 
319 {
320  for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
321  {
322  if( it->IsCache() )
323  return &*it;
324  }
325 
326  return nullptr;
327 }
328 
329 
330 SYMBOL_LIB* SYMBOL_LIBS::FindLibraryByFullFileName( const wxString& aFullFileName )
331 {
332  for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
333  {
334  if( it->GetFullFileName() == aFullFileName )
335  return &*it;
336  }
337 
338  return nullptr;
339 }
340 
341 
342 wxArrayString SYMBOL_LIBS::GetLibraryNames( bool aSorted )
343 {
344  wxArrayString cacheNames;
345  wxArrayString names;
346 
347  for( SYMBOL_LIB& lib : *this )
348  {
349  if( lib.IsCache() && aSorted )
350  cacheNames.Add( lib.GetName() );
351  else
352  names.Add( lib.GetName() );
353  }
354 
355  // Even sorted, the cache library is always at the end of the list.
356  if( aSorted )
357  names.Sort();
358 
359  for( unsigned int i = 0; i<cacheNames.Count(); i++ )
360  names.Add( cacheNames.Item( i ) );
361 
362  return names;
363 }
364 
365 
366 LIB_SYMBOL* SYMBOL_LIBS::FindLibSymbol( const LIB_ID& aLibId, const wxString& aLibraryName )
367 {
368  LIB_SYMBOL* part = nullptr;
369 
370  for( SYMBOL_LIB& lib : *this )
371  {
372  if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
373  continue;
374 
375  part = lib.FindSymbol( aLibId.GetLibItemName().wx_str() );
376 
377  if( part )
378  break;
379  }
380 
381  return part;
382 }
383 
384 
385 void SYMBOL_LIBS::FindLibraryNearEntries( std::vector<LIB_SYMBOL*>& aCandidates,
386  const wxString& aEntryName,
387  const wxString& aLibraryName )
388 {
389  for( SYMBOL_LIB& lib : *this )
390  {
391  if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
392  continue;
393 
394  wxArrayString partNames;
395 
396  lib.GetSymbolNames( partNames );
397 
398  if( partNames.IsEmpty() )
399  continue;
400 
401  for( size_t i = 0; i < partNames.size(); i++ )
402  {
403  if( partNames[i].CmpNoCase( aEntryName ) == 0 )
404  aCandidates.push_back( lib.FindSymbol( partNames[i] ) );
405  }
406  }
407 }
408 
409 
410 int SYMBOL_LIBS::s_modify_generation = 1; // starts at 1 and goes up
412 
413 
415 {
416  int hash = 0;
417 
418  for( SYMBOL_LIBS::const_iterator it = begin(); it != end(); ++it )
419  {
420  hash += it->GetModHash();
421  }
422 
423  // Rebuilding the cache (m_cache) does not change the GetModHash() value,
424  // but changes SYMBOL_LIBS::s_modify_generation.
425  // Take this change in account:
427 
428  return hash;
429 }
430 
431 
432 void SYMBOL_LIBS::LibNamesAndPaths( PROJECT* aProject, bool doSave,
433  wxString* aPaths, wxArrayString* aNames )
434 {
435  wxCHECK_RET( aProject, "Null PROJECT in LibNamesAndPaths" );
436 
437  PROJECT_FILE& project = aProject->GetProjectFile();
438 
439  if( doSave )
440  {
441  if( aPaths )
442  project.m_LegacyLibDir = *aPaths;
443 
444  if( aNames )
445  project.m_LegacyLibNames = *aNames;
446  }
447  else
448  {
449  if( aPaths )
450  *aPaths = project.m_LegacyLibDir;
451 
452  if( aNames )
453  *aNames = project.m_LegacyLibNames;
454  }
455 }
456 
457 
458 const wxString SYMBOL_LIBS::CacheName( const wxString& aFullProjectFilename )
459 {
460  wxFileName name = aFullProjectFilename;
461 
462  name.SetName( name.GetName() + "-cache" );
464 
465  if( name.FileExists() )
466  return name.GetFullPath();
467 
468  return wxEmptyString;
469 }
470 
471 
472 void SYMBOL_LIBS::LoadAllLibraries( PROJECT* aProject, bool aShowProgress )
473 {
474  wxString filename;
475  wxString libs_not_found;
476  SEARCH_STACK* lib_search = aProject->SchSearchS();
477 
478 #if defined(DEBUG) && 0
479  lib_search->Show( __func__ );
480 #endif
481 
482  wxArrayString lib_names;
483 
484  LibNamesAndPaths( aProject, false, nullptr, &lib_names );
485 
486  // Post symbol library table, this should be empty. Only the cache library should get loaded.
487  if( !lib_names.empty() )
488  {
489  APP_PROGRESS_DIALOG lib_dialog( _( "Loading Symbol Libraries" ),
490  wxEmptyString,
491  lib_names.GetCount(),
492  nullptr,
493  false,
494  wxPD_APP_MODAL );
495 
496  if( aShowProgress )
497  {
498  lib_dialog.Show();
499  }
500 
501  wxString progress_message;
502 
503  for( unsigned i = 0; i < lib_names.GetCount(); ++i )
504  {
505  if( aShowProgress )
506  {
507  lib_dialog.Update( i, wxString::Format( _( "Loading %s..." ), lib_names[i] ) );
508  }
509 
510  // lib_names[] does not store the file extension. Set it.
511  // Remember lib_names[i] can contain a '.' in name, so using a wxFileName
512  // before adding the extension can create incorrect full filename
513  wxString fullname = lib_names[i] + "." + LegacySymbolLibFileExtension;
514  // Now the full name is set, we can use a wxFileName.
515  wxFileName fn( fullname );
516 
517  // Skip if the file name is not valid..
518  if( !fn.IsOk() )
519  continue;
520 
521  if( !fn.FileExists() )
522  {
523  filename = lib_search->FindValidPath( fn.GetFullPath() );
524 
525  if( !filename )
526  {
527  libs_not_found += fn.GetFullPath();
528  libs_not_found += '\n';
529  continue;
530  }
531  }
532  else
533  { // ensure the lib filename has a absolute path.
534  // If the lib has no absolute path, and is found in the cwd by fn.FileExists(),
535  // make a full absolute path, to avoid issues with load library functions which
536  // expects an absolute path.
537  if( !fn.IsAbsolute() )
538  fn.MakeAbsolute();
539 
540  filename = fn.GetFullPath();
541  }
542 
543  try
544  {
545  AddLibrary( filename );
546  }
547  catch( const IO_ERROR& ioe )
548  {
549  wxString msg;
550  msg.Printf( _( "Symbol library '%s' failed to load." ), filename );
551 
552  wxLogError( msg + wxS( "\n" ) + ioe.What() );
553  }
554  }
555  }
556 
557  // add the special cache library.
558  wxString cache_name = CacheName( aProject->GetProjectFullName() );
559  SYMBOL_LIB* cache_lib;
560 
561  if( !aProject->IsNullProject() && !cache_name.IsEmpty() )
562  {
563  try
564  {
565  cache_lib = AddLibrary( cache_name );
566 
567  if( cache_lib )
568  cache_lib->SetCache();
569  }
570  catch( const IO_ERROR& ioe )
571  {
572  wxString msg = wxString::Format( _( "Error loading symbol library '%s'." )
573  + wxS( "\n%s" ),
574  cache_name,
575  ioe.What() );
576 
577  THROW_IO_ERROR( msg );
578  }
579  }
580 
581  // Print the libraries not found
582  if( !libs_not_found.IsEmpty() )
583  {
584  // Use a different exception type so catch()er can route to proper use
585  // of the HTML_MESSAGE_BOX.
586  THROW_PARSE_ERROR( wxEmptyString, __func__, TO_UTF8( libs_not_found ), 0, 0 );
587  }
588 }
SCH_LIB_TYPE
virtual bool Update(int aValue, const wxString &aNewMsg=wxEmptyString, bool *aSkip=nullptr) override
wxDateTime timeStamp
Library save time and date.
bool IsCache() const
LIB_SYMBOL_SPTR SharedPtr() const
Definition: lib_symbol.h:107
wxString FindValidPath(const wxString &aFileName) const
Definition: search_stack.h:70
const UTF8 & GetLibItemName() const
Definition: lib_id.h:104
Container for project specific data.
Definition: project.h:62
SYMBOL_LIB * AddLibrary(const wxString &aFileName)
Allocate and adds a symbol library to the library list.
LIB_SYMBOL * FindSymbol(const wxString &aName) const
Find LIB_SYMBOL by aName.
Object used to load, save, search, and otherwise manipulate symbol library files.
wxString GetName() const override
Definition: lib_symbol.h:133
const std::string LegacySymbolLibFileExtension
LIB_SYMBOL * FindLibSymbol(const LIB_ID &aLibId, const wxString &aLibraryName=wxEmptyString)
Search all libraries in the list for a symbol.
void Save(bool aSaveDocFile=true)
virtual PROJECT_FILE & GetProjectFile() const
Definition: project.h:145
static void LibNamesAndPaths(PROJECT *aProject, bool doSave, wxString *aPaths, wxArrayString *aNames=nullptr)
Save or load the names of the currently configured symbol libraries (without paths).
void FindLibraryNearEntries(std::vector< LIB_SYMBOL * > &aCandidates, const wxString &aEntryName, const wxString &aLibraryName=wxEmptyString)
Search all libraries in the list for a LIB_SYMBOL using a case insensitive comparison.
int GetModifyHash()
Return the modification hash for all libraries.
static const wxString CacheName(const wxString &aFullProjectFilename)
Return the name of the cache library after potentially fixing it from an older naming scheme.
int m_mod_hash
incremented each time library is changed.
bool IsBuffering() const
LIB_SYMBOL * RemoveSymbol(LIB_SYMBOL *aEntry)
Safely remove aEntry from the library and return the next entry.
Look for files in a number of paths.
Definition: search_stack.h:41
void SetLib(SYMBOL_LIB *aLibrary)
Definition: lib_symbol.h:177
SCH_IO_MGR::SCH_FILE_T m_pluginType
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
A name/value tuple with unique names and optional values.
Definition: properties.h:33
void EnableBuffering(bool aEnable=true)
Define a library symbol object.
Definition: lib_symbol.h:96
int versionMajor
Library major version number.
static int GetModifyGeneration()
The backing store for a PROJECT, in JSON format.
Definition: project_file.h:64
SYMBOL_LIB * GetLib() const
Definition: lib_symbol.h:176
SYMBOL_LIB(SCH_LIB_TYPE aType, const wxString &aFileName, SCH_IO_MGR::SCH_FILE_T aPluginType=SCH_IO_MGR::SCH_LEGACY)
void LoadAllLibraries(PROJECT *aProject, bool aShowProgress=true)
Load all of the project's libraries into this container, which should be cleared before calling it.
This file contains miscellaneous commonly used macros and functions.
void Create(const wxString &aFileName=wxEmptyString)
void AddSymbol(LIB_SYMBOL *aSymbol)
Add aSymbol entry to library.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
std::unique_ptr< SCH_PLUGIN > m_plugin
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
const wxString GetName() const
Return the file name without path or extension.
Definition of file extensions used in Kicad.
Base window classes and related definitions.
Definition for symbol library class.
#define _(s)
wxFileName fileName
Library file name.
LIB_SYMBOL * ReplaceSymbol(LIB_SYMBOL *aOldSymbol, LIB_SYMBOL *aNewSymbol)
Replace an existing symbol entry in the library.
static const char * PropNoDocFile
The property used internally by the plugin to disable writing the library documentation (....
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition: project.cpp:116
void SetPluginType(SCH_IO_MGR::SCH_FILE_T aPluginType)
virtual bool IsNullProject() const
Check if this project is a null project (i.e.
Definition: project.cpp:134
wxArrayString GetLibraryNames(bool aSorted=true)
Return the list of symbol library file names without path and extension.
static int s_modify_generation
helper for GetModifyHash()
SYMBOL_LIB * FindLibraryByFullFileName(const wxString &aFullFileName)
UTF8 Format() const
Definition: lib_id.cpp:116
A collection of SYMBOL_LIB objects.
void GetSymbolNames(wxArrayString &aNames) const
Load a string array with the names of all the entries in this library.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
static std::mutex s_generationMutex
SYMBOL_LIB * GetCacheLibrary()
void GetSymbols(std::vector< LIB_SYMBOL * > &aSymbols) const
Load a vector with all the entries in this library.
static SYMBOL_LIB * LoadLibrary(const wxString &aFileName)
Allocate and load a symbol library file.
bool isModified
Library modification status.
const char * name
Definition: DXF_plotter.cpp:56
int versionMinor
Library minor version number.
std::unique_ptr< PROPERTIES > m_properties
Library properties.
wxString wx_str() const
Definition: utf8.cpp:46
SCH_LIB_TYPE type
Library type indicator.
wxProgressDialog with the option to also update the application progress on the taskbar
static const char * PropBuffering
The property used internally by the plugin to enable cache buffering which prevents the library file ...
SYMBOL_LIB * FindLibrary(const wxString &aName)
Find a symbol library by aName.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38