From c1f7c1778a9fd8d473f9c41660281093a5e0e94c Mon Sep 17 00:00:00 2001 From: Wayne Stambaugh Date: Sat, 21 Oct 2017 20:48:25 -0400 Subject: [PATCH] Revise symbol rescuer to support symbol library table. Refactor rescue objects so that they can support derivation. Factor out legacy rescuer code to perform legacy project rescues. Create new symbol library table rescuer for rescuing symbol library table based projects. Perform the correct rescue type on project load. Add symbol library table remapping support to the tools menu for run on demand as applicable. Add flag to SCH_SCREENS::UpdateSymbolLinks() to allow forcing the symbol link updates when the library modification hash has not changed. --- eeschema/class_library.cpp | 3 - eeschema/class_sch_screen.h | 16 +- .../dialog_edit_component_in_schematic.cpp | 2 +- eeschema/dialogs/dialog_rescue_each.cpp | 12 +- eeschema/dialogs/dialog_symbol_remap.cpp | 2 +- eeschema/eeschema_id.h | 3 +- eeschema/files-io.cpp | 29 +- eeschema/libarch.cpp | 10 +- eeschema/load_one_schematic_file.cpp | 2 +- eeschema/menubar.cpp | 12 +- eeschema/netlist.cpp | 2 +- eeschema/project_rescue.cpp | 1110 ++++++++++------- eeschema/project_rescue.h | 233 +++- eeschema/sch_base_frame.cpp | 6 +- eeschema/sch_component.cpp | 20 +- eeschema/sch_legacy_plugin.cpp | 9 +- eeschema/sch_screen.cpp | 12 +- eeschema/schframe.cpp | 27 +- eeschema/schframe.h | 9 +- 19 files changed, 961 insertions(+), 558 deletions(-) diff --git a/eeschema/class_library.cpp b/eeschema/class_library.cpp index db6a20f7a4..b3ce6799f7 100644 --- a/eeschema/class_library.cpp +++ b/eeschema/class_library.cpp @@ -444,9 +444,6 @@ LIB_ALIAS* PART_LIBS::FindLibraryAlias( const LIB_ID& aLibId, const wxString& aL } -/* searches all libraries in the list for an entry, using a case insensitive comparison. - * Used to find an entry, when the normal (case sensitive) search fails. - */ void PART_LIBS::FindLibraryNearEntries( std::vector& aCandidates, const wxString& aEntryName, const wxString& aLibraryName ) diff --git a/eeschema/class_sch_screen.h b/eeschema/class_sch_screen.h index 5239b1235f..6cce732bba 100644 --- a/eeschema/class_sch_screen.h +++ b/eeschema/class_sch_screen.h @@ -196,15 +196,18 @@ public: void Place( SCH_EDIT_FRAME* frame, wxDC* DC ) { }; /** - * Function CheckComponentsToPartsLink - * initializes or reinitializes the weak reference - * to the LIB_PART for each SCH_COMPONENT found in m_drawList. + * Initialize or reinitialize the weak reference to the #LIB_PART for each #SCH_COMPONENT + * found in m_drawList. + * * It must be called from: * - Draw function * - when loading a schematic file * - before creating a netlist (in case a library is modified) + * - whenever a symbol library is modified + * + * @param aForce true forces a refresh even if the library modification has hasn't changed. */ - void CheckComponentsToPartsLinks(); + void UpdateSymbolLinks( bool aForce = false ); /** * Function Draw @@ -588,11 +591,12 @@ public: * found in the full schematic. * * It must be called from: - * - Draw function + * - draw functions * - when loading a schematic file * - before creating a netlist (in case a library is modified) + * - whenever any of the libraries are modified. */ - void UpdateSymbolLinks(); + void UpdateSymbolLinks( bool aForce = false ); void TestDanglingEnds(); diff --git a/eeschema/dialogs/dialog_edit_component_in_schematic.cpp b/eeschema/dialogs/dialog_edit_component_in_schematic.cpp index a500ec7add..f12b7dda10 100644 --- a/eeschema/dialogs/dialog_edit_component_in_schematic.cpp +++ b/eeschema/dialogs/dialog_edit_component_in_schematic.cpp @@ -1215,7 +1215,7 @@ void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::UpdateFields( wxCommandEvent& event ) SCH_COMPONENT copy( *m_cmp ); std::list components; components.push_back( © ); - InvokeDialogUpdateFields( m_parent, components, false ); + InvokeDialogUpdateFields( GetParent(), components, false ); // Copy fields from the modified component copy to the dialog buffer m_FieldsBuf.clear(); diff --git a/eeschema/dialogs/dialog_rescue_each.cpp b/eeschema/dialogs/dialog_rescue_each.cpp index 22271fbc9d..c2dbf940e7 100644 --- a/eeschema/dialogs/dialog_rescue_each.cpp +++ b/eeschema/dialogs/dialog_rescue_each.cpp @@ -38,9 +38,10 @@ class DIALOG_RESCUE_EACH: public DIALOG_RESCUE_EACH_BASE { public: /** - * Constructor * This dialog asks the user which rescuable, cached parts he wants to rescue. + * * Any rejects will be pruned from aCandidates. + * * @param aParent - the SCH_EDIT_FRAME calling this * @param aRescuer - the active RESCUER instance * @param aAskShowAgain - if true, a "Never Show Again" button will be included @@ -70,7 +71,7 @@ private: DIALOG_RESCUE_EACH::DIALOG_RESCUE_EACH( SCH_EDIT_FRAME* aParent, RESCUER& aRescuer, - bool aAskShowAgain ) + bool aAskShowAgain ) : DIALOG_RESCUE_EACH_BASE( aParent ), m_Parent( aParent ), m_Rescuer( &aRescuer ), @@ -81,7 +82,7 @@ DIALOG_RESCUE_EACH::DIALOG_RESCUE_EACH( SCH_EDIT_FRAME* aParent, RESCUER& aRescu // Set the info message, customized to include the proper suffix. wxString info_message = - _( "It looks like this project was made using older schematic component libraries.\n" + _( "It looks like this project was made using older schematic symbol libraries.\n" "Some parts may need to be relinked to a different symbol name, and some symbols\n" "may need to be \"rescued\" (cloned and renamed) into a new library.\n" "\n" @@ -157,6 +158,7 @@ void DIALOG_RESCUE_EACH::PopulateInstanceList() wxVector data; int count = 0; + for( SCH_COMPONENT* each_component : *m_Rescuer->GetComponents() ) { if( each_component->GetLibId().Format() != UTF8( selected_part.GetRequestedName() ) ) @@ -290,9 +292,9 @@ void DIALOG_RESCUE_EACH::OnNeverShowClick( wxCommandEvent& aEvent ) wxMessageDialog dlg( m_Parent, _( "Stop showing this tool?\n" "No changes will be made.\n\n" - "This setting can be changed from the \"Component Libraries\" dialog,\n" + "This setting can be changed from the \"Symbol Libraries\" dialog,\n" "and the tool can be activated manually from the \"Tools\" menu." ), - _( "Rescue Components" ), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION ); + _( "Rescue Symbolss" ), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION ); int resp = dlg.ShowModal (); if( resp == wxID_YES ) diff --git a/eeschema/dialogs/dialog_symbol_remap.cpp b/eeschema/dialogs/dialog_symbol_remap.cpp index b3982780d9..ce0b0a14e2 100644 --- a/eeschema/dialogs/dialog_symbol_remap.cpp +++ b/eeschema/dialogs/dialog_symbol_remap.cpp @@ -237,7 +237,7 @@ void DIALOG_SYMBOL_REMAP::remapSymbolsToLibTable( REPORTER& aReporter ) } aReporter.Report( _( "Symbol library table mapping complete!" ), REPORTER::RPT_INFO ); - schematic.UpdateSymbolLinks(); + schematic.UpdateSymbolLinks( true ); } diff --git a/eeschema/eeschema_id.h b/eeschema/eeschema_id.h index e31d0adc65..56f24bdd66 100644 --- a/eeschema/eeschema_id.h +++ b/eeschema/eeschema_id.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2008 Wayne Stambaugh + * Copyright (C) 2008 Wayne Stambaugh * Copyright (C) 2008-2017 KiCad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or @@ -65,6 +65,7 @@ enum id_eeschema_frm /* Schematic editor main menubar IDs. */ ID_RESCUE_CACHED, ID_EDIT_SYM_LIB_TABLE, + ID_REMAP_SYMBOLS, /* Schematic editor horizontal toolbar IDs */ ID_HIERARCHY, diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index bd4562b6b0..c8265f3712 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2013-2017 Wayne Stambaugh + * Copyright (C) 2013 Wayne Stambaugh * Copyright (C) 2013 CERN (www.cern.ch) * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors. * @@ -329,22 +329,16 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in UpdateFileHistory( fullFileName ); - // Check to see whether some old library parts need to be rescued - // Only do this if RescueNeverShow was not set. - wxConfigBase *config = Kiface().KifaceSettings(); - bool rescueNeverShow = false; - config->Read( RescueNeverShowEntry, &rescueNeverShow, false ); - - if( !rescueNeverShow ) - { - RescueProject( false ); - } - SCH_SCREENS schematic; // Convert old projects over to use symbol library table. if( schematic.HasNoFullyDefinedLibIds() ) { + // Ignore the never show rescue setting for one last rescue of legacy symbol + // libraries before remapping to the symbol library table. This ensures the + // best remapping results. + RescueLegacyProject( false ); + // Make backups of current schematics just in case something goes wrong. for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() ) SaveEEFile( screen, false, CREATE_BACKUP_FILE ); @@ -353,6 +347,17 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in dlgRemap.ShowQuasiModal(); } + else + { + // Check to see whether some old library parts need to be rescued + // Only do this if RescueNeverShow was not set. + wxConfigBase *config = Kiface().KifaceSettings(); + bool rescueNeverShow = false; + config->Read( RescueNeverShowEntry, &rescueNeverShow, false ); + + if( !rescueNeverShow ) + RescueSymbolLibTableProject( false ); + } schematic.UpdateSymbolLinks(); // Update all symbol library links for all sheets. GetScreen()->TestDanglingEnds(); // Only perform the dangling end test on root sheet. diff --git a/eeschema/libarch.cpp b/eeschema/libarch.cpp index f11d0decd2..3a18cbff1e 100644 --- a/eeschema/libarch.cpp +++ b/eeschema/libarch.cpp @@ -108,12 +108,12 @@ bool SCH_EDIT_FRAME::CreateArchiveLibrary( const wxString& aFileName ) DisplayError( this, msg ); return false; } + } - if( part ) - { - // AddPart() does first clone the part before adding. - archLib->AddPart( part ); - } + if( part ) + { + // AddPart() does first clone the part before adding. + archLib->AddPart( part ); } } } diff --git a/eeschema/load_one_schematic_file.cpp b/eeschema/load_one_schematic_file.cpp index bb4dc81ff2..ee11d59237 100644 --- a/eeschema/load_one_schematic_file.cpp +++ b/eeschema/load_one_schematic_file.cpp @@ -274,7 +274,7 @@ again." ); #endif // Build links between each components and its part lib LIB_PART - aScreen->CheckComponentsToPartsLinks(); + aScreen->UpdateSymbolLinks(); aScreen->TestDanglingEnds(); diff --git a/eeschema/menubar.cpp b/eeschema/menubar.cpp index c44592a4f8..4a1eed433d 100644 --- a/eeschema/menubar.cpp +++ b/eeschema/menubar.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2009-2016 Wayne Stambaugh + * Copyright (C) 2009 Wayne Stambaugh * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or @@ -489,8 +489,14 @@ void prepareToolsMenu( wxMenu* aParentMenu ) AddMenuItem( aParentMenu, ID_RESCUE_CACHED, - _( "&Rescue Old Component" ), - _( "Find old components in project and rename/rescue them" ), + _( "&Rescue Symbols" ), + _( "Find old symbols in project and rename/rescue them" ), + KiBitmap( rescue_xpm ) ); + + AddMenuItem( aParentMenu, + ID_REMAP_SYMBOLS, + _( "Remap Symbols" ), + _( "Remap legacy library symbols to symbol library table" ), KiBitmap( rescue_xpm ) ); aParentMenu->AppendSeparator(); diff --git a/eeschema/netlist.cpp b/eeschema/netlist.cpp index c39fc47cd2..6252f27c40 100644 --- a/eeschema/netlist.cpp +++ b/eeschema/netlist.cpp @@ -122,7 +122,7 @@ bool SCH_EDIT_FRAME::CreateNetlist( int aFormat, const wxString& aFullFileName, SCH_SCREENS schematic; schematic.UpdateSymbolLinks(); SCH_SHEET_LIST sheets( g_RootSheet ); - sheets.AnnotatePowerSymbols( Prj().SchLibs() ); + sheets.AnnotatePowerSymbols(); schematic.SchematicCleanUp(); } diff --git a/eeschema/project_rescue.cpp b/eeschema/project_rescue.cpp index c757cde900..dce67a4a1c 100644 --- a/eeschema/project_rescue.cpp +++ b/eeschema/project_rescue.cpp @@ -27,10 +27,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include @@ -40,117 +43,16 @@ typedef std::pair COMPONENT_NAME_PAIR; -/** - * Function save_library - * writes the library out to disk. Returns true on success. - * - * @param aLibrary - Library to write - * @param aEditFrame - the calling SCH_EDIT_FRAME - */ -static bool save_library( PART_LIB* aLibrary, SCH_EDIT_FRAME* aEditFrame ) -{ - try - { - aLibrary->Save( false ); - } - catch( ... /* IO_ERROR ioe */ ) - { - wxString msg = wxString::Format( _( "Failed to create component library file '%s'" ), - GetChars( aLibrary->GetFullFileName() ) - ); - DisplayError( aEditFrame, msg ); - return false; - } - - return true; -} - - -/** - * Function insert_library - * inserts a library into the project and refreshes libraries. - * - * @param aProject - project that will be modified - * @param aLibrary - PART_LIB to add - * @param aIndex - index in the list at which the library is to be inserted - * - * @return true on success, false on failure - */ -static bool insert_library( PROJECT *aProject, PART_LIB *aLibrary, size_t aIndex ) -{ - wxArrayString libNames; - wxString libPaths; - - wxString libName = aLibrary->GetName(); - PART_LIBS *libs = dynamic_cast( aProject->GetElem( PROJECT::ELEM_SCH_PART_LIBS ) ); - if( !libs ) - { - libs = new PART_LIBS(); - aProject->SetElem( PROJECT::ELEM_SCH_PART_LIBS, libs ); - } - - try - { - PART_LIBS::LibNamesAndPaths( aProject, false, &libPaths, &libNames ); - - // Make sure the library is not already in the list - while( libNames.Index( libName ) != wxNOT_FOUND ) - libNames.Remove( libName ); - - // Add the library to the list and save - libNames.Insert( libName, aIndex ); - PART_LIBS::LibNamesAndPaths( aProject, true, &libPaths, &libNames ); - } - catch( const IO_ERROR& ) - { - // Could not get or save the current libraries. - return false; - } - - // Save the old libraries in case there is a problem after clear(). We'll - // put them back in. - boost::ptr_vector libsSave; - libsSave.transfer( libsSave.end(), libs->begin(), libs->end(), *libs ); - - aProject->SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL ); - - libs = new PART_LIBS(); - try - { - libs->LoadAllLibraries( aProject ); - } - catch( const PARSE_ERROR& ) - { - // Some libraries were not found. There's no point in showing the error, - // because it was already shown. Just don't do anything. - } - catch( const IO_ERROR& ) - { - // Restore the old list - libs->clear(); - libs->transfer( libs->end(), libsSave.begin(), libsSave.end(), libsSave ); - return false; - } - aProject->SetElem( PROJECT::ELEM_SCH_PART_LIBS, libs ); - - // Update the schematic symbol library links since the library list has changed. - SCH_SCREENS schematic; - - schematic.UpdateSymbolLinks(); - - return true; -} - - // Helper sort function, used in get_components, to sort a component list by lib_id static bool sort_by_libid( const SCH_COMPONENT* ref, SCH_COMPONENT* cmp ) { return ref->GetLibId() < cmp->GetLibId(); } + /** - * Function get_components - * Fills a vector with all of the project's components, to ease iterating over them. + * Fill a vector with all of the project's components, to ease iterating over them. + * * The list is sorted by lib id, therefore components using the same library * symbol are grouped, allowing later faster calculations (one library search by group * of symbols) @@ -168,18 +70,21 @@ static void get_components( std::vector& aComponents ) { if( item->Type() != SCH_COMPONENT_T ) continue; + SCH_COMPONENT* component = static_cast( item ); aComponents.push_back( component ); } } + if( aComponents.empty() ) + return; + // sort aComponents by lib part. Components will be grouped by same lib part. std::sort( aComponents.begin(), aComponents.end(), sort_by_libid ); } /** - * Function find_component * Search the libraries for the first component with a given name. * * @param aName - name to search for @@ -199,6 +104,7 @@ static LIB_PART* find_component( wxString aName, PART_LIBS* aLibs, bool aCached continue; part = each_lib.FindPart( aName ); + if( part ) break; } @@ -207,14 +113,462 @@ static LIB_PART* find_component( wxString aName, PART_LIBS* aLibs, bool aCached } +static wxFileName GetRescueLibraryFileName() +{ + wxFileName fn( g_RootSheet->GetScreen()->GetFileName() ); + fn.SetName( fn.GetName() + wxT( "-rescue" ) ); + fn.SetExt( SchematicLibraryFileExtension ); + return fn; +} + + +RESCUE_CASE_CANDIDATE::RESCUE_CASE_CANDIDATE( const wxString& aRequestedName, + const wxString& aNewName, + LIB_PART* aLibCandidate ) +{ + m_requested_name = aRequestedName; + m_new_name = aNewName; + m_lib_candidate = aLibCandidate; +} + + +void RESCUE_CASE_CANDIDATE::FindRescues( RESCUER& aRescuer, + boost::ptr_vector& aCandidates ) +{ + typedef std::map candidate_map_t; + candidate_map_t candidate_map; + + // Remember the list of components is sorted by part name. + // So a search in libraries is made only once by group + LIB_ALIAS* case_sensitive_match = nullptr; + std::vector case_insensitive_matches; + + wxString last_part_name; + + for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) ) + { + wxString part_name = each_component->GetLibId().GetLibItemName(); + + if( last_part_name != part_name ) + { + // A new part name is found (a new group starts here). + // Search the symbol names candidates only once for this group: + last_part_name = part_name; + case_insensitive_matches.clear(); + + LIB_ID id( wxEmptyString, part_name ); + + case_sensitive_match = aRescuer.GetPrj()->SchLibs()->FindLibraryAlias( id ); + + if( !case_sensitive_match ) + // the case sensitive match failed. Try a case insensitive match + aRescuer.GetPrj()->SchLibs()->FindLibraryNearEntries( case_insensitive_matches, + part_name ); + } + + if( case_sensitive_match || !( case_insensitive_matches.size() ) ) + continue; + + RESCUE_CASE_CANDIDATE candidate( part_name, case_insensitive_matches[0]->GetName(), + case_insensitive_matches[0]->GetPart() ); + + candidate_map[part_name] = candidate; + } + + // Now, dump the map into aCandidates + for( const candidate_map_t::value_type& each_pair : candidate_map ) + { + aCandidates.push_back( new RESCUE_CASE_CANDIDATE( each_pair.second ) ); + } +} + + +wxString RESCUE_CASE_CANDIDATE::GetActionDescription() const +{ + wxString action; + action.Printf( _( "Rename to %s" ), m_new_name ); + return action; +} + + +bool RESCUE_CASE_CANDIDATE::PerformAction( RESCUER* aRescuer ) +{ + for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() ) + { + if( each_component->GetLibId().GetLibItemName() != UTF8( m_requested_name ) ) + continue; + + LIB_ID libId; + + libId.SetLibItemName( m_new_name, false ); + each_component->SetLibId( libId ); + each_component->ClearFlags(); + aRescuer->LogRescue( each_component, m_requested_name, m_new_name ); + } + + return true; +} + + +RESCUE_CACHE_CANDIDATE::RESCUE_CACHE_CANDIDATE( const wxString& aRequestedName, + const wxString& aNewName, + LIB_PART* aCacheCandidate, + LIB_PART* aLibCandidate ) +{ + m_requested_name = aRequestedName; + m_new_name = aNewName; + m_cache_candidate = aCacheCandidate; + m_lib_candidate = aLibCandidate; +} + + +RESCUE_CACHE_CANDIDATE::RESCUE_CACHE_CANDIDATE() +{ + m_cache_candidate = NULL; + m_lib_candidate = NULL; +} + + +void RESCUE_CACHE_CANDIDATE::FindRescues( RESCUER& aRescuer, + boost::ptr_vector& aCandidates ) +{ + typedef std::map candidate_map_t; + candidate_map_t candidate_map; + + // Remember the list of components is sorted by part name. + // So a search in libraries is made only once by group + LIB_PART* cache_match = nullptr; + LIB_PART* lib_match = nullptr; + wxString old_part_name; + + wxString part_name_suffix = aRescuer.GetPartNameSuffix(); + + for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) ) + { + wxString part_name = each_component->GetLibId().GetLibItemName(); + + if( old_part_name != part_name ) + { + // A new part name is found (a new group starts here). + // Search the symbol names candidates only once for this group: + old_part_name = part_name; + cache_match = find_component( part_name, aRescuer.GetPrj()->SchLibs(), true ); + LIB_ID id( wxEmptyString, part_name ); + lib_match = aRescuer.GetPrj()->SchLibs()->FindLibPart( id ); + + // Test whether there is a conflict + if( !cache_match || !lib_match ) + continue; + + if( !cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) ) + continue; + + // May have been rescued already. + wxString new_name = part_name; + + if( new_name.Find( part_name_suffix ) == wxNOT_FOUND ) + new_name += part_name_suffix; + + RESCUE_CACHE_CANDIDATE candidate( part_name, new_name, cache_match, lib_match ); + + candidate_map[part_name] = candidate; + } + } + + // Now, dump the map into aCandidates + for( const candidate_map_t::value_type& each_pair : candidate_map ) + { + aCandidates.push_back( new RESCUE_CACHE_CANDIDATE( each_pair.second ) ); + } +} + + +wxString RESCUE_CACHE_CANDIDATE::GetActionDescription() const +{ + wxString action; + action.Printf( _( "Rescue %s as %s" ), m_requested_name, m_new_name ); + return action; +} + + +bool RESCUE_CACHE_CANDIDATE::PerformAction( RESCUER* aRescuer ) +{ + LIB_PART new_part( *m_cache_candidate ); + new_part.SetName( m_new_name ); + new_part.RemoveAllAliases(); + aRescuer->AddPart( &new_part ); + + for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() ) + { + if( each_component->GetLibId().GetLibItemName() != UTF8( m_requested_name ) ) + continue; + + LIB_ID libId; + + libId.SetLibItemName( m_new_name, false ); + each_component->SetLibId( libId ); + each_component->ClearFlags(); + aRescuer->LogRescue( each_component, m_requested_name, m_new_name ); + } + + return true; +} + + +RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::RESCUE_SYMBOL_LIB_TABLE_CANDIDATE( + const LIB_ID& aRequestedId, + const LIB_ID& aNewId, + LIB_PART* aCacheCandidate, + LIB_PART* aLibCandidate ) : RESCUE_CANDIDATE() +{ + m_requested_id = aRequestedId; + m_requested_name = aRequestedId.GetLibItemName(); + m_new_id = aNewId; + m_lib_candidate = aLibCandidate; + m_cache_candidate = aCacheCandidate; +} + + +RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::RESCUE_SYMBOL_LIB_TABLE_CANDIDATE() +{ + m_cache_candidate = NULL; + m_lib_candidate = NULL; +} + + +void RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::FindRescues( + RESCUER& aRescuer, + boost::ptr_vector& aCandidates ) +{ + typedef std::map candidate_map_t; + + candidate_map_t candidate_map; + + // Remember the list of components is sorted by LIB_ID. + // So a search in libraries is made only once by group + LIB_PART* cache_match = nullptr; + LIB_PART* lib_match = nullptr; + LIB_ID old_part_id; + + wxString part_name_suffix = aRescuer.GetPartNameSuffix(); + + for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) ) + { + LIB_ID part_id = each_component->GetLibId(); + + if( old_part_id != part_id ) + { + // A new part name is found (a new group starts here). + // Search the symbol names candidates only once for this group: + old_part_id = part_id; + cache_match = find_component( part_id.GetLibItemName(), aRescuer.GetPrj()->SchLibs(), + true ); + + lib_match = aRescuer.GetFrame()->GetLibPart( part_id ); + + // Test whether there is a conflict + if( !cache_match || !lib_match ) + continue; + + if( !cache_match->PinsConflictWith( *lib_match, true, true, true, true, false ) ) + continue; + + // May have been rescued already. + wxString new_name = part_id.GetLibItemName(); + + if( new_name.Find( part_name_suffix ) == wxNOT_FOUND ) + new_name += part_name_suffix; + + LIB_ID new_id( GetRescueLibraryFileName().GetName(), new_name ); + + RESCUE_SYMBOL_LIB_TABLE_CANDIDATE candidate( part_id, new_id, cache_match, lib_match ); + + candidate_map[part_id] = candidate; + } + } + + // Now, dump the map into aCandidates + for( const candidate_map_t::value_type& each_pair : candidate_map ) + { + aCandidates.push_back( new RESCUE_SYMBOL_LIB_TABLE_CANDIDATE( each_pair.second ) ); + } +} + + +wxString RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::GetActionDescription() const +{ + wxString action; + action.Printf( _( "Rescue to %s" ), m_new_id.Format().wx_str() ); + return action; +} + + +bool RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::PerformAction( RESCUER* aRescuer ) +{ + LIB_PART new_part( *m_cache_candidate ); + new_part.SetLibId( m_new_id ); + new_part.SetName( m_new_id.GetLibItemName() ); + new_part.RemoveAllAliases(); + aRescuer->AddPart( &new_part ); + + for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() ) + { + if( each_component->GetLibId() != m_requested_id ) + continue; + + each_component->SetLibId( m_new_id ); + each_component->ClearFlags(); + aRescuer->LogRescue( each_component, m_requested_id.Format(), m_new_id.Format() ); + } + + return true; +} + + +RESCUER::RESCUER( SCH_EDIT_FRAME& aEditFrame, PROJECT& aProject ) +{ + get_components( m_components ); + m_prj = &aProject; + m_edit_frame = &aEditFrame; +} + + +void RESCUER::LogRescue( SCH_COMPONENT *aComponent, const wxString &aOldName, + const wxString &aNewName ) +{ + RESCUE_LOG logitem; + logitem.component = aComponent; + logitem.old_name = aOldName; + logitem.new_name = aNewName; + m_rescue_log.push_back( logitem ); +} + + +bool RESCUER::DoRescues() +{ + for( RESCUE_CANDIDATE* each_candidate : m_chosen_candidates ) + { + if( ! each_candidate->PerformAction( this ) ) + return false; + } + + return true; +} + + +void RESCUER::UndoRescues() +{ + for( RESCUE_LOG& each_logitem : m_rescue_log ) + { + LIB_ID libId; + + libId.SetLibItemName( each_logitem.old_name, false ); + each_logitem.component->SetLibId( libId ); + each_logitem.component->ClearFlags(); + } +} + + +wxString RESCUER::GetPartNameSuffix() +{ + wxString suffix = wxT( "-RESCUE-" ); + wxString pname = GetPrj()->GetProjectName(); + + for( size_t i = 0; i < pname.Len(); ++i ) + { + if( isspace( pname[i].GetValue() ) ) + suffix.Append( '_' ); + else + suffix.Append( pname[i] ); + } + + return suffix; +} + + +bool SCH_EDIT_FRAME::RescueLegacyProject( bool aRunningOnDemand ) +{ + LEGACY_RESCUER rescuer( *this, Prj() ); + + return rescueProject( rescuer, aRunningOnDemand ); +} + + +bool SCH_EDIT_FRAME::RescueSymbolLibTableProject( bool aRunningOnDemand ) +{ + SYMBOL_LIB_TABLE_RESCUER rescuer( *this, Prj() ); + + return rescueProject( rescuer, aRunningOnDemand ); +} + + +bool SCH_EDIT_FRAME::rescueProject( RESCUER& aRescuer, bool aRunningOnDemand ) +{ + aRescuer.FindCandidates(); + + if( ! aRescuer.GetCandidateCount() ) + { + if( aRunningOnDemand ) + { + wxMessageDialog dlg( this, _( "This project has nothing to rescue." ), + _( "Project Rescue Helper" ) ); + dlg.ShowModal(); + } + + return true; + } + + aRescuer.RemoveDuplicates(); + + aRescuer.InvokeDialog( !aRunningOnDemand ); + + // If no symbols were rescued, let the user know what's going on. He might + // have clicked cancel by mistake, and should have some indication of that. + if( !aRescuer.GetChosenCandidateCount() ) + { + wxMessageDialog dlg( this, _( "No symbols were rescued." ), + _( "Project Rescue Helper" ) ); + dlg.ShowModal(); + + // Set the modified flag even on Cancel. Many users seem to instinctively want to Save at + // this point, due to the reloading of the symbols, so we'll make the save button active. + OnModify(); + return true; + } + + aRescuer.OpenRescueLibrary(); + + if( !aRescuer.DoRescues() ) + { + aRescuer.UndoRescues(); + return false; + } + + aRescuer.WriteRescueLibrary( this ); + + LIB_VIEW_FRAME* viewer = (LIB_VIEW_FRAME*) Kiway().Player( FRAME_SCH_VIEWER, false ); + + if( viewer ) + viewer->ReCreateListLib(); + + // Clean up wire ends + GetScreen()->SchematicCleanUp(); + m_canvas->Refresh( true ); + OnModify(); + + return true; +} + + void RESCUER::RemoveDuplicates() { std::vector names_seen; for( boost::ptr_vector::iterator it = m_all_candidates.begin(); - it != m_all_candidates.end(); ) + it != m_all_candidates.end(); ) { bool seen_already = false; + for( wxString& name_seen : names_seen ) { if( name_seen == it->GetRequestedName() ) @@ -237,386 +591,218 @@ void RESCUER::RemoveDuplicates() } -class RESCUE_CASE_CANDIDATE: public RESCUE_CANDIDATE -{ - wxString m_requested_name; - wxString m_new_name; - LIB_PART* m_lib_candidate; - -public: - /** - * Function FindRescues - * Grab all possible RESCUE_CASE_CANDIDATES into a vector. - * @param aRescuer - the working RESCUER instance. - * @param aCandidates - the vector the will hold the candidates. - */ - static void FindRescues( RESCUER& aRescuer, boost::ptr_vector& aCandidates ) - { - typedef std::map candidate_map_t; - candidate_map_t candidate_map; - - // Remember the list of components is sorted by part name. - // So a search in libraries is made only once by group - LIB_ALIAS* case_sensitive_match = nullptr; - std::vector case_insensitive_matches; - - wxString last_part_name; - - for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) ) - { - wxString part_name = each_component->GetLibId().GetLibItemName(); - - if( last_part_name != part_name ) - { - // A new part name is found (a new group starts here). - // Search the symbol names candidates only once for this group: - last_part_name = part_name; - case_insensitive_matches.clear(); - - LIB_ID id( wxEmptyString, part_name ); - - case_sensitive_match = aRescuer.GetLibs()->FindLibraryAlias( id ); - - if( !case_sensitive_match ) - // the case sensitive match failed. Try a case insensitive match - aRescuer.GetLibs()->FindLibraryNearEntries( case_insensitive_matches, part_name ); - } - - if( case_sensitive_match || !( case_insensitive_matches.size() ) ) - continue; - - RESCUE_CASE_CANDIDATE candidate( - part_name, case_insensitive_matches[0]->GetName(), - case_insensitive_matches[0]->GetPart() ); - - candidate_map[part_name] = candidate; - } - - // Now, dump the map into aCandidates - for( const candidate_map_t::value_type& each_pair : candidate_map ) - { - aCandidates.push_back( new RESCUE_CASE_CANDIDATE( each_pair.second ) ); - } - } - - /** - * Constructor RESCUE_CANDIDATE - * @param aRequestedName - the name the schematic asks for - * @param aNewName - the name we want to change it to - * @param aLibCandidate - the part that will give us - */ - RESCUE_CASE_CANDIDATE( const wxString& aRequestedName, const wxString& aNewName, - LIB_PART* aLibCandidate ) - : m_requested_name( aRequestedName ), - m_new_name( aNewName ), - m_lib_candidate( aLibCandidate ) { } - - RESCUE_CASE_CANDIDATE() { m_lib_candidate = NULL; } - - virtual wxString GetRequestedName() const override { return m_requested_name; } - virtual wxString GetNewName() const override { return m_new_name; } - virtual LIB_PART* GetLibCandidate() const override { return m_lib_candidate; } - virtual wxString GetActionDescription() const override - { - wxString action; - action.Printf( _( "Rename to %s" ), m_new_name ); - return action; - } - - virtual bool PerformAction( RESCUER* aRescuer ) override - { - for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() ) - { - if( each_component->GetLibId().GetLibItemName() != UTF8( m_requested_name ) ) - continue; - - LIB_ID libId; - - libId.SetLibItemName( m_new_name, false ); - each_component->SetLibId( libId ); - each_component->ClearFlags(); - aRescuer->LogRescue( each_component, m_requested_name, m_new_name ); - } - return true; - } -}; - - -class RESCUE_CACHE_CANDIDATE: public RESCUE_CANDIDATE -{ - wxString m_requested_name; - wxString m_new_name; - LIB_PART* m_cache_candidate; - LIB_PART* m_lib_candidate; - - static std::unique_ptr m_rescue_lib; - -public: - /** - * Function FindRescues - * Grab all possible RESCUE_CACHE_CANDIDATEs into a vector. - * @param aRescuer - the working RESCUER instance. - * @param aCandidates - the vector the will hold the candidates. - */ - static void FindRescues( RESCUER& aRescuer, boost::ptr_vector& aCandidates ) - { - typedef std::map candidate_map_t; - candidate_map_t candidate_map; - - // Remember the list of components is sorted by part name. - // So a search in libraries is made only once by group - LIB_PART* cache_match = nullptr; - LIB_PART* lib_match = nullptr; - wxString old_part_name; - - wxString part_name_suffix = aRescuer.GetPartNameSuffix(); - - for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) ) - { - wxString part_name = each_component->GetLibId().GetLibItemName(); - - if( old_part_name != part_name ) - { - // A new part name is found (a new group starts here). - // Search the symbol names candidates only once for this group: - old_part_name = part_name; - cache_match = find_component( part_name, aRescuer.GetLibs(), /* aCached */ true ); - LIB_ID id( wxEmptyString, part_name ); - lib_match = aRescuer.GetLibs()->FindLibPart( id ); - - // Test whether there is a conflict - if( !cache_match || !lib_match ) - continue; - - if( !cache_match->PinsConflictWith( *lib_match, /* aTestNums */ true, - /* aTestNames */ true, /* aTestType */ true, /* aTestOrientation */ true, - /* aTestLength */ false )) - continue; - - RESCUE_CACHE_CANDIDATE candidate( - part_name, part_name + part_name_suffix, - cache_match, lib_match ); - - candidate_map[part_name] = candidate; - } - } - - // Now, dump the map into aCandidates - for( const candidate_map_t::value_type& each_pair : candidate_map ) - { - aCandidates.push_back( new RESCUE_CACHE_CANDIDATE( each_pair.second ) ); - } - } - - /** - * Constructor RESCUE_CACHE_CANDIDATE - * @param aRequestedName - the name the schematic asks for - * @param aNewName - the name we want to change it to - * @param aCacheCandidate - the part from the cache - * @param aLibCandidate - the part that would be loaded from the library - */ - RESCUE_CACHE_CANDIDATE( const wxString& aRequestedName, const wxString& aNewName, - LIB_PART* aCacheCandidate, LIB_PART* aLibCandidate ) - : m_requested_name( aRequestedName ), - m_new_name( aNewName ), - m_cache_candidate( aCacheCandidate ), - m_lib_candidate( aLibCandidate ) { } - - RESCUE_CACHE_CANDIDATE() - : m_cache_candidate( NULL ), m_lib_candidate( NULL ) {} - - virtual wxString GetRequestedName() const override { return m_requested_name; } - virtual wxString GetNewName() const override { return m_new_name; } - virtual LIB_PART* GetCacheCandidate() const override { return m_cache_candidate; } - virtual LIB_PART* GetLibCandidate() const override { return m_lib_candidate; } - virtual wxString GetActionDescription() const override - { - wxString action; - action.Printf( _( "Rescue %s as %s" ), m_requested_name, m_new_name ); - return action; - } - - /** - * Function OpenRescueLibrary - * Creates the new rescue library. Must be called before calling any PerformAction()s. - */ - static void OpenRescueLibrary() - { - wxFileName fn( g_RootSheet->GetScreen()->GetFileName() ); - fn.SetName( fn.GetName() + wxT( "-rescue" ) ); - fn.SetExt( SchematicLibraryFileExtension ); - - std::unique_ptr rescue_lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, - fn.GetFullPath() ) ); - - m_rescue_lib = std::move( rescue_lib ); - m_rescue_lib->EnableBuffering(); - } - - virtual bool PerformAction( RESCUER* aRescuer ) override - { - LIB_PART new_part( *m_cache_candidate, m_rescue_lib.get() ); - new_part.SetName( m_new_name ); - new_part.RemoveAllAliases(); - RESCUE_CACHE_CANDIDATE::m_rescue_lib.get()->AddPart( &new_part ); - - for( SCH_COMPONENT* each_component : *aRescuer->GetComponents() ) - { - if( each_component->GetLibId().GetLibItemName() != UTF8( m_requested_name ) ) - continue; - - LIB_ID libId; - - libId.SetLibItemName( m_new_name, false ); - each_component->SetLibId( libId ); - each_component->ClearFlags(); - aRescuer->LogRescue( each_component, m_requested_name, m_new_name ); - } - return true; - } - - /** - * Function WriteRescueLibrary - * Writes out the rescue library. Called after successful PerformAction()s. If this fails, - * undo the actions. - * @return True on success. - */ - static bool WriteRescueLibrary( SCH_EDIT_FRAME *aEditFrame, PROJECT* aProject ) - { - if( !save_library( m_rescue_lib.get(), aEditFrame ) ) - return false; - return insert_library( aProject, m_rescue_lib.get(), 0 ); - } -}; - - -std::unique_ptr RESCUE_CACHE_CANDIDATE::m_rescue_lib; - - -RESCUER::RESCUER( SCH_EDIT_FRAME& aEditFrame, PROJECT& aProject ) -{ - get_components( m_components ); - m_prj = &aProject; - m_libs = m_prj->SchLibs(); - m_edit_frame = &aEditFrame; -} - - -void RESCUER::FindCandidates() +void LEGACY_RESCUER::FindCandidates() { RESCUE_CASE_CANDIDATE::FindRescues( *this, m_all_candidates ); RESCUE_CACHE_CANDIDATE::FindRescues( *this, m_all_candidates ); } -void RESCUER::InvokeDialog( bool aAskShowAgain ) +void LEGACY_RESCUER::InvokeDialog( bool aAskShowAgain ) { - InvokeDialogRescueEach( m_edit_frame, *this, aAskShowAgain ); + InvokeDialogRescueEach( m_edit_frame, static_cast< RESCUER& >( *this ), aAskShowAgain ); } -void RESCUER::LogRescue( SCH_COMPONENT *aComponent, const wxString &aOldName, - const wxString &aNewName ) +void LEGACY_RESCUER::OpenRescueLibrary() { - RESCUE_LOG logitem; - logitem.component = aComponent; - logitem.old_name = aOldName; - logitem.new_name = aNewName; - m_rescue_log.push_back( logitem ); + wxFileName fn = GetRescueLibraryFileName(); + + std::unique_ptr rescue_lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, fn.GetFullPath() ) ); + + m_rescue_lib = std::move( rescue_lib ); + m_rescue_lib->EnableBuffering(); } -bool RESCUER::DoRescues() +bool LEGACY_RESCUER::WriteRescueLibrary( SCH_EDIT_FRAME *aEditFrame ) { - for( RESCUE_CANDIDATE* each_candidate : m_chosen_candidates ) + try { - if( ! each_candidate->PerformAction( this ) ) - return false; + m_rescue_lib->Save( false ); } - return true; -} - - -void RESCUER::UndoRescues() -{ - for( RESCUE_LOG& each_logitem : m_rescue_log ) + catch( ... /* IO_ERROR ioe */ ) { - LIB_ID libId; + wxString msg; - libId.SetLibItemName( each_logitem.old_name, false ); - each_logitem.component->SetLibId( libId ); - each_logitem.component->ClearFlags(); - } -} - - -wxString RESCUER::GetPartNameSuffix() -{ - wxString suffix = wxT( "-RESCUE-" ); - wxString pname = GetPrj()->GetProjectName(); - for( size_t i = 0; i < pname.Len(); ++i ) - { - if( isspace( pname[i].GetValue() ) ) - suffix.Append( '_' ); - else - suffix.Append( pname[i] ); - } - - return suffix; -} - - -bool SCH_EDIT_FRAME::RescueProject( bool aRunningOnDemand ) -{ - RESCUER rescuer( *this, Prj() ); - - rescuer.FindCandidates(); - - if( ! rescuer.GetCandidateCount() ) - { - if( aRunningOnDemand ) - { - wxMessageDialog dlg( this, _( "This project has nothing to rescue." ), - _( "Project Rescue Helper" ) ); - dlg.ShowModal(); - } - return true; - } - - rescuer.RemoveDuplicates(); - - rescuer.InvokeDialog( !aRunningOnDemand ); - - // If no components were rescued, let the user know what's going on. He might - // have clicked cancel by mistake, and should have some indication of that. - if( !rescuer.GetChosenCandidateCount() ) - { - wxMessageDialog dlg( this, _( "No symbols were rescued." ), - _( "Project Rescue Helper" ) ); - dlg.ShowModal(); - - // Set the modified flag even on Cancel. Many users seem to instinctively want to Save at - // this point, due to the reloading of the symbols, so we'll make the save button active. - OnModify(); - return true; - } - - RESCUE_CACHE_CANDIDATE::OpenRescueLibrary(); - - if( !rescuer.DoRescues() ) - { - rescuer.UndoRescues(); + msg.Printf( _( "Failed to create symbol library file '%s'" ), + m_rescue_lib->GetFullFileName() ); + DisplayError( aEditFrame, msg ); return false; } - RESCUE_CACHE_CANDIDATE::WriteRescueLibrary( this, &Prj() ); + wxArrayString libNames; + wxString libPaths; - Prj().SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL ); + wxString libName = m_rescue_lib->GetName(); + PART_LIBS *libs = dynamic_cast( m_prj->GetElem( PROJECT::ELEM_SCH_PART_LIBS ) ); - // Clean up wire ends - GetScreen()->SchematicCleanUp(); - m_canvas->Refresh( true ); - OnModify(); + if( !libs ) + { + libs = new PART_LIBS(); + m_prj->SetElem( PROJECT::ELEM_SCH_PART_LIBS, libs ); + } + + try + { + PART_LIBS::LibNamesAndPaths( m_prj, false, &libPaths, &libNames ); + + // Make sure the library is not already in the list + while( libNames.Index( libName ) != wxNOT_FOUND ) + libNames.Remove( libName ); + + // Add the library to the top of the list and save. + libNames.Insert( libName, 0 ); + PART_LIBS::LibNamesAndPaths( m_prj, true, &libPaths, &libNames ); + } + catch( const IO_ERROR& ) + { + // Could not get or save the current libraries. + return false; + } + + // Save the old libraries in case there is a problem after clear(). We'll + // put them back in. + boost::ptr_vector libsSave; + libsSave.transfer( libsSave.end(), libs->begin(), libs->end(), *libs ); + + m_prj->SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL ); + + libs = new PART_LIBS(); + + try + { + libs->LoadAllLibraries( m_prj ); + } + catch( const PARSE_ERROR& ) + { + // Some libraries were not found. There's no point in showing the error, + // because it was already shown. Just don't do anything. + } + catch( const IO_ERROR& ) + { + // Restore the old list + libs->clear(); + libs->transfer( libs->end(), libsSave.begin(), libsSave.end(), libsSave ); + return false; + } + + m_prj->SetElem( PROJECT::ELEM_SCH_PART_LIBS, libs ); + + // Update the schematic symbol library links since the library list has changed. + SCH_SCREENS schematic; + + schematic.UpdateSymbolLinks(); return true; } + + +void LEGACY_RESCUER::AddPart( LIB_PART* aNewPart ) +{ + wxCHECK_RET( aNewPart, "Invalid LIB_PART pointer." ); + + aNewPart->SetLib( m_rescue_lib.get() ); + m_rescue_lib->AddPart( aNewPart ); +} + + +SYMBOL_LIB_TABLE_RESCUER::SYMBOL_LIB_TABLE_RESCUER( SCH_EDIT_FRAME& aEditFrame, + PROJECT& aProject ) : + RESCUER( aEditFrame, aProject ) +{ + m_properties = std::make_unique(); +} + + +void SYMBOL_LIB_TABLE_RESCUER::FindCandidates() +{ + RESCUE_SYMBOL_LIB_TABLE_CANDIDATE::FindRescues( *this, m_all_candidates ); + RESCUE_CACHE_CANDIDATE::FindRescues( *this, m_all_candidates ); +} + + +void SYMBOL_LIB_TABLE_RESCUER::InvokeDialog( bool aAskShowAgain ) +{ + InvokeDialogRescueEach( m_edit_frame, static_cast< RESCUER& >( *this ), aAskShowAgain ); +} + + +void SYMBOL_LIB_TABLE_RESCUER::OpenRescueLibrary() +{ + m_pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) ); + (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = ""; +} + + +bool SYMBOL_LIB_TABLE_RESCUER::WriteRescueLibrary( SCH_EDIT_FRAME *aEditFrame ) +{ + wxString msg; + wxFileName fn = GetRescueLibraryFileName(); + + // If the rescue library already exists in the symbol library table no need save it to add + // it to the table. + if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) ) + { + try + { + m_pi->SaveLibrary( fn.GetFullPath() ); + } + catch( const IO_ERROR& ioe ) + { + msg.Printf( _( "Failed to save rescue library %s." ), fn.GetFullPath() ); + DisplayErrorMessage( aEditFrame, msg, ioe.What() ); + return false; + } + + wxString uri = "${KIPRJMOD}/" + fn.GetFullName(); + SYMBOL_LIB_TABLE_ROW* row = new SYMBOL_LIB_TABLE_ROW( fn.GetName(), uri, + wxString( "Legacy" ) ); + m_prj->SchSymbolLibTable()->InsertRow( row ); + + fn = wxFileName( m_prj->GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() ); + + try + { + m_prj->SchSymbolLibTable()->Save( fn.GetFullPath() ); + } + catch( const IO_ERROR& ioe ) + { + msg.Printf( _( "Error occurred saving project specific symbol library table." ) ); + DisplayErrorMessage( aEditFrame, msg, ioe.What() ); + return false; + } + } + + // Relaod the symbol library table. + m_prj->SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, NULL ); + + // This can only happen if the symbol library table file was currupted on write. + if( !m_prj->SchSymbolLibTable() ) + return false; + + // Update the schematic symbol library links since the library list has changed. + SCH_SCREENS schematic; + + schematic.UpdateSymbolLinks( true ); + return true; +} + + +void SYMBOL_LIB_TABLE_RESCUER::AddPart( LIB_PART* aNewPart ) +{ + wxCHECK_RET( aNewPart, "Invalid LIB_PART pointer." ); + + wxFileName fn = GetRescueLibraryFileName(); + + try + { + if( !m_prj->SchSymbolLibTable()->HasLibrary( fn.GetName() ) ) + m_pi->SaveSymbol( fn.GetFullPath(), new LIB_PART( *aNewPart ), m_properties.get() ); + else + m_prj->SchSymbolLibTable()->SaveSymbol( fn.GetName(), new LIB_PART( *aNewPart ) ); + } + catch( ... /* IO_ERROR */ ) + { + } +} diff --git a/eeschema/project_rescue.h b/eeschema/project_rescue.h index 4ba538f3df..0e6d575e9a 100644 --- a/eeschema/project_rescue.h +++ b/eeschema/project_rescue.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015 Chris Pavlina - * Copyright (C) 2015 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2015-2017 KiCad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -37,15 +37,22 @@ * (if aSilentIfNone is true, the notification is silenced). */ -#include - #include #include #include +#include + +#include +#include + + class LIB_PART; class SCH_COMPONENT; class RESCUER; +class SCH_EDIT_FRAME; +class SCH_LEGACY_PLUGIN; + enum RESCUE_TYPE { @@ -53,45 +60,45 @@ enum RESCUE_TYPE RESCUE_CASE, }; + class RESCUE_CANDIDATE { +protected: + wxString m_requested_name; + wxString m_new_name; + LIB_PART* m_lib_candidate; + public: virtual ~RESCUE_CANDIDATE() {} /** - * Function GetRequestedName * Get the name that was originally requested in the schematic */ - virtual wxString GetRequestedName() const = 0; + virtual wxString GetRequestedName() const { return m_requested_name; } /** - * Function GetNewName * Get the name we're proposing changing it to */ - virtual wxString GetNewName() const = 0; + virtual wxString GetNewName() const { return m_new_name; } /** - * Function GetCacheCandidate * Get the part that can be loaded from the project cache, if possible, or * else NULL. */ virtual LIB_PART* GetCacheCandidate() const { return NULL; } /** - * Function GetLibCandidate * Get the part the would be loaded from the libraries, if possible, or else * NULL. */ - virtual LIB_PART* GetLibCandidate() const { return NULL; } + virtual LIB_PART* GetLibCandidate() const { return m_lib_candidate; } /** - * Function GetActionDescription * Get a description of the action proposed, for displaying in the UI. */ virtual wxString GetActionDescription() const = 0; /** - * Function PerformAction * Perform the actual rescue action. If successful, this must log the rescue using * RESCUER::LogRescue to allow it to be reversed. * @return True on success. @@ -99,6 +106,103 @@ public: virtual bool PerformAction( RESCUER* aRescuer ) = 0; }; + +class RESCUE_CASE_CANDIDATE : public RESCUE_CANDIDATE +{ +public: + /** + * Grab all possible RESCUE_CASE_CANDIDATE objects into a vector. + * + * @param aRescuer - the working RESCUER instance. + * @param aCandidates - the vector the will hold the candidates. + */ + static void FindRescues( RESCUER& aRescuer, boost::ptr_vector& aCandidates ); + + /** + * Constructor RESCUE_CANDIDATE + * @param aRequestedName - the name the schematic asks for + * @param aNewName - the name we want to change it to + * @param aLibCandidate - the part that will give us + */ + RESCUE_CASE_CANDIDATE( const wxString& aRequestedName, const wxString& aNewName, + LIB_PART* aLibCandidate ); + + RESCUE_CASE_CANDIDATE() { m_lib_candidate = NULL; } + + virtual wxString GetActionDescription() const override; + + virtual bool PerformAction( RESCUER* aRescuer ) override; +}; + + +class RESCUE_CACHE_CANDIDATE: public RESCUE_CANDIDATE +{ + LIB_PART* m_cache_candidate; + +public: + /** + * Grab all possible #RESCUE_CACHE_CANDIDATE objectss into a vector. + * + * @param aRescuer - the working RESCUER instance. + * @param aCandidates - the vector the will hold the candidates. + */ + static void FindRescues( RESCUER& aRescuer, boost::ptr_vector& aCandidates ); + + /** + * Constructor RESCUE_CACHE_CANDIDATE + * @param aRequestedName - the name the schematic asks for + * @param aNewName - the name we want to change it to + * @param aCacheCandidate - the part from the cache + * @param aLibCandidate - the part that would be loaded from the library + */ + RESCUE_CACHE_CANDIDATE( const wxString& aRequestedName, const wxString& aNewName, + LIB_PART* aCacheCandidate, LIB_PART* aLibCandidate ); + + RESCUE_CACHE_CANDIDATE(); + + virtual LIB_PART* GetCacheCandidate() const override { return m_cache_candidate; } + + virtual wxString GetActionDescription() const override; + + virtual bool PerformAction( RESCUER* aRescuer ) override; +}; + + +class RESCUE_SYMBOL_LIB_TABLE_CANDIDATE : public RESCUE_CANDIDATE +{ + LIB_ID m_requested_id; + LIB_ID m_new_id; + LIB_PART* m_cache_candidate; + +public: + /** + * Grab all possible RESCUE_SYMBOL_LIB_TABLE_CANDIDATE objects into a vector. + * + * @param aRescuer - the working RESCUER instance. + * @param aCandidates - the vector the will hold the candidates. + */ + static void FindRescues( RESCUER& aRescuer, boost::ptr_vector& aCandidates ); + + /** + * Constructor RESCUE_CANDIDATE + * @param aRequestedName - the name the schematic asks for + * @param aNewName - the name we want to change it to + * @param aCacheCandidate - the part from the cache + * @param aLibCandidate - the part that would be loaded from the library + */ + RESCUE_SYMBOL_LIB_TABLE_CANDIDATE( const LIB_ID& aRequestedId, const LIB_ID& aNewId, + LIB_PART* aCacheCandidate, LIB_PART* aLibCandidate); + + RESCUE_SYMBOL_LIB_TABLE_CANDIDATE(); + + virtual LIB_PART* GetCacheCandidate() const override { return m_cache_candidate; } + + virtual wxString GetActionDescription() const override; + + virtual bool PerformAction( RESCUER* aRescuer ) override; +}; + + class RESCUE_LOG { public: @@ -107,12 +211,13 @@ public: wxString new_name; }; + class RESCUER { +protected: friend class DIALOG_RESCUE_EACH; std::vector m_components; - PART_LIBS* m_libs; PROJECT* m_prj; SCH_EDIT_FRAME* m_edit_frame; @@ -125,74 +230,124 @@ public: RESCUER( SCH_EDIT_FRAME& aEditFrame, PROJECT& aProject ); /** - * Function FindCandidates - * Populate the RESCUER with all possible candidates. + * Writes out the rescue library. Called after successful PerformAction()s. If this fails, + * undo the actions. + * + * @return True on success. */ - void FindCandidates(); + virtual bool WriteRescueLibrary( SCH_EDIT_FRAME *aEditFrame ) = 0; + + virtual void OpenRescueLibrary() = 0; + + /** + * Populate the RESCUER with all possible candidates. + */ + virtual void FindCandidates() = 0; + + virtual void AddPart( LIB_PART* aNewPart ) = 0; + + /** + * Display a dialog to allow the user to select rescues. + * + * @param aAskShowAgain - whether the "Never Show Again" button should be visible + */ + virtual void InvokeDialog( bool aAskShowAgain ) = 0; + + SCH_EDIT_FRAME* GetFrame() { return m_edit_frame; } /** - * Function RemoveDuplicates * Filter out duplicately named rescue candidates. */ void RemoveDuplicates(); /** - * Function GetCandidateCount + * Returen the number of rescue candidates found. */ size_t GetCandidateCount() { return m_all_candidates.size(); } /** - * Function GetChosenCandidateCount + * Get the number of resuce candidates chosen by the user. */ size_t GetChosenCandidateCount() { return m_chosen_candidates.size(); } /** - * Function GetComponents + * Get the list of symbols that need rescued. */ std::vector* GetComponents() { return &m_components; } /** - * Function GetLibs - */ - PART_LIBS* GetLibs() { return m_libs; } - - /** - * Function GetPrj + * Return the #SCH_PROJECT object for access to the symbol libraries. */ PROJECT* GetPrj() { return m_prj; } /** - * Function GetPartNameSuffix * Return the suffix to add to rescued parts. */ wxString GetPartNameSuffix(); /** - * Function InvokeDialog - * Display a dialog to allow the user to select rescues. - * @param aAskShowAgain - whether the "Never Show Again" button should be visible - */ - void InvokeDialog( bool aAskShowAgain ); - - /** - * Function LogRescue - * Used by individual RESCUE_CANDIDATEs to log a rescue for undoing. + * Used by individual #RESCUE_CANDIDATE objects to log a rescue for undoing. */ void LogRescue( SCH_COMPONENT *aComponent, const wxString& aOldName, - const wxString& aNewName ); + const wxString& aNewName ); /** - * Function DoRescues * Perform all chosen rescue actions, logging them to be undone if necessary. + * * @return True on success */ bool DoRescues(); /** - * Function UndoRescues * Reverse the effects of all rescues on the project. */ void UndoRescues(); }; + +class LEGACY_RESCUER : public RESCUER +{ +private: + std::unique_ptr m_rescue_lib; + PART_LIBS* m_libs; + +public: + LEGACY_RESCUER( SCH_EDIT_FRAME& aEditFrame, PROJECT& aProject ) : + RESCUER( aEditFrame, aProject ) + { + } + + virtual void FindCandidates() override; + + virtual void InvokeDialog( bool aAskShowAgain ) override; + + virtual void OpenRescueLibrary() override; + + virtual bool WriteRescueLibrary( SCH_EDIT_FRAME *aEditFrame ) override; + + virtual void AddPart( LIB_PART* aNewPart ) override; +}; + + +class SYMBOL_LIB_TABLE_RESCUER : public RESCUER +{ +private: + SCH_PLUGIN::SCH_PLUGIN_RELEASER m_pi; + + std::unique_ptr< PROPERTIES > m_properties; ///< Library plugin properties + +public: + SYMBOL_LIB_TABLE_RESCUER( SCH_EDIT_FRAME& aEditFrame, PROJECT& aProject ); + + virtual void FindCandidates() override; + + virtual void InvokeDialog( bool aAskShowAgain ) override; + + virtual void OpenRescueLibrary() override; + + virtual bool WriteRescueLibrary( SCH_EDIT_FRAME *aEditFrame ) override; + + virtual void AddPart( LIB_PART* aNewPart ) override; +}; + #endif // _LIB_CACHE_RESCUE_H_ diff --git a/eeschema/sch_base_frame.cpp b/eeschema/sch_base_frame.cpp index 5cc8fd33f6..b351fd63da 100644 --- a/eeschema/sch_base_frame.cpp +++ b/eeschema/sch_base_frame.cpp @@ -40,7 +40,7 @@ LIB_ALIAS* SchGetLibAlias( const LIB_ID& aLibId, SYMBOL_LIB_TABLE* aLibTable, PART_LIB* aCacheLib, wxWindow* aParent, bool aShowErrorMsg ) { - wxCHECK_MSG( aLibId.IsValid(), NULL, "LIB_ID is not valid." ); + // wxCHECK_MSG( aLibId.IsValid(), NULL, "LIB_ID is not valid." ); wxCHECK_MSG( aLibTable, NULL, "Invalid symbol library table." ); LIB_ALIAS* alias = NULL; @@ -309,7 +309,7 @@ void SCH_BASE_FRAME::OnEditSymbolLibTable( wxCommandEvent& aEvent ) LIB_ALIAS* SCH_BASE_FRAME::GetLibAlias( const LIB_ID& aLibId, bool aUseCacheLib, bool aShowErrorMsg ) { - wxCHECK_MSG( aLibId.IsValid(), NULL, "LIB_ID is not valid." ); + // wxCHECK_MSG( aLibId.IsValid(), NULL, "LIB_ID is not valid." ); PART_LIB* cache = ( aUseCacheLib ) ? Prj().SchLibs()->GetCacheLibrary() : NULL; @@ -319,7 +319,7 @@ LIB_ALIAS* SCH_BASE_FRAME::GetLibAlias( const LIB_ID& aLibId, bool aUseCacheLib, LIB_PART* SCH_BASE_FRAME::GetLibPart( const LIB_ID& aLibId, bool aUseCacheLib, bool aShowErrorMsg ) { - wxCHECK_MSG( aLibId.IsValid(), NULL, "LIB_ID is not valid." ); + // wxCHECK_MSG( aLibId.IsValid(), NULL, "LIB_ID is not valid." ); PART_LIB* cache = ( aUseCacheLib ) ? Prj().SchLibs()->GetCacheLibrary() : NULL; diff --git a/eeschema/sch_component.cpp b/eeschema/sch_component.cpp index ebf0ccd5af..d92860b8af 100644 --- a/eeschema/sch_component.cpp +++ b/eeschema/sch_component.cpp @@ -1744,6 +1744,8 @@ const EDA_RECT SCH_COMPONENT::GetBoundingBox() const void SCH_COMPONENT::GetMsgPanelInfo( MSG_PANEL_ITEMS& aList ) { + wxString msg; + // part and alias can differ if alias is not the root if( PART_SPTR part = m_part.lock() ) { @@ -1759,7 +1761,7 @@ void SCH_COMPONENT::GetMsgPanelInfo( MSG_PANEL_ITEMS& aList ) GetRef( m_currentSheetPath ), DARKCYAN ) ); - wxString msg = part->IsPower() ? _( "Power symbol" ) : _( "Value" ); + msg = part->IsPower() ? _( "Power symbol" ) : _( "Value" ); aList.push_back( MSG_PANEL_ITEM( msg, GetField( VALUE )->GetShownText(), DARKCYAN ) ); @@ -1800,8 +1802,20 @@ void SCH_COMPONENT::GetMsgPanelInfo( MSG_PANEL_ITEMS& aList ) aList.push_back( MSG_PANEL_ITEM( _( "Value" ), GetField( VALUE )->GetShownText(), DARKCYAN ) ); - aList.push_back( MSG_PANEL_ITEM( _( "Component" ), GetLibId().GetLibItemName(), BROWN ) ); - aList.push_back( MSG_PANEL_ITEM( _( "Library" ), _( "Error: symbol not found!!!" ), RED ) ); + aList.push_back( MSG_PANEL_ITEM( _( "Symbol" ), GetLibId().GetLibItemName(), BROWN ) ); + + wxString libNickname = GetLibId().GetLibNickname(); + + if( libNickname.empty() ) + { + aList.push_back( MSG_PANEL_ITEM( _( "Library" ), + _( "No library defined!!!" ), RED ) ); + } + else + { + msg.Printf( _( "Symbol not found in %s!!!" ), libNickname ); + aList.push_back( MSG_PANEL_ITEM( _( "Library" ), msg , RED ) ); + } } } diff --git a/eeschema/sch_legacy_plugin.cpp b/eeschema/sch_legacy_plugin.cpp index 4e89700bba..9680695766 100644 --- a/eeschema/sch_legacy_plugin.cpp +++ b/eeschema/sch_legacy_plugin.cpp @@ -479,12 +479,13 @@ static void parseQuotedString( wxString& aString, FILE_LINE_READER& aReader, */ class SCH_LEGACY_PLUGIN_CACHE { + static int m_modHash; // Keep track of the modification status of the library. + wxFileName m_libFileName; // Absolute path and file name is required here. wxDateTime m_fileModTime; LIB_ALIAS_MAP m_aliases; // Map of names of LIB_ALIAS pointers. bool m_isWritable; bool m_isModified; - int m_modHash; // Keep track of the modification status of the library. int m_versionMajor; int m_versionMinor; int m_libType; // Is this cache a component or symbol library. @@ -2031,11 +2032,13 @@ void SCH_LEGACY_PLUGIN::saveText( SCH_TEXT* aText ) } +int SCH_LEGACY_PLUGIN_CACHE::m_modHash = 1; // starts at 1 and goes up + + SCH_LEGACY_PLUGIN_CACHE::SCH_LEGACY_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) : m_libFileName( aFullPathAndFileName ), m_isWritable( true ), - m_isModified( false ), - m_modHash( 1 ) + m_isModified( false ) { m_versionMajor = -1; m_versionMinor = -1; diff --git a/eeschema/sch_screen.cpp b/eeschema/sch_screen.cpp index 1500a6f8c0..68f891bdfa 100644 --- a/eeschema/sch_screen.cpp +++ b/eeschema/sch_screen.cpp @@ -527,7 +527,7 @@ bool SCH_SCREEN::Save( FILE* aFile ) const } -void SCH_SCREEN::CheckComponentsToPartsLinks() +void SCH_SCREEN::UpdateSymbolLinks( bool aForce ) { // Initialize or reinitialize the pointer to the LIB_PART for each component // found in m_drawList, but only if needed (change in lib or schematic) @@ -539,7 +539,7 @@ void SCH_SCREEN::CheckComponentsToPartsLinks() int mod_hash = libs->GetModifyHash(); // Must we resolve? - if( m_modification_sync != mod_hash ) + if( (m_modification_sync != mod_hash) || aForce ) { SCH_TYPE_COLLECTOR c; @@ -561,7 +561,7 @@ void SCH_SCREEN::Draw( EDA_DRAW_PANEL* aCanvas, wxDC* aDC, GR_DRAWMODE aDrawMode */ // Ensure links are up to date, even if a library was reloaded for some reason: - CheckComponentsToPartsLinks(); + UpdateSymbolLinks(); for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) { @@ -582,7 +582,7 @@ void SCH_SCREEN::Draw( EDA_DRAW_PANEL* aCanvas, wxDC* aDC, GR_DRAWMODE aDrawMode void SCH_SCREEN::Plot( PLOTTER* aPlotter ) { // Ensure links are up to date, even if a library was reloaded for some reason: - CheckComponentsToPartsLinks(); + UpdateSymbolLinks(); for( SCH_ITEM* item = m_drawList.begin(); item; item = item->Next() ) { @@ -1508,10 +1508,10 @@ int SCH_SCREENS::GetMarkerCount( enum MARKER_BASE::TYPEMARKER aMarkerType, } -void SCH_SCREENS::UpdateSymbolLinks() +void SCH_SCREENS::UpdateSymbolLinks( bool aForce ) { for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() ) - screen->CheckComponentsToPartsLinks(); + screen->UpdateSymbolLinks( aForce ); } diff --git a/eeschema/schframe.cpp b/eeschema/schframe.cpp index 4dfb2c2577..3e442dc4eb 100644 --- a/eeschema/schframe.cpp +++ b/eeschema/schframe.cpp @@ -58,6 +58,7 @@ #include #include +#include #include #include @@ -251,6 +252,7 @@ BEGIN_EVENT_TABLE( SCH_EDIT_FRAME, EDA_DRAW_FRAME ) EVT_TOOL( ID_POPUP_SCH_CALL_LIBEDIT_AND_LOAD_CMP, SCH_EDIT_FRAME::OnOpenLibraryEditor ) EVT_TOOL( ID_TO_LIBVIEW, SCH_EDIT_FRAME::OnOpenLibraryViewer ) EVT_TOOL( ID_RESCUE_CACHED, SCH_EDIT_FRAME::OnRescueProject ) + EVT_MENU( ID_REMAP_SYMBOLS, SCH_EDIT_FRAME::OnRemapSymbols ) EVT_TOOL( ID_RUN_PCB, SCH_EDIT_FRAME::OnOpenPcbnew ) EVT_TOOL( ID_RUN_PCB_MODULE_EDITOR, SCH_EDIT_FRAME::OnOpenPcbModuleEditor ) @@ -333,6 +335,7 @@ BEGIN_EVENT_TABLE( SCH_EDIT_FRAME, EDA_DRAW_FRAME ) EVT_UPDATE_UI( ID_SAVE_PROJECT, SCH_EDIT_FRAME::OnUpdateSave ) EVT_UPDATE_UI( ID_UPDATE_ONE_SHEET, SCH_EDIT_FRAME::OnUpdateSaveSheet ) EVT_UPDATE_UI( ID_POPUP_SCH_LEAVE_SHEET, SCH_EDIT_FRAME::OnUpdateHierarchySheet ) + EVT_UPDATE_UI( ID_REMAP_SYMBOLS, SCH_EDIT_FRAME::OnUpdateRemapSymbols ) /* Search dialog events. */ EVT_FIND_CLOSE( wxID_ANY, SCH_EDIT_FRAME::OnFindDialogClose ) @@ -823,6 +826,15 @@ void SCH_EDIT_FRAME::OnUpdateSave( wxUpdateUIEvent& aEvent ) } +void SCH_EDIT_FRAME::OnUpdateRemapSymbols( wxUpdateUIEvent& aEvent ) +{ + SCH_SCREENS schematic; + + // The remapping can only be performed on legacy projects. + aEvent.Enable( schematic.HasNoFullyDefinedLibIds() ); +} + + void SCH_EDIT_FRAME::OnUpdateSaveSheet( wxUpdateUIEvent& aEvent ) { aEvent.Enable( GetScreen()->IsModify() ); @@ -1249,7 +1261,20 @@ void SCH_EDIT_FRAME::OnOpenLibraryEditor( wxCommandEvent& event ) void SCH_EDIT_FRAME::OnRescueProject( wxCommandEvent& event ) { - RescueProject( true ); + SCH_SCREENS schematic; + + if( schematic.HasNoFullyDefinedLibIds() ) + RescueLegacyProject( true ); + else + RescueSymbolLibTableProject( true ); +} + + +void SCH_EDIT_FRAME::OnRemapSymbols( wxCommandEvent& event ) +{ + DIALOG_SYMBOL_REMAP dlgRemap( this ); + + dlgRemap.ShowQuasiModal(); } diff --git a/eeschema/schframe.h b/eeschema/schframe.h index 72402e94c0..2a8322bf04 100644 --- a/eeschema/schframe.h +++ b/eeschema/schframe.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015 Jean-Pierre Charras, jp.charras wanadoo.fr - * Copyright (C) 2008-2017 Wayne Stambaugh + * Copyright (C) 2008 Wayne Stambaugh * Copyright (C) 2004-2017 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or @@ -64,6 +64,7 @@ class DIALOG_SCH_FIND; class wxFindDialogEvent; class wxFindReplaceData; class SCHLIB_FILTER; +class RESCUER; /// enum used in RotationMiroir() @@ -890,6 +891,7 @@ private: void OnOpenCvpcb( wxCommandEvent& event ); void OnOpenLibraryEditor( wxCommandEvent& event ); void OnRescueProject( wxCommandEvent& event ); + void OnRemapSymbols( wxCommandEvent& aEvent ); void OnPreferencesOptions( wxCommandEvent& event ); void OnCancelCurrentCommand( wxCommandEvent& aEvent ); @@ -910,6 +912,7 @@ private: void OnUpdateSave( wxUpdateUIEvent& aEvent ); void OnUpdateSaveSheet( wxUpdateUIEvent& aEvent ); void OnUpdateHierarchySheet( wxUpdateUIEvent& aEvent ); + void OnUpdateRemapSymbols( wxUpdateUIEvent& aEvent ); /** * Function UpdateTitle @@ -1378,7 +1381,9 @@ public: * if there are no components to rescue, and a "Never Show Again" button is * displayed. */ - bool RescueProject( bool aRunningOnDemand ); + bool rescueProject( RESCUER& aRescuer, bool aRunningOnDemand ); + bool RescueLegacyProject( bool aRunningOnDemand ); + bool RescueSymbolLibTableProject( bool aRunningOnDemand ); /** * Function PrintPage