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