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.

856 lines
26 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Chris Pavlina <pavlina.chris@gmail.com>
  5. * Copyright (C) 2015-2018 KiCad Developers, see change_log.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <sch_draw_panel.h>
  25. #include <class_library.h>
  26. #include <confirm.h>
  27. #include <invoke_sch_dialog.h>
  28. #include <kicad_device_context.h>
  29. #include <kiway.h>
  30. #include <project_rescue.h>
  31. #include <sch_component.h>
  32. #include <sch_sheet.h>
  33. #include <sch_edit_frame.h>
  34. #include <symbol_lib_table.h>
  35. #include <viewlib_frame.h>
  36. #include <wildcards_and_files_ext.h>
  37. #include <cctype>
  38. #include <map>
  39. typedef std::pair<SCH_COMPONENT*, wxString> COMPONENT_NAME_PAIR;
  40. // Helper sort function, used in get_components, to sort a component list by lib_id
  41. static bool sort_by_libid( const SCH_COMPONENT* ref, SCH_COMPONENT* cmp )
  42. {
  43. return ref->GetLibId() < cmp->GetLibId();
  44. }
  45. /**
  46. * Fill a vector with all of the project's symbols, to ease iterating over them.
  47. *
  48. * The list is sorted by #LIB_ID, therefore components using the same library
  49. * symbol are grouped, allowing later faster calculations (one library search by group
  50. * of symbols)
  51. *
  52. * @param aComponents - a vector that will take the symbols
  53. */
  54. static void get_components( std::vector<SCH_COMPONENT*>& aComponents )
  55. {
  56. SCH_SCREENS screens;
  57. // Get the full list
  58. for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
  59. {
  60. for( SCH_ITEM* item = screen->GetDrawItems(); item; item = item->Next() )
  61. {
  62. if( item->Type() != SCH_COMPONENT_T )
  63. continue;
  64. SCH_COMPONENT* component = static_cast<SCH_COMPONENT*>( item );
  65. aComponents.push_back( component );
  66. }
  67. }
  68. if( aComponents.empty() )
  69. return;
  70. // sort aComponents by lib part. Components will be grouped by same lib part.
  71. std::sort( aComponents.begin(), aComponents.end(), sort_by_libid );
  72. }
  73. /**
  74. * Search the libraries for the first component with a given name.
  75. *
  76. * @param aName - name to search for
  77. * @param aLibs - the loaded PART_LIBS
  78. * @param aCached - whether we are looking for the cached part
  79. */
  80. static LIB_PART* find_component( const wxString& aName, PART_LIBS* aLibs, bool aCached )
  81. {
  82. LIB_PART *part = NULL;
  83. wxString new_name = LIB_ID::FixIllegalChars( aName, LIB_ID::ID_SCH );
  84. for( PART_LIB& each_lib : *aLibs )
  85. {
  86. if( aCached && !each_lib.IsCache() )
  87. continue;
  88. if( !aCached && each_lib.IsCache() )
  89. continue;
  90. part = each_lib.FindPart( new_name );
  91. if( part )
  92. break;
  93. }
  94. return part;
  95. }
  96. static wxFileName GetRescueLibraryFileName()
  97. {
  98. wxFileName fn( g_RootSheet->GetScreen()->GetFileName() );
  99. fn.SetName( fn.GetName() + wxT( "-rescue" ) );
  100. fn.SetExt( SchematicLibraryFileExtension );
  101. return fn;
  102. }
  103. RESCUE_CASE_CANDIDATE::RESCUE_CASE_CANDIDATE( const wxString& aRequestedName,
  104. const wxString& aNewName,
  105. LIB_PART* aLibCandidate )
  106. {
  107. m_requested_name = aRequestedName;
  108. m_new_name = aNewName;
  109. m_lib_candidate = aLibCandidate;
  110. }
  111. void RESCUE_CASE_CANDIDATE::FindRescues( RESCUER& aRescuer,
  112. boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
  113. {
  114. typedef std::map<wxString, RESCUE_CASE_CANDIDATE> candidate_map_t;
  115. candidate_map_t candidate_map;
  116. // Remember the list of components is sorted by part name.
  117. // So a search in libraries is made only once by group
  118. LIB_ALIAS* case_sensitive_match = nullptr;
  119. std::vector<LIB_ALIAS*> case_insensitive_matches;
  120. wxString last_part_name;
  121. for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) )
  122. {
  123. wxString part_name = each_component->GetLibId().GetLibItemName();
  124. if( last_part_name != part_name )
  125. {
  126. // A new part name is found (a new group starts here).
  127. // Search the symbol names candidates only once for this group:
  128. last_part_name = part_name;
  129. case_insensitive_matches.clear();
  130. LIB_ID id( wxEmptyString, part_name );
  131. case_sensitive_match = aRescuer.GetPrj()->SchLibs()->FindLibraryAlias( id );
  132. if( !case_sensitive_match )
  133. // the case sensitive match failed. Try a case insensitive match
  134. aRescuer.GetPrj()->SchLibs()->FindLibraryNearEntries( case_insensitive_matches,
  135. part_name );
  136. }
  137. if( case_sensitive_match || !( case_insensitive_matches.size() ) )
  138. continue;
  139. RESCUE_CASE_CANDIDATE candidate( part_name, case_insensitive_matches[0]->GetName(),
  140. case_insensitive_matches[0]->GetPart() );
  141. candidate_map[part_name] = candidate;
  142. }
  143. // Now, dump the map into aCandidates
  144. for( const candidate_map_t::value_type& each_pair : candidate_map )
  145. {
  146. aCandidates.push_back( new RESCUE_CASE_CANDIDATE( each_pair.second ) );
  147. }
  148. }
  149. wxString RESCUE_CASE_CANDIDATE::GetActionDescription() const
  150. {
  151. wxString action;
  152. action.Printf( _( "Rename to %s" ), m_new_name );
  153. return action;
  154. }
  155. bool RESCUE_CASE_CANDIDATE::PerformAction( RESCUER* aRescuer )
  156. {
  157. for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() )
  158. {
  159. if( each_component->GetLibId().GetLibItemName() != UTF8( m_requested_name ) )
  160. continue;
  161. LIB_ID libId;
  162. libId.SetLibItemName( m_new_name, false );
  163. each_component->SetLibId( libId );
  164. each_component->ClearFlags();
  165. aRescuer->LogRescue( each_component, m_requested_name, m_new_name );
  166. }
  167. return true;
  168. }
  169. RESCUE_CACHE_CANDIDATE::RESCUE_CACHE_CANDIDATE( const wxString& aRequestedName,
  170. const wxString& aNewName,
  171. LIB_PART* aCacheCandidate,
  172. LIB_PART* aLibCandidate )
  173. {
  174. m_requested_name = aRequestedName;
  175. m_new_name = aNewName;
  176. m_cache_candidate = aCacheCandidate;
  177. m_lib_candidate = aLibCandidate;
  178. }
  179. RESCUE_CACHE_CANDIDATE::RESCUE_CACHE_CANDIDATE()
  180. {
  181. m_cache_candidate = NULL;
  182. m_lib_candidate = NULL;
  183. }
  184. void RESCUE_CACHE_CANDIDATE::FindRescues( RESCUER& aRescuer,
  185. boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
  186. {
  187. typedef std::map<wxString, RESCUE_CACHE_CANDIDATE> candidate_map_t;
  188. candidate_map_t candidate_map;
  189. // Remember the list of components is sorted by part name.
  190. // So a search in libraries is made only once by group
  191. LIB_PART* cache_match = nullptr;
  192. LIB_PART* lib_match = nullptr;
  193. wxString old_part_name;
  194. for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) )
  195. {
  196. wxString part_name = each_component->GetLibId().GetLibItemName();
  197. if( old_part_name != part_name )
  198. {
  199. // A new part name is found (a new group starts here).
  200. // Search the symbol names candidates only once for this group:
  201. old_part_name = part_name;
  202. cache_match = find_component( part_name, aRescuer.GetPrj()->SchLibs(), true );
  203. lib_match = find_component( part_name, aRescuer.GetPrj()->SchLibs(), false );
  204. if( !cache_match && !lib_match )
  205. continue;
  206. // Test whether there is a conflict or if the symbol can only be found in the cache
  207. // and the symbol name does not have any illegal characters.
  208. if( LIB_ID::HasIllegalChars( part_name, LIB_ID::ID_SCH ) == -1 )
  209. {
  210. if( cache_match && lib_match &&
  211. !cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) )
  212. continue;
  213. if( !cache_match && lib_match )
  214. continue;
  215. }
  216. // Check if the symbol has already been rescued.
  217. wxString new_name = LIB_ID::FixIllegalChars( part_name, LIB_ID::ID_SCH );
  218. RESCUE_CACHE_CANDIDATE candidate( part_name, new_name, cache_match, lib_match );
  219. candidate_map[part_name] = candidate;
  220. }
  221. }
  222. // Now, dump the map into aCandidates
  223. for( const candidate_map_t::value_type& each_pair : candidate_map )
  224. {
  225. aCandidates.push_back( new RESCUE_CACHE_CANDIDATE( each_pair.second ) );
  226. }
  227. }
  228. wxString RESCUE_CACHE_CANDIDATE::GetActionDescription() const
  229. {
  230. wxString action;
  231. if( !m_cache_candidate && !m_lib_candidate )
  232. action.Printf( _( "Cannot rescue symbol %s which is not available in any library or "
  233. "the cache." ), m_requested_name );
  234. else if( m_cache_candidate && !m_lib_candidate )
  235. action.Printf( _( "Rescue symbol %s found only in cache library to %s." ),
  236. m_requested_name, m_new_name );
  237. else
  238. action.Printf( _( "Rescue modified symbol %s to %s" ),
  239. m_requested_name, m_new_name );
  240. return action;
  241. }
  242. bool RESCUE_CACHE_CANDIDATE::PerformAction( RESCUER* aRescuer )
  243. {
  244. LIB_PART* tmp = ( m_cache_candidate ) ? m_cache_candidate : m_lib_candidate;
  245. wxCHECK_MSG( tmp, false, "Both cache and library symbols undefined." );
  246. LIB_PART new_part( *tmp );
  247. new_part.SetName( m_new_name );
  248. new_part.RemoveAllAliases();
  249. aRescuer->AddPart( &new_part );
  250. for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() )
  251. {
  252. if( each_component->GetLibId().GetLibItemName() != UTF8( m_requested_name ) )
  253. continue;
  254. LIB_ID libId;
  255. libId.SetLibItemName( m_new_name, false );
  256. each_component->SetLibId( libId );
  257. each_component->ClearFlags();
  258. aRescuer->LogRescue( each_component, m_requested_name, m_new_name );
  259. }
  260. return true;
  261. }
  262. RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::RESCUE_SYMBOL_LIB_TABLE_CANDIDATE(
  263. const LIB_ID& aRequestedId,
  264. const LIB_ID& aNewId,
  265. LIB_PART* aCacheCandidate,
  266. LIB_PART* aLibCandidate ) : RESCUE_CANDIDATE()
  267. {
  268. m_requested_id = aRequestedId;
  269. m_requested_name = aRequestedId.Format();
  270. m_new_id = aNewId;
  271. m_lib_candidate = aLibCandidate;
  272. m_cache_candidate = aCacheCandidate;
  273. }
  274. RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::RESCUE_SYMBOL_LIB_TABLE_CANDIDATE()
  275. {
  276. m_cache_candidate = NULL;
  277. m_lib_candidate = NULL;
  278. }
  279. void RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::FindRescues(
  280. RESCUER& aRescuer,
  281. boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
  282. {
  283. typedef std::map<LIB_ID, RESCUE_SYMBOL_LIB_TABLE_CANDIDATE> candidate_map_t;
  284. candidate_map_t candidate_map;
  285. // Remember the list of components is sorted by LIB_ID.
  286. // So a search in libraries is made only once by group
  287. LIB_PART* cache_match = nullptr;
  288. LIB_PART* lib_match = nullptr;
  289. LIB_ID old_part_id;
  290. for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) )
  291. {
  292. LIB_ID part_id = each_component->GetLibId();
  293. if( old_part_id != part_id )
  294. {
  295. // A new part name is found (a new group starts here).
  296. // Search the symbol names candidates only once for this group:
  297. old_part_id = part_id;
  298. cache_match = find_component( part_id.Format().wx_str(), aRescuer.GetPrj()->SchLibs(),
  299. true );
  300. lib_match = aRescuer.GetFrame()->GetLibPart( part_id );
  301. if( !cache_match && !lib_match )
  302. continue;
  303. // Test whether there is a conflict or if the symbol can only be found in the cache.
  304. if( LIB_ID::HasIllegalChars( part_id.GetLibItemName(), LIB_ID::ID_SCH ) == -1 )
  305. {
  306. if( cache_match && lib_match &&
  307. !cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) )
  308. continue;
  309. if( !cache_match && lib_match )
  310. continue;
  311. }
  312. // Fix illegal LIB_ID name characters.
  313. wxString new_name = LIB_ID::FixIllegalChars( part_id.GetLibItemName(), LIB_ID::ID_SCH );
  314. // Differentiate symbol name in the rescue library by appending the symbol library
  315. // table nickname to the symbol name to prevent name clashes in the rescue library.
  316. wxString libNickname = GetRescueLibraryFileName().GetName();
  317. // Spaces in the file name will break the symbol name because they are not
  318. // quoted in the symbol library file format.
  319. libNickname.Replace( " ", "-" );
  320. LIB_ID new_id( libNickname, new_name + "-" + part_id.GetLibNickname().wx_str() );
  321. RESCUE_SYMBOL_LIB_TABLE_CANDIDATE candidate( part_id, new_id, cache_match, lib_match );
  322. candidate_map[part_id] = candidate;
  323. }
  324. }
  325. // Now, dump the map into aCandidates
  326. for( const candidate_map_t::value_type& each_pair : candidate_map )
  327. {
  328. aCandidates.push_back( new RESCUE_SYMBOL_LIB_TABLE_CANDIDATE( each_pair.second ) );
  329. }
  330. }
  331. wxString RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::GetActionDescription() const
  332. {
  333. wxString action;
  334. if( !m_cache_candidate && !m_lib_candidate )
  335. action.Printf( _( "Cannot rescue symbol %s which is not available in any library or "
  336. "the cache." ), m_requested_id.GetLibItemName().wx_str() );
  337. else if( m_cache_candidate && !m_lib_candidate )
  338. action.Printf( _( "Rescue symbol %s found only in cache library to %s." ),
  339. m_requested_id.Format().wx_str(), m_new_id.Format().wx_str() );
  340. else
  341. action.Printf( _( "Rescue modified symbol %s to %s" ),
  342. m_requested_id.Format().wx_str(), m_new_id.Format().wx_str() );
  343. return action;
  344. }
  345. bool RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::PerformAction( RESCUER* aRescuer )
  346. {
  347. LIB_PART* tmp = ( m_cache_candidate ) ? m_cache_candidate : m_lib_candidate;
  348. wxCHECK_MSG( tmp, false, "Both cache and library symbols undefined." );
  349. LIB_PART new_part( *tmp );
  350. new_part.SetLibId( m_new_id );
  351. new_part.SetName( m_new_id.GetLibItemName() );
  352. new_part.RemoveAllAliases();
  353. aRescuer->AddPart( &new_part );
  354. for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() )
  355. {
  356. if( each_component->GetLibId() != m_requested_id )
  357. continue;
  358. each_component->SetLibId( m_new_id );
  359. each_component->ClearFlags();
  360. aRescuer->LogRescue( each_component, m_requested_id.Format(), m_new_id.Format() );
  361. }
  362. return true;
  363. }
  364. RESCUER::RESCUER( SCH_EDIT_FRAME& aEditFrame, PROJECT& aProject )
  365. {
  366. get_components( m_components );
  367. m_prj = &aProject;
  368. m_edit_frame = &aEditFrame;
  369. }
  370. void RESCUER::LogRescue( SCH_COMPONENT *aComponent, const wxString &aOldName,
  371. const wxString &aNewName )
  372. {
  373. RESCUE_LOG logitem;
  374. logitem.component = aComponent;
  375. logitem.old_name = aOldName;
  376. logitem.new_name = aNewName;
  377. m_rescue_log.push_back( logitem );
  378. }
  379. bool RESCUER::DoRescues()
  380. {
  381. for( RESCUE_CANDIDATE* each_candidate : m_chosen_candidates )
  382. {
  383. if( ! each_candidate->PerformAction( this ) )
  384. return false;
  385. }
  386. return true;
  387. }
  388. void RESCUER::UndoRescues()
  389. {
  390. for( RESCUE_LOG& each_logitem : m_rescue_log )
  391. {
  392. LIB_ID libId;
  393. libId.SetLibItemName( each_logitem.old_name, false );
  394. each_logitem.component->SetLibId( libId );
  395. each_logitem.component->ClearFlags();
  396. }
  397. }
  398. bool SCH_EDIT_FRAME::RescueLegacyProject( bool aRunningOnDemand )
  399. {
  400. LEGACY_RESCUER rescuer( *this, Prj() );
  401. return rescueProject( rescuer, aRunningOnDemand );
  402. }
  403. bool SCH_EDIT_FRAME::RescueSymbolLibTableProject( bool aRunningOnDemand )
  404. {
  405. SYMBOL_LIB_TABLE_RESCUER rescuer( *this, Prj() );
  406. return rescueProject( rescuer, aRunningOnDemand );
  407. }
  408. bool SCH_EDIT_FRAME::rescueProject( RESCUER& aRescuer, bool aRunningOnDemand )
  409. {
  410. aRescuer.FindCandidates();
  411. if( ! aRescuer.GetCandidateCount() )
  412. {
  413. if( aRunningOnDemand )
  414. {
  415. wxMessageDialog dlg( this, _( "This project has nothing to rescue." ),
  416. _( "Project Rescue Helper" ) );
  417. dlg.ShowModal();
  418. }
  419. return true;
  420. }
  421. aRescuer.RemoveDuplicates();
  422. aRescuer.InvokeDialog( !aRunningOnDemand );
  423. // If no symbols were rescued, let the user know what's going on. He might
  424. // have clicked cancel by mistake, and should have some indication of that.
  425. if( !aRescuer.GetChosenCandidateCount() )
  426. {
  427. wxMessageDialog dlg( this, _( "No symbols were rescued." ),
  428. _( "Project Rescue Helper" ) );
  429. dlg.ShowModal();
  430. // Set the modified flag even on Cancel. Many users seem to instinctively want to Save at
  431. // this point, due to the reloading of the symbols, so we'll make the save button active.
  432. OnModify();
  433. return true;
  434. }
  435. aRescuer.OpenRescueLibrary();
  436. if( !aRescuer.DoRescues() )
  437. {
  438. aRescuer.UndoRescues();
  439. return false;
  440. }
  441. aRescuer.WriteRescueLibrary( this );
  442. LIB_VIEW_FRAME* viewer = (LIB_VIEW_FRAME*) Kiway().Player( FRAME_SCH_VIEWER, false );
  443. if( viewer )
  444. viewer->ReCreateListLib();
  445. GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
  446. SyncView();
  447. GetCanvas()->Refresh();
  448. OnModify();
  449. return true;
  450. }
  451. void RESCUER::RemoveDuplicates()
  452. {
  453. std::vector<wxString> names_seen;
  454. for( boost::ptr_vector<RESCUE_CANDIDATE>::iterator it = m_all_candidates.begin();
  455. it != m_all_candidates.end(); )
  456. {
  457. bool seen_already = false;
  458. for( wxString& name_seen : names_seen )
  459. {
  460. if( name_seen == it->GetRequestedName() )
  461. {
  462. seen_already = true;
  463. break;
  464. }
  465. }
  466. if( seen_already )
  467. {
  468. it = m_all_candidates.erase( it );
  469. }
  470. else
  471. {
  472. names_seen.push_back( it->GetRequestedName() );
  473. ++it;
  474. }
  475. }
  476. }
  477. void LEGACY_RESCUER::FindCandidates()
  478. {
  479. RESCUE_CASE_CANDIDATE::FindRescues( *this, m_all_candidates );
  480. RESCUE_CACHE_CANDIDATE::FindRescues( *this, m_all_candidates );
  481. }
  482. void LEGACY_RESCUER::InvokeDialog( bool aAskShowAgain )
  483. {
  484. InvokeDialogRescueEach( m_edit_frame, static_cast< RESCUER& >( *this ), aAskShowAgain );
  485. }
  486. void LEGACY_RESCUER::OpenRescueLibrary()
  487. {
  488. wxFileName fn = GetRescueLibraryFileName();
  489. std::unique_ptr<PART_LIB> rescue_lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, fn.GetFullPath() ) );
  490. m_rescue_lib = std::move( rescue_lib );
  491. m_rescue_lib->EnableBuffering();
  492. // If a rescue library already exists copy the contents of that library so we do not
  493. // lose an previous rescues.
  494. PART_LIB* rescueLib = m_prj->SchLibs()->FindLibrary( fn.GetName() );
  495. if( rescueLib )
  496. {
  497. // For items in the rescue library, aliases are the root symbol.
  498. std::vector< LIB_ALIAS* > aliases;
  499. rescueLib->GetAliases( aliases );
  500. for( auto alias : aliases )
  501. {
  502. LIB_PART* part = alias->GetPart();
  503. wxCHECK2( part, continue );
  504. m_rescue_lib->AddPart( new LIB_PART( *part, m_rescue_lib.get() ) );
  505. }
  506. }
  507. }
  508. bool LEGACY_RESCUER::WriteRescueLibrary( SCH_EDIT_FRAME *aEditFrame )
  509. {
  510. try
  511. {
  512. m_rescue_lib->Save( false );
  513. }
  514. catch( ... /* IO_ERROR ioe */ )
  515. {
  516. wxString msg;
  517. msg.Printf( _( "Failed to create symbol library file \"%s\"" ),
  518. m_rescue_lib->GetFullFileName() );
  519. DisplayError( aEditFrame, msg );
  520. return false;
  521. }
  522. wxArrayString libNames;
  523. wxString libPaths;
  524. wxString libName = m_rescue_lib->GetName();
  525. PART_LIBS *libs = dynamic_cast<PART_LIBS*>( m_prj->GetElem( PROJECT::ELEM_SCH_PART_LIBS ) );
  526. if( !libs )
  527. {
  528. libs = new PART_LIBS();
  529. m_prj->SetElem( PROJECT::ELEM_SCH_PART_LIBS, libs );
  530. }
  531. try
  532. {
  533. PART_LIBS::LibNamesAndPaths( m_prj, false, &libPaths, &libNames );
  534. // Make sure the library is not already in the list
  535. while( libNames.Index( libName ) != wxNOT_FOUND )
  536. libNames.Remove( libName );
  537. // Add the library to the top of the list and save.
  538. libNames.Insert( libName, 0 );
  539. PART_LIBS::LibNamesAndPaths( m_prj, true, &libPaths, &libNames );
  540. }
  541. catch( const IO_ERROR& )
  542. {
  543. // Could not get or save the current libraries.
  544. return false;
  545. }
  546. // Save the old libraries in case there is a problem after clear(). We'll
  547. // put them back in.
  548. boost::ptr_vector<PART_LIB> libsSave;
  549. libsSave.transfer( libsSave.end(), libs->begin(), libs->end(), *libs );
  550. m_prj->SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL );
  551. libs = new PART_LIBS();
  552. try
  553. {
  554. libs->LoadAllLibraries( m_prj );
  555. }
  556. catch( const PARSE_ERROR& )
  557. {
  558. // Some libraries were not found. There's no point in showing the error,
  559. // because it was already shown. Just don't do anything.
  560. }
  561. catch( const IO_ERROR& )
  562. {
  563. // Restore the old list
  564. libs->clear();
  565. libs->transfer( libs->end(), libsSave.begin(), libsSave.end(), libsSave );
  566. return false;
  567. }
  568. m_prj->SetElem( PROJECT::ELEM_SCH_PART_LIBS, libs );
  569. // Update the schematic symbol library links since the library list has changed.
  570. SCH_SCREENS schematic;
  571. schematic.UpdateSymbolLinks();
  572. return true;
  573. }
  574. void LEGACY_RESCUER::AddPart( LIB_PART* aNewPart )
  575. {
  576. wxCHECK_RET( aNewPart, "Invalid LIB_PART pointer." );
  577. aNewPart->SetLib( m_rescue_lib.get() );
  578. m_rescue_lib->AddPart( aNewPart );
  579. }
  580. SYMBOL_LIB_TABLE_RESCUER::SYMBOL_LIB_TABLE_RESCUER( SCH_EDIT_FRAME& aEditFrame,
  581. PROJECT& aProject ) :
  582. RESCUER( aEditFrame, aProject )
  583. {
  584. m_properties = std::make_unique<PROPERTIES>();
  585. }
  586. void SYMBOL_LIB_TABLE_RESCUER::FindCandidates()
  587. {
  588. RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::FindRescues( *this, m_all_candidates );
  589. }
  590. void SYMBOL_LIB_TABLE_RESCUER::InvokeDialog( bool aAskShowAgain )
  591. {
  592. InvokeDialogRescueEach( m_edit_frame, static_cast< RESCUER& >( *this ), aAskShowAgain );
  593. }
  594. void SYMBOL_LIB_TABLE_RESCUER::OpenRescueLibrary()
  595. {
  596. m_pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
  597. (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
  598. }
  599. bool SYMBOL_LIB_TABLE_RESCUER::WriteRescueLibrary( SCH_EDIT_FRAME *aEditFrame )
  600. {
  601. wxString msg;
  602. wxFileName fn = GetRescueLibraryFileName();
  603. // If the rescue library already exists in the symbol library table no need save it to add
  604. // it to the table.
  605. if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) )
  606. {
  607. try
  608. {
  609. m_pi->SaveLibrary( fn.GetFullPath() );
  610. }
  611. catch( const IO_ERROR& ioe )
  612. {
  613. msg.Printf( _( "Failed to save rescue library %s." ), fn.GetFullPath() );
  614. DisplayErrorMessage( aEditFrame, msg, ioe.What() );
  615. return false;
  616. }
  617. wxString uri = "${KIPRJMOD}/" + fn.GetFullName();
  618. wxString libNickname = fn.GetName();
  619. // Spaces in the file name will break the symbol name because they are not
  620. // quoted in the symbol library file format.
  621. libNickname.Replace( " ", "-" );
  622. SYMBOL_LIB_TABLE_ROW* row = new SYMBOL_LIB_TABLE_ROW( libNickname, uri,
  623. wxString( "Legacy" ) );
  624. m_prj->SchSymbolLibTable()->InsertRow( row );
  625. fn = wxFileName( m_prj->GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
  626. try
  627. {
  628. m_prj->SchSymbolLibTable()->Save( fn.GetFullPath() );
  629. }
  630. catch( const IO_ERROR& ioe )
  631. {
  632. msg.Printf( _( "Error occurred saving project specific symbol library table." ) );
  633. DisplayErrorMessage( aEditFrame, msg, ioe.What() );
  634. return false;
  635. }
  636. }
  637. // Relaod the symbol library table.
  638. m_prj->SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, NULL );
  639. // This can only happen if the symbol library table file was currupted on write.
  640. if( !m_prj->SchSymbolLibTable() )
  641. return false;
  642. // Update the schematic symbol library links since the library list has changed.
  643. SCH_SCREENS schematic;
  644. schematic.UpdateSymbolLinks( true );
  645. return true;
  646. }
  647. void SYMBOL_LIB_TABLE_RESCUER::AddPart( LIB_PART* aNewPart )
  648. {
  649. wxCHECK_RET( aNewPart, "Invalid LIB_PART pointer." );
  650. wxFileName fn = GetRescueLibraryFileName();
  651. try
  652. {
  653. if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) )
  654. m_pi->SaveSymbol( fn.GetFullPath(), new LIB_PART( *aNewPart ), m_properties.get() );
  655. else
  656. m_prj->SchSymbolLibTable()->SaveSymbol( fn.GetName(), new LIB_PART( *aNewPart ) );
  657. }
  658. catch( ... /* IO_ERROR */ )
  659. {
  660. }
  661. }