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.

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