KiCad PCB EDA Suite
project_rescue.cpp
Go to the documentation of this file.
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-2021 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 
25 #include <sch_draw_panel.h>
26 #include <symbol_library.h>
27 #include <confirm.h>
28 #include <connection_graph.h>
29 #include <invoke_sch_dialog.h>
30 #include <kiway.h>
31 #include <symbol_viewer_frame.h>
32 #include <project_rescue.h>
33 #include <sch_symbol.h>
34 #include <sch_sheet.h>
35 #include <sch_edit_frame.h>
36 #include <schematic.h>
37 #include <symbol_lib_table.h>
39 
40 #include <cctype>
41 #include <map>
42 
43 
44 typedef std::pair<SCH_SYMBOL*, wxString> SYMBOL_NAME_PAIR;
45 
46 
47 // Helper sort function, used in getSymbols, to sort a symbol list by lib_id
48 static bool sort_by_libid( const SCH_SYMBOL* ref, SCH_SYMBOL* cmp )
49 {
50  return ref->GetLibId() < cmp->GetLibId();
51 }
52 
53 
63 static void getSymbols( SCHEMATIC* aSchematic, std::vector<SCH_SYMBOL*>& aSymbols )
64 {
65  SCH_SCREENS screens( aSchematic->Root() );
66 
67  // Get the full list
68  for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
69  {
70  for( auto aItem : screen->Items().OfType( SCH_SYMBOL_T ) )
71  aSymbols.push_back( static_cast<SCH_SYMBOL*>( aItem ) );
72  }
73 
74  if( aSymbols.empty() )
75  return;
76 
77  // sort aSymbols by lib symbol. symbols will be grouped by same lib symbol.
78  std::sort( aSymbols.begin(), aSymbols.end(), sort_by_libid );
79 }
80 
81 
89 static LIB_SYMBOL* findSymbol( const wxString& aName, SYMBOL_LIBS* aLibs, bool aCached )
90 {
91  LIB_SYMBOL *symbol = nullptr;
92  // wxString new_name = LIB_ID::FixIllegalChars( aName, false );
93 
94  for( SYMBOL_LIB& each_lib : *aLibs )
95  {
96  if( aCached && !each_lib.IsCache() )
97  continue;
98 
99  if( !aCached && each_lib.IsCache() )
100  continue;
101 
102  symbol = each_lib.FindSymbol( aName );
103 
104  if( symbol )
105  break;
106  }
107 
108  return symbol;
109 }
110 
111 
112 static wxFileName GetRescueLibraryFileName( SCHEMATIC* aSchematic )
113 {
114  wxFileName fn = aSchematic->GetFileName();
115  fn.SetName( fn.GetName() + wxT( "-rescue" ) );
116  fn.SetExt( LegacySymbolLibFileExtension );
117  return fn;
118 }
119 
120 
121 RESCUE_CASE_CANDIDATE::RESCUE_CASE_CANDIDATE( const wxString& aRequestedName,
122  const wxString& aNewName,
123  LIB_SYMBOL* aLibCandidate,
124  int aUnit,
125  int aConvert )
126 {
127  m_requested_name = aRequestedName;
128  m_new_name = aNewName;
129  m_lib_candidate = aLibCandidate;
130  m_unit = aUnit;
131  m_convert = aConvert;
132 }
133 
134 
136  boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
137 {
138  typedef std::map<wxString, RESCUE_CASE_CANDIDATE> candidate_map_t;
139  candidate_map_t candidate_map;
140 
141  // Remember the list of symbols is sorted by symbol name.
142  // So a search in libraries is made only once by group
143  LIB_SYMBOL* case_sensitive_match = nullptr;
144  std::vector<LIB_SYMBOL*> case_insensitive_matches;
145 
146  wxString symbol_name;
147  wxString search_name;
148  wxString last_symbol_name;
149 
150  for( SCH_SYMBOL* eachSymbol : *( aRescuer.GetSymbols() ) )
151  {
152  symbol_name = eachSymbol->GetLibId().GetLibItemName();
153  search_name = LIB_ID::FixIllegalChars( symbol_name, false );
154 
155  if( last_symbol_name != symbol_name )
156  {
157  // A new symbol name is found (a new group starts here).
158  // Search the symbol names candidates only once for this group:
159  last_symbol_name = symbol_name;
160  case_insensitive_matches.clear();
161 
162  LIB_ID id( wxEmptyString, search_name );
163 
164  case_sensitive_match = aRescuer.GetPrj()->SchLibs()->FindLibSymbol( id );
165 
166  // If the case sensitive match failed, try a case insensitive match.
167  if( !case_sensitive_match )
168  aRescuer.GetPrj()->SchLibs()->FindLibraryNearEntries( case_insensitive_matches,
169  search_name );
170  }
171 
172  if( case_sensitive_match || !( case_insensitive_matches.size() ) )
173  continue;
174 
175  RESCUE_CASE_CANDIDATE candidate( symbol_name, case_insensitive_matches[0]->GetName(),
176  case_insensitive_matches[0],
177  eachSymbol->GetUnit(),
178  eachSymbol->GetConvert() );
179 
180  candidate_map[symbol_name] = candidate;
181  }
182 
183  // Now, dump the map into aCandidates
184  for( const candidate_map_t::value_type& each_pair : candidate_map )
185  {
186  aCandidates.push_back( new RESCUE_CASE_CANDIDATE( each_pair.second ) );
187  }
188 }
189 
190 
192 {
193  wxString action;
194  action.Printf( _( "Rename %s to %s" ), m_requested_name, m_new_name );
195  return action;
196 }
197 
198 
200 {
201  for( SCH_SYMBOL* eachSymbol : *aRescuer->GetSymbols() )
202  {
203  if( eachSymbol->GetLibId().GetLibItemName() != UTF8( m_requested_name ) )
204  continue;
205 
206  LIB_ID libId;
207 
208  libId.SetLibItemName( m_new_name );
209  eachSymbol->SetLibId( libId );
210  eachSymbol->ClearFlags();
211  aRescuer->LogRescue( eachSymbol, m_requested_name, m_new_name );
212  }
213 
214  return true;
215 }
216 
217 
218 RESCUE_CACHE_CANDIDATE::RESCUE_CACHE_CANDIDATE( const wxString& aRequestedName,
219  const wxString& aNewName,
220  LIB_SYMBOL* aCacheCandidate,
221  LIB_SYMBOL* aLibCandidate,
222  int aUnit,
223  int aConvert )
224 {
225  m_requested_name = aRequestedName;
226  m_new_name = aNewName;
227  m_cache_candidate = aCacheCandidate;
228  m_lib_candidate = aLibCandidate;
229  m_unit = aUnit;
230  m_convert = aConvert;
231 }
232 
233 
235 {
236  m_cache_candidate = nullptr;
237  m_lib_candidate = nullptr;
238 }
239 
240 
242  boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
243 {
244  typedef std::map<wxString, RESCUE_CACHE_CANDIDATE> candidate_map_t;
245  candidate_map_t candidate_map;
246 
247  // Remember the list of symbols is sorted by symbol name.
248  // So a search in libraries is made only once by group
249  LIB_SYMBOL* cache_match = nullptr;
250  LIB_SYMBOL* lib_match = nullptr;
251  wxString symbol_name;
252  wxString search_name;
253  wxString old_symbol_name;
254 
255  for( SCH_SYMBOL* eachSymbol : *( aRescuer.GetSymbols() ) )
256  {
257  symbol_name = eachSymbol->GetLibId().GetLibItemName();
258  search_name = LIB_ID::FixIllegalChars( symbol_name, false );
259 
260  if( old_symbol_name != symbol_name )
261  {
262  // A new symbol name is found (a new group starts here).
263  // Search the symbol names candidates only once for this group:
264  old_symbol_name = symbol_name;
265  cache_match = findSymbol( search_name, aRescuer.GetPrj()->SchLibs(), true );
266  lib_match = findSymbol( search_name, aRescuer.GetPrj()->SchLibs(), false );
267 
268  if( !cache_match && !lib_match )
269  continue;
270 
271  // Test whether there is a conflict or if the symbol can only be found in the cache
272  // and the symbol name does not have any illegal characters.
273  if( cache_match && lib_match &&
274  !cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) )
275  continue;
276 
277  if( !cache_match && lib_match )
278  continue;
279 
280  // Check if the symbol has already been rescued.
281  RESCUE_CACHE_CANDIDATE candidate( symbol_name, search_name, cache_match, lib_match,
282  eachSymbol->GetUnit(),
283  eachSymbol->GetConvert() );
284 
285  candidate_map[symbol_name] = candidate;
286  }
287  }
288 
289  // Now, dump the map into aCandidates
290  for( const candidate_map_t::value_type& each_pair : candidate_map )
291  {
292  aCandidates.push_back( new RESCUE_CACHE_CANDIDATE( each_pair.second ) );
293  }
294 }
295 
296 
298 {
299  wxString action;
300 
302  action.Printf( _( "Cannot rescue symbol %s which is not available in any library or "
303  "the cache." ), m_requested_name );
304  else if( m_cache_candidate && !m_lib_candidate )
305  action.Printf( _( "Rescue symbol %s found only in cache library to %s." ),
307  else
308  action.Printf( _( "Rescue modified symbol %s to %s" ),
310 
311  return action;
312 }
313 
314 
316 {
318 
319  wxCHECK_MSG( tmp, false, "Both cache and library symbols undefined." );
320 
321  std::unique_ptr<LIB_SYMBOL> new_symbol = tmp->Flatten();
322  new_symbol->SetName( m_new_name );
323  aRescuer->AddSymbol( new_symbol.get() );
324 
325  for( SCH_SYMBOL* eachSymbol : *aRescuer->GetSymbols() )
326  {
327  if( eachSymbol->GetLibId().GetLibItemName() != UTF8( m_requested_name ) )
328  continue;
329 
330  LIB_ID libId;
331 
332  libId.SetLibItemName( m_new_name );
333  eachSymbol->SetLibId( libId );
334  eachSymbol->ClearFlags();
335  aRescuer->LogRescue( eachSymbol, m_requested_name, m_new_name );
336  }
337 
338  return true;
339 }
340 
341 
343  const LIB_ID& aRequestedId,
344  const LIB_ID& aNewId,
345  LIB_SYMBOL* aCacheCandidate,
346  LIB_SYMBOL* aLibCandidate,
347  int aUnit,
348  int aConvert ) : RESCUE_CANDIDATE()
349 {
350  m_requested_id = aRequestedId;
351  m_requested_name = aRequestedId.Format();
352  m_new_id = aNewId;
353  m_lib_candidate = aLibCandidate;
354  m_cache_candidate = aCacheCandidate;
355  m_unit = aUnit;
356  m_convert = aConvert;
357 }
358 
359 
361 {
362  m_cache_candidate = nullptr;
363  m_lib_candidate = nullptr;
364 }
365 
366 
368  RESCUER& aRescuer,
369  boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
370 {
371  typedef std::map<LIB_ID, RESCUE_SYMBOL_LIB_TABLE_CANDIDATE> candidate_map_t;
372 
373  candidate_map_t candidate_map;
374 
375  // Remember the list of symbols is sorted by LIB_ID.
376  // So a search in libraries is made only once by group
377  LIB_SYMBOL* cache_match = nullptr;
378  LIB_SYMBOL* lib_match = nullptr;
379  LIB_ID old_symbol_id;
380 
381  for( SCH_SYMBOL* eachSymbol : *( aRescuer.GetSymbols() ) )
382  {
383  const LIB_ID& symbol_id = eachSymbol->GetLibId();
384 
385  if( old_symbol_id != symbol_id )
386  {
387  // A new symbol name is found (a new group starts here).
388  // Search the symbol names candidates only once for this group:
389  old_symbol_id = symbol_id;
390 
391  // Get the library symbol from the cache library. It will be a flattened
392  // symbol by default (no inheritance).
393  cache_match = findSymbol( symbol_id.Format().wx_str(), aRescuer.GetPrj()->SchLibs(),
394  true );
395 
396  // Get the library symbol from the symbol library table.
397  lib_match = SchGetLibSymbol( symbol_id, aRescuer.GetPrj()->SchSymbolLibTable() );
398 
399  if( !cache_match && !lib_match )
400  continue;
401 
402  LIB_SYMBOL_SPTR lib_match_parent;
403 
404  // If it's a derive symbol, use the parent symbol to perform the pin test.
405  if( lib_match && lib_match->IsAlias() )
406  {
407  lib_match_parent = lib_match->GetParent().lock();
408 
409  if( !lib_match_parent )
410  lib_match = nullptr;
411  else
412  lib_match = lib_match_parent.get();
413  }
414 
415  // Test whether there is a conflict or if the symbol can only be found in the cache.
416  if( LIB_ID::HasIllegalChars( symbol_id.GetLibItemName() ) == -1 )
417  {
418  if( cache_match && lib_match &&
419  !cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) )
420  {
421  continue;
422  }
423 
424  if( !cache_match && lib_match )
425  continue;
426  }
427 
428  // Fix illegal LIB_ID name characters.
429  wxString new_name = LIB_ID::FixIllegalChars( symbol_id.GetLibItemName(), false );
430 
431  // Differentiate symbol name in the rescue library by appending the symbol library
432  // table nickname to the symbol name to prevent name clashes in the rescue library.
433  wxString libNickname = GetRescueLibraryFileName( aRescuer.Schematic() ).GetName();
434 
435  // Spaces in the file name will break the symbol name because they are not
436  // quoted in the symbol library file format.
437  libNickname.Replace( " ", "-" );
438  LIB_ID new_id( libNickname, new_name + "-" + symbol_id.GetLibNickname().wx_str() );
439 
440  RESCUE_SYMBOL_LIB_TABLE_CANDIDATE candidate( symbol_id, new_id, cache_match, lib_match,
441  eachSymbol->GetUnit(),
442  eachSymbol->GetConvert() );
443 
444  candidate_map[symbol_id] = candidate;
445  }
446  }
447 
448  // Now, dump the map into aCandidates
449  for( const candidate_map_t::value_type& each_pair : candidate_map )
450  {
451  aCandidates.push_back( new RESCUE_SYMBOL_LIB_TABLE_CANDIDATE( each_pair.second ) );
452  }
453 }
454 
455 
457 {
458  wxString action;
459 
461  {
462  action.Printf( _( "Cannot rescue symbol %s which is not available in any library or "
463  "the cache." ),
465  }
466  else if( m_cache_candidate && !m_lib_candidate )
467  {
468  action.Printf( _( "Rescue symbol %s found only in cache library to %s." ),
470  m_new_id.Format().wx_str() );
471  }
472  else
473  {
474  action.Printf( _( "Rescue modified symbol %s to %s" ),
476  m_new_id.Format().wx_str() );
477  }
478 
479  return action;
480 }
481 
482 
484 {
486 
487  wxCHECK_MSG( tmp, false, "Both cache and library symbols undefined." );
488 
489  std::unique_ptr<LIB_SYMBOL> new_symbol = tmp->Flatten();
490  new_symbol->SetLibId( m_new_id );
491  new_symbol->SetName( m_new_id.GetLibItemName() );
492  aRescuer->AddSymbol( new_symbol.get() );
493 
494  for( SCH_SYMBOL* eachSymbol : *aRescuer->GetSymbols() )
495  {
496  if( eachSymbol->GetLibId() != m_requested_id )
497  continue;
498 
499  eachSymbol->SetLibId( m_new_id );
500  eachSymbol->ClearFlags();
501  aRescuer->LogRescue( eachSymbol, m_requested_id.Format(), m_new_id.Format() );
502  }
503 
504  return true;
505 }
506 
507 
508 RESCUER::RESCUER( PROJECT& aProject, SCHEMATIC* aSchematic, SCH_SHEET_PATH* aCurrentSheet,
509  EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType )
510 {
511  m_schematic = aSchematic ? aSchematic : aCurrentSheet->LastScreen()->Schematic();
512 
513  wxASSERT( m_schematic );
514 
515  if( m_schematic )
517 
518  m_prj = &aProject;
519  m_currentSheet = aCurrentSheet;
520  m_galBackEndType = aGalBackEndType;
521 }
522 
523 
524 void RESCUER::LogRescue( SCH_SYMBOL* aSymbol, const wxString &aOldName,
525  const wxString &aNewName )
526 {
527  RESCUE_LOG logitem;
528  logitem.symbol = aSymbol;
529  logitem.old_name = aOldName;
530  logitem.new_name = aNewName;
531  m_rescue_log.push_back( logitem );
532 }
533 
534 
536 {
537  for( RESCUE_CANDIDATE* each_candidate : m_chosen_candidates )
538  {
539  if( ! each_candidate->PerformAction( this ) )
540  return false;
541  }
542 
543  return true;
544 }
545 
546 
548 {
549  for( RESCUE_LOG& each_logitem : m_rescue_log )
550  {
551  LIB_ID libId;
552 
553  libId.SetLibItemName( each_logitem.old_name );
554  each_logitem.symbol->SetLibId( libId );
555  each_logitem.symbol->ClearFlags();
556  }
557 }
558 
559 
560 bool RESCUER::RescueProject( wxWindow* aParent, RESCUER& aRescuer, bool aRunningOnDemand )
561 {
562  aRescuer.FindCandidates();
563 
564  if( !aRescuer.GetCandidateCount() )
565  {
566  if( aRunningOnDemand )
567  {
568  wxMessageDialog dlg( aParent, _( "This project has nothing to rescue." ),
569  _( "Project Rescue Helper" ) );
570  dlg.ShowModal();
571  }
572 
573  return true;
574  }
575 
576  aRescuer.RemoveDuplicates();
577  aRescuer.InvokeDialog( aParent, !aRunningOnDemand );
578 
579  // If no symbols were rescued, let the user know what's going on. He might
580  // have clicked cancel by mistake, and should have some indication of that.
581  if( !aRescuer.GetChosenCandidateCount() )
582  {
583  wxMessageDialog dlg( aParent, _( "No symbols were rescued." ),
584  _( "Project Rescue Helper" ) );
585  dlg.ShowModal();
586 
587  // Set the modified flag even on Cancel. Many users seem to instinctively want to Save at
588  // this point, due to the reloading of the symbols, so we'll make the save button active.
589  return true;
590  }
591 
592  aRescuer.OpenRescueLibrary();
593 
594  if( !aRescuer.DoRescues() )
595  {
596  aRescuer.UndoRescues();
597  return false;
598  }
599 
600  aRescuer.WriteRescueLibrary( aParent );
601 
602  return true;
603 }
604 
605 
607 {
608  std::vector<wxString> names_seen;
609 
610  for( boost::ptr_vector<RESCUE_CANDIDATE>::iterator it = m_all_candidates.begin();
611  it != m_all_candidates.end(); )
612  {
613  bool seen_already = false;
614 
615  for( wxString& name_seen : names_seen )
616  {
617  if( name_seen == it->GetRequestedName() )
618  {
619  seen_already = true;
620  break;
621  }
622  }
623 
624  if( seen_already )
625  {
626  it = m_all_candidates.erase( it );
627  }
628  else
629  {
630  names_seen.push_back( it->GetRequestedName() );
631  ++it;
632  }
633  }
634 }
635 
636 
638 {
641 }
642 
643 
644 void LEGACY_RESCUER::InvokeDialog( wxWindow* aParent, bool aAskShowAgain )
645 {
646  InvokeDialogRescueEach( aParent, static_cast< RESCUER& >( *this ), m_currentSheet,
647  m_galBackEndType, aAskShowAgain );
648 }
649 
650 
652 {
653  wxFileName fn = GetRescueLibraryFileName( m_schematic );
654 
655  std::unique_ptr<SYMBOL_LIB> rescue_lib = std::make_unique<SYMBOL_LIB>( SCH_LIB_TYPE::LT_EESCHEMA,
656  fn.GetFullPath() );
657 
658  m_rescue_lib = std::move( rescue_lib );
659  m_rescue_lib->EnableBuffering();
660 
661  // If a rescue library already exists copy the contents of that library so we do not
662  // lose an previous rescues.
663  SYMBOL_LIB* rescueLib = m_prj->SchLibs()->FindLibrary( fn.GetName() );
664 
665  if( rescueLib )
666  {
667  // For items in the rescue library, aliases are the root symbol.
668  std::vector< LIB_SYMBOL* > symbols;
669 
670  rescueLib->GetSymbols( symbols );
671 
672  for( auto symbol : symbols )
673  {
674  // The LIB_SYMBOL copy constructor flattens derived symbols (formerly known as aliases).
675  m_rescue_lib->AddSymbol( new LIB_SYMBOL( *symbol, m_rescue_lib.get() ) );
676  }
677  }
678 }
679 
680 
681 bool LEGACY_RESCUER::WriteRescueLibrary( wxWindow *aParent )
682 {
683  try
684  {
685  m_rescue_lib->Save( false );
686  }
687  catch( ... /* IO_ERROR ioe */ )
688  {
689  wxString msg;
690 
691  msg.Printf( _( "Failed to create symbol library file '%s'." ),
692  m_rescue_lib->GetFullFileName() );
693  DisplayError( aParent, msg );
694  return false;
695  }
696 
697  wxArrayString libNames;
698  wxString libPaths;
699 
700  wxString libName = m_rescue_lib->GetName();
701  SYMBOL_LIBS *libs = dynamic_cast<SYMBOL_LIBS*>( m_prj->GetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS ) );
702 
703  if( !libs )
704  {
705  libs = new SYMBOL_LIBS();
707  }
708 
709  try
710  {
711  SYMBOL_LIBS::LibNamesAndPaths( m_prj, false, &libPaths, &libNames );
712 
713  // Make sure the library is not already in the list
714  while( libNames.Index( libName ) != wxNOT_FOUND )
715  libNames.Remove( libName );
716 
717  // Add the library to the top of the list and save.
718  libNames.Insert( libName, 0 );
719  SYMBOL_LIBS::LibNamesAndPaths( m_prj, true, &libPaths, &libNames );
720  }
721  catch( const IO_ERROR& )
722  {
723  // Could not get or save the current libraries.
724  return false;
725  }
726 
727  // Save the old libraries in case there is a problem after clear(). We'll
728  // put them back in.
729  boost::ptr_vector<SYMBOL_LIB> libsSave;
730  libsSave.transfer( libsSave.end(), libs->begin(), libs->end(), *libs );
731 
733 
734  libs = new SYMBOL_LIBS();
735 
736  try
737  {
738  libs->LoadAllLibraries( m_prj );
739  }
740  catch( const PARSE_ERROR& )
741  {
742  // Some libraries were not found. There's no point in showing the error,
743  // because it was already shown. Just don't do anything.
744  }
745  catch( const IO_ERROR& )
746  {
747  // Restore the old list
748  libs->clear();
749  libs->transfer( libs->end(), libsSave.begin(), libsSave.end(), libsSave );
750  return false;
751  }
752 
754 
755  // Update the schematic symbol library links since the library list has changed.
756  SCH_SCREENS schematic( m_schematic->Root() );
757  schematic.UpdateSymbolLinks();
758  return true;
759 }
760 
761 
763 {
764  wxCHECK_RET( aNewSymbol, "Invalid LIB_SYMBOL pointer." );
765 
766  aNewSymbol->SetLib( m_rescue_lib.get() );
767  m_rescue_lib->AddSymbol( aNewSymbol );
768 }
769 
770 
772  SCH_SHEET_PATH* aCurrentSheet,
773  EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType ) :
774  RESCUER( aProject, aSchematic, aCurrentSheet, aGalBackEndType )
775 {
776  m_properties = std::make_unique<PROPERTIES>();
777 }
778 
779 
781 {
783 }
784 
785 
786 void SYMBOL_LIB_TABLE_RESCUER::InvokeDialog( wxWindow* aParent, bool aAskShowAgain )
787 {
788  InvokeDialogRescueEach( aParent, static_cast< RESCUER& >( *this ), m_currentSheet,
789  m_galBackEndType, aAskShowAgain );
790 }
791 
792 
794 {
795  m_pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
796  (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
797 }
798 
799 
801 {
802  wxString msg;
803  wxFileName fn = GetRescueLibraryFileName( m_schematic );
804 
805  // If the rescue library already exists in the symbol library table no need save it to add
806  // it to the table.
807  if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) )
808  {
809  try
810  {
811  m_pi->SaveLibrary( fn.GetFullPath() );
812  }
813  catch( const IO_ERROR& ioe )
814  {
815  msg.Printf( _( "Failed to save rescue library %s." ), fn.GetFullPath() );
816  DisplayErrorMessage( aParent, msg, ioe.What() );
817  return false;
818  }
819 
820  wxString uri = "${KIPRJMOD}/" + fn.GetFullName();
821  wxString libNickname = fn.GetName();
822 
823  // Spaces in the file name will break the symbol name because they are not
824  // quoted in the symbol library file format.
825  libNickname.Replace( " ", "-" );
826 
827  SYMBOL_LIB_TABLE_ROW* row = new SYMBOL_LIB_TABLE_ROW( libNickname, uri,
828  wxString( "Legacy" ) );
829  m_prj->SchSymbolLibTable()->InsertRow( row );
830 
832 
833  try
834  {
835  m_prj->SchSymbolLibTable()->Save( fn.GetFullPath() );
836  }
837  catch( const IO_ERROR& ioe )
838  {
839  msg.Printf( _( "Error occurred saving project specific symbol library table." ) );
840  DisplayErrorMessage( aParent, msg, ioe.What() );
841  return false;
842  }
843  }
844 
845  // Relaod the symbol library table.
847 
848  // This can only happen if the symbol library table file was corrupted on write.
849  if( !m_prj->SchSymbolLibTable() )
850  return false;
851 
852  // Update the schematic symbol library links since the library list has changed.
853  SCH_SCREENS schematic( m_schematic->Root() );
854  schematic.UpdateSymbolLinks();
855  return true;
856 }
857 
858 
860 {
861  wxCHECK_RET( aNewSymbol, "Invalid LIB_SYMBOL pointer." );
862 
863  wxFileName fn = GetRescueLibraryFileName( m_schematic );
864 
865  try
866  {
867  if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) )
868  m_pi->SaveSymbol( fn.GetFullPath(), new LIB_SYMBOL( *aNewSymbol ), m_properties.get() );
869  else
870  m_prj->SchSymbolLibTable()->SaveSymbol( fn.GetName(), new LIB_SYMBOL( *aNewSymbol ) );
871  }
872  catch( ... /* IO_ERROR */ )
873  {
874  }
875 }
virtual void InvokeDialog(wxWindow *aParent, bool aAskShowAgain) override
Display a dialog to allow the user to select rescues.
static bool sort_by_libid(const SCH_SYMBOL *ref, SCH_SYMBOL *cmp)
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:279
EDA_DRAW_PANEL_GAL::GAL_TYPE m_galBackEndType
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:70
virtual void FindCandidates() override
Populate the RESCUER with all possible candidates.
static const wxString & GetSymbolLibTableFileName()
LIB_SYMBOL_REF & GetParent()
Definition: lib_symbol.h:124
virtual bool WriteRescueLibrary(wxWindow *aParent)=0
Write the rescue library.
virtual wxString GetActionDescription() const override
Get a description of the action proposed, for displaying in the UI.
SCHEMATIC * Schematic()
virtual void FindCandidates()=0
Populate the RESCUER with all possible candidates.
const UTF8 & GetLibItemName() const
Definition: lib_id.h:104
size_t GetChosenCandidateCount()
Get the number of rescue candidates chosen by the user.
Hold a record identifying a symbol library accessed by the appropriate symbol library SCH_PLUGIN obje...
virtual void AddSymbol(LIB_SYMBOL *aNewSymbol) override
Container for project specific data.
Definition: project.h:62
std::unique_ptr< SYMBOL_LIB > m_rescue_lib
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:292
PROJECT * GetPrj()
Return the #SCH_PROJECT object for access to the symbol libraries.
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition: lib_id.cpp:189
Holds all the data relating to one schematic.
Definition: schematic.h:59
RESCUER(PROJECT &aProject, SCHEMATIC *aSchematic, SCH_SHEET_PATH *aCurrentSheet, EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackeEndType)
This file is part of the common library.
Object used to load, save, search, and otherwise manipulate symbol library files.
const std::string LegacySymbolLibFileExtension
static void LibNamesAndPaths(PROJECT *aProject, bool doSave, wxString *aPaths, wxArrayString *aNames=nullptr)
Save or load the names of the currently configured symbol libraries (without paths).
static wxFileName GetRescueLibraryFileName(SCHEMATIC *aSchematic)
LIB_SYMBOL * m_cache_candidate
std::shared_ptr< LIB_SYMBOL > LIB_SYMBOL_SPTR
shared pointer to LIB_SYMBOL
Definition: lib_symbol.h:42
virtual void OpenRescueLibrary() override
virtual void InvokeDialog(wxWindow *aParent, bool aAskShowAgain)=0
Display a dialog to allow the user to select rescues.
void set(SCH_PLUGIN *aPlugin)
Definition: sch_io_mgr.h:507
void SetLib(SYMBOL_LIB *aLibrary)
Definition: lib_symbol.h:177
virtual _ELEM * GetElem(ELEM_T aIndex)
Get and set the elements for this project.
Definition: project.cpp:246
int InvokeDialogRescueEach(wxWindow *aParent, RESCUER &aRescuer, SCH_SHEET_PATH *aCurrentSheet, EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType, bool aAskShowAgain)
This dialog asks the user which rescuable, cached parts he wants to rescue.
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
LIB_SYMBOL * SchGetLibSymbol(const LIB_ID &aLibId, SYMBOL_LIB_TABLE *aLibTable, SYMBOL_LIB *aCacheLib, wxWindow *aParent, bool aShowErrorMsg)
Load symbol from symbol library table.
std::vector< SCH_SYMBOL * > m_symbols
Define a library symbol object.
Definition: lib_symbol.h:96
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:122
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
Definition: lib_symbol.cpp:331
SYMBOL_LIB_TABLE_RESCUER(PROJECT &aProject, SCHEMATIC *aSchematic, SCH_SHEET_PATH *aCurrentSheet, EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackeEndType)
std::pair< SCH_SYMBOL *, wxString > SYMBOL_NAME_PAIR
static LIB_SYMBOL * findSymbol(const wxString &aName, SYMBOL_LIBS *aLibs, bool aCached)
Search the libraries for the first symbol with a given name.
void LoadAllLibraries(PROJECT *aProject, bool aShowProgress=true)
Load all of the project's libraries into this container, which should be cleared before calling it.
static void getSymbols(SCHEMATIC *aSchematic, std::vector< SCH_SYMBOL * > &aSymbols)
Fill a vector with all of the project's symbols, to ease iterating over them.
bool PinsConflictWith(const LIB_SYMBOL &aOtherSymbol, bool aTestNums, bool aTestNames, bool aTestType, bool aTestOrientation, bool aTestLength) const
Return true if this symbol's pins do not match another symbol's pins.
Definition: lib_symbol.cpp:751
virtual void SetElem(ELEM_T aIndex, _ELEM *aElem)
Definition: project.cpp:258
boost::ptr_vector< RESCUE_CANDIDATE > m_all_candidates
void RemoveDuplicates()
Filter out duplicately named rescue candidates.
static bool RescueProject(wxWindow *aParent, RESCUER &aRescuer, bool aRunningOnDemand)
SCH_SHEET_PATH * m_currentSheet
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
static void FindRescues(RESCUER &aRescuer, boost::ptr_vector< RESCUE_CANDIDATE > &aCandidates)
Grab all possible RESCUE_CACHE_CANDIDATE objects into a vector.
LIB_ID GetLibId() const override
Definition: lib_symbol.h:135
SCH_PLUGIN::SCH_PLUGIN_RELEASER m_pi
virtual bool PerformAction(RESCUER *aRescuer) override
Perform the actual rescue action.
virtual wxString GetActionDescription() const override
Get a description of the action proposed, for displaying in the UI.
size_t GetCandidateCount()
Return the number of rescue candidates found.
static void FindRescues(RESCUER &aRescuer, boost::ptr_vector< RESCUE_CANDIDATE > &aCandidates)
Grab all possible RESCUE_SYMBOL_LIB_TABLE_CANDIDATE objects into a vector.
virtual wxString GetActionDescription() const override
Get a description of the action proposed, for displaying in the UI.
std::unique_ptr< PROPERTIES > m_properties
Library plugin properties.
Definition of file extensions used in Kicad.
PROJECT * m_prj
Definition for symbol library class.
#define _(s)
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:90
wxString GetFileName() const override
Helper to retrieve the filename from the root sheet screen.
Definition: schematic.cpp:161
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition: lib_id.cpp:108
LIB_SYMBOL * m_lib_candidate
UTF8 Format() const
Definition: lib_id.cpp:116
virtual void OpenRescueLibrary()=0
A collection of SYMBOL_LIB objects.
void GetSymbols(std::vector< LIB_SYMBOL * > &aSymbols) const
Load a vector with all the entries in this library.
std::vector< RESCUE_CANDIDATE * > m_chosen_candidates
SCH_SHEET & Root() const
Definition: schematic.h:92
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:118
virtual bool WriteRescueLibrary(wxWindow *aParent) override
Write the rescue library.
virtual void OpenRescueLibrary() override
static int HasIllegalChars(const UTF8 &aLibItemName)
Examine aLibItemName for invalid LIB_ID item name characters.
Definition: lib_id.cpp:173
Schematic symbol object.
Definition: sch_symbol.h:78
SCH_SCREEN * LastScreen()
bool DoRescues()
Perform all chosen rescue actions, logging them to be undone if necessary.
SCH_SYMBOL * symbol
SCHEMATIC * Schematic() const
Definition: sch_screen.cpp:92
std::vector< SCH_SYMBOL * > * GetSymbols()
Get the list of symbols that need rescued.
void LogRescue(SCH_SYMBOL *aSymbol, const wxString &aOldName, const wxString &aNewName)
Used by individual RESCUE_CANDIDATE objects to log a rescue for undoing.
SCHEMATIC * m_schematic
wxString wx_str() const
Definition: utf8.cpp:46
static const char * PropBuffering
The property used internally by the plugin to enable cache buffering which prevents the library file ...
virtual bool WriteRescueLibrary(wxWindow *aParent) override
Write the rescue library.
std::vector< RESCUE_LOG > m_rescue_log
wxString m_requested_name
virtual void AddSymbol(LIB_SYMBOL *aNewSymbol)=0
virtual void InvokeDialog(wxWindow *aParent, bool aAskShowAgain) override
Display a dialog to allow the user to select rescues.
virtual bool PerformAction(RESCUER *aRescuer) override
Perform the actual rescue action.
wxString old_name
virtual bool PerformAction(RESCUER *aRescuer) override
Perform the actual rescue action.
static void FindRescues(RESCUER &aRescuer, boost::ptr_vector< RESCUE_CANDIDATE > &aCandidates)
Grab all possible RESCUE_CASE_CANDIDATE objects into a vector.
void UndoRescues()
Reverse the effects of all rescues on the project.
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in the full schematic.
virtual void AddSymbol(LIB_SYMBOL *aNewSymbol) override
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
wxString new_name
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:568
virtual void FindCandidates() override
Populate the RESCUER with all possible candidates.
const LIB_ID & GetLibId() const
Definition: sch_symbol.h:147