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.

1290 lines
37 KiB

3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 CERN
  5. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Maciej Suminski <maciej.suminski@cern.ch>
  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 3
  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. * https://www.gnu.org/licenses/gpl-3.0.html
  21. * or you may search the http://www.gnu.org website for the version 3 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. #include "symbol_library_manager.h"
  26. #include <symbol_library.h>
  27. #include <dialogs/html_message_box.h>
  28. #include <symbol_edit_frame.h>
  29. #include <symbol_lib_table.h>
  30. #include <env_paths.h>
  31. #include <pgm_base.h>
  32. #include <project_sch.h>
  33. #include <kiway.h>
  34. #include <core/profile.h>
  35. #include <wx_filename.h>
  36. #include <sch_io/kicad_legacy/sch_io_kicad_legacy.h>
  37. #include <symbol_async_loader.h>
  38. #include <progress_reporter.h>
  39. #include <list>
  40. #include <locale_io.h>
  41. #include <confirm.h>
  42. #include <string_utils.h>
  43. #include "lib_logger.h"
  44. SYMBOL_LIBRARY_MANAGER::SYMBOL_LIBRARY_MANAGER( SCH_BASE_FRAME& aFrame ) :
  45. m_frame( aFrame )
  46. {
  47. m_logger = new LIB_LOGGER();
  48. }
  49. SYMBOL_LIBRARY_MANAGER::~SYMBOL_LIBRARY_MANAGER()
  50. {
  51. delete m_logger;
  52. }
  53. void SYMBOL_LIBRARY_MANAGER::Preload( PROGRESS_REPORTER& aReporter )
  54. {
  55. SYMBOL_ASYNC_LOADER loader( symTable()->GetLogicalLibs(), symTable(), false, nullptr,
  56. &aReporter );
  57. LOCALE_IO toggle;
  58. loader.Start();
  59. while( !loader.Done() )
  60. {
  61. if( !aReporter.KeepRefreshing() )
  62. break;
  63. wxMilliSleep( 33 /* 30 FPS refresh rate */ );
  64. }
  65. loader.Join();
  66. if( !loader.GetErrors().IsEmpty() )
  67. {
  68. HTML_MESSAGE_BOX dlg( &m_frame, _( "Load Error" ) );
  69. dlg.MessageSet( _( "Errors loading symbols:" ) );
  70. wxString msg = loader.GetErrors();
  71. msg.Replace( "\n", "<BR>" );
  72. dlg.AddHTML_Text( msg );
  73. dlg.ShowModal();
  74. }
  75. }
  76. bool SYMBOL_LIBRARY_MANAGER::HasModifications() const
  77. {
  78. for( const auto& [name, buffer] : m_libs )
  79. {
  80. if( buffer.IsModified() )
  81. return true;
  82. }
  83. return false;
  84. }
  85. int SYMBOL_LIBRARY_MANAGER::GetHash() const
  86. {
  87. int hash = symTable()->GetModifyHash();
  88. for( const auto& [name, buffer] : m_libs )
  89. hash += buffer.GetHash();
  90. return hash;
  91. }
  92. int SYMBOL_LIBRARY_MANAGER::GetLibraryHash( const wxString& aLibrary ) const
  93. {
  94. const auto libBufIt = m_libs.find( aLibrary );
  95. if( libBufIt != m_libs.end() )
  96. return libBufIt->second.GetHash();
  97. SYMBOL_LIB_TABLE_ROW* row = GetLibrary( aLibrary );
  98. // return -1 if library does not exist or 0 if not modified
  99. return row ? std::hash<std::string>{}( aLibrary.ToStdString() +
  100. row->GetFullURI( true ).ToStdString() ) : -1;
  101. }
  102. wxArrayString SYMBOL_LIBRARY_MANAGER::GetLibraryNames() const
  103. {
  104. wxArrayString res;
  105. for( const wxString& libName : symTable()->GetLogicalLibs() )
  106. {
  107. // Database libraries are hidden from the symbol editor at the moment
  108. SYMBOL_LIB_TABLE_ROW* row = GetLibrary( libName );
  109. if( !row || row->SchLibType() == SCH_IO_MGR::SCH_DATABASE )
  110. continue;
  111. res.Add( libName );
  112. }
  113. return res;
  114. }
  115. SYMBOL_LIB_TABLE_ROW* SYMBOL_LIBRARY_MANAGER::GetLibrary( const wxString& aLibrary ) const
  116. {
  117. SYMBOL_LIB_TABLE_ROW* row = nullptr;
  118. try
  119. {
  120. row = symTable()->FindRow( aLibrary, true );
  121. }
  122. catch( const IO_ERROR& e )
  123. {
  124. wxString msg;
  125. msg.Printf( _( "Library '%s' not found in the Symbol Library Table." ), aLibrary );
  126. DisplayErrorMessage( &m_frame, msg, e.What() );
  127. }
  128. return row;
  129. }
  130. bool SYMBOL_LIBRARY_MANAGER::SaveLibrary( const wxString& aLibrary, const wxString& aFileName,
  131. SCH_IO_MGR::SCH_FILE_T aFileType )
  132. {
  133. wxCHECK( aFileType != SCH_IO_MGR::SCH_FILE_T::SCH_LEGACY, false );
  134. wxCHECK_MSG( LibraryExists( aLibrary ), false,
  135. wxString::Format( "Library missing: %s", aLibrary ) );
  136. wxFileName fn( aFileName );
  137. wxCHECK( !fn.FileExists() || fn.IsFileWritable(), false );
  138. IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( aFileType ) );
  139. bool res = true; // assume all libraries are successfully saved
  140. std::map<std::string, UTF8> properties;
  141. properties.emplace( SCH_IO_KICAD_LEGACY::PropBuffering, "" );
  142. auto it = m_libs.find( aLibrary );
  143. if( it != m_libs.end() )
  144. {
  145. // Handle buffered library
  146. LIB_BUFFER& libBuf = it->second;
  147. const auto& symbolBuffers = libBuf.GetBuffers();
  148. for( const std::shared_ptr<SYMBOL_BUFFER>& symbolBuf : symbolBuffers )
  149. {
  150. wxCHECK2( symbolBuf, continue );
  151. if( !libBuf.SaveBuffer( *symbolBuf, aFileName, &*pi, true ) )
  152. {
  153. // Something went wrong, but try to save other libraries
  154. res = false;
  155. }
  156. }
  157. // clear the deleted symbols buffer only if data is saved to the original file
  158. wxFileName original, destination( aFileName );
  159. SYMBOL_LIB_TABLE_ROW* row = GetLibrary( aLibrary );
  160. if( row )
  161. {
  162. original = row->GetFullURI();
  163. original.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
  164. }
  165. destination.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
  166. if( res && original == destination )
  167. libBuf.ClearDeletedBuffer();
  168. }
  169. else
  170. {
  171. // Handle original library
  172. for( LIB_SYMBOL* symbol : getOriginalSymbols( aLibrary ) )
  173. {
  174. LIB_SYMBOL* newSymbol;
  175. try
  176. {
  177. if( symbol->IsDerived() )
  178. {
  179. std::shared_ptr< LIB_SYMBOL > oldParent = symbol->GetParent().lock();
  180. wxCHECK_MSG( oldParent, false,
  181. wxString::Format( wxT( "Derived symbol '%s' found with "
  182. "undefined parent." ),
  183. symbol->GetName() ) );
  184. LIB_SYMBOL* libParent = pi->LoadSymbol( aLibrary, oldParent->GetName(),
  185. &properties );
  186. if( !libParent )
  187. {
  188. libParent = new LIB_SYMBOL( *oldParent.get() );
  189. pi->SaveSymbol( aLibrary, libParent, &properties );
  190. }
  191. newSymbol = new LIB_SYMBOL( *symbol );
  192. newSymbol->SetParent( libParent );
  193. pi->SaveSymbol( aLibrary, newSymbol, &properties );
  194. }
  195. else if( !pi->LoadSymbol( aLibrary, symbol->GetName(), &properties ) )
  196. {
  197. pi->SaveSymbol( aLibrary, new LIB_SYMBOL( *symbol ), &properties );
  198. }
  199. }
  200. catch( ... )
  201. {
  202. res = false;
  203. break;
  204. }
  205. }
  206. }
  207. try
  208. {
  209. pi->SaveLibrary( aFileName );
  210. }
  211. catch( ... )
  212. {
  213. // return false because something happens.
  214. // The library is not successfully saved
  215. res = false;
  216. }
  217. return res;
  218. }
  219. bool SYMBOL_LIBRARY_MANAGER::IsLibraryModified( const wxString& aLibrary ) const
  220. {
  221. auto it = m_libs.find( aLibrary );
  222. return it != m_libs.end() ? it->second.IsModified() : false;
  223. }
  224. bool SYMBOL_LIBRARY_MANAGER::IsSymbolModified( const wxString& aSymbolName,
  225. const wxString& aLibrary ) const
  226. {
  227. auto libIt = m_libs.find( aLibrary );
  228. if( libIt == m_libs.end() )
  229. return false;
  230. const LIB_BUFFER& buf = libIt->second;
  231. const std::shared_ptr<SYMBOL_BUFFER> symbolBuf = buf.GetBuffer( aSymbolName );
  232. return symbolBuf ? symbolBuf->IsModified() : false;
  233. }
  234. void SYMBOL_LIBRARY_MANAGER::SetSymbolModified( const wxString& aSymbolName,
  235. const wxString& aLibrary )
  236. {
  237. auto libIt = m_libs.find( aLibrary );
  238. if( libIt == m_libs.end() )
  239. return;
  240. const LIB_BUFFER& buf = libIt->second;
  241. std::shared_ptr<SYMBOL_BUFFER> symbolBuf = buf.GetBuffer( aSymbolName );
  242. wxCHECK( symbolBuf, /* void */ );
  243. symbolBuf->GetScreen()->SetContentModified();
  244. }
  245. bool SYMBOL_LIBRARY_MANAGER::ClearLibraryModified( const wxString& aLibrary ) const
  246. {
  247. auto libIt = m_libs.find( aLibrary );
  248. if( libIt == m_libs.end() )
  249. return false;
  250. for( auto& symbolBuf : libIt->second.GetBuffers() )
  251. {
  252. SCH_SCREEN* screen = symbolBuf->GetScreen();
  253. if( screen )
  254. screen->SetContentModified( false );
  255. }
  256. return true;
  257. }
  258. bool SYMBOL_LIBRARY_MANAGER::ClearSymbolModified( const wxString& aSymbolName,
  259. const wxString& aLibrary ) const
  260. {
  261. auto libIt = m_libs.find( aLibrary );
  262. if( libIt == m_libs.end() )
  263. return false;
  264. auto symbolBuf = libIt->second.GetBuffer( aSymbolName );
  265. wxCHECK( symbolBuf, false );
  266. symbolBuf->GetScreen()->SetContentModified( false );
  267. return true;
  268. }
  269. bool SYMBOL_LIBRARY_MANAGER::IsLibraryReadOnly( const wxString& aLibrary ) const
  270. {
  271. wxCHECK_MSG( LibraryExists( aLibrary ), true,
  272. wxString::Format( "Library missing: %s", aLibrary ) );
  273. return !symTable()->IsSymbolLibWritable( aLibrary );
  274. }
  275. bool SYMBOL_LIBRARY_MANAGER::IsLibraryLoaded( const wxString& aLibrary ) const
  276. {
  277. wxCHECK_MSG( LibraryExists( aLibrary ), false,
  278. wxString::Format( "Library missing: %s", aLibrary ) );
  279. return symTable()->IsSymbolLibLoaded( aLibrary );
  280. }
  281. std::list<LIB_SYMBOL*> SYMBOL_LIBRARY_MANAGER::EnumerateSymbols( const wxString& aLibrary ) const
  282. {
  283. std::list<LIB_SYMBOL*> ret;
  284. wxCHECK_MSG( LibraryExists( aLibrary ), ret,
  285. wxString::Format( "Library missing: %s", aLibrary ) );
  286. auto libIt = m_libs.find( aLibrary );
  287. if( libIt != m_libs.end() )
  288. {
  289. for( const std::shared_ptr<SYMBOL_BUFFER>& symbolBuf : libIt->second.GetBuffers() )
  290. ret.push_back( &symbolBuf->GetSymbol() );
  291. }
  292. else
  293. {
  294. std::vector<LIB_SYMBOL*> symbols;
  295. try
  296. {
  297. symTable()->LoadSymbolLib( symbols, aLibrary );
  298. }
  299. catch( const IO_ERROR& e )
  300. {
  301. wxLogWarning( e.Problem() );
  302. }
  303. std::copy( symbols.begin(), symbols.end(), std::back_inserter( ret ) );
  304. }
  305. return ret;
  306. }
  307. LIB_SYMBOL* SYMBOL_LIBRARY_MANAGER::GetBufferedSymbol( const wxString& aSymbolName,
  308. const wxString& aLibrary )
  309. {
  310. wxCHECK_MSG( LibraryExists( aLibrary ), nullptr,
  311. wxString::Format( "Library missing: %s, for symbol %s", aLibrary, aSymbolName ) );
  312. // try the library buffers first
  313. LIB_BUFFER& libBuf = getLibraryBuffer( aLibrary );
  314. LIB_SYMBOL* bufferedSymbol = libBuf.GetSymbol( aSymbolName );
  315. if( !bufferedSymbol ) // no buffer symbol found
  316. {
  317. // create a copy of the symbol
  318. try
  319. {
  320. LIB_SYMBOL* symbol = symTable()->LoadSymbol( aLibrary, aSymbolName );
  321. if( symbol == nullptr )
  322. THROW_IO_ERROR( _( "Symbol not found." ) );
  323. LIB_SYMBOL* bufferedParent = nullptr;
  324. // Create parent symbols on demand so parent symbol can be set.
  325. if( symbol->IsDerived() )
  326. {
  327. std::shared_ptr<LIB_SYMBOL> parent = symbol->GetParent().lock();
  328. wxCHECK_MSG( parent, nullptr,
  329. wxString::Format( "Derived symbol '%s' found with undefined parent.",
  330. symbol->GetName() ) );
  331. // Check if the parent symbol buffer has already be created.
  332. bufferedParent = libBuf.GetSymbol( parent->GetName() );
  333. if( !bufferedParent )
  334. {
  335. auto newParent = std::make_unique<LIB_SYMBOL>( *parent.get() );
  336. bufferedParent = newParent.get();
  337. libBuf.CreateBuffer( std::move( newParent ), std::make_unique<SCH_SCREEN>() );
  338. }
  339. }
  340. auto newSymbol = std::make_unique<LIB_SYMBOL>( *symbol );
  341. bufferedSymbol = newSymbol.get();
  342. if( bufferedParent )
  343. newSymbol->SetParent( bufferedParent );
  344. libBuf.CreateBuffer( std::move( newSymbol ), std::make_unique<SCH_SCREEN>() );
  345. }
  346. catch( const IO_ERROR& e )
  347. {
  348. wxString msg;
  349. msg.Printf( _( "Error loading symbol %s from library '%s'." ),
  350. aSymbolName,
  351. aLibrary );
  352. DisplayErrorMessage( &m_frame, msg, e.What() );
  353. bufferedSymbol = nullptr;
  354. }
  355. }
  356. return bufferedSymbol;
  357. }
  358. SCH_SCREEN* SYMBOL_LIBRARY_MANAGER::GetScreen( const wxString& aSymbolName,
  359. const wxString& aLibrary )
  360. {
  361. wxCHECK_MSG( LibraryExists( aLibrary ), nullptr,
  362. wxString::Format( "Library missing: %s, for symbol %s", aLibrary, aSymbolName ) );
  363. wxCHECK_MSG( !aSymbolName.IsEmpty(), nullptr,
  364. wxString::Format( "Symbol missing in library: %s", aLibrary ) );
  365. auto it = m_libs.find( aLibrary );
  366. wxCHECK( it != m_libs.end(), nullptr );
  367. LIB_BUFFER& buf = it->second;
  368. std::shared_ptr<SYMBOL_BUFFER> symbolBuf = buf.GetBuffer( aSymbolName );
  369. return symbolBuf ? symbolBuf->GetScreen() : nullptr;
  370. }
  371. bool SYMBOL_LIBRARY_MANAGER::UpdateSymbol( LIB_SYMBOL* aSymbol, const wxString& aLibrary )
  372. {
  373. wxCHECK_MSG( aSymbol, false, wxString::Format( "Null symbol in library: %s", aLibrary ) );
  374. wxCHECK_MSG( LibraryExists( aLibrary ), false,
  375. wxString::Format( "Library missing: %s, for symbol %s", aLibrary,
  376. aSymbol->GetName() ) );
  377. LIB_BUFFER& libBuf = getLibraryBuffer( aLibrary );
  378. std::shared_ptr<SYMBOL_BUFFER> symbolBuf = libBuf.GetBuffer( aSymbol->GetName() );
  379. if( symbolBuf ) // Existing symbol.
  380. {
  381. LIB_SYMBOL& bufferedSymbol = symbolBuf->GetSymbol();
  382. // If we are coming from a different library, the library ID needs to be preserved
  383. const LIB_ID libId = bufferedSymbol.GetLibId();
  384. bufferedSymbol = *aSymbol;
  385. bufferedSymbol.SetLibId( libId );
  386. symbolBuf->GetScreen()->SetContentModified();
  387. }
  388. else // New symbol
  389. {
  390. auto symbolCopy = std::make_unique<LIB_SYMBOL>( *aSymbol, nullptr );
  391. symbolCopy->SetLibId( LIB_ID( aLibrary, aSymbol->GetLibId().GetLibItemName() ) );
  392. auto newScreen = std::make_unique<SCH_SCREEN>();
  393. SCH_SCREEN& screen = *newScreen;
  394. libBuf.CreateBuffer( std::move( symbolCopy ), std::move( newScreen ) );
  395. screen.SetContentModified();
  396. }
  397. return true;
  398. }
  399. bool SYMBOL_LIBRARY_MANAGER::UpdateSymbolAfterRename( LIB_SYMBOL* aSymbol, const wxString& aOldName,
  400. const wxString& aLibrary )
  401. {
  402. LIB_BUFFER& libBuf = getLibraryBuffer( aLibrary );
  403. std::shared_ptr<SYMBOL_BUFFER> symbolBuf = libBuf.GetBuffer( aOldName );
  404. wxCHECK( symbolBuf && aSymbol, false );
  405. libBuf.UpdateBuffer( *symbolBuf, *aSymbol );
  406. OnDataChanged();
  407. return true;
  408. }
  409. LIB_ID SYMBOL_LIBRARY_MANAGER::RevertSymbol( const wxString& aSymbolName, const wxString& aLibrary )
  410. {
  411. auto it = m_libs.find( aLibrary );
  412. if( it == m_libs.end() ) // no items to flush
  413. return LIB_ID( aLibrary, aSymbolName );
  414. std::shared_ptr<SYMBOL_BUFFER> symbolBuf = it->second.GetBuffer( aSymbolName );
  415. wxCHECK( symbolBuf, LIB_ID( aLibrary, aSymbolName ) );
  416. LIB_SYMBOL original( symbolBuf->GetOriginal() );
  417. if( original.GetName() != aSymbolName )
  418. {
  419. UpdateSymbolAfterRename( &original, aSymbolName, aLibrary );
  420. }
  421. else
  422. {
  423. // copy the initial data to the current symbol to restore
  424. symbolBuf->GetSymbol() = original;
  425. OnDataChanged();
  426. }
  427. return LIB_ID( aLibrary, original.GetName() );
  428. }
  429. bool SYMBOL_LIBRARY_MANAGER::RevertLibrary( const wxString& aLibrary )
  430. {
  431. auto it = m_libs.find( aLibrary );
  432. if( it == m_libs.end() ) // nothing to reverse
  433. return false;
  434. m_libs.erase( it );
  435. OnDataChanged();
  436. return true;
  437. }
  438. bool SYMBOL_LIBRARY_MANAGER::RevertAll()
  439. {
  440. bool retv = true;
  441. // Nothing to revert.
  442. if( GetHash() == 0 )
  443. return true;
  444. for( const auto& lib : m_libs )
  445. {
  446. if( !lib.second.IsModified() )
  447. continue;
  448. for( const std::shared_ptr<SYMBOL_BUFFER>& buffer : lib.second.GetBuffers() )
  449. {
  450. if( !buffer->IsModified() )
  451. continue;
  452. RevertSymbol( lib.first, buffer->GetOriginal().GetName() );
  453. }
  454. }
  455. return retv;
  456. }
  457. bool SYMBOL_LIBRARY_MANAGER::RemoveSymbol( const wxString& aSymbolName, const wxString& aLibrary )
  458. {
  459. LIB_BUFFER& libBuf = getLibraryBuffer( aLibrary );
  460. std::shared_ptr<SYMBOL_BUFFER> symbolBuf = libBuf.GetBuffer( aSymbolName );
  461. wxCHECK( symbolBuf, false );
  462. bool retv = true;
  463. retv &= libBuf.DeleteBuffer( *symbolBuf );
  464. OnDataChanged();
  465. return retv;
  466. }
  467. LIB_SYMBOL* SYMBOL_LIBRARY_MANAGER::GetSymbol( const wxString& aSymbolName,
  468. const wxString& aLibrary ) const
  469. {
  470. // Try the library buffers first
  471. auto libIt = m_libs.find( aLibrary );
  472. if( libIt != m_libs.end() )
  473. {
  474. LIB_SYMBOL* symbol = libIt->second.GetSymbol( aSymbolName );
  475. if( symbol )
  476. return symbol;
  477. }
  478. // Get the original symbol
  479. LIB_SYMBOL* symbol = nullptr;
  480. try
  481. {
  482. symbol = symTable()->LoadSymbol( aLibrary, aSymbolName );
  483. }
  484. catch( const IO_ERROR& e )
  485. {
  486. wxString msg;
  487. msg.Printf( _( "Cannot load symbol '%s' from library '%s'." ),
  488. aSymbolName,
  489. aLibrary );
  490. DisplayErrorMessage( &m_frame, msg, e.What() );
  491. }
  492. return symbol;
  493. }
  494. bool SYMBOL_LIBRARY_MANAGER::SymbolExists( const wxString& aSymbolName,
  495. const wxString& aLibrary ) const
  496. {
  497. auto libBufIt = m_libs.find( aLibrary );
  498. LIB_SYMBOL* symbol = nullptr;
  499. if( libBufIt != m_libs.end() )
  500. return libBufIt->second.GetBuffer( aSymbolName ) != nullptr;
  501. try
  502. {
  503. symbol = symTable()->LoadSymbol( aLibrary, aSymbolName );
  504. }
  505. catch( IO_ERROR& )
  506. {
  507. // checking if certain symbol exists, so its absence is perfectly fine
  508. }
  509. return symbol != nullptr;
  510. }
  511. bool SYMBOL_LIBRARY_MANAGER::SymbolNameInUse( const wxString& aName, const wxString& aLibrary )
  512. {
  513. wxArrayString existing;
  514. GetSymbolNames( aLibrary, existing );
  515. for( wxString& candidate : existing )
  516. {
  517. if( candidate.CmpNoCase( aName ) == 0 )
  518. return true;
  519. }
  520. return false;
  521. }
  522. bool SYMBOL_LIBRARY_MANAGER::LibraryExists( const wxString& aLibrary, bool aCheckEnabled ) const
  523. {
  524. if( aLibrary.IsEmpty() )
  525. return false;
  526. if( m_libs.count( aLibrary ) > 0 )
  527. return true;
  528. return symTable()->HasLibrary( aLibrary, aCheckEnabled );
  529. }
  530. wxString SYMBOL_LIBRARY_MANAGER::GetUniqueLibraryName() const
  531. {
  532. wxString name = "New_Library";
  533. if( !LibraryExists( name ) )
  534. return name;
  535. name += "_";
  536. for( unsigned int i = 0; i < std::numeric_limits<unsigned int>::max(); ++i )
  537. {
  538. if( !LibraryExists( name + wxString::Format( "%u", i ) ) )
  539. return name + wxString::Format( "%u", i );
  540. }
  541. wxFAIL;
  542. return wxEmptyString;
  543. }
  544. void SYMBOL_LIBRARY_MANAGER::GetSymbolNames( const wxString& aLibraryName,
  545. wxArrayString& aSymbolNames,
  546. SYMBOL_NAME_FILTER aFilter )
  547. {
  548. LIB_BUFFER& libBuf = getLibraryBuffer( aLibraryName );
  549. libBuf.GetSymbolNames( aSymbolNames, aFilter );
  550. }
  551. bool SYMBOL_LIBRARY_MANAGER:: HasDerivedSymbols( const wxString& aSymbolName,
  552. const wxString& aLibraryName )
  553. {
  554. LIB_BUFFER& libBuf = getLibraryBuffer( aLibraryName );
  555. return libBuf.HasDerivedSymbols( aSymbolName );
  556. }
  557. size_t SYMBOL_LIBRARY_MANAGER::GetLibraryCount() const
  558. {
  559. return symTable()->GetLogicalLibs().size();
  560. }
  561. wxString SYMBOL_LIBRARY_MANAGER::getLibraryName( const wxString& aFilePath )
  562. {
  563. wxFileName fn( aFilePath );
  564. return fn.GetName();
  565. }
  566. bool SYMBOL_LIBRARY_MANAGER::addLibrary( const wxString& aFilePath, bool aCreate,
  567. SYMBOL_LIB_TABLE& aTable )
  568. {
  569. wxString libName = getLibraryName( aFilePath );
  570. wxCHECK( !LibraryExists( libName ), false ); // either create or add an existing one
  571. // try to use path normalized to an environmental variable or project path
  572. wxString relPath = NormalizePath( aFilePath, &Pgm().GetLocalEnvVariables(), &m_frame.Prj() );
  573. SCH_IO_MGR::SCH_FILE_T schFileType = SCH_IO_MGR::GuessPluginTypeFromLibPath( aFilePath,
  574. aCreate ? KICTL_CREATE : 0 );
  575. if( schFileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
  576. schFileType = SCH_IO_MGR::SCH_LEGACY;
  577. wxString typeName = SCH_IO_MGR::ShowType( schFileType );
  578. SYMBOL_LIB_TABLE_ROW* libRow = new SYMBOL_LIB_TABLE_ROW( libName, relPath, typeName );
  579. aTable.InsertRow( libRow );
  580. if( aCreate )
  581. {
  582. wxCHECK( schFileType != SCH_IO_MGR::SCH_FILE_T::SCH_LEGACY, false );
  583. try
  584. {
  585. aTable.CreateSymbolLib( libName );
  586. }
  587. catch( const IO_ERROR& )
  588. {
  589. aTable.RemoveRow( libRow );
  590. return false;
  591. }
  592. }
  593. OnDataChanged();
  594. return true;
  595. }
  596. SYMBOL_LIB_TABLE* SYMBOL_LIBRARY_MANAGER::symTable() const
  597. {
  598. return PROJECT_SCH::SchSymbolLibTable( &m_frame.Prj() );
  599. }
  600. std::set<LIB_SYMBOL*> SYMBOL_LIBRARY_MANAGER::getOriginalSymbols( const wxString& aLibrary )
  601. {
  602. std::set<LIB_SYMBOL*> symbols;
  603. wxCHECK( LibraryExists( aLibrary ), symbols );
  604. try
  605. {
  606. wxArrayString symbolNames;
  607. symTable()->EnumerateSymbolLib( aLibrary, symbolNames );
  608. for( const auto& symbolName : symbolNames )
  609. {
  610. LIB_SYMBOL* symbol = symTable()->LoadSymbol( aLibrary, symbolName );
  611. symbols.insert( symbol );
  612. }
  613. }
  614. catch( const IO_ERROR& e )
  615. {
  616. wxString msg;
  617. msg.Printf( _( "Cannot enumerate library '%s'." ), aLibrary );
  618. DisplayErrorMessage( &m_frame, msg, e.What() );
  619. }
  620. return symbols;
  621. }
  622. LIB_BUFFER& SYMBOL_LIBRARY_MANAGER::getLibraryBuffer( const wxString& aLibrary )
  623. {
  624. auto it = m_libs.find( aLibrary );
  625. if( it != m_libs.end() )
  626. return it->second;
  627. // The requested buffer does not exist yet, so create one
  628. auto ret = m_libs.emplace( aLibrary, LIB_BUFFER( aLibrary ) );
  629. LIB_BUFFER& buf = ret.first->second;
  630. for( LIB_SYMBOL* symbol : getOriginalSymbols( aLibrary ) )
  631. {
  632. if( symbol->IsDerived() )
  633. {
  634. std::shared_ptr<LIB_SYMBOL> oldParent = symbol->GetParent().lock();
  635. wxCHECK_MSG( oldParent, buf,
  636. wxString::Format( "Derived symbol '%s' found with undefined parent.",
  637. symbol->GetName() ) );
  638. LIB_SYMBOL* libParent = buf.GetSymbol( oldParent->GetName() );
  639. if( !libParent )
  640. {
  641. auto newParent = std::make_unique<LIB_SYMBOL>( *oldParent.get() );
  642. libParent = newParent.get();
  643. buf.CreateBuffer( std::move( newParent ), std::make_unique<SCH_SCREEN>() );
  644. }
  645. auto newSymbol = std::make_unique<LIB_SYMBOL>( *symbol );
  646. newSymbol->SetParent( libParent );
  647. buf.CreateBuffer( std::move( newSymbol ), std::make_unique<SCH_SCREEN>() );
  648. }
  649. else if( !buf.GetSymbol( symbol->GetName() ) )
  650. {
  651. buf.CreateBuffer( std::make_unique<LIB_SYMBOL>( *symbol ),
  652. std::make_unique<SCH_SCREEN>() );
  653. }
  654. }
  655. return buf;
  656. }
  657. bool SYMBOL_LIBRARY_MANAGER::UpdateLibraryBuffer( const wxString& aLibrary )
  658. {
  659. try
  660. {
  661. m_libs.erase( aLibrary );
  662. getLibraryBuffer( aLibrary );
  663. }
  664. catch(const std::exception& e)
  665. {
  666. wxLogError( _( "Error updating library buffer: %s" ), e.what() );
  667. return false;
  668. }
  669. catch( const IO_ERROR& e )
  670. {
  671. wxLogError( _( "Error updating library buffer: %s" ), e.What() );
  672. return false;
  673. }
  674. catch(...)
  675. {
  676. wxLogError( _( "Error updating library buffer." ) );
  677. return false;
  678. }
  679. getLibraryBuffer( aLibrary );
  680. return true;
  681. }
  682. SYMBOL_BUFFER::SYMBOL_BUFFER( std::unique_ptr<LIB_SYMBOL> aSymbol,
  683. std::unique_ptr<SCH_SCREEN> aScreen ) :
  684. m_screen( std::move( aScreen ) ), m_symbol( std::move( aSymbol ) )
  685. {
  686. wxASSERT( m_symbol );
  687. m_original = std::make_unique<LIB_SYMBOL>( *m_symbol );
  688. wxASSERT( m_original );
  689. }
  690. SYMBOL_BUFFER::~SYMBOL_BUFFER()
  691. {
  692. }
  693. void SYMBOL_BUFFER::SetSymbol( std::unique_ptr<LIB_SYMBOL> aSymbol )
  694. {
  695. wxCHECK( m_symbol != aSymbol, /* void */ );
  696. wxASSERT( aSymbol );
  697. m_symbol = std::move( aSymbol );
  698. // If the symbol moves libraries then the original moves with it
  699. if( m_original->GetLibId().GetLibNickname() != m_symbol->GetLibId().GetLibNickname() )
  700. {
  701. m_original->SetLibId( LIB_ID( m_symbol->GetLibId().GetLibNickname(),
  702. m_original->GetLibId().GetLibItemName() ) );
  703. }
  704. }
  705. void SYMBOL_BUFFER::SetOriginal( std::unique_ptr<LIB_SYMBOL> aSymbol )
  706. {
  707. wxCHECK( m_original != aSymbol, /* void */ );
  708. wxASSERT( aSymbol );
  709. m_original = std::move( aSymbol );
  710. // The original is not allowed to have a different library than its symbol
  711. if( m_original->GetLibId().GetLibNickname() != m_symbol->GetLibId().GetLibNickname() )
  712. {
  713. m_original->SetLibId( LIB_ID( m_symbol->GetLibId().GetLibNickname(),
  714. m_original->GetLibId().GetLibItemName() ) );
  715. }
  716. }
  717. bool SYMBOL_BUFFER::IsModified() const
  718. {
  719. return m_screen && m_screen->IsContentModified();
  720. }
  721. LIB_SYMBOL* LIB_BUFFER::GetSymbol( const wxString& aAlias ) const
  722. {
  723. auto buf = GetBuffer( aAlias );
  724. if( !buf )
  725. return nullptr;
  726. LIB_SYMBOL& symbol = buf->GetSymbol();
  727. return &symbol;
  728. }
  729. bool LIB_BUFFER::CreateBuffer( std::unique_ptr<LIB_SYMBOL> aCopy,
  730. std::unique_ptr<SCH_SCREEN> aScreen )
  731. {
  732. wxASSERT( aCopy );
  733. wxASSERT( aCopy->GetLib() == nullptr );
  734. // Set the parent library name,
  735. // otherwise it is empty as no library has been given as the owner during object construction
  736. LIB_ID libId = aCopy->GetLibId();
  737. libId.SetLibNickname( m_libName );
  738. aCopy->SetLibId( libId );
  739. auto symbolBuf = std::make_shared<SYMBOL_BUFFER>( std::move( aCopy ), std::move( aScreen ) );
  740. m_symbols.push_back( symbolBuf );
  741. ++m_hash;
  742. return true;
  743. }
  744. bool LIB_BUFFER::UpdateBuffer( SYMBOL_BUFFER& aSymbolBuf, const LIB_SYMBOL& aCopy )
  745. {
  746. LIB_SYMBOL& bufferedSymbol = aSymbolBuf.GetSymbol();
  747. bufferedSymbol = aCopy;
  748. ++m_hash;
  749. return true;
  750. }
  751. bool LIB_BUFFER::DeleteBuffer( const SYMBOL_BUFFER& aSymbolBuf )
  752. {
  753. const auto sameBufferPredicate = [&]( const std::shared_ptr<SYMBOL_BUFFER>& aBuf )
  754. {
  755. return aBuf.get() == &aSymbolBuf;
  756. };
  757. auto symbolBufIt = std::find_if( m_symbols.begin(), m_symbols.end(), sameBufferPredicate );
  758. wxCHECK( symbolBufIt != m_symbols.end(), false );
  759. bool retv = true;
  760. // Remove all derived symbols to prevent broken inheritance.
  761. if( HasDerivedSymbols( aSymbolBuf.GetSymbol().GetName() )
  762. && ( removeChildSymbols( aSymbolBuf ) == 0 ) )
  763. {
  764. retv = false;
  765. }
  766. m_deleted.emplace_back( *symbolBufIt );
  767. m_symbols.erase( symbolBufIt );
  768. ++m_hash;
  769. return retv;
  770. }
  771. bool LIB_BUFFER::SaveBuffer( SYMBOL_BUFFER& aSymbolBuf, const wxString& aFileName, SCH_IO* aPlugin,
  772. bool aBuffer )
  773. {
  774. wxCHECK( !aFileName.IsEmpty(), false );
  775. wxString errorMsg = _( "Error saving symbol %s to library '%s'." ) + wxS( "\n%s" );
  776. // Set properties to prevent saving the file on every symbol save.
  777. std::map<std::string, UTF8> properties;
  778. properties.emplace( SCH_IO_KICAD_LEGACY::PropBuffering, "" );
  779. LIB_SYMBOL& libSymbol = aSymbolBuf.GetSymbol();
  780. {
  781. LIB_SYMBOL& originalSymbol = aSymbolBuf.GetOriginal();
  782. const wxString originalName = originalSymbol.GetName();
  783. // Delete the original symbol if the symbol name has been changed.
  784. if( libSymbol.GetName() != originalSymbol.GetName() )
  785. {
  786. try
  787. {
  788. if( aPlugin->LoadSymbol( aFileName, originalName ) )
  789. aPlugin->DeleteSymbol( aFileName, originalName, &properties );
  790. }
  791. catch( const IO_ERROR& ioe )
  792. {
  793. wxLogError( errorMsg, UnescapeString( originalName ), aFileName, ioe.What() );
  794. return false;
  795. }
  796. }
  797. }
  798. LIB_SYMBOL* parentSymbol = nullptr;
  799. if( libSymbol.IsDerived() )
  800. {
  801. LIB_SYMBOL* newCachedSymbol = new LIB_SYMBOL( libSymbol );
  802. std::shared_ptr<LIB_SYMBOL> bufferedParent = libSymbol.GetParent().lock();
  803. parentSymbol = newCachedSymbol;
  804. wxCHECK( bufferedParent, false );
  805. LIB_SYMBOL* cachedParent = nullptr;
  806. try
  807. {
  808. cachedParent = aPlugin->LoadSymbol( aFileName, bufferedParent->GetName() );
  809. }
  810. catch( const IO_ERROR& )
  811. {
  812. return false;
  813. }
  814. if( !cachedParent )
  815. {
  816. cachedParent = new LIB_SYMBOL( *bufferedParent.get() );
  817. newCachedSymbol->SetParent( cachedParent );
  818. try
  819. {
  820. aPlugin->SaveSymbol( aFileName, cachedParent, aBuffer ? &properties : nullptr );
  821. }
  822. catch( const IO_ERROR& ioe )
  823. {
  824. wxLogError( errorMsg, UnescapeString( cachedParent->GetName() ), aFileName,
  825. ioe.What() );
  826. return false;
  827. }
  828. try
  829. {
  830. aPlugin->SaveSymbol( aFileName, newCachedSymbol, aBuffer ? &properties : nullptr );
  831. }
  832. catch( const IO_ERROR& ioe )
  833. {
  834. wxLogError( errorMsg, UnescapeString( newCachedSymbol->GetName() ), aFileName,
  835. ioe.What() );
  836. return false;
  837. }
  838. auto originalParent = std::make_unique<LIB_SYMBOL>( *bufferedParent.get() );
  839. LIB_SYMBOL& parentRef = *originalParent;
  840. aSymbolBuf.SetOriginal( std::move( originalParent ) );
  841. auto newSymbol = std::make_unique<LIB_SYMBOL>( libSymbol );
  842. newSymbol->SetParent( &parentRef );
  843. aSymbolBuf.SetOriginal( std::move( newSymbol ) );
  844. }
  845. else
  846. {
  847. newCachedSymbol->SetParent( cachedParent );
  848. try
  849. {
  850. aPlugin->SaveSymbol( aFileName, newCachedSymbol, aBuffer ? &properties : nullptr );
  851. }
  852. catch( const IO_ERROR& ioe )
  853. {
  854. wxLogError( errorMsg, UnescapeString( newCachedSymbol->GetName() ), aFileName,
  855. ioe.What() );
  856. return false;
  857. }
  858. auto originalBufferedParent = GetBuffer( bufferedParent->GetName() );
  859. wxCHECK( originalBufferedParent, false );
  860. auto newSymbol = std::make_unique<LIB_SYMBOL>( libSymbol );
  861. newSymbol->SetParent( &originalBufferedParent->GetSymbol() );
  862. aSymbolBuf.SetOriginal( std::move( newSymbol ) );
  863. }
  864. }
  865. else
  866. {
  867. parentSymbol = new LIB_SYMBOL( libSymbol );
  868. try
  869. {
  870. aPlugin->SaveSymbol( aFileName, parentSymbol, aBuffer ? &properties : nullptr );
  871. }
  872. catch( const IO_ERROR& ioe )
  873. {
  874. wxLogError( errorMsg, UnescapeString( libSymbol.GetName() ), aFileName, ioe.What() );
  875. return false;
  876. }
  877. aSymbolBuf.SetOriginal( std::make_unique<LIB_SYMBOL>( libSymbol ) );
  878. }
  879. wxArrayString derivedSymbols;
  880. // Reparent all symbols derived from the saved symbol.
  881. if( GetDerivedSymbolNames( libSymbol.GetName(), derivedSymbols ) != 0 )
  882. {
  883. // Save the derived symbols.
  884. for( const wxString& entry : derivedSymbols )
  885. {
  886. std::shared_ptr<SYMBOL_BUFFER> symbol = GetBuffer( entry );
  887. wxCHECK2( symbol, continue );
  888. LIB_SYMBOL* derivedSymbol = new LIB_SYMBOL( symbol->GetSymbol() );
  889. derivedSymbol->SetParent( parentSymbol );
  890. try
  891. {
  892. aPlugin->SaveSymbol( aFileName, new LIB_SYMBOL( *derivedSymbol ),
  893. aBuffer ? &properties : nullptr );
  894. }
  895. catch( const IO_ERROR& ioe )
  896. {
  897. wxLogError( errorMsg, UnescapeString( derivedSymbol->GetName() ), aFileName,
  898. ioe.What() );
  899. return false;
  900. }
  901. }
  902. }
  903. ++m_hash;
  904. return true;
  905. }
  906. std::shared_ptr<SYMBOL_BUFFER> LIB_BUFFER::GetBuffer( const wxString& aSymbolName ) const
  907. {
  908. for( std::shared_ptr<SYMBOL_BUFFER> entry : m_symbols )
  909. {
  910. if( entry->GetSymbol().GetName() == aSymbolName )
  911. return entry;
  912. }
  913. return std::shared_ptr<SYMBOL_BUFFER>( nullptr );
  914. }
  915. bool LIB_BUFFER::HasDerivedSymbols( const wxString& aParentName ) const
  916. {
  917. for( const std::shared_ptr<SYMBOL_BUFFER>& entry : m_symbols )
  918. {
  919. if( entry->GetSymbol().IsDerived() )
  920. {
  921. LIB_SYMBOL_SPTR parent = entry->GetSymbol().GetParent().lock();
  922. // Check for inherited symbol without a valid parent.
  923. wxCHECK( parent, false );
  924. if( parent->GetName() == aParentName )
  925. return true;
  926. }
  927. }
  928. return false;
  929. }
  930. void LIB_BUFFER::GetSymbolNames( wxArrayString& aSymbolNames, SYMBOL_NAME_FILTER aFilter )
  931. {
  932. for( std::shared_ptr<SYMBOL_BUFFER>& entry : m_symbols )
  933. {
  934. const LIB_SYMBOL& symbol = entry->GetSymbol();
  935. if( ( symbol.IsDerived() && ( aFilter == SYMBOL_NAME_FILTER::ROOT_ONLY ) )
  936. || ( symbol.IsRoot() && ( aFilter == SYMBOL_NAME_FILTER::DERIVED_ONLY ) ) )
  937. {
  938. continue;
  939. }
  940. aSymbolNames.Add( UnescapeString( symbol.GetName() ) );
  941. }
  942. }
  943. size_t LIB_BUFFER::GetDerivedSymbolNames( const wxString& aSymbolName, wxArrayString& aList )
  944. {
  945. wxCHECK( !aSymbolName.IsEmpty(), 0 );
  946. for( std::shared_ptr<SYMBOL_BUFFER>& entry : m_symbols )
  947. {
  948. const LIB_SYMBOL& symbol = entry->GetSymbol();
  949. if( symbol.IsDerived() )
  950. {
  951. LIB_SYMBOL_SPTR parent = symbol.GetParent().lock();
  952. // Check for inherited symbol without a valid parent.
  953. wxCHECK2( parent, continue );
  954. if( parent->GetName() == aSymbolName )
  955. {
  956. aList.Add( symbol.GetName() );
  957. GetDerivedSymbolNames( symbol.GetName(), aList );
  958. }
  959. }
  960. }
  961. return aList.GetCount();
  962. }
  963. int LIB_BUFFER::removeChildSymbols( const SYMBOL_BUFFER& aSymbolBuf )
  964. {
  965. int cnt = 0;
  966. wxArrayString derivedSymbolNames;
  967. std::deque<std::shared_ptr<SYMBOL_BUFFER>>::iterator it;
  968. if( GetDerivedSymbolNames( aSymbolBuf.GetSymbol().GetName(), derivedSymbolNames ) )
  969. {
  970. for( const wxString& symbolName : derivedSymbolNames )
  971. {
  972. it = std::find_if( m_symbols.begin(), m_symbols.end(),
  973. [symbolName]( std::shared_ptr<SYMBOL_BUFFER>& buf )
  974. {
  975. return buf->GetSymbol().GetName() == symbolName;
  976. } );
  977. wxCHECK2( it != m_symbols.end(), continue );
  978. m_deleted.emplace_back( *it );
  979. m_symbols.erase( it );
  980. cnt += 1;
  981. }
  982. }
  983. return cnt;
  984. }