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.

871 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-2019 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 = SchGetLibPart( part_id, aRescuer.GetPrj()->SchSymbolLibTable() );
  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( PROJECT& aProject, SCH_SHEET_PATH* aCurrentSheet,
  365. EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType )
  366. {
  367. get_components( m_components );
  368. m_prj = &aProject;
  369. m_currentSheet = aCurrentSheet;
  370. m_galBackEndType = aGalBackEndType;
  371. }
  372. void RESCUER::LogRescue( SCH_COMPONENT *aComponent, const wxString &aOldName,
  373. const wxString &aNewName )
  374. {
  375. RESCUE_LOG logitem;
  376. logitem.component = aComponent;
  377. logitem.old_name = aOldName;
  378. logitem.new_name = aNewName;
  379. m_rescue_log.push_back( logitem );
  380. }
  381. bool RESCUER::DoRescues()
  382. {
  383. for( RESCUE_CANDIDATE* each_candidate : m_chosen_candidates )
  384. {
  385. if( ! each_candidate->PerformAction( this ) )
  386. return false;
  387. }
  388. return true;
  389. }
  390. void RESCUER::UndoRescues()
  391. {
  392. for( RESCUE_LOG& each_logitem : m_rescue_log )
  393. {
  394. LIB_ID libId;
  395. libId.SetLibItemName( each_logitem.old_name, false );
  396. each_logitem.component->SetLibId( libId );
  397. each_logitem.component->ClearFlags();
  398. }
  399. }
  400. bool SCH_EDIT_FRAME::RescueLegacyProject( bool aRunningOnDemand )
  401. {
  402. LEGACY_RESCUER rescuer( Prj(), &GetCurrentSheet(), GetGalCanvas()->GetBackend() );
  403. return rescueProject( rescuer, aRunningOnDemand );
  404. }
  405. bool SCH_EDIT_FRAME::RescueSymbolLibTableProject( bool aRunningOnDemand )
  406. {
  407. SYMBOL_LIB_TABLE_RESCUER rescuer( Prj(), &GetCurrentSheet(), GetGalCanvas()->GetBackend() );
  408. return rescueProject( rescuer, aRunningOnDemand );
  409. }
  410. bool SCH_EDIT_FRAME::rescueProject( RESCUER& aRescuer, bool aRunningOnDemand )
  411. {
  412. if( !RESCUER::RescueProject( this, aRescuer, aRunningOnDemand ) )
  413. return false;
  414. if( aRescuer.GetCandidateCount() )
  415. {
  416. LIB_VIEW_FRAME* viewer = (LIB_VIEW_FRAME*) Kiway().Player( FRAME_SCH_VIEWER, false );
  417. if( viewer )
  418. viewer->ReCreateListLib();
  419. GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
  420. SyncView();
  421. GetCanvas()->Refresh();
  422. OnModify();
  423. }
  424. return true;
  425. }
  426. bool RESCUER::RescueProject( wxWindow* aParent, RESCUER& aRescuer, bool aRunningOnDemand )
  427. {
  428. aRescuer.FindCandidates();
  429. if( !aRescuer.GetCandidateCount() )
  430. {
  431. if( aRunningOnDemand )
  432. {
  433. wxMessageDialog dlg( aParent, _( "This project has nothing to rescue." ),
  434. _( "Project Rescue Helper" ) );
  435. dlg.ShowModal();
  436. }
  437. return true;
  438. }
  439. aRescuer.RemoveDuplicates();
  440. aRescuer.InvokeDialog( aParent, !aRunningOnDemand );
  441. // If no symbols were rescued, let the user know what's going on. He might
  442. // have clicked cancel by mistake, and should have some indication of that.
  443. if( !aRescuer.GetChosenCandidateCount() )
  444. {
  445. wxMessageDialog dlg( aParent, _( "No symbols were rescued." ),
  446. _( "Project Rescue Helper" ) );
  447. dlg.ShowModal();
  448. // Set the modified flag even on Cancel. Many users seem to instinctively want to Save at
  449. // this point, due to the reloading of the symbols, so we'll make the save button active.
  450. return true;
  451. }
  452. aRescuer.OpenRescueLibrary();
  453. if( !aRescuer.DoRescues() )
  454. {
  455. aRescuer.UndoRescues();
  456. return false;
  457. }
  458. aRescuer.WriteRescueLibrary( aParent );
  459. return true;
  460. }
  461. void RESCUER::RemoveDuplicates()
  462. {
  463. std::vector<wxString> names_seen;
  464. for( boost::ptr_vector<RESCUE_CANDIDATE>::iterator it = m_all_candidates.begin();
  465. it != m_all_candidates.end(); )
  466. {
  467. bool seen_already = false;
  468. for( wxString& name_seen : names_seen )
  469. {
  470. if( name_seen == it->GetRequestedName() )
  471. {
  472. seen_already = true;
  473. break;
  474. }
  475. }
  476. if( seen_already )
  477. {
  478. it = m_all_candidates.erase( it );
  479. }
  480. else
  481. {
  482. names_seen.push_back( it->GetRequestedName() );
  483. ++it;
  484. }
  485. }
  486. }
  487. void LEGACY_RESCUER::FindCandidates()
  488. {
  489. RESCUE_CASE_CANDIDATE::FindRescues( *this, m_all_candidates );
  490. RESCUE_CACHE_CANDIDATE::FindRescues( *this, m_all_candidates );
  491. }
  492. void LEGACY_RESCUER::InvokeDialog( wxWindow* aParent, bool aAskShowAgain )
  493. {
  494. InvokeDialogRescueEach( aParent, static_cast< RESCUER& >( *this ), m_currentSheet,
  495. m_galBackEndType, aAskShowAgain );
  496. }
  497. void LEGACY_RESCUER::OpenRescueLibrary()
  498. {
  499. wxFileName fn = GetRescueLibraryFileName();
  500. std::unique_ptr<PART_LIB> rescue_lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, fn.GetFullPath() ) );
  501. m_rescue_lib = std::move( rescue_lib );
  502. m_rescue_lib->EnableBuffering();
  503. // If a rescue library already exists copy the contents of that library so we do not
  504. // lose an previous rescues.
  505. PART_LIB* rescueLib = m_prj->SchLibs()->FindLibrary( fn.GetName() );
  506. if( rescueLib )
  507. {
  508. // For items in the rescue library, aliases are the root symbol.
  509. std::vector< LIB_ALIAS* > aliases;
  510. rescueLib->GetAliases( aliases );
  511. for( auto alias : aliases )
  512. {
  513. LIB_PART* part = alias->GetPart();
  514. wxCHECK2( part, continue );
  515. m_rescue_lib->AddPart( new LIB_PART( *part, m_rescue_lib.get() ) );
  516. }
  517. }
  518. }
  519. bool LEGACY_RESCUER::WriteRescueLibrary( wxWindow *aParent )
  520. {
  521. try
  522. {
  523. m_rescue_lib->Save( false );
  524. }
  525. catch( ... /* IO_ERROR ioe */ )
  526. {
  527. wxString msg;
  528. msg.Printf( _( "Failed to create symbol library file \"%s\"" ),
  529. m_rescue_lib->GetFullFileName() );
  530. DisplayError( aParent, msg );
  531. return false;
  532. }
  533. wxArrayString libNames;
  534. wxString libPaths;
  535. wxString libName = m_rescue_lib->GetName();
  536. PART_LIBS *libs = dynamic_cast<PART_LIBS*>( m_prj->GetElem( PROJECT::ELEM_SCH_PART_LIBS ) );
  537. if( !libs )
  538. {
  539. libs = new PART_LIBS();
  540. m_prj->SetElem( PROJECT::ELEM_SCH_PART_LIBS, libs );
  541. }
  542. try
  543. {
  544. PART_LIBS::LibNamesAndPaths( m_prj, false, &libPaths, &libNames );
  545. // Make sure the library is not already in the list
  546. while( libNames.Index( libName ) != wxNOT_FOUND )
  547. libNames.Remove( libName );
  548. // Add the library to the top of the list and save.
  549. libNames.Insert( libName, 0 );
  550. PART_LIBS::LibNamesAndPaths( m_prj, true, &libPaths, &libNames );
  551. }
  552. catch( const IO_ERROR& )
  553. {
  554. // Could not get or save the current libraries.
  555. return false;
  556. }
  557. // Save the old libraries in case there is a problem after clear(). We'll
  558. // put them back in.
  559. boost::ptr_vector<PART_LIB> libsSave;
  560. libsSave.transfer( libsSave.end(), libs->begin(), libs->end(), *libs );
  561. m_prj->SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL );
  562. libs = new PART_LIBS();
  563. try
  564. {
  565. libs->LoadAllLibraries( m_prj );
  566. }
  567. catch( const PARSE_ERROR& )
  568. {
  569. // Some libraries were not found. There's no point in showing the error,
  570. // because it was already shown. Just don't do anything.
  571. }
  572. catch( const IO_ERROR& )
  573. {
  574. // Restore the old list
  575. libs->clear();
  576. libs->transfer( libs->end(), libsSave.begin(), libsSave.end(), libsSave );
  577. return false;
  578. }
  579. m_prj->SetElem( PROJECT::ELEM_SCH_PART_LIBS, libs );
  580. // Update the schematic symbol library links since the library list has changed.
  581. SCH_SCREENS schematic;
  582. schematic.UpdateSymbolLinks();
  583. return true;
  584. }
  585. void LEGACY_RESCUER::AddPart( LIB_PART* aNewPart )
  586. {
  587. wxCHECK_RET( aNewPart, "Invalid LIB_PART pointer." );
  588. aNewPart->SetLib( m_rescue_lib.get() );
  589. m_rescue_lib->AddPart( aNewPart );
  590. }
  591. SYMBOL_LIB_TABLE_RESCUER::SYMBOL_LIB_TABLE_RESCUER( PROJECT& aProject,
  592. SCH_SHEET_PATH* aCurrentSheet,
  593. EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType ) :
  594. RESCUER( aProject, aCurrentSheet, aGalBackEndType )
  595. {
  596. m_properties = std::make_unique<PROPERTIES>();
  597. }
  598. void SYMBOL_LIB_TABLE_RESCUER::FindCandidates()
  599. {
  600. RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::FindRescues( *this, m_all_candidates );
  601. }
  602. void SYMBOL_LIB_TABLE_RESCUER::InvokeDialog( wxWindow* aParent, bool aAskShowAgain )
  603. {
  604. InvokeDialogRescueEach( aParent, static_cast< RESCUER& >( *this ), m_currentSheet,
  605. m_galBackEndType, aAskShowAgain );
  606. }
  607. void SYMBOL_LIB_TABLE_RESCUER::OpenRescueLibrary()
  608. {
  609. m_pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
  610. (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
  611. }
  612. bool SYMBOL_LIB_TABLE_RESCUER::WriteRescueLibrary( wxWindow *aParent )
  613. {
  614. wxString msg;
  615. wxFileName fn = GetRescueLibraryFileName();
  616. // If the rescue library already exists in the symbol library table no need save it to add
  617. // it to the table.
  618. if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) )
  619. {
  620. try
  621. {
  622. m_pi->SaveLibrary( fn.GetFullPath() );
  623. }
  624. catch( const IO_ERROR& ioe )
  625. {
  626. msg.Printf( _( "Failed to save rescue library %s." ), fn.GetFullPath() );
  627. DisplayErrorMessage( aParent, msg, ioe.What() );
  628. return false;
  629. }
  630. wxString uri = "${KIPRJMOD}/" + fn.GetFullName();
  631. wxString libNickname = fn.GetName();
  632. // Spaces in the file name will break the symbol name because they are not
  633. // quoted in the symbol library file format.
  634. libNickname.Replace( " ", "-" );
  635. SYMBOL_LIB_TABLE_ROW* row = new SYMBOL_LIB_TABLE_ROW( libNickname, uri,
  636. wxString( "Legacy" ) );
  637. m_prj->SchSymbolLibTable()->InsertRow( row );
  638. fn = wxFileName( m_prj->GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
  639. try
  640. {
  641. m_prj->SchSymbolLibTable()->Save( fn.GetFullPath() );
  642. }
  643. catch( const IO_ERROR& ioe )
  644. {
  645. msg.Printf( _( "Error occurred saving project specific symbol library table." ) );
  646. DisplayErrorMessage( aParent, msg, ioe.What() );
  647. return false;
  648. }
  649. }
  650. // Relaod the symbol library table.
  651. m_prj->SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, NULL );
  652. // This can only happen if the symbol library table file was currupted on write.
  653. if( !m_prj->SchSymbolLibTable() )
  654. return false;
  655. // Update the schematic symbol library links since the library list has changed.
  656. SCH_SCREENS schematic;
  657. schematic.UpdateSymbolLinks( true );
  658. return true;
  659. }
  660. void SYMBOL_LIB_TABLE_RESCUER::AddPart( LIB_PART* aNewPart )
  661. {
  662. wxCHECK_RET( aNewPart, "Invalid LIB_PART pointer." );
  663. wxFileName fn = GetRescueLibraryFileName();
  664. try
  665. {
  666. if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) )
  667. m_pi->SaveSymbol( fn.GetFullPath(), new LIB_PART( *aNewPart ), m_properties.get() );
  668. else
  669. m_prj->SchSymbolLibTable()->SaveSymbol( fn.GetName(), new LIB_PART( *aNewPart ) );
  670. }
  671. catch( ... /* IO_ERROR */ )
  672. {
  673. }
  674. }