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.

750 lines
22 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 Wayne Stambaugh <stambaughw@gmail.com>
  5. * Copyright (C) 2022 CERN
  6. * Copyright The 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. #include <env_vars.h>
  26. #include <lib_id.h>
  27. #include <lib_table_lexer.h>
  28. #include <paths.h>
  29. #include <pgm_base.h>
  30. #include <search_stack.h>
  31. #include <settings/kicad_settings.h>
  32. #include <settings/settings_manager.h>
  33. #include <systemdirsappend.h>
  34. #include <symbol_lib_table.h>
  35. #include <lib_symbol.h>
  36. #include <sch_io/database/sch_io_database.h>
  37. #include <dialogs/dialog_database_lib_settings.h>
  38. #include <wx/dir.h>
  39. #include "sim/sim_model.h"
  40. #define OPT_SEP '|' ///< options separator character
  41. using namespace LIB_TABLE_T;
  42. const char* SYMBOL_LIB_TABLE::PropPowerSymsOnly = "pwr_sym_only";
  43. const char* SYMBOL_LIB_TABLE::PropNonPowerSymsOnly = "non_pwr_sym_only";
  44. int SYMBOL_LIB_TABLE::m_modifyHash = 1; // starts at 1 and goes up
  45. /// The global symbol library table. This is not dynamically allocated because
  46. /// in a multiple project environment we must keep its address constant (since it is
  47. /// the fallback table for multiple projects).
  48. SYMBOL_LIB_TABLE g_symbolLibraryTable;
  49. bool SYMBOL_LIB_TABLE_ROW::operator==( const SYMBOL_LIB_TABLE_ROW& aRow ) const
  50. {
  51. return LIB_TABLE_ROW::operator == ( aRow ) && type == aRow.type;
  52. }
  53. void SYMBOL_LIB_TABLE_ROW::SetType( const wxString& aType )
  54. {
  55. type = SCH_IO_MGR::EnumFromStr( aType );
  56. if( type == SCH_IO_MGR::SCH_FILE_UNKNOWN )
  57. type = SCH_IO_MGR::SCH_KICAD;
  58. plugin.reset();
  59. }
  60. bool SYMBOL_LIB_TABLE_ROW::Refresh()
  61. {
  62. if( !plugin )
  63. {
  64. wxArrayString dummyList;
  65. plugin.reset( SCH_IO_MGR::FindPlugin( type ) );
  66. SetLoaded( false );
  67. plugin->SetLibTable( static_cast<SYMBOL_LIB_TABLE*>( GetParent() ) );
  68. plugin->EnumerateSymbolLib( dummyList, GetFullURI( true ), GetProperties() );
  69. SetLoaded( true );
  70. return true;
  71. }
  72. return false;
  73. }
  74. void SYMBOL_LIB_TABLE_ROW::GetSubLibraryNames( std::vector<wxString>& aNames ) const
  75. {
  76. if( !plugin )
  77. return;
  78. plugin->GetSubLibraryNames( aNames );
  79. }
  80. wxString SYMBOL_LIB_TABLE_ROW::GetSubLibraryDescription( const wxString& aName ) const
  81. {
  82. if( !plugin )
  83. return wxEmptyString;
  84. return plugin->GetSubLibraryDescription( aName );
  85. }
  86. void SYMBOL_LIB_TABLE_ROW::ShowSettingsDialog( wxWindow* aParent ) const
  87. {
  88. wxCHECK( plugin, /* void */ );
  89. if( type != SCH_IO_MGR::SCH_DATABASE )
  90. return;
  91. DIALOG_DATABASE_LIB_SETTINGS dlg( aParent, static_cast<SCH_IO_DATABASE*>( plugin.get() ) );
  92. dlg.ShowModal();
  93. }
  94. SYMBOL_LIB_TABLE::SYMBOL_LIB_TABLE( SYMBOL_LIB_TABLE* aFallBackTable ) :
  95. LIB_TABLE( aFallBackTable )
  96. {
  97. // not copying fall back, simply search aFallBackTable separately
  98. // if "nickName not found".
  99. }
  100. SYMBOL_LIB_TABLE& SYMBOL_LIB_TABLE::GetGlobalLibTable()
  101. {
  102. return g_symbolLibraryTable;
  103. }
  104. void SYMBOL_LIB_TABLE::Parse( LIB_TABLE_LEXER* in )
  105. {
  106. T tok;
  107. wxString errMsg; // to collect error messages
  108. // This table may be nested within a larger s-expression, or not.
  109. // Allow for parser of that optional containing s-expression to have looked ahead.
  110. if( in->CurTok() != T_sym_lib_table )
  111. {
  112. in->NeedLEFT();
  113. if( ( tok = in->NextTok() ) != T_sym_lib_table )
  114. in->Expecting( T_sym_lib_table );
  115. }
  116. while( ( tok = in->NextTok() ) != T_RIGHT )
  117. {
  118. std::unique_ptr< SYMBOL_LIB_TABLE_ROW > row = std::make_unique<SYMBOL_LIB_TABLE_ROW>();
  119. if( tok == T_EOF )
  120. in->Expecting( T_RIGHT );
  121. if( tok != T_LEFT )
  122. in->Expecting( T_LEFT );
  123. // in case there is a "row integrity" error, tell where later.
  124. int lineNum = in->CurLineNumber();
  125. tok = in->NextTok();
  126. // Optionally parse the current version number
  127. if( tok == T_version )
  128. {
  129. in->NeedNUMBER( "version" );
  130. m_version = std::stoi( in->CurText() );
  131. in->NeedRIGHT();
  132. continue;
  133. }
  134. if( tok != T_lib )
  135. in->Expecting( T_lib );
  136. // (name NICKNAME)
  137. in->NeedLEFT();
  138. if( ( tok = in->NextTok() ) != T_name )
  139. in->Expecting( T_name );
  140. in->NeedSYMBOLorNUMBER();
  141. row->SetNickName( in->FromUTF8() );
  142. in->NeedRIGHT();
  143. // After (name), remaining (lib) elements are order independent, and in
  144. // some cases optional.
  145. bool sawType = false;
  146. bool sawOpts = false;
  147. bool sawDesc = false;
  148. bool sawUri = false;
  149. bool sawDisabled = false;
  150. bool sawHidden = false;
  151. while( ( tok = in->NextTok() ) != T_RIGHT )
  152. {
  153. if( tok == T_EOF )
  154. in->Unexpected( T_EOF );
  155. if( tok != T_LEFT )
  156. in->Expecting( T_LEFT );
  157. tok = in->NeedSYMBOLorNUMBER();
  158. switch( tok )
  159. {
  160. case T_uri:
  161. if( sawUri )
  162. in->Duplicate( tok );
  163. sawUri = true;
  164. in->NeedSYMBOLorNUMBER();
  165. row->SetFullURI( in->FromUTF8() );
  166. break;
  167. case T_type:
  168. if( sawType )
  169. in->Duplicate( tok );
  170. sawType = true;
  171. in->NeedSYMBOLorNUMBER();
  172. row->SetType( in->FromUTF8() );
  173. break;
  174. case T_options:
  175. if( sawOpts )
  176. in->Duplicate( tok );
  177. sawOpts = true;
  178. in->NeedSYMBOLorNUMBER();
  179. row->SetOptions( in->FromUTF8() );
  180. break;
  181. case T_descr:
  182. if( sawDesc )
  183. in->Duplicate( tok );
  184. sawDesc = true;
  185. in->NeedSYMBOLorNUMBER();
  186. row->SetDescr( in->FromUTF8() );
  187. break;
  188. case T_disabled:
  189. if( sawDisabled )
  190. in->Duplicate( tok );
  191. sawDisabled = true;
  192. row->SetEnabled( false );
  193. break;
  194. case T_hidden:
  195. if( sawHidden )
  196. in->Duplicate( tok );
  197. sawHidden = true;
  198. row->SetVisible( false );
  199. break;
  200. default:
  201. in->Unexpected( tok );
  202. }
  203. in->NeedRIGHT();
  204. }
  205. if( !sawType )
  206. in->Expecting( T_type );
  207. if( !sawUri )
  208. in->Expecting( T_uri );
  209. // All nickNames within this table fragment must be unique, so we do not use doReplace
  210. // in doInsertRow(). (However a fallBack table can have a conflicting nickName and ours
  211. // will supersede that one since in FindLib() we search this table before any fall back.)
  212. wxString nickname = row->GetNickName(); // store it to be able to used it
  213. // after row deletion if an error occurs
  214. bool doReplace = false;
  215. LIB_TABLE_ROW* tmp = row.release();
  216. if( !doInsertRow( tmp, doReplace ) )
  217. {
  218. delete tmp; // The table did not take ownership of the row.
  219. wxString msg = wxString::Format( _( "Duplicate library nickname '%s' found in symbol "
  220. "library table file line %d" ),
  221. nickname,
  222. lineNum );
  223. if( !errMsg.IsEmpty() )
  224. errMsg << '\n';
  225. errMsg << msg;
  226. }
  227. }
  228. if( !errMsg.IsEmpty() )
  229. THROW_IO_ERROR( errMsg );
  230. }
  231. void SYMBOL_LIB_TABLE::Format( OUTPUTFORMATTER* aOutput, int aIndentLevel ) const
  232. {
  233. aOutput->Print( aIndentLevel, "(sym_lib_table\n" );
  234. aOutput->Print( aIndentLevel + 1, "(version %d)\n", m_version );
  235. for( const LIB_TABLE_ROW& row : m_rows )
  236. row.Format( aOutput, aIndentLevel + 1 );
  237. aOutput->Print( aIndentLevel, ")\n" );
  238. }
  239. int SYMBOL_LIB_TABLE::GetModifyHash()
  240. {
  241. int hash = 0;
  242. std::vector< wxString > libNames = GetLogicalLibs();
  243. for( const auto& libName : libNames )
  244. {
  245. const SYMBOL_LIB_TABLE_ROW* row = FindRow( libName, true );
  246. if( !row || !row->plugin )
  247. {
  248. continue;
  249. }
  250. hash += row->plugin->GetModifyHash();
  251. }
  252. hash += m_modifyHash;
  253. return hash;
  254. }
  255. void SYMBOL_LIB_TABLE::EnumerateSymbolLib( const wxString& aNickname, wxArrayString& aAliasNames,
  256. bool aPowerSymbolsOnly )
  257. {
  258. SYMBOL_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  259. wxCHECK( row && row->plugin, /* void */ );
  260. wxString options = row->GetOptions();
  261. if( aPowerSymbolsOnly )
  262. row->SetOptions( row->GetOptions() + " " + PropPowerSymsOnly );
  263. row->SetLoaded( false );
  264. row->plugin->EnumerateSymbolLib( aAliasNames, row->GetFullURI( true ), row->GetProperties() );
  265. row->SetLoaded( true );
  266. if( aPowerSymbolsOnly )
  267. row->SetOptions( options );
  268. }
  269. SYMBOL_LIB_TABLE_ROW* SYMBOL_LIB_TABLE::FindRow( const wxString& aNickname, bool aCheckIfEnabled )
  270. {
  271. SYMBOL_LIB_TABLE_ROW* row =
  272. dynamic_cast< SYMBOL_LIB_TABLE_ROW* >( findRow( aNickname, aCheckIfEnabled ) );
  273. if( !row )
  274. return nullptr;
  275. // We've been 'lazy' up until now, but it cannot be deferred any longer,
  276. // instantiate a PLUGIN of the proper kind if it is not already in this
  277. // SYMBOL_LIB_TABLE_ROW.
  278. if( !row->plugin )
  279. row->setPlugin( SCH_IO_MGR::FindPlugin( row->type ) );
  280. row->plugin->SetLibTable( this );
  281. return row;
  282. }
  283. void SYMBOL_LIB_TABLE::LoadSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
  284. const wxString& aNickname, bool aPowerSymbolsOnly )
  285. {
  286. SYMBOL_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  287. if( !row || !row->plugin )
  288. return;
  289. std::lock_guard<std::mutex> lock( row->GetMutex() );
  290. wxString options = row->GetOptions();
  291. if( aPowerSymbolsOnly )
  292. row->SetOptions( row->GetOptions() + " " + PropPowerSymsOnly );
  293. row->SetLoaded( false );
  294. row->plugin->SetLibTable( this );
  295. row->plugin->EnumerateSymbolLib( aSymbolList, row->GetFullURI( true ), row->GetProperties() );
  296. row->SetLoaded( true );
  297. if( aPowerSymbolsOnly )
  298. row->SetOptions( options );
  299. // The library cannot know its own name, because it might have been renamed or moved.
  300. // Therefore footprints cannot know their own library nickname when residing in
  301. // a symbol library.
  302. // Only at this API layer can we tell the symbol about its actual library nickname.
  303. for( LIB_SYMBOL* symbol : aSymbolList )
  304. {
  305. LIB_ID id = symbol->GetLibId();
  306. id.SetLibNickname( row->GetNickName() );
  307. symbol->SetLibId( id );
  308. }
  309. }
  310. LIB_SYMBOL* SYMBOL_LIB_TABLE::LoadSymbol( const wxString& aNickname, const wxString& aSymbolName )
  311. {
  312. SYMBOL_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  313. if( !row || !row->plugin )
  314. return nullptr;
  315. // If another thread is loading this library at the moment; continue
  316. std::unique_lock<std::mutex> lock( row->GetMutex(), std::try_to_lock );
  317. if( !lock.owns_lock() )
  318. return nullptr;
  319. LIB_SYMBOL* symbol = row->plugin->LoadSymbol( row->GetFullURI( true ), aSymbolName,
  320. row->GetProperties() );
  321. if( symbol )
  322. {
  323. // The library cannot know its own name, because it might have been renamed or moved.
  324. // Therefore footprints cannot know their own library nickname when residing in
  325. // a symbol library.
  326. // Only at this API layer can we tell the symbol about its actual library nickname.
  327. LIB_ID id = symbol->GetLibId();
  328. id.SetLibNickname( row->GetNickName() );
  329. symbol->SetLibId( id );
  330. SIM_MODEL::MigrateSimModel<LIB_SYMBOL>( *symbol, nullptr );
  331. }
  332. return symbol;
  333. }
  334. SYMBOL_LIB_TABLE::SAVE_T SYMBOL_LIB_TABLE::SaveSymbol( const wxString& aNickname,
  335. const LIB_SYMBOL* aSymbol, bool aOverwrite )
  336. {
  337. const SYMBOL_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  338. wxCHECK( row && row->plugin, SAVE_SKIPPED );
  339. if( !row->plugin->IsLibraryWritable( row->GetFullURI( true ) ) )
  340. return SAVE_SKIPPED;
  341. if( !aOverwrite )
  342. {
  343. // Try loading the footprint to see if it already exists, caller wants overwrite
  344. // protection, which is atypical, not the default.
  345. wxString name = aSymbol->GetLibId().GetLibItemName();
  346. std::unique_ptr<LIB_SYMBOL> symbol( row->plugin->LoadSymbol( row->GetFullURI( true ),
  347. name, row->GetProperties() ) );
  348. if( symbol.get() )
  349. return SAVE_SKIPPED;
  350. }
  351. try
  352. {
  353. row->plugin->SaveSymbol( row->GetFullURI( true ), aSymbol, row->GetProperties() );
  354. }
  355. catch( const IO_ERROR& )
  356. {
  357. return SAVE_SKIPPED;
  358. }
  359. return SAVE_OK;
  360. }
  361. void SYMBOL_LIB_TABLE::DeleteSymbol( const wxString& aNickname, const wxString& aSymbolName )
  362. {
  363. const SYMBOL_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  364. wxCHECK( row && row->plugin, /* void */ );
  365. return row->plugin->DeleteSymbol( row->GetFullURI( true ), aSymbolName, row->GetProperties() );
  366. }
  367. bool SYMBOL_LIB_TABLE::IsSymbolLibWritable( const wxString& aNickname )
  368. {
  369. const SYMBOL_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  370. wxCHECK( row && row->plugin, false );
  371. return row->plugin->IsLibraryWritable( row->GetFullURI( true ) );
  372. }
  373. bool SYMBOL_LIB_TABLE::IsSymbolLibLoaded( const wxString& aNickname )
  374. {
  375. const SYMBOL_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  376. wxCHECK( row, false );
  377. return row->GetIsLoaded();
  378. }
  379. void SYMBOL_LIB_TABLE::DeleteSymbolLib( const wxString& aNickname )
  380. {
  381. const SYMBOL_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  382. wxCHECK( row && row->plugin, /* void */ );
  383. row->plugin->DeleteLibrary( row->GetFullURI( true ), row->GetProperties() );
  384. }
  385. void SYMBOL_LIB_TABLE::CreateSymbolLib( const wxString& aNickname )
  386. {
  387. const SYMBOL_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  388. wxCHECK( row && row->plugin, /* void */ );
  389. row->plugin->CreateLibrary( row->GetFullURI( true ), row->GetProperties() );
  390. }
  391. LIB_SYMBOL* SYMBOL_LIB_TABLE::LoadSymbolWithOptionalNickname( const LIB_ID& aLibId )
  392. {
  393. wxString nickname = aLibId.GetLibNickname();
  394. wxString name = aLibId.GetLibItemName();
  395. if( nickname.size() )
  396. {
  397. return LoadSymbol( nickname, name );
  398. }
  399. else
  400. {
  401. // nickname is empty, sequentially search (alphabetically) all libs/nicks for first match:
  402. std::vector<wxString> nicks = GetLogicalLibs();
  403. // Search each library going through libraries alphabetically.
  404. for( unsigned i = 0; i < nicks.size(); ++i )
  405. {
  406. // FootprintLoad() returns NULL on not found, does not throw exception
  407. // unless there's an IO_ERROR.
  408. LIB_SYMBOL* ret = LoadSymbol( nicks[i], name );
  409. if( ret )
  410. return ret;
  411. }
  412. return nullptr;
  413. }
  414. }
  415. const wxString SYMBOL_LIB_TABLE::GlobalPathEnvVariableName()
  416. {
  417. return ENV_VAR::GetVersionedEnvVarName( wxS( "SYMBOL_DIR" ) );
  418. }
  419. class PCM_SYM_LIB_TRAVERSER final : public wxDirTraverser
  420. {
  421. public:
  422. explicit PCM_SYM_LIB_TRAVERSER( const wxString& aPath, SYMBOL_LIB_TABLE& aTable,
  423. const wxString& aPrefix ) :
  424. m_lib_table( aTable ),
  425. m_path_prefix( aPath ),
  426. m_lib_prefix( aPrefix )
  427. {
  428. wxFileName f( aPath, "" );
  429. m_prefix_dir_count = f.GetDirCount();
  430. }
  431. wxDirTraverseResult OnFile( const wxString& aFilePath ) override
  432. {
  433. wxFileName file = wxFileName::FileName( aFilePath );
  434. // consider a file to be a lib if it's name ends with .kicad_sym and
  435. // it is under $KICADn_3RD_PARTY/symbols/<pkgid>/ i.e. has nested level of at least +2
  436. if( file.GetExt() == wxT( "kicad_sym" ) && file.GetDirCount() >= m_prefix_dir_count + 2 )
  437. {
  438. wxString versionedPath = wxString::Format( wxS( "${%s}" ),
  439. ENV_VAR::GetVersionedEnvVarName( wxS( "3RD_PARTY" ) ) );
  440. wxArrayString parts = file.GetDirs();
  441. parts.RemoveAt( 0, m_prefix_dir_count );
  442. parts.Insert( versionedPath, 0 );
  443. parts.Add( file.GetFullName() );
  444. wxString libPath = wxJoin( parts, '/' );
  445. if( !m_lib_table.HasLibraryWithPath( libPath ) )
  446. {
  447. wxString name = parts.Last().substr( 0, parts.Last().length() - 10 );
  448. wxString nickname = wxString::Format( "%s%s", m_lib_prefix, name );
  449. if( m_lib_table.HasLibrary( nickname ) )
  450. {
  451. int increment = 1;
  452. do
  453. {
  454. nickname = wxString::Format( "%s%s_%d", m_lib_prefix, name, increment );
  455. increment++;
  456. } while( m_lib_table.HasLibrary( nickname ) );
  457. }
  458. m_lib_table.InsertRow(
  459. new SYMBOL_LIB_TABLE_ROW( nickname, libPath, wxT( "KiCad" ), wxEmptyString,
  460. _( "Added by Plugin and Content Manager" ) ),
  461. false );
  462. }
  463. }
  464. return wxDIR_CONTINUE;
  465. }
  466. wxDirTraverseResult OnDir( const wxString& dirPath ) override { return wxDIR_CONTINUE; }
  467. private:
  468. SYMBOL_LIB_TABLE& m_lib_table;
  469. wxString m_path_prefix;
  470. wxString m_lib_prefix;
  471. size_t m_prefix_dir_count;
  472. };
  473. bool SYMBOL_LIB_TABLE::LoadGlobalTable( SYMBOL_LIB_TABLE& aTable )
  474. {
  475. bool tableExists = true;
  476. wxFileName fn = GetGlobalTableFileName();
  477. if( !fn.FileExists() )
  478. {
  479. tableExists = false;
  480. if( !wxFileName::DirExists( fn.GetPath() )
  481. && !wxFileName::Mkdir( fn.GetPath(), 0x777, wxPATH_MKDIR_FULL ) )
  482. {
  483. THROW_IO_ERROR( wxString::Format( _( "Cannot create global library table path '%s'." ),
  484. fn.GetPath() ) );
  485. }
  486. // Attempt to copy the default global file table from the KiCad
  487. // template folder to the user's home configuration path.
  488. SEARCH_STACK ss;
  489. SystemDirsAppend( &ss );
  490. const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
  491. std::optional<wxString> v = ENV_VAR::GetVersionedEnvVarValue( envVars,
  492. wxT( "TEMPLATE_DIR" ) );
  493. if( v && !v->IsEmpty() )
  494. ss.AddPaths( *v, 0 );
  495. wxString fileName = ss.FindValidPath( FILEEXT::SymbolLibraryTableFileName );
  496. // The fallback is to create an empty global symbol table for the user to populate.
  497. if( fileName.IsEmpty() || !::wxCopyFile( fileName, fn.GetFullPath(), false ) )
  498. {
  499. SYMBOL_LIB_TABLE emptyTable;
  500. emptyTable.Save( fn.GetFullPath() );
  501. }
  502. }
  503. aTable.Load( fn.GetFullPath() );
  504. KICAD_SETTINGS* cfg = GetAppSettings<KICAD_SETTINGS>( "kicad" );
  505. wxString packagesPath;
  506. const ENV_VAR_MAP& vars = Pgm().GetLocalEnvVariables();
  507. if( std::optional<wxString> v = ENV_VAR::GetVersionedEnvVarValue( vars, wxT( "3RD_PARTY" ) ) )
  508. packagesPath = *v;
  509. if( cfg && cfg->m_PcmLibAutoAdd )
  510. {
  511. // Scan for libraries in PCM packages directory
  512. wxFileName d( packagesPath, "" );
  513. d.AppendDir( "symbols" );
  514. if( d.DirExists() )
  515. {
  516. PCM_SYM_LIB_TRAVERSER traverser( packagesPath, aTable, cfg->m_PcmLibPrefix );
  517. wxDir dir( d.GetPath() );
  518. dir.Traverse( traverser );
  519. }
  520. }
  521. if( cfg && cfg->m_PcmLibAutoRemove )
  522. {
  523. // Remove PCM libraries that no longer exist
  524. std::vector<wxString> to_remove;
  525. for( size_t i = 0; i < aTable.GetCount(); i++ )
  526. {
  527. LIB_TABLE_ROW& row = aTable.At( i );
  528. wxString path = row.GetFullURI( true );
  529. if( path.StartsWith( packagesPath ) && !wxFile::Exists( path ) )
  530. to_remove.push_back( row.GetNickName() );
  531. }
  532. for( const wxString& nickName : to_remove )
  533. {
  534. SYMBOL_LIB_TABLE_ROW* row = aTable.FindRow( nickName );
  535. wxCHECK2( row, continue );
  536. aTable.RemoveRow( row );
  537. }
  538. }
  539. return tableExists;
  540. }
  541. bool SYMBOL_LIB_TABLE::operator==( const SYMBOL_LIB_TABLE& aOther ) const
  542. {
  543. if( m_rows.size() != aOther.m_rows.size() )
  544. return false;
  545. unsigned i;
  546. for( i = 0; i < m_rows.size(); ++i )
  547. {
  548. const SYMBOL_LIB_TABLE_ROW& curr = static_cast<const SYMBOL_LIB_TABLE_ROW&>( m_rows[i] );
  549. const SYMBOL_LIB_TABLE_ROW& curr_other =
  550. static_cast<const SYMBOL_LIB_TABLE_ROW&>( aOther.m_rows[i] );
  551. if( curr != curr_other )
  552. return false;
  553. }
  554. return true;
  555. }
  556. wxString SYMBOL_LIB_TABLE::GetGlobalTableFileName()
  557. {
  558. wxFileName fn;
  559. fn.SetPath( PATHS::GetUserSettingsPath() );
  560. fn.SetName( FILEEXT::SymbolLibraryTableFileName );
  561. return fn.GetFullPath();
  562. }
  563. const wxString SYMBOL_LIB_TABLE::GetSymbolLibTableFileName()
  564. {
  565. return FILEEXT::SymbolLibraryTableFileName;
  566. }