You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

621 lines
17 KiB

4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  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) 2022 CERN
  7. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  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 <wildcards_and_files_ext.h>
  33. #include <project/project_file.h>
  34. #include <project_rescue.h>
  35. #include <project_sch.h>
  36. #include <widgets/app_progress_dialog.h>
  37. #include <symbol_library.h>
  38. #include <sch_io/kicad_legacy/sch_io_kicad_legacy.h>
  39. #include <wx/log.h>
  40. #include <wx/progdlg.h>
  41. #include <wx/tokenzr.h>
  42. #include "sim/sim_model.h"
  43. SYMBOL_LIB::SYMBOL_LIB( SCH_LIB_TYPE aType, const wxString& aFileName,
  44. SCH_IO_MGR::SCH_FILE_T aPluginType ) :
  45. m_pluginType( aPluginType )
  46. {
  47. type = aType;
  48. isModified = false;
  49. timeStamp = 0;
  50. timeStamp = wxDateTime::Now();
  51. versionMajor = 0; // Will be updated after reading the lib file
  52. versionMinor = 0; // Will be updated after reading the lib file
  53. fileName = aFileName;
  54. if( !fileName.IsOk() )
  55. fileName = "unnamed.lib";
  56. m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
  57. m_properties = std::make_unique<std::map<std::string, UTF8>>();
  58. m_mod_hash = 0;
  59. }
  60. SYMBOL_LIB::~SYMBOL_LIB()
  61. {
  62. }
  63. void SYMBOL_LIB::Save( bool aSaveDocFile )
  64. {
  65. wxCHECK_RET( m_plugin != nullptr,
  66. wxString::Format( wxT( "no plugin defined for library `%s`." ),
  67. fileName.GetFullPath() ) );
  68. std::map<std::string, UTF8> props;
  69. if( !aSaveDocFile )
  70. props[ SCH_IO_KICAD_LEGACY::PropNoDocFile ] = "";
  71. m_plugin->SaveLibrary( fileName.GetFullPath(), &props );
  72. isModified = false;
  73. }
  74. void SYMBOL_LIB::Create( const wxString& aFileName )
  75. {
  76. wxString tmpFileName = fileName.GetFullPath();
  77. if( !aFileName.IsEmpty() )
  78. tmpFileName = aFileName;
  79. m_plugin->CreateLibrary( tmpFileName, m_properties.get() );
  80. }
  81. void SYMBOL_LIB::SetPluginType( SCH_IO_MGR::SCH_FILE_T aPluginType )
  82. {
  83. if( m_pluginType != aPluginType )
  84. {
  85. m_pluginType = aPluginType;
  86. m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
  87. }
  88. }
  89. bool SYMBOL_LIB::IsCache() const
  90. {
  91. return m_properties->contains( SCH_IO_KICAD_LEGACY::PropNoDocFile );
  92. }
  93. void SYMBOL_LIB::SetCache()
  94. {
  95. (*m_properties)[ SCH_IO_KICAD_LEGACY::PropNoDocFile ] = "";
  96. }
  97. bool SYMBOL_LIB::IsBuffering() const
  98. {
  99. return m_properties->contains( SCH_IO_KICAD_LEGACY::PropBuffering );
  100. }
  101. void SYMBOL_LIB::EnableBuffering( bool aEnable )
  102. {
  103. if( aEnable )
  104. (*m_properties)[ SCH_IO_KICAD_LEGACY::PropBuffering ] = "";
  105. else
  106. m_properties->erase( SCH_IO_KICAD_LEGACY::PropBuffering );
  107. }
  108. void SYMBOL_LIB::GetSymbolNames( wxArrayString& aNames ) const
  109. {
  110. m_plugin->EnumerateSymbolLib( aNames, fileName.GetFullPath(), m_properties.get() );
  111. aNames.Sort();
  112. }
  113. void SYMBOL_LIB::GetSymbols( std::vector<LIB_SYMBOL*>& aSymbols ) const
  114. {
  115. m_plugin->EnumerateSymbolLib( aSymbols, fileName.GetFullPath(), m_properties.get() );
  116. std::sort( aSymbols.begin(), aSymbols.end(),
  117. [](LIB_SYMBOL *lhs, LIB_SYMBOL *rhs) -> bool
  118. {
  119. return lhs->GetName() < rhs->GetName();
  120. } );
  121. }
  122. LIB_SYMBOL* SYMBOL_LIB::FindSymbol( const wxString& aName ) const
  123. {
  124. LIB_SYMBOL* symbol = m_plugin->LoadSymbol( fileName.GetFullPath(), aName, m_properties.get() );
  125. if( symbol )
  126. {
  127. // Set the library to this even though technically the legacy cache plugin owns the
  128. // symbols. This allows the symbol library table conversion tool to determine the
  129. // correct library where the symbol was found.
  130. if( !symbol->GetLib() )
  131. symbol->SetLib( const_cast<SYMBOL_LIB*>( this ) );
  132. SIM_MODEL::MigrateSimModel<LIB_SYMBOL>( *symbol, nullptr );
  133. }
  134. return symbol;
  135. }
  136. LIB_SYMBOL* SYMBOL_LIB::FindSymbol( const LIB_ID& aLibId ) const
  137. {
  138. return FindSymbol( aLibId.Format().wx_str() );
  139. }
  140. void SYMBOL_LIB::AddSymbol( LIB_SYMBOL* aSymbol )
  141. {
  142. // add a clone, not the caller's copy, the plugin take ownership of the new symbol.
  143. m_plugin->SaveSymbol( fileName.GetFullPath(),
  144. new LIB_SYMBOL( *aSymbol->SharedPtr().get(), this ),
  145. m_properties.get() );
  146. // If we are not buffering, the library file is updated immediately when the plugin
  147. // SaveSymbol() function is called.
  148. if( IsBuffering() )
  149. isModified = true;
  150. ++m_mod_hash;
  151. }
  152. LIB_SYMBOL* SYMBOL_LIB::RemoveSymbol( LIB_SYMBOL* aEntry )
  153. {
  154. wxCHECK_MSG( aEntry != nullptr, nullptr, "NULL pointer cannot be removed from library." );
  155. m_plugin->DeleteSymbol( fileName.GetFullPath(), aEntry->GetName(), m_properties.get() );
  156. // If we are not buffering, the library file is updated immediately when the plugin
  157. // SaveSymbol() function is called.
  158. if( IsBuffering() )
  159. isModified = true;
  160. ++m_mod_hash;
  161. return nullptr;
  162. }
  163. LIB_SYMBOL* SYMBOL_LIB::ReplaceSymbol( LIB_SYMBOL* aOldSymbol, LIB_SYMBOL* aNewSymbol )
  164. {
  165. wxASSERT( aOldSymbol != nullptr );
  166. wxASSERT( aNewSymbol != nullptr );
  167. m_plugin->DeleteSymbol( fileName.GetFullPath(), aOldSymbol->GetName(), m_properties.get() );
  168. LIB_SYMBOL* my_part = new LIB_SYMBOL( *aNewSymbol, this );
  169. m_plugin->SaveSymbol( fileName.GetFullPath(), my_part, m_properties.get() );
  170. // If we are not buffering, the library file is updated immediately when the plugin
  171. // SaveSymbol() function is called.
  172. if( IsBuffering() )
  173. isModified = true;
  174. ++m_mod_hash;
  175. return my_part;
  176. }
  177. SYMBOL_LIB* SYMBOL_LIB::LoadSymbolLibrary( const wxString& aFileName )
  178. {
  179. std::unique_ptr<SYMBOL_LIB> lib = std::make_unique<SYMBOL_LIB>( SCH_LIB_TYPE::LT_EESCHEMA,
  180. aFileName );
  181. std::vector<LIB_SYMBOL*> parts;
  182. // This loads the library.
  183. lib->GetSymbols( parts );
  184. // Now, set the LIB_SYMBOL m_library member but it will only be used
  185. // when loading legacy libraries in the future. Once the symbols in the
  186. // schematic have a full #LIB_ID, this will not get called.
  187. for( size_t ii = 0; ii < parts.size(); ii++ )
  188. {
  189. LIB_SYMBOL* part = parts[ii];
  190. part->SetLib( lib.get() );
  191. }
  192. SYMBOL_LIB* ret = lib.release();
  193. return ret;
  194. }
  195. SYMBOL_LIB* SYMBOL_LIBS::AddLibrary( const wxString& aFileName )
  196. {
  197. SYMBOL_LIB* lib;
  198. wxFileName fn = aFileName;
  199. // Don't reload the library if it is already loaded.
  200. lib = FindLibrary( fn.GetName() );
  201. if( lib )
  202. return lib;
  203. try
  204. {
  205. lib = SYMBOL_LIB::LoadSymbolLibrary( aFileName );
  206. push_back( lib );
  207. return lib;
  208. }
  209. catch( ... )
  210. {
  211. return nullptr;
  212. }
  213. }
  214. SYMBOL_LIB* SYMBOL_LIBS::AddLibrary( const wxString& aFileName, SYMBOL_LIBS::iterator& aIterator )
  215. {
  216. // Don't reload the library if it is already loaded.
  217. wxFileName fn( aFileName );
  218. SYMBOL_LIB* lib = FindLibrary( fn.GetName() );
  219. if( lib )
  220. return lib;
  221. try
  222. {
  223. lib = SYMBOL_LIB::LoadSymbolLibrary( aFileName );
  224. if( aIterator >= begin() && aIterator < end() )
  225. insert( aIterator, lib );
  226. else
  227. push_back( lib );
  228. return lib;
  229. }
  230. catch( ... )
  231. {
  232. return nullptr;
  233. }
  234. }
  235. bool SYMBOL_LIBS::ReloadLibrary( const wxString &aFileName )
  236. {
  237. wxFileName fn = aFileName;
  238. SYMBOL_LIB* lib = FindLibrary( fn.GetName() );
  239. // Check if the library already exists.
  240. if( !lib )
  241. return false;
  242. // Create a clone of the library pointer in case we need to re-add it
  243. SYMBOL_LIB *cloneLib = lib;
  244. // Try to find the iterator of the library
  245. for( auto it = begin(); it != end(); ++it )
  246. {
  247. if( it->GetName() == fn.GetName() )
  248. {
  249. // Remove the old library and keep the pointer
  250. lib = &*it;
  251. release( it );
  252. break;
  253. }
  254. }
  255. // Try to reload the library
  256. try
  257. {
  258. lib = SYMBOL_LIB::LoadSymbolLibrary( aFileName );
  259. // If the library is successfully reloaded, add it back to the set.
  260. push_back( lib );
  261. return true;
  262. }
  263. catch( ... )
  264. {
  265. // If an exception occurs, ensure that the SYMBOL_LIBS remains unchanged
  266. // by re-adding the old library back to the set.
  267. push_back( cloneLib );
  268. return false;
  269. }
  270. }
  271. SYMBOL_LIB* SYMBOL_LIBS::FindLibrary( const wxString& aName )
  272. {
  273. for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
  274. {
  275. if( it->GetName() == aName )
  276. return &*it;
  277. }
  278. return nullptr;
  279. }
  280. SYMBOL_LIB* SYMBOL_LIBS::GetCacheLibrary()
  281. {
  282. for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
  283. {
  284. if( it->IsCache() )
  285. return &*it;
  286. }
  287. return nullptr;
  288. }
  289. SYMBOL_LIB* SYMBOL_LIBS::FindLibraryByFullFileName( const wxString& aFullFileName )
  290. {
  291. for( SYMBOL_LIBS::iterator it = begin(); it!=end(); ++it )
  292. {
  293. if( it->GetFullFileName() == aFullFileName )
  294. return &*it;
  295. }
  296. return nullptr;
  297. }
  298. wxArrayString SYMBOL_LIBS::GetLibraryNames( bool aSorted )
  299. {
  300. wxArrayString cacheNames;
  301. wxArrayString names;
  302. for( SYMBOL_LIB& lib : *this )
  303. {
  304. if( lib.IsCache() && aSorted )
  305. cacheNames.Add( lib.GetName() );
  306. else
  307. names.Add( lib.GetName() );
  308. }
  309. // Even sorted, the cache library is always at the end of the list.
  310. if( aSorted )
  311. names.Sort();
  312. for( unsigned int i = 0; i<cacheNames.Count(); i++ )
  313. names.Add( cacheNames.Item( i ) );
  314. return names;
  315. }
  316. LIB_SYMBOL* SYMBOL_LIBS::FindLibSymbol( const LIB_ID& aLibId, const wxString& aLibraryName )
  317. {
  318. LIB_SYMBOL* part = nullptr;
  319. for( SYMBOL_LIB& lib : *this )
  320. {
  321. if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
  322. continue;
  323. part = lib.FindSymbol( aLibId.GetLibItemName().wx_str() );
  324. if( part )
  325. break;
  326. }
  327. return part;
  328. }
  329. void SYMBOL_LIBS::FindLibraryNearEntries( std::vector<LIB_SYMBOL*>& aCandidates,
  330. const wxString& aEntryName,
  331. const wxString& aLibraryName )
  332. {
  333. for( SYMBOL_LIB& lib : *this )
  334. {
  335. if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
  336. continue;
  337. wxArrayString partNames;
  338. lib.GetSymbolNames( partNames );
  339. if( partNames.IsEmpty() )
  340. continue;
  341. for( size_t i = 0; i < partNames.size(); i++ )
  342. {
  343. if( partNames[i].CmpNoCase( aEntryName ) == 0 )
  344. aCandidates.push_back( lib.FindSymbol( partNames[i] ) );
  345. }
  346. }
  347. }
  348. void SYMBOL_LIBS::GetLibNamesAndPaths( PROJECT* aProject, wxString* aPaths, wxArrayString* aNames )
  349. {
  350. wxCHECK_RET( aProject, "Null PROJECT in GetLibNamesAndPaths" );
  351. PROJECT_FILE& project = aProject->GetProjectFile();
  352. if( aPaths )
  353. *aPaths = project.m_LegacyLibDir;
  354. if( aNames )
  355. *aNames = project.m_LegacyLibNames;
  356. }
  357. void SYMBOL_LIBS::SetLibNamesAndPaths( PROJECT* aProject, const wxString& aPaths,
  358. const wxArrayString& aNames )
  359. {
  360. wxCHECK_RET( aProject, "Null PROJECT in SetLibNamesAndPaths" );
  361. PROJECT_FILE& project = aProject->GetProjectFile();
  362. project.m_LegacyLibDir = aPaths;
  363. project.m_LegacyLibNames = aNames;
  364. }
  365. const wxString SYMBOL_LIBS::CacheName( const wxString& aFullProjectFilename )
  366. {
  367. wxFileName filename( aFullProjectFilename );
  368. wxString name = filename.GetName();
  369. filename.SetName( name + "-cache" );
  370. filename.SetExt( FILEEXT::LegacySymbolLibFileExtension );
  371. if( filename.FileExists() )
  372. return filename.GetFullPath();
  373. // Try the old (2007) cache name
  374. filename.SetName( name + ".cache" );
  375. if( filename.FileExists() )
  376. return filename.GetFullPath();
  377. return wxEmptyString;
  378. }
  379. void SYMBOL_LIBS::LoadAllLibraries( PROJECT* aProject, bool aShowProgress )
  380. {
  381. wxString filename;
  382. wxString libs_not_found;
  383. SEARCH_STACK* lib_search = PROJECT_SCH::SchSearchS( aProject );
  384. #if defined(DEBUG) && 0
  385. lib_search->Show( __func__ );
  386. #endif
  387. wxArrayString lib_names;
  388. GetLibNamesAndPaths( aProject, nullptr, &lib_names );
  389. // Post symbol library table, this should be empty. Only the cache library should get loaded.
  390. if( !lib_names.empty() )
  391. {
  392. APP_PROGRESS_DIALOG lib_dialog( _( "Loading Symbol Libraries" ),
  393. wxEmptyString,
  394. lib_names.GetCount(),
  395. nullptr,
  396. false,
  397. wxPD_APP_MODAL );
  398. if( aShowProgress )
  399. {
  400. lib_dialog.Show();
  401. }
  402. for( unsigned i = 0; i < lib_names.GetCount(); ++i )
  403. {
  404. if( aShowProgress )
  405. {
  406. lib_dialog.Update( i, wxString::Format( _( "Loading %s..." ), lib_names[i] ) );
  407. }
  408. // lib_names[] does not store the file extension. Set it.
  409. // Remember lib_names[i] can contain a '.' in name, so using a wxFileName
  410. // before adding the extension can create incorrect full filename
  411. wxString fullname = lib_names[i] + "." + FILEEXT::LegacySymbolLibFileExtension;
  412. // Now the full name is set, we can use a wxFileName.
  413. wxFileName fn( fullname );
  414. // Skip if the file name is not valid..
  415. if( !fn.IsOk() )
  416. continue;
  417. if( !fn.FileExists() )
  418. {
  419. filename = lib_search->FindValidPath( fn.GetFullPath() );
  420. if( !filename )
  421. {
  422. libs_not_found += fn.GetFullPath();
  423. libs_not_found += '\n';
  424. continue;
  425. }
  426. }
  427. else
  428. { // ensure the lib filename has a absolute path.
  429. // If the lib has no absolute path, and is found in the cwd by fn.FileExists(),
  430. // make a full absolute path, to avoid issues with load library functions which
  431. // expects an absolute path.
  432. if( !fn.IsAbsolute() )
  433. fn.MakeAbsolute();
  434. filename = fn.GetFullPath();
  435. }
  436. try
  437. {
  438. AddLibrary( filename );
  439. }
  440. catch( const IO_ERROR& ioe )
  441. {
  442. wxString msg;
  443. msg.Printf( _( "Symbol library '%s' failed to load." ), filename );
  444. wxLogError( msg + wxS( "\n" ) + ioe.What() );
  445. }
  446. }
  447. }
  448. // add the special cache library.
  449. wxString cache_name = CacheName( aProject->GetProjectFullName() );
  450. SYMBOL_LIB* cache_lib;
  451. if( !aProject->IsNullProject() && !cache_name.IsEmpty() )
  452. {
  453. try
  454. {
  455. cache_lib = AddLibrary( cache_name );
  456. if( cache_lib )
  457. cache_lib->SetCache();
  458. }
  459. catch( const IO_ERROR& ioe )
  460. {
  461. wxString msg = wxString::Format( _( "Error loading symbol library '%s'." )
  462. + wxS( "\n%s" ),
  463. cache_name,
  464. ioe.What() );
  465. THROW_IO_ERROR( msg );
  466. }
  467. }
  468. // Print the libraries not found
  469. if( !libs_not_found.IsEmpty() )
  470. {
  471. // Use a different exception type so catch()er can route to proper use
  472. // of the HTML_MESSAGE_BOX.
  473. THROW_PARSE_ERROR( wxEmptyString, __func__, TO_UTF8( libs_not_found ), 0, 0 );
  474. }
  475. }