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.

684 lines
18 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
  6. * Copyright (C) 2004-2017 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. /**
  26. * @file class_library.cpp
  27. */
  28. #include <algorithm>
  29. #include <fctsys.h>
  30. #include <kiface_i.h>
  31. #include <gr_basic.h>
  32. #include <macros.h>
  33. #include <eda_base_frame.h>
  34. #include <kicad_string.h>
  35. #include <gestfich.h>
  36. #include <eda_doc.h>
  37. #include <richio.h>
  38. #include <config_params.h>
  39. #include <wildcards_and_files_ext.h>
  40. #include <project_rescue.h>
  41. #include <properties.h>
  42. #include <general.h>
  43. #include <class_library.h>
  44. #include <sch_legacy_plugin.h>
  45. #include <wx/progdlg.h>
  46. #include <wx/tokenzr.h>
  47. #include <wx/regex.h>
  48. #define DUPLICATE_NAME_MSG \
  49. _( "Library \"%s\" has duplicate entry name \"%s\".\n" \
  50. "This may cause some unexpected behavior when loading components into a schematic." )
  51. PART_LIB::PART_LIB( int aType, const wxString& aFileName, SCH_IO_MGR::SCH_FILE_T aPluginType ) :
  52. // start @ != 0 so each additional library added
  53. // is immediately detectable, zero would not be.
  54. m_mod_hash( PART_LIBS::s_modify_generation ),
  55. m_pluginType( aPluginType )
  56. {
  57. type = aType;
  58. isModified = false;
  59. timeStamp = 0;
  60. timeStamp = wxDateTime::Now();
  61. versionMajor = 0; // Will be updated after reading the lib file
  62. versionMinor = 0; // Will be updated after reading the lib file
  63. fileName = aFileName;
  64. if( !fileName.IsOk() )
  65. fileName = "unnamed.lib";
  66. m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
  67. m_properties = std::make_unique<PROPERTIES>();
  68. }
  69. PART_LIB::~PART_LIB()
  70. {
  71. }
  72. void PART_LIB::Save( bool aSaveDocFile )
  73. {
  74. wxCHECK_RET( m_plugin != NULL, wxString::Format( "no plugin defined for library `%s`.",
  75. fileName.GetFullPath() ) );
  76. PROPERTIES props;
  77. if( !aSaveDocFile )
  78. props[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = "";
  79. m_plugin->SaveLibrary( fileName.GetFullPath(), &props );
  80. isModified = false;
  81. }
  82. void PART_LIB::Create( const wxString& aFileName )
  83. {
  84. wxString tmpFileName = fileName.GetFullPath();
  85. if( !aFileName.IsEmpty() )
  86. tmpFileName = aFileName;
  87. m_plugin->CreateSymbolLib( tmpFileName, m_properties.get() );
  88. }
  89. void PART_LIB::SetPluginType( SCH_IO_MGR::SCH_FILE_T aPluginType )
  90. {
  91. if( m_pluginType != aPluginType )
  92. {
  93. m_pluginType = aPluginType;
  94. m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
  95. }
  96. }
  97. bool PART_LIB::IsCache() const
  98. {
  99. return m_properties->Exists( SCH_LEGACY_PLUGIN::PropNoDocFile );
  100. }
  101. void PART_LIB::SetCache()
  102. {
  103. (*m_properties)[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = "";
  104. }
  105. bool PART_LIB::IsBuffering() const
  106. {
  107. return m_properties->Exists( SCH_LEGACY_PLUGIN::PropBuffering );
  108. }
  109. void PART_LIB::EnableBuffering( bool aEnable )
  110. {
  111. if( aEnable )
  112. (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
  113. else
  114. m_properties->Clear( SCH_LEGACY_PLUGIN::PropBuffering );
  115. }
  116. void PART_LIB::GetAliasNames( wxArrayString& aNames ) const
  117. {
  118. m_plugin->EnumerateSymbolLib( aNames, fileName.GetFullPath(), m_properties.get() );
  119. aNames.Sort();
  120. }
  121. void PART_LIB::GetAliases( std::vector<LIB_ALIAS*>& aAliases ) const
  122. {
  123. m_plugin->EnumerateSymbolLib( aAliases, fileName.GetFullPath(), m_properties.get() );
  124. std::sort( aAliases.begin(), aAliases.end(),
  125. [](LIB_ALIAS *lhs, LIB_ALIAS *rhs) -> bool
  126. { return lhs->GetName() < rhs->GetName(); });
  127. }
  128. void PART_LIB::GetEntryTypePowerNames( wxArrayString& aNames ) const
  129. {
  130. std::vector<LIB_ALIAS*> aliases;
  131. m_plugin->EnumerateSymbolLib( aliases, fileName.GetFullPath() );
  132. for( size_t i = 0; i < aliases.size(); i++ )
  133. {
  134. LIB_ALIAS* alias = aliases[i];
  135. LIB_PART* root = alias->GetPart();
  136. if( !root || !root->IsPower() )
  137. continue;
  138. aNames.Add( alias->GetName() );
  139. }
  140. aNames.Sort();
  141. }
  142. LIB_ALIAS* PART_LIB::FindAlias( const wxString& aName ) const
  143. {
  144. LIB_ALIAS* alias = m_plugin->LoadSymbol( fileName.GetFullPath(), aName, m_properties.get() );
  145. // Set the library to this even though technically the legacy cache plugin owns the
  146. // symbols. This allows the symbol library table conversion tool to determine the
  147. // correct library where the symbol was found.
  148. if( alias && alias->GetPart() && !alias->GetPart()->GetLib() )
  149. alias->GetPart()->SetLib( const_cast<PART_LIB*>( this ) );
  150. return alias;
  151. }
  152. LIB_ALIAS* PART_LIB::FindAlias( const LIB_ID& aLibId ) const
  153. {
  154. return FindAlias( aLibId.Format().wx_str() );
  155. }
  156. LIB_PART* PART_LIB::FindPart( const wxString& aName ) const
  157. {
  158. LIB_ALIAS* alias = FindAlias( aName );
  159. if( alias != NULL )
  160. return alias->GetPart();
  161. return NULL;
  162. }
  163. LIB_PART* PART_LIB::FindPart( const LIB_ID& aLibId ) const
  164. {
  165. return FindPart( aLibId.Format().wx_str() );
  166. }
  167. bool PART_LIB::HasPowerParts() const
  168. {
  169. // return true if at least one power part is found in lib
  170. std::vector<LIB_ALIAS*> aliases;
  171. m_plugin->EnumerateSymbolLib( aliases, fileName.GetFullPath(), m_properties.get() );
  172. for( size_t i = 0; i < aliases.size(); i++ )
  173. {
  174. LIB_ALIAS* alias = aliases[i];
  175. LIB_PART* root = alias->GetPart();
  176. if( !root || root->IsPower() )
  177. return true;
  178. }
  179. return false;
  180. }
  181. void PART_LIB::AddPart( LIB_PART* aPart )
  182. {
  183. // add a clone, not the caller's copy, the plugin take ownership of the new symbol.
  184. m_plugin->SaveSymbol( fileName.GetFullPath(), new LIB_PART( *aPart, this ), m_properties.get() );
  185. // If we are not buffering, the library file is updated immediately when the plugin
  186. // SaveSymbol() function is called.
  187. if( IsBuffering() )
  188. isModified = true;
  189. ++m_mod_hash;
  190. }
  191. LIB_ALIAS* PART_LIB::RemoveAlias( LIB_ALIAS* aEntry )
  192. {
  193. wxCHECK_MSG( aEntry != NULL, NULL, "NULL pointer cannot be removed from library." );
  194. m_plugin->DeleteAlias( fileName.GetFullPath(), aEntry->GetName(), m_properties.get() );
  195. // If we are not buffering, the library file is updated immediately when the plugin
  196. // SaveSymbol() function is called.
  197. if( IsBuffering() )
  198. isModified = true;
  199. ++m_mod_hash;
  200. return NULL;
  201. }
  202. LIB_PART* PART_LIB::ReplacePart( LIB_PART* aOldPart, LIB_PART* aNewPart )
  203. {
  204. wxASSERT( aOldPart != NULL );
  205. wxASSERT( aNewPart != NULL );
  206. m_plugin->DeleteSymbol( fileName.GetFullPath(), aOldPart->GetName(), m_properties.get() );
  207. LIB_PART* my_part = new LIB_PART( *aNewPart, this );
  208. m_plugin->SaveSymbol( fileName.GetFullPath(), my_part, m_properties.get() );
  209. // If we are not buffering, the library file is updated immediately when the plugin
  210. // SaveSymbol() function is called.
  211. if( IsBuffering() )
  212. isModified = true;
  213. ++m_mod_hash;
  214. return my_part;
  215. }
  216. PART_LIB* PART_LIB::LoadLibrary( const wxString& aFileName )
  217. {
  218. std::unique_ptr<PART_LIB> lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, aFileName ) );
  219. std::vector<LIB_ALIAS*> aliases;
  220. // This loads the library.
  221. lib->GetAliases( aliases );
  222. // Now, set the LIB_PART m_library member but it will only be used
  223. // when loading legacy libraries in the future. Once the symbols in the
  224. // schematic have a full #LIB_ID, this will not get called.
  225. for( size_t ii = 0; ii < aliases.size(); ii++ )
  226. {
  227. LIB_ALIAS* alias = aliases[ii];
  228. if( alias->GetPart() )
  229. alias->GetPart()->SetLib( lib.get() );
  230. }
  231. PART_LIB* ret = lib.release();
  232. return ret;
  233. }
  234. PART_LIB* PART_LIBS::AddLibrary( const wxString& aFileName )
  235. {
  236. PART_LIB* lib;
  237. wxFileName fn = aFileName;
  238. // Don't reload the library if it is already loaded.
  239. lib = FindLibrary( fn.GetName() );
  240. if( lib )
  241. return lib;
  242. lib = PART_LIB::LoadLibrary( aFileName );
  243. push_back( lib );
  244. return lib;
  245. }
  246. PART_LIB* PART_LIBS::AddLibrary( const wxString& aFileName, PART_LIBS::iterator& aIterator )
  247. {
  248. // Don't reload the library if it is already loaded.
  249. wxFileName fn( aFileName );
  250. PART_LIB* lib = FindLibrary( fn.GetName() );
  251. if( lib )
  252. return lib;
  253. lib = PART_LIB::LoadLibrary( aFileName );
  254. if( aIterator >= begin() && aIterator < end() )
  255. insert( aIterator, lib );
  256. else
  257. push_back( lib );
  258. return lib;
  259. }
  260. PART_LIB* PART_LIBS::FindLibrary( const wxString& aName )
  261. {
  262. for( PART_LIBS::iterator it = begin(); it!=end(); ++it )
  263. {
  264. if( it->GetName() == aName )
  265. return &*it;
  266. }
  267. return NULL;
  268. }
  269. PART_LIB* PART_LIBS::GetCacheLibrary()
  270. {
  271. for( PART_LIBS::iterator it = begin(); it!=end(); ++it )
  272. {
  273. if( it->IsCache() )
  274. return &*it;
  275. }
  276. return NULL;
  277. }
  278. PART_LIB* PART_LIBS::FindLibraryByFullFileName( const wxString& aFullFileName )
  279. {
  280. for( PART_LIBS::iterator it = begin(); it!=end(); ++it )
  281. {
  282. if( it->GetFullFileName() == aFullFileName )
  283. return &*it;
  284. }
  285. return NULL;
  286. }
  287. wxArrayString PART_LIBS::GetLibraryNames( bool aSorted )
  288. {
  289. wxArrayString cacheNames;
  290. wxArrayString names;
  291. for( PART_LIB& lib : *this )
  292. {
  293. if( lib.IsCache() && aSorted )
  294. cacheNames.Add( lib.GetName() );
  295. else
  296. names.Add( lib.GetName() );
  297. }
  298. // Even sorted, the cache library is always at the end of the list.
  299. if( aSorted )
  300. names.Sort();
  301. for( unsigned int i = 0; i<cacheNames.Count(); i++ )
  302. names.Add( cacheNames.Item( i ) );
  303. return names;
  304. }
  305. LIB_PART* PART_LIBS::FindLibPart( const LIB_ID& aLibId, const wxString& aLibraryName )
  306. {
  307. LIB_PART* part = NULL;
  308. for( PART_LIB& lib : *this )
  309. {
  310. if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
  311. continue;
  312. part = lib.FindPart( aLibId.GetLibItemName().wx_str() );
  313. if( part )
  314. break;
  315. }
  316. return part;
  317. }
  318. LIB_ALIAS* PART_LIBS::FindLibraryAlias( const LIB_ID& aLibId, const wxString& aLibraryName )
  319. {
  320. LIB_ALIAS* entry = NULL;
  321. for( PART_LIB& lib : *this )
  322. {
  323. if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
  324. continue;
  325. entry = lib.FindAlias( aLibId.GetLibItemName().wx_str() );
  326. if( entry )
  327. break;
  328. }
  329. return entry;
  330. }
  331. void PART_LIBS::FindLibraryNearEntries( std::vector<LIB_ALIAS*>& aCandidates,
  332. const wxString& aEntryName,
  333. const wxString& aLibraryName )
  334. {
  335. for( PART_LIB& lib : *this )
  336. {
  337. if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
  338. continue;
  339. wxArrayString aliasNames;
  340. lib.GetAliasNames( aliasNames );
  341. if( aliasNames.IsEmpty() )
  342. continue;
  343. for( size_t i = 0; i < aliasNames.size(); i++ )
  344. {
  345. if( aliasNames[i].CmpNoCase( aEntryName ) == 0 )
  346. aCandidates.push_back( lib.FindAlias( aliasNames[i] ) );
  347. }
  348. }
  349. }
  350. int PART_LIBS::s_modify_generation = 1; // starts at 1 and goes up
  351. int PART_LIBS::GetModifyHash()
  352. {
  353. int hash = 0;
  354. for( PART_LIBS::const_iterator it = begin(); it != end(); ++it )
  355. {
  356. hash += it->GetModHash();
  357. }
  358. // Rebuilding the cache (m_cache) does not change the GetModHash() value,
  359. // but changes PART_LIBS::s_modify_generation.
  360. // Take this change in account:
  361. hash += PART_LIBS::s_modify_generation;
  362. return hash;
  363. }
  364. void PART_LIBS::LibNamesAndPaths( PROJECT* aProject, bool doSave,
  365. wxString* aPaths, wxArrayString* aNames )
  366. {
  367. wxString pro = aProject->GetProjectFullName();
  368. PARAM_CFG_ARRAY ca;
  369. if( aPaths )
  370. ca.push_back( new PARAM_CFG_FILENAME( "LibDir", aPaths ) );
  371. if( aNames )
  372. ca.push_back( new PARAM_CFG_LIBNAME_LIST( wxT( "LibName" ), aNames, GROUP_SCH_LIBS ) );
  373. if( doSave )
  374. {
  375. aProject->ConfigSave( Kiface().KifaceSearch(), GROUP_SCH, ca );
  376. /*
  377. {
  378. wxString msg = wxString::Format( _(
  379. "Unable save project's \"%s\" file" ),
  380. GetChars( pro )
  381. );
  382. THROW_IO_ERROR( msg );
  383. }
  384. */
  385. }
  386. else
  387. {
  388. if( !aProject->ConfigLoad( Kiface().KifaceSearch(), GROUP_SCH, ca ) )
  389. {
  390. wxString msg = wxString::Format( _(
  391. "Unable to load project's \"%s\" file" ),
  392. GetChars( pro )
  393. );
  394. THROW_IO_ERROR( msg );
  395. }
  396. }
  397. }
  398. const wxString PART_LIBS::CacheName( const wxString& aFullProjectFilename )
  399. {
  400. wxFileName name = aFullProjectFilename;
  401. name.SetName( name.GetName() + "-cache" );
  402. name.SetExt( SchematicLibraryFileExtension );
  403. if( name.FileExists() )
  404. return name.GetFullPath();
  405. return wxEmptyString;
  406. }
  407. void PART_LIBS::LoadAllLibraries( PROJECT* aProject, bool aShowProgress )
  408. {
  409. wxString filename;
  410. wxString libs_not_found;
  411. SEARCH_STACK* lib_search = aProject->SchSearchS();
  412. #if defined(DEBUG) && 0
  413. lib_search->Show( __func__ );
  414. #endif
  415. wxArrayString lib_names;
  416. LibNamesAndPaths( aProject, false, NULL, &lib_names );
  417. // Post symbol library table, this should be empty. Only the cache library should get loaded.
  418. if( !lib_names.empty() )
  419. {
  420. wxProgressDialog lib_dialog( _( "Loading Symbol Libraries" ),
  421. wxEmptyString,
  422. lib_names.GetCount(),
  423. NULL,
  424. wxPD_APP_MODAL );
  425. if( aShowProgress )
  426. {
  427. lib_dialog.Show();
  428. }
  429. wxString progress_message;
  430. for( unsigned i = 0; i < lib_names.GetCount(); ++i )
  431. {
  432. if( aShowProgress )
  433. {
  434. lib_dialog.Update( i, _( "Loading " + lib_names[i] ) );
  435. }
  436. // lib_names[] does not store the file extension. Set it.
  437. // Remember lib_names[i] can contain a '.' in name, so using a wxFileName
  438. // before adding the extension can create incorrect full filename
  439. wxString fullname = lib_names[i] + "." + SchematicLibraryFileExtension;
  440. // Now the full name is set, we can use a wxFileName.
  441. wxFileName fn( fullname );
  442. // Skip if the file name is not valid..
  443. if( !fn.IsOk() )
  444. continue;
  445. if( !fn.FileExists() )
  446. {
  447. filename = lib_search->FindValidPath( fn.GetFullPath() );
  448. if( !filename )
  449. {
  450. libs_not_found += fn.GetFullPath();
  451. libs_not_found += '\n';
  452. continue;
  453. }
  454. }
  455. else
  456. { // ensure the lib filename has a absolute path.
  457. // If the lib has no absolute path, and is found in the cwd by fn.FileExists(),
  458. // make a full absolute path, to avoid issues with load library functions which
  459. // expects an absolute path.
  460. if( !fn.IsAbsolute() )
  461. fn.MakeAbsolute();
  462. filename = fn.GetFullPath();
  463. }
  464. try
  465. {
  466. AddLibrary( filename );
  467. }
  468. catch( const IO_ERROR& ioe )
  469. {
  470. wxString msg;
  471. msg.Printf( _( "Symbol library \"%s\" failed to load. Error:\n %s" ),
  472. GetChars( filename ), GetChars( ioe.What() ) );
  473. wxLogError( msg );
  474. }
  475. }
  476. }
  477. // add the special cache library.
  478. wxString cache_name = CacheName( aProject->GetProjectFullName() );
  479. PART_LIB* cache_lib;
  480. if( !cache_name.IsEmpty() )
  481. {
  482. try
  483. {
  484. cache_lib = AddLibrary( cache_name );
  485. if( cache_lib )
  486. cache_lib->SetCache();
  487. }
  488. catch( const IO_ERROR& ioe )
  489. {
  490. wxString msg = wxString::Format( _(
  491. "Symbol library \"%s\" failed to load.\nError: %s" ),
  492. GetChars( cache_name ),
  493. GetChars( ioe.What() )
  494. );
  495. THROW_IO_ERROR( msg );
  496. }
  497. }
  498. // Print the libraries not found
  499. if( !libs_not_found.IsEmpty() )
  500. {
  501. // Use a different exception type so catch()er can route to proper use
  502. // of the HTML_MESSAGE_BOX.
  503. THROW_PARSE_ERROR( wxEmptyString, __func__, TO_UTF8( libs_not_found ), 0, 0 );
  504. }
  505. #if defined(DEBUG) && 1
  506. printf( "%s: lib_names:\n", __func__ );
  507. for( PART_LIBS::const_iterator it = begin(); it < end(); ++it )
  508. printf( " %s\n", TO_UTF8( it->GetName() ) );
  509. #endif
  510. }