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.

697 lines
22 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012-2024 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <kiface_base.h>
  24. #include <env_vars.h>
  25. #include <lib_id.h>
  26. #include <lib_table_lexer.h>
  27. #include <paths.h>
  28. #include <pgm_base.h>
  29. #include <search_stack.h>
  30. #include <settings/kicad_settings.h>
  31. #include <settings/settings_manager.h>
  32. #include <systemdirsappend.h>
  33. #include <wildcards_and_files_ext.h>
  34. #include <design_block_info.h>
  35. #include <design_block_lib_table.h>
  36. #include <design_block.h>
  37. #include <wx/dir.h>
  38. #include <wx/hash.h>
  39. #define OPT_SEP '|' ///< options separator character
  40. /// The global design block library table. This is not dynamically allocated because
  41. /// in a multiple project environment we must keep its address constant (since it is
  42. /// the fallback table for multiple projects).
  43. DESIGN_BLOCK_LIB_TABLE GDesignBlockTable;
  44. /// The global footprint info table. This is performance-intensive to build so we
  45. /// keep a hash-stamped global version. Any deviation from the request vs. stored
  46. /// hash will result in it being rebuilt.
  47. DESIGN_BLOCK_LIST_IMPL GDesignBlockList;
  48. using namespace LIB_TABLE_T;
  49. static const wxChar global_tbl_name[] = wxT( "design-block-lib-table" );
  50. bool DESIGN_BLOCK_LIB_TABLE_ROW::operator==( const DESIGN_BLOCK_LIB_TABLE_ROW& aRow ) const
  51. {
  52. return LIB_TABLE_ROW::operator==( aRow ) && type == aRow.type;
  53. }
  54. void DESIGN_BLOCK_LIB_TABLE_ROW::SetType( const wxString& aType )
  55. {
  56. type = DESIGN_BLOCK_IO_MGR::EnumFromStr( aType );
  57. if( DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T( -1 ) == type )
  58. type = DESIGN_BLOCK_IO_MGR::KICAD_SEXP;
  59. plugin.reset();
  60. }
  61. DESIGN_BLOCK_LIB_TABLE::DESIGN_BLOCK_LIB_TABLE( DESIGN_BLOCK_LIB_TABLE* aFallBackTable ) :
  62. LIB_TABLE( aFallBackTable )
  63. {
  64. // not copying fall back, simply search aFallBackTable separately
  65. // if "nickName not found".
  66. }
  67. void DESIGN_BLOCK_LIB_TABLE::Parse( LIB_TABLE_LEXER* in )
  68. {
  69. T tok;
  70. wxString errMsg; // to collect error messages
  71. // This table may be nested within a larger s-expression, or not.
  72. // Allow for parser of that optional containing s-epression to have looked ahead.
  73. if( in->CurTok() != T_design_block_lib_table )
  74. {
  75. in->NeedLEFT();
  76. if( ( tok = in->NextTok() ) != T_design_block_lib_table )
  77. in->Expecting( T_design_block_lib_table );
  78. }
  79. while( ( tok = in->NextTok() ) != T_RIGHT )
  80. {
  81. std::unique_ptr<DESIGN_BLOCK_LIB_TABLE_ROW> row =
  82. std::make_unique<DESIGN_BLOCK_LIB_TABLE_ROW>();
  83. if( tok == T_EOF )
  84. in->Expecting( T_RIGHT );
  85. if( tok != T_LEFT )
  86. in->Expecting( T_LEFT );
  87. // in case there is a "row integrity" error, tell where later.
  88. int lineNum = in->CurLineNumber();
  89. tok = in->NextTok();
  90. // Optionally parse the current version number
  91. if( tok == T_version )
  92. {
  93. in->NeedNUMBER( "version" );
  94. m_version = std::stoi( in->CurText() );
  95. in->NeedRIGHT();
  96. continue;
  97. }
  98. if( tok != T_lib )
  99. in->Expecting( T_lib );
  100. // (name NICKNAME)
  101. in->NeedLEFT();
  102. if( ( tok = in->NextTok() ) != T_name )
  103. in->Expecting( T_name );
  104. in->NeedSYMBOLorNUMBER();
  105. row->SetNickName( in->FromUTF8() );
  106. in->NeedRIGHT();
  107. // After (name), remaining (lib) elements are order independent, and in
  108. // some cases optional.
  109. bool sawType = false;
  110. bool sawOpts = false;
  111. bool sawDesc = false;
  112. bool sawUri = false;
  113. bool sawDisabled = false;
  114. while( ( tok = in->NextTok() ) != T_RIGHT )
  115. {
  116. if( tok == T_EOF )
  117. in->Unexpected( T_EOF );
  118. if( tok != T_LEFT )
  119. in->Expecting( T_LEFT );
  120. tok = in->NeedSYMBOLorNUMBER();
  121. switch( tok )
  122. {
  123. case T_uri:
  124. if( sawUri )
  125. in->Duplicate( tok );
  126. sawUri = true;
  127. in->NeedSYMBOLorNUMBER();
  128. row->SetFullURI( in->FromUTF8() );
  129. break;
  130. case T_type:
  131. if( sawType )
  132. in->Duplicate( tok );
  133. sawType = true;
  134. in->NeedSYMBOLorNUMBER();
  135. row->SetType( in->FromUTF8() );
  136. break;
  137. case T_options:
  138. if( sawOpts )
  139. in->Duplicate( tok );
  140. sawOpts = true;
  141. in->NeedSYMBOLorNUMBER();
  142. row->SetOptions( in->FromUTF8() );
  143. break;
  144. case T_descr:
  145. if( sawDesc )
  146. in->Duplicate( tok );
  147. sawDesc = true;
  148. in->NeedSYMBOLorNUMBER();
  149. row->SetDescr( in->FromUTF8() );
  150. break;
  151. case T_disabled:
  152. if( sawDisabled )
  153. in->Duplicate( tok );
  154. sawDisabled = true;
  155. row->SetEnabled( false );
  156. break;
  157. case T_hidden:
  158. // Hiding design block libraries is not yet supported. Unclear what path can set this
  159. // attribute, but clear it on load.
  160. row->SetVisible();
  161. break;
  162. default: in->Unexpected( tok );
  163. }
  164. in->NeedRIGHT();
  165. }
  166. if( !sawType )
  167. in->Expecting( T_type );
  168. if( !sawUri )
  169. in->Expecting( T_uri );
  170. // All nickNames within this table fragment must be unique, so we do not use doReplace
  171. // in doInsertRow(). (However a fallBack table can have a conflicting nickName and ours
  172. // will supercede that one since in FindLib() we search this table before any fall back.)
  173. wxString nickname = row->GetNickName(); // store it to be able to used it
  174. // after row deletion if an error occurs
  175. bool doReplace = false;
  176. LIB_TABLE_ROW* tmp = row.release();
  177. if( !doInsertRow( tmp, doReplace ) )
  178. {
  179. delete tmp; // The table did not take ownership of the row.
  180. wxString msg = wxString::Format( _( "Duplicate library nickname '%s' found in "
  181. "design block library table file line %d." ),
  182. nickname, lineNum );
  183. if( !errMsg.IsEmpty() )
  184. errMsg << '\n';
  185. errMsg << msg;
  186. }
  187. }
  188. if( !errMsg.IsEmpty() )
  189. THROW_IO_ERROR( errMsg );
  190. }
  191. bool DESIGN_BLOCK_LIB_TABLE::operator==( const DESIGN_BLOCK_LIB_TABLE& aDesignBlockTable ) const
  192. {
  193. if( m_rows.size() == aDesignBlockTable.m_rows.size() )
  194. {
  195. for( unsigned i = 0; i < m_rows.size(); ++i )
  196. {
  197. if( (DESIGN_BLOCK_LIB_TABLE_ROW&) m_rows[i]
  198. != (DESIGN_BLOCK_LIB_TABLE_ROW&) aDesignBlockTable.m_rows[i] )
  199. return false;
  200. }
  201. return true;
  202. }
  203. return false;
  204. }
  205. void DESIGN_BLOCK_LIB_TABLE::Format( OUTPUTFORMATTER* aOutput, int aIndentLevel ) const
  206. {
  207. aOutput->Print( aIndentLevel, "(design_block_lib_table\n" );
  208. aOutput->Print( aIndentLevel + 1, "(version %d)\n", m_version );
  209. for( LIB_TABLE_ROWS_CITER it = m_rows.begin(); it != m_rows.end(); ++it )
  210. it->Format( aOutput, aIndentLevel + 1 );
  211. aOutput->Print( aIndentLevel, ")\n" );
  212. }
  213. long long DESIGN_BLOCK_LIB_TABLE::GenerateTimestamp( const wxString* aNickname )
  214. {
  215. long long hash = 0;
  216. if( aNickname )
  217. {
  218. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( *aNickname, true );
  219. wxCHECK( row && row->plugin, hash );
  220. return row->plugin->GetLibraryTimestamp( row->GetFullURI( true ) )
  221. + wxHashTable::MakeKey( *aNickname );
  222. }
  223. for( const wxString& nickname : GetLogicalLibs() )
  224. {
  225. const DESIGN_BLOCK_LIB_TABLE_ROW* row = nullptr;
  226. try
  227. {
  228. row = FindRow( nickname, true );
  229. }
  230. catch( ... )
  231. {
  232. // Do nothing if not found: just skip.
  233. }
  234. wxCHECK2( row && row->plugin, continue );
  235. hash += row->plugin->GetLibraryTimestamp( row->GetFullURI( true ) )
  236. + wxHashTable::MakeKey( nickname );
  237. }
  238. return hash;
  239. }
  240. void DESIGN_BLOCK_LIB_TABLE::DesignBlockEnumerate( wxArrayString& aDesignBlockNames,
  241. const wxString& aNickname, bool aBestEfforts )
  242. {
  243. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  244. wxASSERT( row->plugin );
  245. row->plugin->DesignBlockEnumerate( aDesignBlockNames, row->GetFullURI( true ), aBestEfforts,
  246. row->GetProperties() );
  247. }
  248. void DESIGN_BLOCK_LIB_TABLE::PrefetchLib( const wxString& aNickname )
  249. {
  250. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  251. wxASSERT( row->plugin );
  252. row->plugin->PrefetchLib( row->GetFullURI( true ), row->GetProperties() );
  253. }
  254. const DESIGN_BLOCK_LIB_TABLE_ROW* DESIGN_BLOCK_LIB_TABLE::FindRow( const wxString& aNickname,
  255. bool aCheckIfEnabled )
  256. {
  257. DESIGN_BLOCK_LIB_TABLE_ROW* row =
  258. static_cast<DESIGN_BLOCK_LIB_TABLE_ROW*>( findRow( aNickname, aCheckIfEnabled ) );
  259. if( !row )
  260. {
  261. wxString msg = wxString::Format(
  262. _( "design-block-lib-table files contain no library named '%s'." ), aNickname );
  263. THROW_IO_ERROR( msg );
  264. }
  265. if( !row->plugin )
  266. row->setPlugin( DESIGN_BLOCK_IO_MGR::FindPlugin( row->type ) );
  267. return row;
  268. }
  269. static void setLibNickname( DESIGN_BLOCK* aModule, const wxString& aNickname,
  270. const wxString& aDesignBlockName )
  271. {
  272. // The library cannot know its own name, because it might have been renamed or moved.
  273. // Therefore design blocks cannot know their own library nickname when residing in
  274. // a design block library.
  275. // Only at this API layer can we tell the design block about its actual library nickname.
  276. if( aModule )
  277. {
  278. // remove "const"-ness, I really do want to set nickname without
  279. // having to copy the LIB_ID and its two strings, twice each.
  280. LIB_ID& dbid = (LIB_ID&) aModule->GetLibId();
  281. // Catch any misbehaving plugin, which should be setting internal design block name properly:
  282. wxASSERT( aDesignBlockName == dbid.GetLibItemName().wx_str() );
  283. // and clearing nickname
  284. wxASSERT( !dbid.GetLibNickname().size() );
  285. dbid.SetLibNickname( aNickname );
  286. }
  287. }
  288. const DESIGN_BLOCK*
  289. DESIGN_BLOCK_LIB_TABLE::GetEnumeratedDesignBlock( const wxString& aNickname,
  290. const wxString& aDesignBlockName )
  291. {
  292. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  293. wxASSERT( row->plugin );
  294. return row->plugin->GetEnumeratedDesignBlock( row->GetFullURI( true ), aDesignBlockName,
  295. row->GetProperties() );
  296. }
  297. bool DESIGN_BLOCK_LIB_TABLE::DesignBlockExists( const wxString& aNickname,
  298. const wxString& aDesignBlockName )
  299. {
  300. try
  301. {
  302. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  303. wxASSERT( row->plugin );
  304. return row->plugin->DesignBlockExists( row->GetFullURI( true ), aDesignBlockName,
  305. row->GetProperties() );
  306. }
  307. catch( ... )
  308. {
  309. return false;
  310. }
  311. }
  312. DESIGN_BLOCK* DESIGN_BLOCK_LIB_TABLE::DesignBlockLoad( const wxString& aNickname,
  313. const wxString& aDesignBlockName,
  314. bool aKeepUUID )
  315. {
  316. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  317. wxASSERT( row->plugin );
  318. DESIGN_BLOCK* ret = row->plugin->DesignBlockLoad( row->GetFullURI( true ), aDesignBlockName,
  319. aKeepUUID, row->GetProperties() );
  320. setLibNickname( ret, row->GetNickName(), aDesignBlockName );
  321. return ret;
  322. }
  323. DESIGN_BLOCK_LIB_TABLE::SAVE_T
  324. DESIGN_BLOCK_LIB_TABLE::DesignBlockSave( const wxString& aNickname,
  325. const DESIGN_BLOCK* aDesignBlock, bool aOverwrite )
  326. {
  327. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  328. wxASSERT( row->plugin );
  329. if( !aOverwrite )
  330. {
  331. // Try loading the design block to see if it already exists, caller wants overwrite
  332. // protection, which is atypical, not the default.
  333. wxString DesignBlockname = aDesignBlock->GetLibId().GetLibItemName();
  334. std::unique_ptr<DESIGN_BLOCK> design_block( row->plugin->DesignBlockLoad(
  335. row->GetFullURI( true ), DesignBlockname, row->GetProperties() ) );
  336. if( design_block.get() )
  337. return SAVE_SKIPPED;
  338. }
  339. row->plugin->DesignBlockSave( row->GetFullURI( true ), aDesignBlock, row->GetProperties() );
  340. return SAVE_OK;
  341. }
  342. void DESIGN_BLOCK_LIB_TABLE::DesignBlockDelete( const wxString& aNickname,
  343. const wxString& aDesignBlockName )
  344. {
  345. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  346. wxASSERT( row->plugin );
  347. return row->plugin->DesignBlockDelete( row->GetFullURI( true ), aDesignBlockName,
  348. row->GetProperties() );
  349. }
  350. bool DESIGN_BLOCK_LIB_TABLE::IsDesignBlockLibWritable( const wxString& aNickname )
  351. {
  352. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  353. wxASSERT( row->plugin );
  354. return row->plugin->IsLibraryWritable( row->GetFullURI( true ) );
  355. }
  356. void DESIGN_BLOCK_LIB_TABLE::DesignBlockLibDelete( const wxString& aNickname )
  357. {
  358. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  359. wxASSERT( row->plugin );
  360. row->plugin->DeleteLibrary( row->GetFullURI( true ), row->GetProperties() );
  361. }
  362. void DESIGN_BLOCK_LIB_TABLE::DesignBlockLibCreate( const wxString& aNickname )
  363. {
  364. const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
  365. wxASSERT( row->plugin );
  366. row->plugin->CreateLibrary( row->GetFullURI( true ), row->GetProperties() );
  367. }
  368. DESIGN_BLOCK*
  369. DESIGN_BLOCK_LIB_TABLE::DesignBlockLoadWithOptionalNickname( const LIB_ID& aDesignBlockId,
  370. bool aKeepUUID )
  371. {
  372. wxString nickname = aDesignBlockId.GetLibNickname();
  373. wxString DesignBlockname = aDesignBlockId.GetLibItemName();
  374. if( nickname.size() )
  375. {
  376. return DesignBlockLoad( nickname, DesignBlockname, aKeepUUID );
  377. }
  378. // nickname is empty, sequentially search (alphabetically) all libs/nicks for first match:
  379. else
  380. {
  381. std::vector<wxString> nicks = GetLogicalLibs();
  382. // Search each library going through libraries alphabetically.
  383. for( unsigned i = 0; i < nicks.size(); ++i )
  384. {
  385. // DesignBlockLoad() returns NULL on not found, does not throw exception
  386. // unless there's an IO_ERROR.
  387. DESIGN_BLOCK* ret = DesignBlockLoad( nicks[i], DesignBlockname, aKeepUUID );
  388. if( ret )
  389. return ret;
  390. }
  391. return nullptr;
  392. }
  393. }
  394. const wxString DESIGN_BLOCK_LIB_TABLE::GlobalPathEnvVariableName()
  395. {
  396. return ENV_VAR::GetVersionedEnvVarName( wxS( "DESIGN_BLOCK_DIR" ) );
  397. }
  398. class PCM_DESIGN_BLOCK_LIB_TRAVERSER final : public wxDirTraverser
  399. {
  400. public:
  401. explicit PCM_DESIGN_BLOCK_LIB_TRAVERSER( const wxString& aPath, DESIGN_BLOCK_LIB_TABLE& aTable,
  402. const wxString& aPrefix ) :
  403. m_lib_table( aTable ),
  404. m_path_prefix( aPath ), m_lib_prefix( aPrefix )
  405. {
  406. wxFileName f( aPath, wxS( "" ) );
  407. m_prefix_dir_count = f.GetDirCount();
  408. }
  409. wxDirTraverseResult OnFile( const wxString& aFilePath ) override { return wxDIR_CONTINUE; }
  410. wxDirTraverseResult OnDir( const wxString& dirPath ) override
  411. {
  412. wxFileName dir = wxFileName::DirName( dirPath );
  413. // consider a directory to be a lib if it's name ends with the design block lib dir extension
  414. // it is under $KICADn_3RD_PARTY/design_blocks/<pkgid>/ i.e. has nested level of at least +3
  415. if( dirPath.EndsWith( wxS( "." ) + FILEEXT::KiCadDesignBlockLibPathExtension )
  416. && dir.GetDirCount() >= m_prefix_dir_count + 3 )
  417. {
  418. wxString versionedPath = wxString::Format(
  419. wxS( "${%s}" ), ENV_VAR::GetVersionedEnvVarName( wxS( "3RD_PARTY" ) ) );
  420. wxArrayString parts = dir.GetDirs();
  421. parts.RemoveAt( 0, m_prefix_dir_count );
  422. parts.Insert( versionedPath, 0 );
  423. wxString libPath = wxJoin( parts, '/' );
  424. if( !m_lib_table.HasLibraryWithPath( libPath ) )
  425. {
  426. wxString name = parts.Last().substr( 0, parts.Last().length() - 7 );
  427. wxString nickname = wxString::Format( wxS( "%s%s" ), m_lib_prefix, name );
  428. if( m_lib_table.HasLibrary( nickname ) )
  429. {
  430. int increment = 1;
  431. do
  432. {
  433. nickname =
  434. wxString::Format( wxS( "%s%s_%d" ), m_lib_prefix, name, increment );
  435. increment++;
  436. } while( m_lib_table.HasLibrary( nickname ) );
  437. }
  438. m_lib_table.InsertRow( new DESIGN_BLOCK_LIB_TABLE_ROW(
  439. nickname, libPath, wxT( "KiCad" ), wxEmptyString,
  440. _( "Added by Plugin and Content Manager" ) ) );
  441. }
  442. }
  443. return wxDIR_CONTINUE;
  444. }
  445. private:
  446. DESIGN_BLOCK_LIB_TABLE& m_lib_table;
  447. wxString m_path_prefix;
  448. wxString m_lib_prefix;
  449. size_t m_prefix_dir_count;
  450. };
  451. bool DESIGN_BLOCK_LIB_TABLE::LoadGlobalTable( DESIGN_BLOCK_LIB_TABLE& aTable )
  452. {
  453. bool tableExists = true;
  454. wxFileName fn = GetGlobalTableFileName();
  455. if( !fn.FileExists() )
  456. {
  457. tableExists = false;
  458. if( !fn.DirExists() && !fn.Mkdir( 0x777, wxPATH_MKDIR_FULL ) )
  459. {
  460. THROW_IO_ERROR( wxString::Format( _( "Cannot create global library table path '%s'." ),
  461. fn.GetPath() ) );
  462. }
  463. // Attempt to copy the default global file table from the KiCad
  464. // template folder to the user's home configuration path.
  465. SEARCH_STACK ss;
  466. SystemDirsAppend( &ss );
  467. const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
  468. std::optional<wxString> v =
  469. ENV_VAR::GetVersionedEnvVarValue( envVars, wxT( "TEMPLATE_DIR" ) );
  470. if( v && !v->IsEmpty() )
  471. ss.AddPaths( *v, 0 );
  472. wxString fileName = ss.FindValidPath( global_tbl_name );
  473. // The fallback is to create an empty global design block table for the user to populate.
  474. if( fileName.IsEmpty() || !::wxCopyFile( fileName, fn.GetFullPath(), false ) )
  475. {
  476. DESIGN_BLOCK_LIB_TABLE emptyTable;
  477. emptyTable.Save( fn.GetFullPath() );
  478. }
  479. }
  480. aTable.clear();
  481. aTable.Load( fn.GetFullPath() );
  482. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  483. KICAD_SETTINGS* settings = mgr.GetAppSettings<KICAD_SETTINGS>( "kicad" );
  484. const ENV_VAR_MAP& env = Pgm().GetLocalEnvVariables();
  485. wxString packagesPath;
  486. if( std::optional<wxString> v = ENV_VAR::GetVersionedEnvVarValue( env, wxT( "3RD_PARTY" ) ) )
  487. packagesPath = *v;
  488. if( settings->m_PcmLibAutoAdd )
  489. {
  490. // Scan for libraries in PCM packages directory
  491. wxFileName d( packagesPath, wxS( "" ) );
  492. d.AppendDir( wxS( "design_blocks" ) );
  493. if( d.DirExists() )
  494. {
  495. PCM_DESIGN_BLOCK_LIB_TRAVERSER traverser( packagesPath, aTable,
  496. settings->m_PcmLibPrefix );
  497. wxDir dir( d.GetPath() );
  498. dir.Traverse( traverser );
  499. }
  500. }
  501. if( settings->m_PcmLibAutoRemove )
  502. {
  503. // Remove PCM libraries that no longer exist
  504. std::vector<wxString> to_remove;
  505. for( size_t i = 0; i < aTable.GetCount(); i++ )
  506. {
  507. LIB_TABLE_ROW& row = aTable.At( i );
  508. wxString path = row.GetFullURI( true );
  509. if( path.StartsWith( packagesPath ) && !wxDir::Exists( path ) )
  510. to_remove.push_back( row.GetNickName() );
  511. }
  512. for( const wxString& nickName : to_remove )
  513. aTable.RemoveRow( aTable.FindRow( nickName ) );
  514. }
  515. return tableExists;
  516. }
  517. DESIGN_BLOCK_LIB_TABLE& DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable()
  518. {
  519. return GDesignBlockTable;
  520. }
  521. DESIGN_BLOCK_LIST_IMPL& DESIGN_BLOCK_LIB_TABLE::GetGlobalList()
  522. {
  523. return GDesignBlockList;
  524. }
  525. wxString DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName()
  526. {
  527. wxFileName fn;
  528. fn.SetPath( PATHS::GetUserSettingsPath() );
  529. fn.SetName( global_tbl_name );
  530. return fn.GetFullPath();
  531. }