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-2020 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 <class_library.h>
27 #include <confirm.h>
28 #include <connection_graph.h>
29 #include <invoke_sch_dialog.h>
30 #include <kiway.h>
31 #include <lib_view_frame.h>
32 #include <project_rescue.h>
33 #include <sch_component.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_COMPONENT*, wxString> COMPONENT_NAME_PAIR;
45 
46 
47 // Helper sort function, used in get_components, to sort a component list by lib_id
48 static bool sort_by_libid( const SCH_COMPONENT* ref, SCH_COMPONENT* cmp )
49 {
50  return ref->GetLibId() < cmp->GetLibId();
51 }
52 
53 
63 static void get_components( SCHEMATIC* aSchematic, std::vector<SCH_COMPONENT*>& aComponents )
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_COMPONENT_T ) )
71  aComponents.push_back( static_cast<SCH_COMPONENT*>( aItem ) );
72  }
73 
74  if( aComponents.empty() )
75  return;
76 
77  // sort aComponents by lib part. Components will be grouped by same lib part.
78  std::sort( aComponents.begin(), aComponents.end(), sort_by_libid );
79 }
80 
81 
89 static LIB_PART* find_component( const wxString& aName, PART_LIBS* aLibs, bool aCached )
90 {
91  LIB_PART *part = NULL;
92  wxString new_name = LIB_ID::FixIllegalChars( aName, LIB_ID::ID_SCH );
93 
94  for( PART_LIB& each_lib : *aLibs )
95  {
96  if( aCached && !each_lib.IsCache() )
97  continue;
98 
99  if( !aCached && each_lib.IsCache() )
100  continue;
101 
102  part = each_lib.FindPart( new_name );
103 
104  if( part )
105  break;
106  }
107 
108  return part;
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_PART* 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  // Remember the list of components is sorted by part name.
141  // So a search in libraries is made only once by group
142  LIB_PART* case_sensitive_match = nullptr;
143  std::vector<LIB_PART*> case_insensitive_matches;
144 
145  wxString last_part_name;
146 
147  for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) )
148  {
149  wxString part_name = each_component->GetLibId().GetLibItemName();
150 
151  if( last_part_name != part_name )
152  {
153  // A new part name is found (a new group starts here).
154  // Search the symbol names candidates only once for this group:
155  last_part_name = part_name;
156  case_insensitive_matches.clear();
157 
158  LIB_ID id( wxEmptyString, part_name );
159 
160  case_sensitive_match = aRescuer.GetPrj()->SchLibs()->FindLibPart( id );
161 
162  if( !case_sensitive_match )
163  // the case sensitive match failed. Try a case insensitive match
164  aRescuer.GetPrj()->SchLibs()->FindLibraryNearEntries( case_insensitive_matches,
165  part_name );
166  }
167 
168  if( case_sensitive_match || !( case_insensitive_matches.size() ) )
169  continue;
170 
171  RESCUE_CASE_CANDIDATE candidate( part_name, case_insensitive_matches[0]->GetName(),
172  case_insensitive_matches[0],
173  each_component->GetUnit(),
174  each_component->GetConvert() );
175 
176  candidate_map[part_name] = candidate;
177  }
178 
179  // Now, dump the map into aCandidates
180  for( const candidate_map_t::value_type& each_pair : candidate_map )
181  {
182  aCandidates.push_back( new RESCUE_CASE_CANDIDATE( each_pair.second ) );
183  }
184 }
185 
186 
188 {
189  wxString action;
190  action.Printf( _( "Rename to %s" ), m_new_name );
191  return action;
192 }
193 
194 
196 {
197  for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() )
198  {
199  if( each_component->GetLibId().GetLibItemName() != UTF8( m_requested_name ) )
200  continue;
201 
202  LIB_ID libId;
203 
204  libId.SetLibItemName( m_new_name, false );
205  each_component->SetLibId( libId );
206  each_component->ClearFlags();
207  aRescuer->LogRescue( each_component, m_requested_name, m_new_name );
208  }
209 
210  return true;
211 }
212 
213 
214 RESCUE_CACHE_CANDIDATE::RESCUE_CACHE_CANDIDATE( const wxString& aRequestedName,
215  const wxString& aNewName,
216  LIB_PART* aCacheCandidate,
217  LIB_PART* aLibCandidate,
218  int aUnit,
219  int aConvert )
220 {
221  m_requested_name = aRequestedName;
222  m_new_name = aNewName;
223  m_cache_candidate = aCacheCandidate;
224  m_lib_candidate = aLibCandidate;
225  m_unit = aUnit;
226  m_convert = aConvert;
227 }
228 
229 
231 {
234 }
235 
236 
238  boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
239 {
240  typedef std::map<wxString, RESCUE_CACHE_CANDIDATE> candidate_map_t;
241  candidate_map_t candidate_map;
242 
243  // Remember the list of components is sorted by part name.
244  // So a search in libraries is made only once by group
245  LIB_PART* cache_match = nullptr;
246  LIB_PART* lib_match = nullptr;
247  wxString old_part_name;
248 
249  for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) )
250  {
251  wxString part_name = each_component->GetLibId().GetLibItemName();
252 
253  if( old_part_name != part_name )
254  {
255  // A new part name is found (a new group starts here).
256  // Search the symbol names candidates only once for this group:
257  old_part_name = part_name;
258  cache_match = find_component( part_name, aRescuer.GetPrj()->SchLibs(), true );
259  lib_match = find_component( part_name, aRescuer.GetPrj()->SchLibs(), false );
260 
261  if( !cache_match && !lib_match )
262  continue;
263 
264  // Test whether there is a conflict or if the symbol can only be found in the cache
265  // and the symbol name does not have any illegal characters.
266  if( LIB_ID::HasIllegalChars( part_name, LIB_ID::ID_SCH ) == -1 )
267  {
268  if( cache_match && lib_match &&
269  !cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) )
270  continue;
271 
272  if( !cache_match && lib_match )
273  continue;
274  }
275 
276  // Check if the symbol has already been rescued.
277  wxString new_name = LIB_ID::FixIllegalChars( part_name, LIB_ID::ID_SCH );
278 
279  RESCUE_CACHE_CANDIDATE candidate( part_name, new_name, cache_match, lib_match,
280  each_component->GetUnit(),
281  each_component->GetConvert() );
282 
283  candidate_map[part_name] = candidate;
284  }
285  }
286 
287  // Now, dump the map into aCandidates
288  for( const candidate_map_t::value_type& each_pair : candidate_map )
289  {
290  aCandidates.push_back( new RESCUE_CACHE_CANDIDATE( each_pair.second ) );
291  }
292 }
293 
294 
296 {
297  wxString action;
298 
300  action.Printf( _( "Cannot rescue symbol %s which is not available in any library or "
301  "the cache." ), m_requested_name );
302  else if( m_cache_candidate && !m_lib_candidate )
303  action.Printf( _( "Rescue symbol %s found only in cache library to %s." ),
305  else
306  action.Printf( _( "Rescue modified symbol %s to %s" ),
308 
309  return action;
310 }
311 
312 
314 {
316 
317  wxCHECK_MSG( tmp, false, "Both cache and library symbols undefined." );
318 
319  std::unique_ptr<LIB_PART> new_part = tmp->Flatten();
320  new_part->SetName( m_new_name );
321  aRescuer->AddPart( new_part.get() );
322 
323  for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() )
324  {
325  if( each_component->GetLibId().GetLibItemName() != UTF8( m_requested_name ) )
326  continue;
327 
328  LIB_ID libId;
329 
330  libId.SetLibItemName( m_new_name, false );
331  each_component->SetLibId( libId );
332  each_component->ClearFlags();
333  aRescuer->LogRescue( each_component, m_requested_name, m_new_name );
334  }
335 
336  return true;
337 }
338 
339 
341  const LIB_ID& aRequestedId,
342  const LIB_ID& aNewId,
343  LIB_PART* aCacheCandidate,
344  LIB_PART* aLibCandidate,
345  int aUnit,
346  int aConvert ) : RESCUE_CANDIDATE()
347 {
348  m_requested_id = aRequestedId;
349  m_requested_name = aRequestedId.Format();
350  m_new_id = aNewId;
351  m_lib_candidate = aLibCandidate;
352  m_cache_candidate = aCacheCandidate;
353  m_unit = aUnit;
354  m_convert = aConvert;
355 }
356 
357 
359 {
362 }
363 
364 
366  RESCUER& aRescuer,
367  boost::ptr_vector<RESCUE_CANDIDATE>& aCandidates )
368 {
369  typedef std::map<LIB_ID, RESCUE_SYMBOL_LIB_TABLE_CANDIDATE> candidate_map_t;
370 
371  candidate_map_t candidate_map;
372 
373  // Remember the list of components is sorted by LIB_ID.
374  // So a search in libraries is made only once by group
375  LIB_PART* cache_match = nullptr;
376  LIB_PART* lib_match = nullptr;
377  LIB_ID old_part_id;
378 
379  for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) )
380  {
381  const LIB_ID& part_id = each_component->GetLibId();
382 
383  if( old_part_id != part_id )
384  {
385  // A new part name is found (a new group starts here).
386  // Search the symbol names candidates only once for this group:
387  old_part_id = part_id;
388 
389  // Get the library symbol from the cache library. It will be a flattened
390  // symbol by default (no inheritance).
391  cache_match = find_component( part_id.Format().wx_str(), aRescuer.GetPrj()->SchLibs(),
392  true );
393 
394  // Get the library symbol from the symbol library table.
395  lib_match = SchGetLibPart( part_id, aRescuer.GetPrj()->SchSymbolLibTable() );
396 
397  if( !cache_match && !lib_match )
398  continue;
399 
400  PART_SPTR lib_match_parent;
401 
402  // If it's a derive symbol, use the parent symbol to perform the pin test.
403  if( lib_match && lib_match->IsAlias() )
404  {
405  lib_match_parent = lib_match->GetParent().lock();
406 
407  if( !lib_match_parent )
408  {
409  lib_match = nullptr;
410  }
411  else
412  {
413  lib_match = lib_match_parent.get();
414  }
415  }
416 
417  // Test whether there is a conflict or if the symbol can only be found in the cache.
418  if( LIB_ID::HasIllegalChars( part_id.GetLibItemName(), LIB_ID::ID_SCH ) == -1 )
419  {
420  if( cache_match && lib_match &&
421  !cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) )
422  continue;
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( part_id.GetLibItemName(), LIB_ID::ID_SCH );
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 + "-" + part_id.GetLibNickname().wx_str() );
439 
440  RESCUE_SYMBOL_LIB_TABLE_CANDIDATE candidate( part_id, new_id, cache_match, lib_match,
441  each_component->GetUnit(),
442  each_component->GetConvert() );
443 
444  candidate_map[part_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  action.Printf( _( "Cannot rescue symbol %s which is not available in any library or "
462  "the cache." ), m_requested_id.GetLibItemName().wx_str() );
463  else if( m_cache_candidate && !m_lib_candidate )
464  action.Printf( _( "Rescue symbol %s found only in cache library to %s." ),
466  else
467  action.Printf( _( "Rescue modified symbol %s to %s" ),
469 
470  return action;
471 }
472 
473 
475 {
477 
478  wxCHECK_MSG( tmp, false, "Both cache and library symbols undefined." );
479 
480  std::unique_ptr<LIB_PART> new_part = tmp->Flatten();
481  new_part->SetLibId( m_new_id );
482  new_part->SetName( m_new_id.GetLibItemName() );
483  aRescuer->AddPart( new_part.get() );
484 
485  for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() )
486  {
487  if( each_component->GetLibId() != m_requested_id )
488  continue;
489 
490  each_component->SetLibId( m_new_id );
491  each_component->ClearFlags();
492  aRescuer->LogRescue( each_component, m_requested_id.Format(), m_new_id.Format() );
493  }
494 
495  return true;
496 }
497 
498 
499 RESCUER::RESCUER( PROJECT& aProject, SCHEMATIC* aSchematic, SCH_SHEET_PATH* aCurrentSheet,
500  EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType )
501 {
502  m_schematic = aSchematic ? aSchematic : aCurrentSheet->LastScreen()->Schematic();
503 
504  wxASSERT( m_schematic );
505 
506  if( m_schematic )
508 
509  m_prj = &aProject;
510  m_currentSheet = aCurrentSheet;
511  m_galBackEndType = aGalBackEndType;
512 }
513 
514 
515 void RESCUER::LogRescue( SCH_COMPONENT *aComponent, const wxString &aOldName,
516  const wxString &aNewName )
517 {
518  RESCUE_LOG logitem;
519  logitem.component = aComponent;
520  logitem.old_name = aOldName;
521  logitem.new_name = aNewName;
522  m_rescue_log.push_back( logitem );
523 }
524 
525 
527 {
528  for( RESCUE_CANDIDATE* each_candidate : m_chosen_candidates )
529  {
530  if( ! each_candidate->PerformAction( this ) )
531  return false;
532  }
533 
534  return true;
535 }
536 
537 
539 {
540  for( RESCUE_LOG& each_logitem : m_rescue_log )
541  {
542  LIB_ID libId;
543 
544  libId.SetLibItemName( each_logitem.old_name, false );
545  each_logitem.component->SetLibId( libId );
546  each_logitem.component->ClearFlags();
547  }
548 }
549 
550 
551 bool RESCUER::RescueProject( wxWindow* aParent, RESCUER& aRescuer, bool aRunningOnDemand )
552 {
553  aRescuer.FindCandidates();
554 
555  if( !aRescuer.GetCandidateCount() )
556  {
557  if( aRunningOnDemand )
558  {
559  wxMessageDialog dlg( aParent, _( "This project has nothing to rescue." ),
560  _( "Project Rescue Helper" ) );
561  dlg.ShowModal();
562  }
563 
564  return true;
565  }
566 
567  aRescuer.RemoveDuplicates();
568  aRescuer.InvokeDialog( aParent, !aRunningOnDemand );
569 
570  // If no symbols were rescued, let the user know what's going on. He might
571  // have clicked cancel by mistake, and should have some indication of that.
572  if( !aRescuer.GetChosenCandidateCount() )
573  {
574  wxMessageDialog dlg( aParent, _( "No symbols were rescued." ),
575  _( "Project Rescue Helper" ) );
576  dlg.ShowModal();
577 
578  // Set the modified flag even on Cancel. Many users seem to instinctively want to Save at
579  // this point, due to the reloading of the symbols, so we'll make the save button active.
580  return true;
581  }
582 
583  aRescuer.OpenRescueLibrary();
584 
585  if( !aRescuer.DoRescues() )
586  {
587  aRescuer.UndoRescues();
588  return false;
589  }
590 
591  aRescuer.WriteRescueLibrary( aParent );
592 
593  return true;
594 }
595 
596 
598 {
599  std::vector<wxString> names_seen;
600 
601  for( boost::ptr_vector<RESCUE_CANDIDATE>::iterator it = m_all_candidates.begin();
602  it != m_all_candidates.end(); )
603  {
604  bool seen_already = false;
605 
606  for( wxString& name_seen : names_seen )
607  {
608  if( name_seen == it->GetRequestedName() )
609  {
610  seen_already = true;
611  break;
612  }
613  }
614 
615  if( seen_already )
616  {
617  it = m_all_candidates.erase( it );
618  }
619  else
620  {
621  names_seen.push_back( it->GetRequestedName() );
622  ++it;
623  }
624  }
625 }
626 
627 
629 {
632 }
633 
634 
635 void LEGACY_RESCUER::InvokeDialog( wxWindow* aParent, bool aAskShowAgain )
636 {
637  InvokeDialogRescueEach( aParent, static_cast< RESCUER& >( *this ), m_currentSheet,
638  m_galBackEndType, aAskShowAgain );
639 }
640 
641 
643 {
644  wxFileName fn = GetRescueLibraryFileName( m_schematic );
645 
646  std::unique_ptr<PART_LIB> rescue_lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, fn.GetFullPath() ) );
647 
648  m_rescue_lib = std::move( rescue_lib );
649  m_rescue_lib->EnableBuffering();
650 
651  // If a rescue library already exists copy the contents of that library so we do not
652  // lose an previous rescues.
653  PART_LIB* rescueLib = m_prj->SchLibs()->FindLibrary( fn.GetName() );
654 
655  if( rescueLib )
656  {
657  // For items in the rescue library, aliases are the root symbol.
658  std::vector< LIB_PART* > symbols;
659 
660  rescueLib->GetParts( symbols );
661 
662  for( auto symbol : symbols )
663  {
664  // The LIB_PART copy constructor flattens derived symbols (formerly known as aliases).
665  m_rescue_lib->AddPart( new LIB_PART( *symbol, m_rescue_lib.get() ) );
666  }
667  }
668 }
669 
670 
671 bool LEGACY_RESCUER::WriteRescueLibrary( wxWindow *aParent )
672 {
673  try
674  {
675  m_rescue_lib->Save( false );
676  }
677  catch( ... /* IO_ERROR ioe */ )
678  {
679  wxString msg;
680 
681  msg.Printf( _( "Failed to create symbol library file \"%s\"" ),
682  m_rescue_lib->GetFullFileName() );
683  DisplayError( aParent, msg );
684  return false;
685  }
686 
687  wxArrayString libNames;
688  wxString libPaths;
689 
690  wxString libName = m_rescue_lib->GetName();
691  PART_LIBS *libs = dynamic_cast<PART_LIBS*>( m_prj->GetElem( PROJECT::ELEM_SCH_PART_LIBS ) );
692 
693  if( !libs )
694  {
695  libs = new PART_LIBS();
697  }
698 
699  try
700  {
701  PART_LIBS::LibNamesAndPaths( m_prj, false, &libPaths, &libNames );
702 
703  // Make sure the library is not already in the list
704  while( libNames.Index( libName ) != wxNOT_FOUND )
705  libNames.Remove( libName );
706 
707  // Add the library to the top of the list and save.
708  libNames.Insert( libName, 0 );
709  PART_LIBS::LibNamesAndPaths( m_prj, true, &libPaths, &libNames );
710  }
711  catch( const IO_ERROR& )
712  {
713  // Could not get or save the current libraries.
714  return false;
715  }
716 
717  // Save the old libraries in case there is a problem after clear(). We'll
718  // put them back in.
719  boost::ptr_vector<PART_LIB> libsSave;
720  libsSave.transfer( libsSave.end(), libs->begin(), libs->end(), *libs );
721 
723 
724  libs = new PART_LIBS();
725 
726  try
727  {
728  libs->LoadAllLibraries( m_prj );
729  }
730  catch( const PARSE_ERROR& )
731  {
732  // Some libraries were not found. There's no point in showing the error,
733  // because it was already shown. Just don't do anything.
734  }
735  catch( const IO_ERROR& )
736  {
737  // Restore the old list
738  libs->clear();
739  libs->transfer( libs->end(), libsSave.begin(), libsSave.end(), libsSave );
740  return false;
741  }
742 
744 
745  // Update the schematic symbol library links since the library list has changed.
746  SCH_SCREENS schematic( m_schematic->Root() );
747  schematic.UpdateSymbolLinks();
748  return true;
749 }
750 
751 
753 {
754  wxCHECK_RET( aNewPart, "Invalid LIB_PART pointer." );
755 
756  aNewPart->SetLib( m_rescue_lib.get() );
757  m_rescue_lib->AddPart( aNewPart );
758 }
759 
760 
762  SCH_SHEET_PATH* aCurrentSheet,
763  EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType ) :
764  RESCUER( aProject, aSchematic, aCurrentSheet, aGalBackEndType )
765 {
766  m_properties = std::make_unique<PROPERTIES>();
767 }
768 
769 
771 {
773 }
774 
775 
776 void SYMBOL_LIB_TABLE_RESCUER::InvokeDialog( wxWindow* aParent, bool aAskShowAgain )
777 {
778  InvokeDialogRescueEach( aParent, static_cast< RESCUER& >( *this ), m_currentSheet,
779  m_galBackEndType, aAskShowAgain );
780 }
781 
782 
784 {
785  m_pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
786  (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
787 }
788 
789 
791 {
792  wxString msg;
793  wxFileName fn = GetRescueLibraryFileName( m_schematic );
794 
795  // If the rescue library already exists in the symbol library table no need save it to add
796  // it to the table.
797  if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) )
798  {
799  try
800  {
801  m_pi->SaveLibrary( fn.GetFullPath() );
802  }
803  catch( const IO_ERROR& ioe )
804  {
805  msg.Printf( _( "Failed to save rescue library %s." ), fn.GetFullPath() );
806  DisplayErrorMessage( aParent, msg, ioe.What() );
807  return false;
808  }
809 
810  wxString uri = "${KIPRJMOD}/" + fn.GetFullName();
811  wxString libNickname = fn.GetName();
812 
813  // Spaces in the file name will break the symbol name because they are not
814  // quoted in the symbol library file format.
815  libNickname.Replace( " ", "-" );
816 
817  SYMBOL_LIB_TABLE_ROW* row = new SYMBOL_LIB_TABLE_ROW( libNickname, uri,
818  wxString( "Legacy" ) );
819  m_prj->SchSymbolLibTable()->InsertRow( row );
820 
822 
823  try
824  {
825  m_prj->SchSymbolLibTable()->Save( fn.GetFullPath() );
826  }
827  catch( const IO_ERROR& ioe )
828  {
829  msg.Printf( _( "Error occurred saving project specific symbol library table." ) );
830  DisplayErrorMessage( aParent, msg, ioe.What() );
831  return false;
832  }
833  }
834 
835  // Relaod the symbol library table.
837 
838  // This can only happen if the symbol library table file was currupted on write.
839  if( !m_prj->SchSymbolLibTable() )
840  return false;
841 
842  // Update the schematic symbol library links since the library list has changed.
843  SCH_SCREENS schematic( m_schematic->Root() );
844  schematic.UpdateSymbolLinks();
845  return true;
846 }
847 
848 
850 {
851  wxCHECK_RET( aNewPart, "Invalid LIB_PART pointer." );
852 
853  wxFileName fn = GetRescueLibraryFileName( m_schematic );
854 
855  try
856  {
857  if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) )
858  m_pi->SaveSymbol( fn.GetFullPath(), new LIB_PART( *aNewPart ), m_properties.get() );
859  else
860  m_prj->SchSymbolLibTable()->SaveSymbol( fn.GetName(), new LIB_PART( *aNewPart ) );
861  }
862  catch( ... /* IO_ERROR */ )
863  {
864  }
865 }
virtual void InvokeDialog(wxWindow *aParent, bool aAskShowAgain) override
Display a dialog to allow the user to select rescues.
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:239
EDA_DRAW_PANEL_GAL::GAL_TYPE m_galBackEndType
UTF8 is an 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to...
Definition: utf8.h:73
virtual void FindCandidates() override
Populate the RESCUER with all possible candidates.
void SetLib(PART_LIB *aLibrary)
static const wxString & GetSymbolLibTableFileName()
bool PinsConflictWith(LIB_PART &aOtherPart, bool aTestNums, bool aTestNames, bool aTestType, bool aTestOrientation, bool aTestLength)
Return true if this part's pins do not match another part's pins.
virtual bool WriteRescueLibrary(wxWindow *aParent)=0
Writes out 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:114
size_t GetChosenCandidateCount()
Get the number of resuce candidates chosen by the user.
Hold a record identifying a symbol library accessed by the appropriate symbol library SCH_PLUGIN obje...
static void LibNamesAndPaths(PROJECT *aProject, bool doSave, wxString *aPaths, wxArrayString *aNames=NULL)
Save or load the names of the currently configured part libraries (without paths).
LIB_ID GetLibId() const override
PROJECT holds project specific data.
Definition: project.h:61
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:252
PROJECT * GetPrj()
Return the #SCH_PROJECT object for access to the symbol libraries.
void LoadAllLibraries(PROJECT *aProject, bool aShowProgress=true)
Load all of the project's libraries into this container, which should be cleared before calling it.
Holds all the data relating to one schematic A schematic may consist of one or more sheets (and one r...
Definition: schematic.h:42
RESCUER(PROJECT &aProject, SCHEMATIC *aSchematic, SCH_SHEET_PATH *aCurrentSheet, EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackeEndType)
This file is part of the common library.
const std::string LegacySymbolLibFileExtension
static wxFileName GetRescueLibraryFileName(SCHEMATIC *aSchematic)
virtual void OpenRescueLibrary() override
static int HasIllegalChars(const UTF8 &aLibItemName, LIB_ID_TYPE aType)
Examine aLibItemName for invalid LIB_ID item name characters.
Definition: lib_id.cpp:336
static bool sort_by_libid(const SCH_COMPONENT *ref, SCH_COMPONENT *cmp)
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:496
VTBL_ENTRY _ELEM * GetElem(ELEM_T aIndex)
Typically wrapped somewhere else in a more meaningful function wrapper.
Definition: project.cpp:240
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
wxString GetFileName() const
Helper to retrieve the filename from the root sheet screen.
Definition: schematic.cpp:128
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:122
SYMBOL_LIB_TABLE_RESCUER(PROJECT &aProject, SCHEMATIC *aSchematic, SCH_SHEET_PATH *aCurrentSheet, EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackeEndType)
LIB_PART * m_lib_candidate
SCH_COMPONENT * component
VTBL_ENTRY void SetElem(ELEM_T aIndex, _ELEM *aElem)
Definition: project.cpp:252
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
#define NULL
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
static void FindRescues(RESCUER &aRescuer, boost::ptr_vector< RESCUE_CANDIDATE > &aCandidates)
Grab all possible RESCUE_CACHE_CANDIDATE objectss into a vector.
SCH_PLUGIN::SCH_PLUGIN_RELEASER m_pi
std::shared_ptr< LIB_PART > PART_SPTR
shared pointer to LIB_PART
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()
Returen 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.
PART_REF & GetParent()
std::unique_ptr< PROPERTIES > m_properties
Library plugin properties.
Define a library symbol object.
Definition of file extensions used in Kicad.
PROJECT * m_prj
std::pair< SCH_COMPONENT *, wxString > COMPONENT_NAME_PAIR
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:97
std::vector< SCH_COMPONENT * > m_components
static LIB_PART * find_component(const wxString &aName, PART_LIBS *aLibs, bool aCached)
Search the libraries for the first component with a given name.
SCH_SHEET_PATH.
std::unique_ptr< LIB_PART > Flatten() const
Return a flattened symbol inheritance to the caller.
UTF8 Format() const
Definition: lib_id.cpp:237
int SetLibItemName(const UTF8 &aLibItemName, bool aTestForRev=true)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition: lib_id.cpp:206
virtual void OpenRescueLibrary()=0
void GetParts(std::vector< LIB_PART * > &aPart) const
Load a vector with all the entries in this library.
A collection of PART_LIB objects.
std::vector< RESCUE_CANDIDATE * > m_chosen_candidates
SCH_SHEET & Root() const
Definition: schematic.h:97
Struct PARSE_ERROR contains a filename or source description, a problem input line,...
Definition: ki_exception.h:123
virtual bool WriteRescueLibrary(wxWindow *aParent) override
Writes out the rescue library.
std::unique_ptr< PART_LIB > m_rescue_lib
virtual void OpenRescueLibrary() override
SCH_SCREEN * LastScreen()
Function LastScreen.
bool DoRescues()
Perform all chosen rescue actions, logging them to be undone if necessary.
#define _(s)
Definition: 3d_actions.cpp:33
SCHEMATIC * Schematic() const
Definition: sch_screen.cpp:93
SCHEMATIC * m_schematic
wxString wx_str() const
Definition: utf8.cpp:51
Schematic symbol object.
Definition: sch_component.h:88
virtual void AddPart(LIB_PART *aNewPart)=0
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
Writes out the rescue library.
std::vector< SCH_COMPONENT * > * GetComponents()
Get the list of symbols that need rescued.
std::vector< RESCUE_LOG > m_rescue_log
void LogRescue(SCH_COMPONENT *aComponent, const wxString &aOldName, const wxString &aNewName)
Used by individual RESCUE_CANDIDATE objects to log a rescue for undoing.
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, LIB_ID_TYPE aType, bool aLib=false)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition: lib_id.cpp:352
wxString m_requested_name
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.
virtual void AddPart(LIB_PART *aNewPart) override
wxString old_name
LIB_PART * SchGetLibPart(const LIB_ID &aLibId, SYMBOL_LIB_TABLE *aLibTable, PART_LIB *aCacheLib, wxWindow *aParent, bool aShowErrorMsg)
Load symbol from symbol library table.
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.
Definition for part library class.
virtual void AddPart(LIB_PART *aNewPart) override
void UndoRescues()
Reverse the effects of all rescues on the project.
void clear()
Definition: utf8.h:113
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_PART reference for each SCH_COMPONENT found in the full schematic.
const LIB_ID & GetLibId() const
Object used to load, save, search, and otherwise manipulate symbol library files.
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
wxString new_name
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:550
static void get_components(SCHEMATIC *aSchematic, std::vector< SCH_COMPONENT * > &aComponents)
Fill a vector with all of the project's symbols, to ease iterating over them.
virtual void FindCandidates() override
Populate the RESCUER with all possible candidates.