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