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.

599 lines
16 KiB

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