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>
33 #include <wildcards_and_files_ext.h>
34 #include <project/project_file.h>
35 #include <project_rescue.h>
36 #include <properties.h>
37 #include <widgets/app_progress_dialog.h>
38
39 #include <general.h>
40 #include <symbol_library.h>
41 #include <sch_plugins/legacy/sch_legacy_plugin.h>
42
43 #include <wx/log.h>
44 #include <wx/progdlg.h>
45 #include <wx/tokenzr.h>
46
SYMBOL_LIB(SCH_LIB_TYPE aType,const wxString & aFileName,SCH_IO_MGR::SCH_FILE_T aPluginType)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
~SYMBOL_LIB()71 SYMBOL_LIB::~SYMBOL_LIB()
72 {
73 }
74
75
Save(bool aSaveDocFile)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 )
84 props[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = "";
85
86 m_plugin->SaveLibrary( fileName.GetFullPath(), &props );
87 isModified = false;
88 }
89
90
Create(const wxString & aFileName)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
SetPluginType(SCH_IO_MGR::SCH_FILE_T aPluginType)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
IsCache() const112 bool SYMBOL_LIB::IsCache() const
113 {
114 return m_properties->Exists( SCH_LEGACY_PLUGIN::PropNoDocFile );
115 }
116
117
SetCache()118 void SYMBOL_LIB::SetCache()
119 {
120 (*m_properties)[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = "";
121 }
122
123
IsBuffering() const124 bool SYMBOL_LIB::IsBuffering() const
125 {
126 return m_properties->Exists( SCH_LEGACY_PLUGIN::PropBuffering );
127 }
128
129
EnableBuffering(bool aEnable)130 void SYMBOL_LIB::EnableBuffering( bool aEnable )
131 {
132 if( aEnable )
133 (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
134 else
135 m_properties->Clear( SCH_LEGACY_PLUGIN::PropBuffering );
136 }
137
138
GetSymbolNames(wxArrayString & aNames) const139 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
GetSymbols(std::vector<LIB_SYMBOL * > & aSymbols) const147 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
FindSymbol(const wxString & aName) const157 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
FindSymbol(const LIB_ID & aLibId) const171 LIB_SYMBOL* SYMBOL_LIB::FindSymbol( const LIB_ID& aLibId ) const
172 {
173 return FindSymbol( aLibId.Format().wx_str() );
174 }
175
176
AddSymbol(LIB_SYMBOL * aSymbol)177 void SYMBOL_LIB::AddSymbol( LIB_SYMBOL* aSymbol )
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
RemoveSymbol(LIB_SYMBOL * aEntry)193 LIB_SYMBOL* SYMBOL_LIB::RemoveSymbol( LIB_SYMBOL* aEntry )
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
ReplaceSymbol(LIB_SYMBOL * aOldSymbol,LIB_SYMBOL * aNewSymbol)209 LIB_SYMBOL* SYMBOL_LIB::ReplaceSymbol( LIB_SYMBOL* aOldSymbol, LIB_SYMBOL* aNewSymbol )
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
LoadLibrary(const wxString & aFileName)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
AddLibrary(const wxString & aFileName)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
AddLibrary(const wxString & aFileName,SYMBOL_LIBS::iterator & aIterator)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
FindLibrary(const wxString & aName)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
GetCacheLibrary()318 SYMBOL_LIB* SYMBOL_LIBS::GetCacheLibrary()
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
FindLibraryByFullFileName(const wxString & aFullFileName)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
GetLibraryNames(bool aSorted)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
FindLibSymbol(const LIB_ID & aLibId,const wxString & aLibraryName)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
FindLibraryNearEntries(std::vector<LIB_SYMBOL * > & aCandidates,const wxString & aEntryName,const wxString & aLibraryName)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
411 std::mutex SYMBOL_LIBS::s_generationMutex;
412
413
GetModifyHash()414 int SYMBOL_LIBS::GetModifyHash()
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:
426 hash += SYMBOL_LIBS::GetModifyGeneration();
427
428 return hash;
429 }
430
431
LibNamesAndPaths(PROJECT * aProject,bool doSave,wxString * aPaths,wxArrayString * aNames)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
CacheName(const wxString & aFullProjectFilename)458 const wxString SYMBOL_LIBS::CacheName( const wxString& aFullProjectFilename )
459 {
460 wxFileName name = aFullProjectFilename;
461
462 name.SetName( name.GetName() + "-cache" );
463 name.SetExt( LegacySymbolLibFileExtension );
464
465 if( name.FileExists() )
466 return name.GetFullPath();
467
468 return wxEmptyString;
469 }
470
471
LoadAllLibraries(PROJECT * aProject,bool aShowProgress)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 }
589