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>
38 #include <wildcards_and_files_ext.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
sort_by_libid(const SCH_SYMBOL * ref,SCH_SYMBOL * cmp)48 static bool sort_by_libid( const SCH_SYMBOL* ref, SCH_SYMBOL* cmp )
49 {
50 return ref->GetLibId() < cmp->GetLibId();
51 }
52
53
54 /**
55 * Fill a vector with all of the project's symbols, to ease iterating over them.
56 *
57 * The list is sorted by #LIB_ID, therefore symbols using the same library
58 * symbol are grouped, allowing later faster calculations (one library search by group
59 * of symbols)
60 *
61 * @param aSymbols is a vector that will take the symbols.
62 */
getSymbols(SCHEMATIC * aSchematic,std::vector<SCH_SYMBOL * > & aSymbols)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
82 /**
83 * Search the libraries for the first symbol with a given name.
84 *
85 * @param aName - name to search for
86 * @param aLibs - the loaded SYMBOL_LIBS
87 * @param aCached - whether we are looking for the cached symbol
88 */
findSymbol(const wxString & aName,SYMBOL_LIBS * aLibs,bool aCached)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
GetRescueLibraryFileName(SCHEMATIC * aSchematic)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
RESCUE_CASE_CANDIDATE(const wxString & aRequestedName,const wxString & aNewName,LIB_SYMBOL * aLibCandidate,int aUnit,int aConvert)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
FindRescues(RESCUER & aRescuer,boost::ptr_vector<RESCUE_CANDIDATE> & aCandidates)135 void RESCUE_CASE_CANDIDATE::FindRescues( RESCUER& aRescuer,
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
GetActionDescription() const191 wxString RESCUE_CASE_CANDIDATE::GetActionDescription() const
192 {
193 wxString action;
194 action.Printf( _( "Rename %s to %s" ), m_requested_name, m_new_name );
195 return action;
196 }
197
198
PerformAction(RESCUER * aRescuer)199 bool RESCUE_CASE_CANDIDATE::PerformAction( RESCUER* aRescuer )
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
RESCUE_CACHE_CANDIDATE(const wxString & aRequestedName,const wxString & aNewName,LIB_SYMBOL * aCacheCandidate,LIB_SYMBOL * aLibCandidate,int aUnit,int aConvert)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
RESCUE_CACHE_CANDIDATE()234 RESCUE_CACHE_CANDIDATE::RESCUE_CACHE_CANDIDATE()
235 {
236 m_cache_candidate = nullptr;
237 m_lib_candidate = nullptr;
238 }
239
240
FindRescues(RESCUER & aRescuer,boost::ptr_vector<RESCUE_CANDIDATE> & aCandidates)241 void RESCUE_CACHE_CANDIDATE::FindRescues( RESCUER& aRescuer,
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
GetActionDescription() const297 wxString RESCUE_CACHE_CANDIDATE::GetActionDescription() const
298 {
299 wxString action;
300
301 if( !m_cache_candidate && !m_lib_candidate )
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." ),
306 m_requested_name, m_new_name );
307 else
308 action.Printf( _( "Rescue modified symbol %s to %s" ),
309 m_requested_name, m_new_name );
310
311 return action;
312 }
313
314
PerformAction(RESCUER * aRescuer)315 bool RESCUE_CACHE_CANDIDATE::PerformAction( RESCUER* aRescuer )
316 {
317 LIB_SYMBOL* tmp = ( m_cache_candidate ) ? m_cache_candidate : m_lib_candidate;
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
RESCUE_SYMBOL_LIB_TABLE_CANDIDATE(const LIB_ID & aRequestedId,const LIB_ID & aNewId,LIB_SYMBOL * aCacheCandidate,LIB_SYMBOL * aLibCandidate,int aUnit,int aConvert)342 RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::RESCUE_SYMBOL_LIB_TABLE_CANDIDATE(
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
RESCUE_SYMBOL_LIB_TABLE_CANDIDATE()360 RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::RESCUE_SYMBOL_LIB_TABLE_CANDIDATE()
361 {
362 m_cache_candidate = nullptr;
363 m_lib_candidate = nullptr;
364 }
365
366
FindRescues(RESCUER & aRescuer,boost::ptr_vector<RESCUE_CANDIDATE> & aCandidates)367 void RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::FindRescues(
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
GetActionDescription() const456 wxString RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::GetActionDescription() const
457 {
458 wxString action;
459
460 if( !m_cache_candidate && !m_lib_candidate )
461 {
462 action.Printf( _( "Cannot rescue symbol %s which is not available in any library or "
463 "the cache." ),
464 m_requested_id.GetLibItemName().wx_str() );
465 }
466 else if( m_cache_candidate && !m_lib_candidate )
467 {
468 action.Printf( _( "Rescue symbol %s found only in cache library to %s." ),
469 m_requested_id.Format().wx_str(),
470 m_new_id.Format().wx_str() );
471 }
472 else
473 {
474 action.Printf( _( "Rescue modified symbol %s to %s" ),
475 m_requested_id.Format().wx_str(),
476 m_new_id.Format().wx_str() );
477 }
478
479 return action;
480 }
481
482
PerformAction(RESCUER * aRescuer)483 bool RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::PerformAction( RESCUER* aRescuer )
484 {
485 LIB_SYMBOL* tmp = ( m_cache_candidate ) ? m_cache_candidate : m_lib_candidate;
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
RESCUER(PROJECT & aProject,SCHEMATIC * aSchematic,SCH_SHEET_PATH * aCurrentSheet,EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType)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 )
516 getSymbols( m_schematic, m_symbols );
517
518 m_prj = &aProject;
519 m_currentSheet = aCurrentSheet;
520 m_galBackEndType = aGalBackEndType;
521 }
522
523
LogRescue(SCH_SYMBOL * aSymbol,const wxString & aOldName,const wxString & aNewName)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
DoRescues()535 bool RESCUER::DoRescues()
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
UndoRescues()547 void RESCUER::UndoRescues()
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
RescueProject(wxWindow * aParent,RESCUER & aRescuer,bool aRunningOnDemand)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
RemoveDuplicates()606 void RESCUER::RemoveDuplicates()
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
FindCandidates()637 void LEGACY_RESCUER::FindCandidates()
638 {
639 RESCUE_CASE_CANDIDATE::FindRescues( *this, m_all_candidates );
640 RESCUE_CACHE_CANDIDATE::FindRescues( *this, m_all_candidates );
641 }
642
643
InvokeDialog(wxWindow * aParent,bool aAskShowAgain)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
OpenRescueLibrary()651 void LEGACY_RESCUER::OpenRescueLibrary()
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
WriteRescueLibrary(wxWindow * aParent)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();
706 m_prj->SetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS, 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
732 m_prj->SetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS, nullptr );
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
753 m_prj->SetElem( PROJECT::ELEM_SCH_SYMBOL_LIBS, libs );
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
AddSymbol(LIB_SYMBOL * aNewSymbol)762 void LEGACY_RESCUER::AddSymbol( LIB_SYMBOL* aNewSymbol )
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
SYMBOL_LIB_TABLE_RESCUER(PROJECT & aProject,SCHEMATIC * aSchematic,SCH_SHEET_PATH * aCurrentSheet,EDA_DRAW_PANEL_GAL::GAL_TYPE aGalBackEndType)771 SYMBOL_LIB_TABLE_RESCUER::SYMBOL_LIB_TABLE_RESCUER( PROJECT& aProject, SCHEMATIC* aSchematic,
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
FindCandidates()780 void SYMBOL_LIB_TABLE_RESCUER::FindCandidates()
781 {
782 RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::FindRescues( *this, m_all_candidates );
783 }
784
785
InvokeDialog(wxWindow * aParent,bool aAskShowAgain)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
OpenRescueLibrary()793 void SYMBOL_LIB_TABLE_RESCUER::OpenRescueLibrary()
794 {
795 m_pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
796 (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
797 }
798
799
WriteRescueLibrary(wxWindow * aParent)800 bool SYMBOL_LIB_TABLE_RESCUER::WriteRescueLibrary( wxWindow *aParent )
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
831 fn = wxFileName( m_prj->GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
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.
846 m_prj->SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, nullptr );
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
AddSymbol(LIB_SYMBOL * aNewSymbol)859 void SYMBOL_LIB_TABLE_RESCUER::AddSymbol( LIB_SYMBOL* aNewSymbol )
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 }
876