diff --git a/common/dialogs/eda_list_dialog.cpp b/common/dialogs/eda_list_dialog.cpp index 6dcfb09773..3f5415b3b3 100644 --- a/common/dialogs/eda_list_dialog.cpp +++ b/common/dialogs/eda_list_dialog.cpp @@ -63,17 +63,9 @@ EDA_LIST_DIALOG::EDA_LIST_DIALOG( wxWindow* aParent, const wxString& aTitle, // columns, different column names, and column widths. m_hash_key = TO_UTF8( aTitle ); - if( !aExtraCheckboxes.empty() ) + for( const auto& [label, valuePtr] : aExtraCheckboxes ) { - m_ExtrasSizer->AddSpacer( 5 ); - - for( const auto& [label, valuePtr] : aExtraCheckboxes ) - { - wxCheckBox* cb = new wxCheckBox( this, wxID_ANY, label ); - cb->SetValue( *valuePtr ); - m_ExtrasSizer->Add( cb, 0, wxBOTTOM, 5 ); - m_extraCheckboxMap[cb] = valuePtr; - } + AddExtraCheckbox( label, valuePtr ); } SetupStandardButtons(); @@ -93,6 +85,20 @@ EDA_LIST_DIALOG::EDA_LIST_DIALOG( wxWindow* aParent, const wxString& aTitle, boo } +void EDA_LIST_DIALOG::AddExtraCheckbox( const wxString& aLabel, bool* aValuePtr ) +{ + if( m_extraCheckboxMap.size() == 0 ) + { + m_ExtrasSizer->AddSpacer( 5 ); + } + + wxCheckBox* cb = new wxCheckBox( this, wxID_ANY, aLabel ); + cb->SetValue( *aValuePtr ); + m_ExtrasSizer->Add( cb, 0, wxBOTTOM, 5 ); + m_extraCheckboxMap[cb] = aValuePtr; +} + + bool EDA_LIST_DIALOG::Show( bool show ) { bool retVal = DIALOG_SHIM::Show( show ); diff --git a/eeschema/symbol_editor/symbol_editor.cpp b/eeschema/symbol_editor/symbol_editor.cpp index 6147cb875a..d9306861f0 100644 --- a/eeschema/symbol_editor/symbol_editor.cpp +++ b/eeschema/symbol_editor/symbol_editor.cpp @@ -481,15 +481,23 @@ void SYMBOL_EDIT_FRAME::SaveSymbolCopyAs( bool aOpenCopy ) * with the "leaf" symbol at the start and the "rootiest" symbol at the end. * * If the symbol is not an alias, the list will contain only the symbol itself. + * + * If aIncludeLeaf is false, the leaf symbol (the one that was actually named) + * is not included in the list, so the list may be empty if the symbol is not derived. */ -static std::vector GetParentChain( const LIB_SYMBOL& aSymbol ) +static std::vector GetParentChain( const LIB_SYMBOL& aSymbol, bool aIncludeLeaf = true ) { - std::vector chain( { aSymbol.SharedPtr() } ); + std::vector chain; + LIB_SYMBOL_SPTR sym = aSymbol.SharedPtr(); - while( chain.back()->IsDerived() ) + if( aIncludeLeaf ) + chain.push_back( sym ); + + while( sym->IsDerived() ) { - LIB_SYMBOL_SPTR parent = chain.back()->GetParent().lock(); + LIB_SYMBOL_SPTR parent = sym->GetParent().lock(); chain.push_back( parent ); + sym = parent; } return chain; @@ -524,7 +532,7 @@ static std::pair CheckSavingIntoOwnInheritance( LIB_SYMBOL_LIBRARY_M bool inDescendents = false; { - const std::vector parentChainFromUs = GetParentChain( aSymbol ); + const std::vector parentChainFromUs = GetParentChain( aSymbol, true ); // Ignore the leaf symbol (0) - that must match for( size_t i = 1; i < parentChainFromUs.size(); ++i ) @@ -540,7 +548,7 @@ static std::pair CheckSavingIntoOwnInheritance( LIB_SYMBOL_LIBRARY_M { LIB_SYMBOL* targetSymbol = aLibMgr.GetSymbol( aNewSymbolName, aNewLibraryName ); - const std::vector parentChainFromTarget = GetParentChain( *targetSymbol ); + const std::vector parentChainFromTarget = GetParentChain( *targetSymbol, true ); const wxString oldSymbolName = aSymbol.GetName(); // Ignore the leaf symbol - it'll match if we're saving the symbol @@ -568,22 +576,24 @@ static std::pair CheckSavingIntoOwnInheritance( LIB_SYMBOL_LIBRARY_M */ static std::vector CheckForParentalChainConflicts( LIB_SYMBOL_LIBRARY_MANAGER& aLibMgr, LIB_SYMBOL& aSymbol, + bool aFlattenSymbol, const wxString& newSymbolName, const wxString& newLibraryName ) { std::vector conflicts; const wxString& oldLibraryName = aSymbol.GetLibId().GetLibNickname(); - if( newLibraryName == oldLibraryName ) + if( newLibraryName == oldLibraryName || aFlattenSymbol ) { // Saving into the same library - the only conflict could be the symbol itself + // Different library and flattening - ditto if( aLibMgr.SymbolNameInUse( newSymbolName, newLibraryName ) ) conflicts.push_back( newSymbolName ); } else { - // In a different library, check the whole chain - const std::vector parentChain = GetParentChain( aSymbol ); + // In a different library with parents - check the whole chain + const std::vector parentChain = GetParentChain( aSymbol, true ); for( size_t i = 0; i < parentChain.size(); ++i ) { @@ -627,23 +637,40 @@ public: // PROMPT }; - SYMBOL_SAVE_AS_HANDLER( LIB_SYMBOL_LIBRARY_MANAGER& aLibMgr, CONFLICT_STRATEGY aStrategy, - bool aValueFollowsName ) : + SYMBOL_SAVE_AS_HANDLER( LIB_SYMBOL_LIBRARY_MANAGER& aLibMgr, CONFLICT_STRATEGY aStrategy, bool aValueFollowsName ) : m_libMgr( aLibMgr ), m_strategy( aStrategy ), m_valueFollowsName( aValueFollowsName ) { } - bool DoSave( LIB_SYMBOL& symbol, const wxString& aNewSymName, const wxString& aNewLibName ) + bool DoSave( LIB_SYMBOL& symbol, const wxString& aNewSymName, const wxString& aNewLibName, bool aFlattenSymbol ) { + std::unique_ptr flattenedSymbol; // for ownership std::vector parentChain; - // If we're saving into the same library, we don't need to check the parental chain - // because we can just keep the same parent symbol - if( aNewLibName == symbol.GetLibId().GetLibNickname().wx_str() ) + + const bool sameLib = aNewLibName == symbol.GetLibId().GetLibNickname().wx_str(); + + if( !sameLib && aFlattenSymbol ) + { + // If we're not copying parent symbols, we need to flatten the symbol + // and only save that. + flattenedSymbol = symbol.Flatten(); + wxCHECK( flattenedSymbol, false ); + + parentChain.push_back( flattenedSymbol->SharedPtr() ); + } + else if( sameLib ) + { + // If we're saving into the same library, we don't need to check the parental chain + // because we can just keep the same parent symbol parentChain.push_back( symbol.SharedPtr() ); + } else - parentChain = GetParentChain( symbol ); + { + // Need to copy all parent symbols + parentChain = GetParentChain( symbol, true ); + } std::vector newNames; @@ -746,20 +773,39 @@ class SAVE_SYMBOL_AS_DIALOG : public EDA_LIST_DIALOG public: using SymLibNameValidator = std::function; - SAVE_SYMBOL_AS_DIALOG( SYMBOL_EDIT_FRAME* aParent, const wxString& aSymbolName, - const wxString& aLibraryPreselect, SymLibNameValidator aValidator, - SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY& aConflictStrategy ) : + struct PARAMS + { + wxString m_SymbolName; + wxString m_LibraryName; + bool m_FlattenSymbol; + SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY m_ConflictStrategy; + }; + + SAVE_SYMBOL_AS_DIALOG( SYMBOL_EDIT_FRAME* aParent, + PARAMS& aParams, + SymLibNameValidator aValidator, + const std::vector& aParentSymbolNames ) : EDA_LIST_DIALOG( aParent, _( "Save Symbol As" ), false ), m_validator( std::move( aValidator ) ), - m_conflictStrategy( aConflictStrategy ) + m_params( aParams ) { SYMBOL_LIB_TABLE* tbl = PROJECT_SCH::SchSymbolLibTable( &Prj() ); std::vector libNicknames = tbl->GetLogicalLibs(); wxArrayString headers; std::vector itemsToDisplay; + if( aParentSymbolNames.size() ) + { + // This is a little trick to word - when saving to another library, "copy parents" makes sense, + // but when in the same library, the parents will be untouched in any case. + const wxString aParentNames = AccumulateDescriptions( aParentSymbolNames ); + AddExtraCheckbox( + wxString::Format( "Flatten/remove symbol inheritance (current parent symbols: %s)", aParentNames ), + &m_params.m_FlattenSymbol ); + } + aParent->GetLibraryItemsForListDialog( headers, itemsToDisplay ); - initDialog( headers, itemsToDisplay, aLibraryPreselect ); + initDialog( headers, itemsToDisplay, m_params.m_LibraryName ); SetListLabel( _( "Save in library:" ) ); SetOKLabel( _( "Save" ) ); @@ -769,7 +815,7 @@ public: wxStaticText* label = new wxStaticText( this, wxID_ANY, _( "Name:" ) ); bNameSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_symbolNameCtrl = new wxTextCtrl( this, wxID_ANY, UnescapeString( aSymbolName ) ); + m_symbolNameCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString ); bNameSizer->Add( m_symbolNameCtrl, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); wxButton* newLibraryButton = new wxButton( this, ID_MAKE_NEW_LIBRARY, _( "New Library..." ) ); @@ -798,7 +844,8 @@ public: Centre(); } - wxString GetSymbolName() +protected: + wxString getSymbolName() const { wxString symbolName = m_symbolNameCtrl->GetValue(); symbolName.Trim( true ); @@ -807,26 +854,38 @@ public: return EscapeString( symbolName, CTX_LIBID ); } -protected: + bool TransferDataToWindow() override + { + m_symbolNameCtrl->SetValue( UnescapeString( m_params.m_SymbolName ) ); + return true; + } + bool TransferDataFromWindow() override { - int ret = m_validator( GetTextSelection(), GetSymbolName() ); + // This updates m_params.m_FlattenSymbol + // Do this now, so the validator can use it + GetExtraCheckboxValues(); + + m_params.m_SymbolName = getSymbolName(); + m_params.m_LibraryName = GetTextSelection(); + + int ret = m_validator( m_params.m_LibraryName, m_params.m_SymbolName ); if( ret == wxID_CANCEL ) return false; if( ret == ID_OVERWRITE_CONFLICTS ) - m_conflictStrategy = SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY::OVERWRITE; + m_params.m_ConflictStrategy = SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY::OVERWRITE; else if( ret == ID_RENAME_CONFLICTS ) - m_conflictStrategy = SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY::RENAME; + m_params.m_ConflictStrategy = SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY::RENAME; return true; } private: - wxTextCtrl* m_symbolNameCtrl; - SymLibNameValidator m_validator; - SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY& m_conflictStrategy; + wxTextCtrl* m_symbolNameCtrl; + SymLibNameValidator m_validator; + PARAMS& m_params; }; @@ -843,6 +902,7 @@ void SYMBOL_EDIT_FRAME::saveSymbolCopyAs( bool aOpenCopy ) bool valueFollowsName = symbol->GetValueField().GetText() == symbolName; wxString msg; bool done = false; + bool flattenSymbol = false; // This is the function that will be called when the user clicks OK in the dialog and checks // if the proposed name has problems, and asks for clarification. @@ -898,8 +958,8 @@ void SYMBOL_EDIT_FRAME::saveSymbolCopyAs( bool aOpenCopy ) return wxID_CANCEL; } - const std::vector conflicts = CheckForParentalChainConflicts( *m_libMgr, *symbol, - newName, newLib ); + const std::vector conflicts = + CheckForParentalChainConflicts( *m_libMgr, *symbol, flattenSymbol, newName, newLib ); if( conflicts.size() == 1 && conflicts.front() == newName ) { @@ -946,10 +1006,27 @@ void SYMBOL_EDIT_FRAME::saveSymbolCopyAs( bool aOpenCopy ) auto strategy = SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY::OVERWRITE; + std::vector parentSymbolNames; + if( symbol->IsDerived() ) + { + // The parents are everything but the leaf symbol + std::vector> parentChain = GetParentChain( *symbol, false ); + + for( const auto& parent : parentChain ) + parentSymbolNames.push_back( parent->GetName() ); + } + + SAVE_SYMBOL_AS_DIALOG::PARAMS params{ + symbolName, + libraryName, + flattenSymbol, + strategy, + }; + // Keep asking the user for a new name until they give a valid one or cancel the operation while( !done ) { - SAVE_SYMBOL_AS_DIALOG dlg( this, symbolName, libraryName, dialogValidatorFunc, strategy ); + SAVE_SYMBOL_AS_DIALOG dlg( this, params, dialogValidatorFunc, parentSymbolNames ); int ret = dlg.ShowModal(); @@ -961,19 +1038,16 @@ void SYMBOL_EDIT_FRAME::saveSymbolCopyAs( bool aOpenCopy ) case wxID_OK: // No conflicts case ID_OVERWRITE_CONFLICTS: case ID_RENAME_CONFLICTS: - symbolName = dlg.GetSymbolName(); - libraryName = dlg.GetTextSelection(); - - if( ret == ID_RENAME_CONFLICTS ) - strategy = SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY::RENAME; - + { done = true; break; - + } case ID_MAKE_NEW_LIBRARY: { wxFileName newLibrary( AddLibraryFile( true ) ); - libraryName = newLibrary.GetName(); + params.m_LibraryName = newLibrary.GetName(); + + // Go round again to ask for the symbol name break; } @@ -982,14 +1056,14 @@ void SYMBOL_EDIT_FRAME::saveSymbolCopyAs( bool aOpenCopy ) } } - SYMBOL_SAVE_AS_HANDLER saver( *m_libMgr, strategy, valueFollowsName ); + SYMBOL_SAVE_AS_HANDLER saver( *m_libMgr, params.m_ConflictStrategy, valueFollowsName ); - saver.DoSave( *symbol, symbolName, libraryName ); + saver.DoSave( *symbol, params.m_SymbolName, params.m_LibraryName, params.m_FlattenSymbol ); SyncLibraries( false ); if( aOpenCopy ) - LoadSymbol( symbolName, libraryName, 1 ); + LoadSymbol( params.m_SymbolName, params.m_LibraryName, 1 ); } @@ -1053,7 +1127,7 @@ void SYMBOL_EDIT_FRAME::ExportSymbol() return; } - saver.DoSave( *flattenedSymbol, symbol->GetName(), libraryName ); + saver.DoSave( *flattenedSymbol, symbol->GetName(), libraryName, false ); SyncLibraries( false ); return; diff --git a/include/eda_list_dialog.h b/include/eda_list_dialog.h index 0c72adfcf2..f21a270839 100644 --- a/include/eda_list_dialog.h +++ b/include/eda_list_dialog.h @@ -73,6 +73,11 @@ public: */ wxString GetTextSelection( int aColumn = 0 ); + /** + * Add a checkbox value to the dialog. + */ + void AddExtraCheckbox( const wxString& aLabel, bool* aValuePtr ); + /** * Fills in the value pointers from the checkboxes after the dialog has run. */