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.

614 lines
16 KiB

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