diff --git a/common/commit.cpp b/common/commit.cpp index 281d813cee..9d78fc9fab 100644 --- a/common/commit.cpp +++ b/common/commit.cpp @@ -45,7 +45,7 @@ COMMIT::~COMMIT() } -COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ) +COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen ) { // CHT_MODIFY and CHT_DONE are not compatible wxASSERT( ( aChangeType & ( CHT_MODIFY | CHT_DONE ) ) != ( CHT_MODIFY | CHT_DONE ) ); @@ -56,11 +56,11 @@ COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ) { case CHT_ADD: wxASSERT( m_changedItems.find( aItem ) == m_changedItems.end() ); - makeEntry( aItem, CHT_ADD | flag ); + makeEntry( aItem, CHT_ADD | flag, nullptr, aScreen ); return *this; case CHT_REMOVE: - makeEntry( aItem, CHT_REMOVE | flag ); + makeEntry( aItem, CHT_REMOVE | flag, nullptr, aScreen ); return *this; case CHT_MODIFY: @@ -71,7 +71,7 @@ COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ) wxASSERT( clone ); if( clone ) - return createModified( parent, clone, flag ); + return createModified( parent, clone, flag, aScreen ); break; } @@ -84,16 +84,17 @@ COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ) } -COMMIT& COMMIT::Stage( std::vector& container, CHANGE_TYPE aChangeType ) +COMMIT& COMMIT::Stage( std::vector &container, CHANGE_TYPE aChangeType, + BASE_SCREEN *aScreen ) { for( EDA_ITEM* item : container ) - Stage( item, aChangeType ); + Stage( item, aChangeType, aScreen); return *this; } -COMMIT& COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag ) +COMMIT& COMMIT::Stage( const PICKED_ITEMS_LIST &aItems, UNDO_REDO aModFlag, BASE_SCREEN *aScreen ) { for( unsigned int i = 0; i < aItems.GetCount(); i++ ) { @@ -108,11 +109,11 @@ COMMIT& COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag ) assert( change_type == UNDO_REDO::CHANGED ); // There was already a copy created, so use it - Modified( item, copy ); + Modified( item, copy, aScreen ); } else { - Stage( item, convert( change_type ) ); + Stage( item, convert( change_type ), aScreen ); } } @@ -120,15 +121,15 @@ COMMIT& COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag ) } -int COMMIT::GetStatus( EDA_ITEM* aItem ) +int COMMIT::GetStatus( EDA_ITEM* aItem, BASE_SCREEN *aScreen ) { - COMMIT_LINE* entry = findEntry( parentObject( aItem ) ); + COMMIT_LINE* entry = findEntry( parentObject( aItem ), aScreen ); return entry ? entry->m_type : 0; } -COMMIT& COMMIT::createModified( EDA_ITEM* aItem, EDA_ITEM* aCopy, int aExtraFlags ) +COMMIT& COMMIT::createModified( EDA_ITEM* aItem, EDA_ITEM* aCopy, int aExtraFlags, BASE_SCREEN* aScreen ) { EDA_ITEM* parent = parentObject( aItem ); auto entryIt = m_changedItems.find( parent ); @@ -139,22 +140,22 @@ COMMIT& COMMIT::createModified( EDA_ITEM* aItem, EDA_ITEM* aCopy, int aExtraFlag return *this; // item has been already modified once } - makeEntry( parent, CHT_MODIFY | aExtraFlags, aCopy ); + makeEntry( parent, CHT_MODIFY | aExtraFlags, aCopy, aScreen ); return *this; } -void COMMIT::makeEntry( EDA_ITEM* aItem, CHANGE_TYPE aType, EDA_ITEM* aCopy ) +void COMMIT::makeEntry( EDA_ITEM* aItem, CHANGE_TYPE aType, EDA_ITEM* aCopy, BASE_SCREEN *aScreen ) { // Expect an item copy if it is going to be modified wxASSERT( !!aCopy == ( ( aType & CHT_TYPE ) == CHT_MODIFY ) ); if( m_changedItems.find( aItem ) != m_changedItems.end() ) { - alg::delete_if( m_changes, [aItem]( const COMMIT_LINE& aEnt ) + alg::delete_if( m_changes, [aItem, aScreen]( const COMMIT_LINE& aEnt ) { - return aEnt.m_item == aItem; + return aEnt.m_item == aItem && aEnt.m_screen == aScreen; } ); } @@ -163,17 +164,18 @@ void COMMIT::makeEntry( EDA_ITEM* aItem, CHANGE_TYPE aType, EDA_ITEM* aCopy ) ent.m_item = aItem; ent.m_type = aType; ent.m_copy = aCopy; + ent.m_screen = aScreen; m_changedItems.insert( aItem ); m_changes.push_back( ent ); } -COMMIT::COMMIT_LINE* COMMIT::findEntry( EDA_ITEM* aItem ) +COMMIT::COMMIT_LINE* COMMIT::findEntry( EDA_ITEM* aItem, BASE_SCREEN *aScreen ) { for( COMMIT_LINE& change : m_changes ) { - if( change.m_item == aItem ) + if( change.m_item == aItem && change.m_screen == aScreen ) return &change; } diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 3fb057ec78..7067946489 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -317,6 +317,7 @@ set( EESCHEMA_SRCS sch_validators.cpp sch_view.cpp schematic.cpp + schematic_commit.cpp schematic_settings.cpp schematic_undo_redo.cpp sheet.cpp diff --git a/eeschema/annotate.cpp b/eeschema/annotate.cpp index a9d8bb1491..4e3f10148a 100644 --- a/eeschema/annotate.cpp +++ b/eeschema/annotate.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -57,16 +58,20 @@ void SCH_EDIT_FRAME::mapExistingAnnotation( std::map& aMap ) } -void SCH_EDIT_FRAME::DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRecursive, - bool* aAppendUndo ) +void SCH_EDIT_FRAME::DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRecursive ) { + + SCH_SHEET_LIST sheets = Schematic().GetSheets(); + SCH_SCREEN* screen = GetScreen(); + SCH_SHEET_PATH currentSheet = GetCurrentSheet(); + SCHEMATIC_COMMIT commit( this ); + auto clearSymbolAnnotation = [&]( EDA_ITEM* aItem, SCH_SCREEN* aScreen, SCH_SHEET_PATH* aSheet, bool aResetPrefixes ) { SCH_SYMBOL* symbol = static_cast( aItem ); + commit.Modify( aItem, aScreen ); - SaveCopyInUndoList( aScreen, symbol, UNDO_REDO::CHANGED, *aAppendUndo ); - *aAppendUndo = true; symbol->ClearAnnotation( aSheet, aResetPrefixes ); }; @@ -77,10 +82,6 @@ void SCH_EDIT_FRAME::DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRe clearSymbolAnnotation( item, aScreen, aSheet, aResetPrefixes ); }; - SCH_SHEET_LIST sheets = Schematic().GetSheets(); - SCH_SCREEN* screen = GetScreen(); - SCH_SHEET_PATH currentSheet = GetCurrentSheet(); - switch( aAnnotateScope ) { case ANNOTATE_ALL: @@ -151,9 +152,9 @@ void SCH_EDIT_FRAME::DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRe if( erc_dlg ) static_cast( erc_dlg )->UpdateAnnotationWarning(); - SyncView(); + commit.Push( _( "Delete Annotation" ) ); + GetCanvas()->Refresh(); - OnModify(); // Must go after OnModify() so the connectivity graph has been updated UpdateNetHighlightStatus(); diff --git a/eeschema/dialogs/dialog_annotate.cpp b/eeschema/dialogs/dialog_annotate.cpp index fe60e546ae..db37181bd2 100644 --- a/eeschema/dialogs/dialog_annotate.cpp +++ b/eeschema/dialogs/dialog_annotate.cpp @@ -245,9 +245,7 @@ void DIALOG_ANNOTATE::OnApplyClick( wxCommandEvent& event ) void DIALOG_ANNOTATE::OnClearAnnotationClick( wxCommandEvent& event ) { - bool appendUndo = false; - - m_Parent->DeleteAnnotation( GetScope(), GetRecursive(), &appendUndo ); + m_Parent->DeleteAnnotation( GetScope(), GetRecursive() ); m_btnClear->Enable( false ); } diff --git a/eeschema/lib_field.h b/eeschema/lib_field.h index 72e75116a6..f41d7c8506 100644 --- a/eeschema/lib_field.h +++ b/eeschema/lib_field.h @@ -73,6 +73,11 @@ public: return wxT( "LIB_FIELD" ); } + static inline bool ClassOf( const EDA_ITEM* aItem ) + { + return aItem && aItem->Type() == LIB_FIELD_T; + } + wxString GetTypeName() const override { return _( "Field" ); diff --git a/eeschema/lib_item.h b/eeschema/lib_item.h index 0688c415b7..53f28df20c 100644 --- a/eeschema/lib_item.h +++ b/eeschema/lib_item.h @@ -92,6 +92,26 @@ public: */ virtual wxString GetTypeName() const = 0; + static inline bool ClassOf( const EDA_ITEM* aItem ) + { + if( !aItem ) + return false; + + switch ( aItem->Type() ) + { + case LIB_SHAPE_T: + case LIB_TEXT_T: + case LIB_TEXTBOX_T: + case LIB_PIN_T: + case LIB_FIELD_T: + return true; + default: + break; + } + + return false; + } + /** * Begin drawing a symbol library draw item at \a aPosition. * diff --git a/eeschema/lib_pin.h b/eeschema/lib_pin.h index dddd9b83d9..95e2c8177e 100644 --- a/eeschema/lib_pin.h +++ b/eeschema/lib_pin.h @@ -66,6 +66,11 @@ public: return wxT( "LIB_PIN" ); } + static inline bool ClassOf( const EDA_ITEM* aItem ) + { + return aItem && aItem->Type() == LIB_PIN_T; + } + wxString GetTypeName() const override { return _( "Pin" ); diff --git a/eeschema/lib_shape.h b/eeschema/lib_shape.h index 372b6d0818..f1f1cfd6fc 100644 --- a/eeschema/lib_shape.h +++ b/eeschema/lib_shape.h @@ -44,6 +44,11 @@ public: return wxT( "LIB_SHAPE" ); } + static inline bool ClassOf( const EDA_ITEM* aItem ) + { + return aItem && aItem->Type() == LIB_SHAPE_T; + } + wxString GetTypeName() const override { return ShowShape(); diff --git a/eeschema/lib_symbol.cpp b/eeschema/lib_symbol.cpp index 1e2562d018..461f24aafd 100644 --- a/eeschema/lib_symbol.cpp +++ b/eeschema/lib_symbol.cpp @@ -1336,6 +1336,13 @@ wxString LIB_SYMBOL::GetPrefix() } +void LIB_SYMBOL::RunOnChildren( const std::function& aFunction ) +{ + for( LIB_ITEM& item : m_drawings ) + aFunction( &item ); +} + + int LIB_SYMBOL::UpdateFieldOrdinals() { int retv = 0; diff --git a/eeschema/lib_symbol.h b/eeschema/lib_symbol.h index 0f95a63f70..4a6691c96d 100644 --- a/eeschema/lib_symbol.h +++ b/eeschema/lib_symbol.h @@ -134,6 +134,11 @@ public: return wxT( "LIB_SYMBOL" ); } + static inline bool ClassOf( const EDA_ITEM* aItem ) + { + return aItem && aItem->Type() == LIB_SYMBOL_T; + } + virtual void SetName( const wxString& aName ); wxString GetName() const override { return m_name; } @@ -309,6 +314,8 @@ public: wxString GetPrefix(); + void RunOnChildren( const std::function& aFunction ); + /** * Order optional field indices. * diff --git a/eeschema/lib_text.h b/eeschema/lib_text.h index c6db9b6a66..ea9622324c 100644 --- a/eeschema/lib_text.h +++ b/eeschema/lib_text.h @@ -50,6 +50,11 @@ public: return wxT( "LIB_TEXT" ); } + static inline bool ClassOf( const EDA_ITEM* aItem ) + { + return aItem && aItem->Type() == LIB_TEXT_T; + } + wxString GetTypeName() const override { return _( "Text" ); diff --git a/eeschema/sch_base_frame.h b/eeschema/sch_base_frame.h index e286d5eb3a..407bb5b15b 100644 --- a/eeschema/sch_base_frame.h +++ b/eeschema/sch_base_frame.h @@ -216,7 +216,7 @@ public: * Add an item to the screen (and view) * aScreen is the screen the item is located on, if not the current screen */ - void AddToScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen ); + void AddToScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen = nullptr ); /** * Remove an item from the screen (and view) diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index dc3ad0b780..0b62c6871a 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -344,9 +344,8 @@ public: * Clear the current symbol annotation. * * @param aCurrentSheetOnly Where to clear the annotation. See #ANNOTATE_SCOPE_T - * @param appendUndo true to add the action to the previous undo list */ - void DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRecursive, bool* appendUndo ); + void DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRecursive ); /** * Annotate the symbols in the schematic that are not currently annotated. Multi-unit symbols diff --git a/eeschema/schematic_commit.cpp b/eeschema/schematic_commit.cpp new file mode 100644 index 0000000000..2735f3106b --- /dev/null +++ b/eeschema/schematic_commit.cpp @@ -0,0 +1,484 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 KiCad Developers, see AUTHORS.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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + + +SCHEMATIC_COMMIT::SCHEMATIC_COMMIT( TOOL_MANAGER* aToolMgr ) : + m_toolMgr( aToolMgr ), + m_isLibEditor( false ) +{ +} + + +SCHEMATIC_COMMIT::SCHEMATIC_COMMIT( EE_TOOL_BASE* aTool ) +{ + m_toolMgr = aTool->GetManager(); + m_isLibEditor = aTool->IsSymbolEditor(); +} + + +SCHEMATIC_COMMIT::SCHEMATIC_COMMIT( EDA_DRAW_FRAME* aFrame ) +{ + m_toolMgr = aFrame->GetToolManager(); + m_isLibEditor = aFrame->IsType( FRAME_SCH_SYMBOL_EDITOR ); +} + + +SCHEMATIC_COMMIT::~SCHEMATIC_COMMIT() +{ +} + + +COMMIT& SCHEMATIC_COMMIT::Stage( EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen ) +{ + wxCHECK( aItem, *this ); + + aItem->ClearFlags( IS_MODIFIED_CHILD ); + + // If aItem belongs a symbol, the full symbol will be saved because undo/redo does + // not handle "sub items" modifications. + if( aItem->GetParent() && aItem->GetParent()->IsType( { SCH_SYMBOL_T, LIB_SYMBOL_T } ) ) + { + aItem->SetFlags( IS_MODIFIED_CHILD ); + aItem = aItem->GetParent(); + } + + + return COMMIT::Stage( aItem, aChangeType, aScreen ); +} + + +COMMIT& SCHEMATIC_COMMIT::Stage( std::vector &container, CHANGE_TYPE aChangeType, + BASE_SCREEN *aScreen ) +{ + for( EDA_ITEM* item : container ) + Stage( item, aChangeType, aScreen ); + + return *this; +} + + +COMMIT& SCHEMATIC_COMMIT::Stage( const PICKED_ITEMS_LIST &aItems, UNDO_REDO aModFlag, + BASE_SCREEN *aScreen ) +{ + return COMMIT::Stage( aItems, aModFlag, aScreen ); +} + + +void SCHEMATIC_COMMIT::pushLibEdit( const wxString& aMessage, int aCommitFlags ) +{ + // Objects potentially interested in changes: + PICKED_ITEMS_LIST undoList; + KIGFX::VIEW* view = m_toolMgr->GetView(); + + SYMBOL_EDIT_FRAME* sym_frame = static_cast( m_toolMgr->GetToolHolder() ); + LIB_SYMBOL* symbol = sym_frame->GetCurSymbol(); + std::set savedModules; + EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + bool itemsDeselected = false; + bool selectedModified = false; + + if( Empty() ) + return; + + for( COMMIT_LINE& ent : m_changes ) + { + int changeType = ent.m_type & CHT_TYPE; + int changeFlags = ent.m_type & CHT_FLAGS; + LIB_ITEM* libItem = static_cast( ent.m_item ); + + wxASSERT( ent.m_item ); + + // Module items need to be saved in the undo buffer before modification + if( ent.m_item->Type() != LIB_SYMBOL_T ) + { + ent.m_item = ent.m_item->GetParent(); + wxASSERT( ent.m_item ); + } + + // We have not saved the symbol yet, so let's create an entry + if( savedModules.count( ent.m_item ) == 0 ) + { + if( !( aCommitFlags & SKIP_UNDO ) && sym_frame ) + sym_frame->SaveCopyInUndoList( ent.m_item, UNDO_REDO::CHANGED, aCommitFlags & APPEND_UNDO ); + + savedModules.insert( ent.m_item ); + } + + if( ent.m_item->IsSelected() ) + selectedModified = true; + + symbol->RunOnChildren( + [&selectedModified]( LIB_ITEM* aItem ) + { + if( aItem->HasFlag( IS_MODIFIED_CHILD ) ) + selectedModified = true; + } ); + + switch( changeType ) + { + case CHT_ADD: + { + wxASSERT( libItem->Type() != LIB_SYMBOL_T ); + + libItem->SetParent( symbol ); + + if( !( changeFlags & CHT_DONE ) ) + symbol->AddDrawItem( libItem ); + + if( view ) + view->Add( libItem ); + + break; + } + + case CHT_REMOVE: + { + if( libItem->IsSelected() ) + { + if( selTool ) + selTool->RemoveItemFromSel( libItem, true /* quiet mode */ ); + + itemsDeselected = true; + } + + // Avoid removing mandatory fields + if( libItem->Type() == LIB_FIELD_T && static_cast( libItem )->IsMandatory() ) + break; + + if( view ) + view->Remove( libItem ); + + if( !( changeFlags & CHT_DONE ) ) + symbol->RemoveDrawItem( libItem ); + + break; + + } + + case CHT_MODIFY: + { + if( view ) + { + view->Update( libItem ); + + symbol->RunOnChildren( + [&]( LIB_ITEM* aChild ) + { + view->Update( aChild ); + }); + } + + // if no undo entry is needed, the copy would create a memory leak + if( aCommitFlags & SKIP_UNDO ) + delete ent.m_copy; + + break; + } + + default: + wxASSERT( false ); + break; + } + } + + m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } ); + + if( itemsDeselected ) + m_toolMgr->PostEvent( EVENTS::UnselectedEvent ); + + if( selectedModified ) + m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified ); + + if( sym_frame && !( aCommitFlags & SKIP_SET_DIRTY ) ) + sym_frame->OnModify(); + + clear(); +} + + +void SCHEMATIC_COMMIT::pushSchEdit( const wxString& aMessage, int aCommitFlags ) +{ + // Objects potentially interested in changes: + PICKED_ITEMS_LIST undoList; + KIGFX::VIEW* view = m_toolMgr->GetView(); + + SCH_EDIT_FRAME* frame = static_cast( m_toolMgr->GetToolHolder() ); + EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + bool itemsDeselected = false; + bool selectedModified = false; + + if( Empty() ) + return; + + for( COMMIT_LINE& ent : m_changes ) + { + int changeType = ent.m_type & CHT_TYPE; + int changeFlags = ent.m_type & CHT_FLAGS; + SCH_ITEM* schItem = static_cast( ent.m_item ); + SCH_SCREEN* screen = static_cast( ent.m_screen ); + + wxASSERT( ent.m_item ); + + if( ent.m_item->IsSelected() ) + selectedModified = true; + + switch( changeType ) + { + case CHT_ADD: + { + if( !schItem->GetParent() ) + { + if( !( aCommitFlags & SKIP_UNDO ) ) + undoList.PushItem( ITEM_PICKER( screen, schItem, UNDO_REDO::NEWITEM ) ); + + if( !( changeFlags & CHT_DONE ) ) + frame->GetScreen()->Append( schItem ); + } + + if( view ) + view->Add( schItem ); + + break; + } + + case CHT_REMOVE: + { + if( !( aCommitFlags & SKIP_UNDO ) ) + undoList.PushItem( ITEM_PICKER( screen, schItem, UNDO_REDO::DELETED ) ); + + if( schItem->IsSelected() ) + { + if( selTool ) + selTool->RemoveItemFromSel( schItem, true /* quiet mode */ ); + + itemsDeselected = true; + } + + if( schItem->Type() == SCH_FIELD_T ) + { + static_cast( schItem )->SetVisible( false ); + break; + } + + if( view ) + view->Remove( schItem ); + + if( !( changeFlags & CHT_DONE ) ) + frame->GetScreen()->Remove( schItem ); + + break; + } + + case CHT_MODIFY: + { + if( !( aCommitFlags & SKIP_UNDO ) ) + { + ITEM_PICKER itemWrapper( screen, schItem, UNDO_REDO::CHANGED ); + wxASSERT( ent.m_copy ); + itemWrapper.SetLink( ent.m_copy ); + undoList.PushItem( itemWrapper ); + } + + if( view ) + view->Update( schItem ); + + // if no undo entry is needed, the copy would create a memory leak + if( aCommitFlags & SKIP_UNDO ) + delete ent.m_copy; + + break; + } + + default: + wxASSERT( false ); + break; + } + } + + if( !( aCommitFlags & SKIP_UNDO ) && frame ) + frame->SaveCopyInUndoList(undoList, UNDO_REDO::UNSPECIFIED, aCommitFlags & APPEND_UNDO ); + + m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } ); + + if( itemsDeselected ) + m_toolMgr->PostEvent( EVENTS::UnselectedEvent ); + + if( selectedModified ) + m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified ); + + if( frame && !( aCommitFlags & SKIP_SET_DIRTY ) ) + frame->OnModify(); + + clear(); +} + + +void SCHEMATIC_COMMIT::Push( const wxString& aMessage, int aCommitFlags ) +{ + if( m_isLibEditor ) + pushLibEdit( aMessage, aCommitFlags ); + else + pushSchEdit( aMessage, aCommitFlags ); +} + + +EDA_ITEM* SCHEMATIC_COMMIT::parentObject( EDA_ITEM* aItem ) const +{ + if( SCH_SYMBOL* parentSymbol = dyn_cast( aItem->GetParent() ) ) + return parentSymbol; + + if( LIB_SYMBOL* parentSymbol = dyn_cast( aItem->GetParent() ) ) + return parentSymbol; + + if( m_isLibEditor ) + return static_cast( m_toolMgr->GetToolHolder() )->GetCurSymbol(); + + return aItem; +} + + +EDA_ITEM* SCHEMATIC_COMMIT::makeImage( EDA_ITEM* aItem ) const +{ + if( m_isLibEditor ) + return new LIB_SYMBOL( + *static_cast( m_toolMgr->GetToolHolder() )->GetCurSymbol() ); + + return aItem->Clone(); +} + + +void SCHEMATIC_COMMIT::revertLibEdit() +{ + // The first element in the commit is the original, and libedit + // just saves copies of the whole symbol, so grab the original and discard the rest + SYMBOL_EDIT_FRAME* sym_frame = static_cast( m_toolMgr->GetToolHolder() ); + LIB_SYMBOL* sym = static_cast( m_changes.front().m_item ); + + sym_frame->SetCurSymbol( sym, false ); + + for( size_t ii = 1; ii < m_changes.size(); ++ii ) + delete m_changes[ii].m_item; + + clear(); +} + + +void SCHEMATIC_COMMIT::Revert() +{ + PICKED_ITEMS_LIST undoList; + KIGFX::VIEW* view = m_toolMgr->GetView(); + + if( m_changes.empty() ) + return; + + if( m_isLibEditor ) + { + revertLibEdit(); + return; + } + + SCHEMATIC& schematic = static_cast( m_toolMgr->GetToolHolder() )->Schematic(); + + for( auto it = m_changes.rbegin(); it != m_changes.rend(); ++it ) + { + COMMIT_LINE& ent = *it; + SCH_ITEM* item = static_cast( ent.m_item ); + SCH_ITEM* copy = static_cast( ent.m_copy ); + SCH_SCREEN* screen = static_cast( ent.m_screen ); + int changeType = ent.m_type & CHT_TYPE; + int changeFlags = ent.m_type & CHT_FLAGS; + + switch( changeType ) + { + case CHT_ADD: + if( !( changeFlags & CHT_DONE ) ) + break; + + view->Remove( item ); + screen->Remove( item ); + break; + + case CHT_REMOVE: + if( !( changeFlags & CHT_DONE ) ) + break; + + view->Add( item ); + screen->Append( item ); + break; + + case CHT_MODIFY: + { + view->Remove( item ); + item->SwapData( copy ); + + // Special cases for items which have instance data + if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T + && item->Type() == SCH_FIELD_T ) + { + SCH_FIELD* field = static_cast( item ); + SCH_SYMBOL* symbol = static_cast( item->GetParent() ); + + if( field->GetId() == REFERENCE_FIELD ) + { + symbol->SetRef( schematic.GetSheets().FindSheetForScreen( screen ), + field->GetText() ); + } + } + + view->Add( item ); + + delete copy; + break; + } + + default: + wxASSERT( false ); + break; + } + } + + EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + selTool->RebuildSelection(); + + clear(); +} + diff --git a/eeschema/schematic_commit.h b/eeschema/schematic_commit.h new file mode 100644 index 0000000000..e619a7b8b7 --- /dev/null +++ b/eeschema/schematic_commit.h @@ -0,0 +1,80 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors. + * @author Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef SCHEMATIC_COMMIT_H +#define SCHEMATIC_COMMIT_H + +#include + +class PICKED_ITEMS_LIST; +class TOOL_MANAGER; +class SCH_EDIT_FRAME; +class EDA_DRAW_FRAME; +class TOOL_BASE; + +template +class EE_TOOL_BASE; + +#define SKIP_UNDO 0x0001 +#define APPEND_UNDO 0x0002 +#define SKIP_SET_DIRTY 0x0004 +#define SKIP_CONNECTIVITY 0x0008 + +class SCHEMATIC_COMMIT : public COMMIT +{ +public: + SCHEMATIC_COMMIT( TOOL_MANAGER* aToolMgr ); + SCHEMATIC_COMMIT( EDA_DRAW_FRAME* aFrame ); + SCHEMATIC_COMMIT( EE_TOOL_BASE* aFrame ); + + virtual ~SCHEMATIC_COMMIT(); + + virtual void Push( const wxString& aMessage = wxT( "A commit" ), + int aCommitFlags = 0 ) override; + + virtual void Revert() override; + COMMIT& Stage( EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen = nullptr ) + override; + COMMIT& Stage( std::vector &container, CHANGE_TYPE aChangeType, + BASE_SCREEN *aScreen = nullptr ) override; + COMMIT& Stage( const PICKED_ITEMS_LIST &aItems, UNDO_REDO aModFlag = UNDO_REDO::UNSPECIFIED, + BASE_SCREEN *aScreen = nullptr ) override; + +private: + EDA_ITEM* parentObject( EDA_ITEM* aItem ) const override; + + EDA_ITEM* makeImage( EDA_ITEM* aItem ) const override; + + void pushLibEdit( const wxString& aMessage, int aCommitFlags ); + void pushSchEdit( const wxString& aMessage, int aCommitFlags ); + + void revertLibEdit(); + +private: + TOOL_MANAGER* m_toolMgr; + bool m_isLibEditor; +}; + +#endif diff --git a/eeschema/symbol_editor/symbol_editor_undo_redo.cpp b/eeschema/symbol_editor/symbol_editor_undo_redo.cpp index 78ef608f9c..cb003d21c1 100644 --- a/eeschema/symbol_editor/symbol_editor_undo_redo.cpp +++ b/eeschema/symbol_editor/symbol_editor_undo_redo.cpp @@ -30,6 +30,7 @@ #include #include + void SYMBOL_EDIT_FRAME::SaveCopyInUndoList( EDA_ITEM* aItem, UNDO_REDO aUndoType, bool aAppend ) { wxASSERT_MSG( !aAppend, "Append not needed/supported for symbol editor" ); diff --git a/eeschema/tools/ee_tool_base.h b/eeschema/tools/ee_tool_base.h index 063e758a01..939becc713 100644 --- a/eeschema/tools/ee_tool_base.h +++ b/eeschema/tools/ee_tool_base.h @@ -32,12 +32,12 @@ #include #include #include -#include #include +#include +#include #include #include - class EE_SELECTION; /** @@ -92,9 +92,20 @@ public: m_isSymbolEditor = dynamic_cast( m_frame ) != nullptr; } + if( aReason != RUN ) + m_commit = std::make_unique( m_toolMgr ); + m_view = static_cast( getView() ); } + /** + * Returns true if the tool is running in the symbol editor + */ + bool IsSymbolEditor() const + { + return m_isSymbolEditor; + } + protected: /** * Similar to getView()->Update(), but handles items that are redrawn by their parents @@ -190,6 +201,8 @@ protected: KIGFX::SCH_VIEW* m_view; EE_SELECTION_TOOL* m_selectionTool; bool m_isSymbolEditor; + + std::unique_ptr m_commit; }; diff --git a/include/commit.h b/include/commit.h index bc10948c75..8f7bb612a9 100644 --- a/include/commit.h +++ b/include/commit.h @@ -34,6 +34,7 @@ #include class EDA_ITEM; +class BASE_SCREEN; ///< Types of changes enum CHANGE_TYPE { @@ -75,39 +76,39 @@ public: virtual ~COMMIT(); ///< Add a new item to the model - COMMIT& Add( EDA_ITEM* aItem ) + COMMIT& Add( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr ) { - return Stage( aItem, CHT_ADD ); + return Stage( aItem, CHT_ADD, aScreen ); } ///< Notify observers that aItem has been added - COMMIT& Added( EDA_ITEM* aItem ) + COMMIT& Added( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr ) { - return Stage( aItem, CHT_ADD | CHT_DONE ); + return Stage( aItem, CHT_ADD | CHT_DONE, aScreen ); } ///< Remove a new item from the model - COMMIT& Remove( EDA_ITEM* aItem ) + COMMIT& Remove( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr ) { - return Stage( aItem, CHT_REMOVE ); + return Stage( aItem, CHT_REMOVE, aScreen ); } ///< Notify observers that aItem has been removed - COMMIT& Removed( EDA_ITEM* aItem ) + COMMIT& Removed( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr ) { - return Stage( aItem, CHT_REMOVE | CHT_DONE ); + return Stage( aItem, CHT_REMOVE | CHT_DONE, aScreen ); } ///< Modify a given item in the model. ///< Must be called before modification is performed. - COMMIT& Modify( EDA_ITEM* aItem ) + COMMIT& Modify( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr ) { - return Stage( aItem, CHT_MODIFY ); + return Stage( aItem, CHT_MODIFY, aScreen ); } ///< Create an undo entry for an item that has been already modified. Requires a copy done ///< before the modification. - COMMIT& Modified( EDA_ITEM* aItem, EDA_ITEM* aCopy ) + COMMIT& Modified( EDA_ITEM* aItem, EDA_ITEM* aCopy, BASE_SCREEN *aScreen = nullptr ) { return createModified( aItem, aCopy ); } @@ -123,12 +124,15 @@ public: } ///< Add a change of the item aItem of type aChangeType to the change list. - virtual COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ); + virtual COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, + BASE_SCREEN *aScreen = nullptr ); - virtual COMMIT& Stage( std::vector& container, CHANGE_TYPE aChangeType ); + virtual COMMIT& Stage( std::vector& container, CHANGE_TYPE aChangeType, + BASE_SCREEN *aScreen = nullptr ); virtual COMMIT& Stage( const PICKED_ITEMS_LIST& aItems, - UNDO_REDO aModFlag = UNDO_REDO::UNSPECIFIED ); + UNDO_REDO aModFlag = UNDO_REDO::UNSPECIFIED, + BASE_SCREEN *aScreen = nullptr ); ///< Execute the changes. virtual void Push( const wxString& aMessage = wxT( "A commit" ), int aFlags = 0 ) = 0; @@ -142,7 +146,7 @@ public: } ///< Returns status of an item. - int GetStatus( EDA_ITEM* aItem ); + int GetStatus( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr ); protected: struct COMMIT_LINE @@ -150,6 +154,7 @@ protected: EDA_ITEM* m_item; ///< Main item that is added/deleted/modified EDA_ITEM* m_copy; ///< Optional copy of the item CHANGE_TYPE m_type; ///< Modification type + BASE_SCREEN* m_screen; }; // Should be called in Push() & Revert() methods @@ -159,16 +164,18 @@ protected: m_changes.clear(); } - COMMIT& createModified( EDA_ITEM* aItem, EDA_ITEM* aCopy, int aExtraFlags = 0 ); + COMMIT& createModified( EDA_ITEM* aItem, EDA_ITEM* aCopy, int aExtraFlags = 0, + BASE_SCREEN *aScreen = nullptr ); - virtual void makeEntry( EDA_ITEM* aItem, CHANGE_TYPE aType, EDA_ITEM* aCopy = nullptr ); + virtual void makeEntry( EDA_ITEM* aItem, CHANGE_TYPE aType, EDA_ITEM* aCopy = nullptr, + BASE_SCREEN *aScreen = nullptr ); /** * Search for an entry describing change for a particular item. * * @return null if there is no related entry. */ - COMMIT_LINE* findEntry( EDA_ITEM* aItem ); + COMMIT_LINE* findEntry( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr ); virtual EDA_ITEM* parentObject( EDA_ITEM* aItem ) const = 0; diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp index 3ed53dfa1f..447acfb8a6 100644 --- a/pcbnew/board_commit.cpp +++ b/pcbnew/board_commit.cpp @@ -75,7 +75,7 @@ BOARD* BOARD_COMMIT::GetBoard() const } -COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ) +COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen ) { wxCHECK( aItem, *this ); @@ -127,15 +127,15 @@ COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ) } -COMMIT& BOARD_COMMIT::Stage( std::vector& container, CHANGE_TYPE aChangeType ) +COMMIT& BOARD_COMMIT::Stage( std::vector& container, CHANGE_TYPE aChangeType, BASE_SCREEN* aScreen ) { - return COMMIT::Stage( container, aChangeType ); + return COMMIT::Stage( container, aChangeType, aScreen ); } -COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag ) +COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag, BASE_SCREEN* aScreen ) { - return COMMIT::Stage( aItems, aModFlag ); + return COMMIT::Stage( aItems, aModFlag, aScreen ); } diff --git a/pcbnew/board_commit.h b/pcbnew/board_commit.h index 8db5ceb466..3caed389d9 100644 --- a/pcbnew/board_commit.h +++ b/pcbnew/board_commit.h @@ -57,10 +57,13 @@ public: int aCommitFlags = 0 ) override; virtual void Revert() override; - COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ) override; - COMMIT& Stage( std::vector& container, CHANGE_TYPE aChangeType ) override; + COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType, + BASE_SCREEN* aScreen = nullptr ) override; + COMMIT& Stage( std::vector& container, CHANGE_TYPE aChangeType, + BASE_SCREEN* aScreen = nullptr ) override; COMMIT& Stage( const PICKED_ITEMS_LIST& aItems, - UNDO_REDO aModFlag = UNDO_REDO::UNSPECIFIED ) override; + UNDO_REDO aModFlag = UNDO_REDO::UNSPECIFIED, + BASE_SCREEN* aScreen = nullptr ) override; private: EDA_ITEM* parentObject( EDA_ITEM* aItem ) const override;