Add COMMIT structure to Schematic and Symbol editors

Provides a single-point access for modifying the schematic and symbol
elements that allows chaining updates and reverting partial changes.
Standardizes the undo hierarchy between pcb and schematic editors

As this is another layer on the existing undo/redo structure, the
initial commit does not replace all undo/redo calls currently existing.
These will be handled in a series of follow-on commits
This commit is contained in:
Seth Hillbrand 2023-04-28 17:02:42 -07:00
parent 251161dbeb
commit f4b43617e7
20 changed files with 705 additions and 62 deletions

View File

@ -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 // CHT_MODIFY and CHT_DONE are not compatible
wxASSERT( ( aChangeType & ( CHT_MODIFY | CHT_DONE ) ) != ( CHT_MODIFY | CHT_DONE ) ); 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: case CHT_ADD:
wxASSERT( m_changedItems.find( aItem ) == m_changedItems.end() ); wxASSERT( m_changedItems.find( aItem ) == m_changedItems.end() );
makeEntry( aItem, CHT_ADD | flag ); makeEntry( aItem, CHT_ADD | flag, nullptr, aScreen );
return *this; return *this;
case CHT_REMOVE: case CHT_REMOVE:
makeEntry( aItem, CHT_REMOVE | flag ); makeEntry( aItem, CHT_REMOVE | flag, nullptr, aScreen );
return *this; return *this;
case CHT_MODIFY: case CHT_MODIFY:
@ -71,7 +71,7 @@ COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType )
wxASSERT( clone ); wxASSERT( clone );
if( clone ) if( clone )
return createModified( parent, clone, flag ); return createModified( parent, clone, flag, aScreen );
break; break;
} }
@ -84,16 +84,17 @@ COMMIT& COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType )
} }
COMMIT& COMMIT::Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType ) COMMIT& COMMIT::Stage( std::vector<EDA_ITEM*> &container, CHANGE_TYPE aChangeType,
BASE_SCREEN *aScreen )
{ {
for( EDA_ITEM* item : container ) for( EDA_ITEM* item : container )
Stage( item, aChangeType ); Stage( item, aChangeType, aScreen);
return *this; 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++ ) 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 ); assert( change_type == UNDO_REDO::CHANGED );
// There was already a copy created, so use it // There was already a copy created, so use it
Modified( item, copy ); Modified( item, copy, aScreen );
} }
else 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; 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 ); EDA_ITEM* parent = parentObject( aItem );
auto entryIt = m_changedItems.find( parent ); 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 return *this; // item has been already modified once
} }
makeEntry( parent, CHT_MODIFY | aExtraFlags, aCopy ); makeEntry( parent, CHT_MODIFY | aExtraFlags, aCopy, aScreen );
return *this; 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 // Expect an item copy if it is going to be modified
wxASSERT( !!aCopy == ( ( aType & CHT_TYPE ) == CHT_MODIFY ) ); wxASSERT( !!aCopy == ( ( aType & CHT_TYPE ) == CHT_MODIFY ) );
if( m_changedItems.find( aItem ) != m_changedItems.end() ) 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_item = aItem;
ent.m_type = aType; ent.m_type = aType;
ent.m_copy = aCopy; ent.m_copy = aCopy;
ent.m_screen = aScreen;
m_changedItems.insert( aItem ); m_changedItems.insert( aItem );
m_changes.push_back( ent ); 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 ) for( COMMIT_LINE& change : m_changes )
{ {
if( change.m_item == aItem ) if( change.m_item == aItem && change.m_screen == aScreen )
return &change; return &change;
} }

View File

@ -317,6 +317,7 @@ set( EESCHEMA_SRCS
sch_validators.cpp sch_validators.cpp
sch_view.cpp sch_view.cpp
schematic.cpp schematic.cpp
schematic_commit.cpp
schematic_settings.cpp schematic_settings.cpp
schematic_undo_redo.cpp schematic_undo_redo.cpp
sheet.cpp sheet.cpp

View File

@ -27,6 +27,7 @@
#include <reporter.h> #include <reporter.h>
#include <sch_edit_frame.h> #include <sch_edit_frame.h>
#include <schematic.h> #include <schematic.h>
#include <schematic_commit.h>
#include <erc_settings.h> #include <erc_settings.h>
#include <sch_reference_list.h> #include <sch_reference_list.h>
#include <symbol_library.h> #include <symbol_library.h>
@ -57,16 +58,20 @@ void SCH_EDIT_FRAME::mapExistingAnnotation( std::map<wxString, wxString>& aMap )
} }
void SCH_EDIT_FRAME::DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRecursive, void SCH_EDIT_FRAME::DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRecursive )
bool* aAppendUndo )
{ {
SCH_SHEET_LIST sheets = Schematic().GetSheets();
SCH_SCREEN* screen = GetScreen();
SCH_SHEET_PATH currentSheet = GetCurrentSheet();
SCHEMATIC_COMMIT commit( this );
auto clearSymbolAnnotation = auto clearSymbolAnnotation =
[&]( EDA_ITEM* aItem, SCH_SCREEN* aScreen, SCH_SHEET_PATH* aSheet, bool aResetPrefixes ) [&]( EDA_ITEM* aItem, SCH_SCREEN* aScreen, SCH_SHEET_PATH* aSheet, bool aResetPrefixes )
{ {
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( aItem ); SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( aItem );
commit.Modify( aItem, aScreen );
SaveCopyInUndoList( aScreen, symbol, UNDO_REDO::CHANGED, *aAppendUndo );
*aAppendUndo = true;
symbol->ClearAnnotation( aSheet, aResetPrefixes ); symbol->ClearAnnotation( aSheet, aResetPrefixes );
}; };
@ -77,10 +82,6 @@ void SCH_EDIT_FRAME::DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRe
clearSymbolAnnotation( item, aScreen, aSheet, aResetPrefixes ); clearSymbolAnnotation( item, aScreen, aSheet, aResetPrefixes );
}; };
SCH_SHEET_LIST sheets = Schematic().GetSheets();
SCH_SCREEN* screen = GetScreen();
SCH_SHEET_PATH currentSheet = GetCurrentSheet();
switch( aAnnotateScope ) switch( aAnnotateScope )
{ {
case ANNOTATE_ALL: case ANNOTATE_ALL:
@ -151,9 +152,9 @@ void SCH_EDIT_FRAME::DeleteAnnotation( ANNOTATE_SCOPE_T aAnnotateScope, bool aRe
if( erc_dlg ) if( erc_dlg )
static_cast<DIALOG_ERC*>( erc_dlg )->UpdateAnnotationWarning(); static_cast<DIALOG_ERC*>( erc_dlg )->UpdateAnnotationWarning();
SyncView(); commit.Push( _( "Delete Annotation" ) );
GetCanvas()->Refresh(); GetCanvas()->Refresh();
OnModify();
// Must go after OnModify() so the connectivity graph has been updated // Must go after OnModify() so the connectivity graph has been updated
UpdateNetHighlightStatus(); UpdateNetHighlightStatus();

View File

@ -245,9 +245,7 @@ void DIALOG_ANNOTATE::OnApplyClick( wxCommandEvent& event )
void DIALOG_ANNOTATE::OnClearAnnotationClick( wxCommandEvent& event ) void DIALOG_ANNOTATE::OnClearAnnotationClick( wxCommandEvent& event )
{ {
bool appendUndo = false; m_Parent->DeleteAnnotation( GetScope(), GetRecursive() );
m_Parent->DeleteAnnotation( GetScope(), GetRecursive(), &appendUndo );
m_btnClear->Enable( false ); m_btnClear->Enable( false );
} }

View File

@ -73,6 +73,11 @@ public:
return wxT( "LIB_FIELD" ); return wxT( "LIB_FIELD" );
} }
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && aItem->Type() == LIB_FIELD_T;
}
wxString GetTypeName() const override wxString GetTypeName() const override
{ {
return _( "Field" ); return _( "Field" );

View File

@ -92,6 +92,26 @@ public:
*/ */
virtual wxString GetTypeName() const = 0; 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. * Begin drawing a symbol library draw item at \a aPosition.
* *

View File

@ -66,6 +66,11 @@ public:
return wxT( "LIB_PIN" ); return wxT( "LIB_PIN" );
} }
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && aItem->Type() == LIB_PIN_T;
}
wxString GetTypeName() const override wxString GetTypeName() const override
{ {
return _( "Pin" ); return _( "Pin" );

View File

@ -44,6 +44,11 @@ public:
return wxT( "LIB_SHAPE" ); return wxT( "LIB_SHAPE" );
} }
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && aItem->Type() == LIB_SHAPE_T;
}
wxString GetTypeName() const override wxString GetTypeName() const override
{ {
return ShowShape(); return ShowShape();

View File

@ -1336,6 +1336,13 @@ wxString LIB_SYMBOL::GetPrefix()
} }
void LIB_SYMBOL::RunOnChildren( const std::function<void( LIB_ITEM* )>& aFunction )
{
for( LIB_ITEM& item : m_drawings )
aFunction( &item );
}
int LIB_SYMBOL::UpdateFieldOrdinals() int LIB_SYMBOL::UpdateFieldOrdinals()
{ {
int retv = 0; int retv = 0;

View File

@ -134,6 +134,11 @@ public:
return wxT( "LIB_SYMBOL" ); 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 ); virtual void SetName( const wxString& aName );
wxString GetName() const override { return m_name; } wxString GetName() const override { return m_name; }
@ -309,6 +314,8 @@ public:
wxString GetPrefix(); wxString GetPrefix();
void RunOnChildren( const std::function<void( LIB_ITEM* )>& aFunction );
/** /**
* Order optional field indices. * Order optional field indices.
* *

View File

@ -50,6 +50,11 @@ public:
return wxT( "LIB_TEXT" ); return wxT( "LIB_TEXT" );
} }
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && aItem->Type() == LIB_TEXT_T;
}
wxString GetTypeName() const override wxString GetTypeName() const override
{ {
return _( "Text" ); return _( "Text" );

View File

@ -216,7 +216,7 @@ public:
* Add an item to the screen (and view) * Add an item to the screen (and view)
* aScreen is the screen the item is located on, if not the current screen * 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) * Remove an item from the screen (and view)

View File

@ -344,9 +344,8 @@ public:
* Clear the current symbol annotation. * Clear the current symbol annotation.
* *
* @param aCurrentSheetOnly Where to clear the annotation. See #ANNOTATE_SCOPE_T * @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 * Annotate the symbols in the schematic that are not currently annotated. Multi-unit symbols

View File

@ -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 <macros.h>
#include <tool/tool_manager.h>
#include <tools/ee_tool_base.h>
#include <lib_item.h>
#include <lib_pin.h>
#include <lib_shape.h>
#include <lib_symbol.h>
#include <lib_text.h>
#include <sch_screen.h>
#include <sch_sheet_path.h>
#include <schematic.h>
#include <view/view.h>
#include <schematic_commit.h>
#include <functional>
SCHEMATIC_COMMIT::SCHEMATIC_COMMIT( TOOL_MANAGER* aToolMgr ) :
m_toolMgr( aToolMgr ),
m_isLibEditor( false )
{
}
SCHEMATIC_COMMIT::SCHEMATIC_COMMIT( EE_TOOL_BASE<SCH_BASE_FRAME>* 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<EDA_ITEM*> &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<SYMBOL_EDIT_FRAME*>( m_toolMgr->GetToolHolder() );
LIB_SYMBOL* symbol = sym_frame->GetCurSymbol();
std::set<EDA_ITEM*> savedModules;
EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
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<LIB_ITEM*>( 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<LIB_FIELD*>( 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<SCH_EDIT_FRAME*>( m_toolMgr->GetToolHolder() );
EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
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<SCH_ITEM*>( ent.m_item );
SCH_SCREEN* screen = static_cast<SCH_SCREEN*>( 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<SCH_FIELD*>( 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<SCH_SYMBOL*>( aItem->GetParent() ) )
return parentSymbol;
if( LIB_SYMBOL* parentSymbol = dyn_cast<LIB_SYMBOL*>( aItem->GetParent() ) )
return parentSymbol;
if( m_isLibEditor )
return static_cast<SYMBOL_EDIT_FRAME*>( m_toolMgr->GetToolHolder() )->GetCurSymbol();
return aItem;
}
EDA_ITEM* SCHEMATIC_COMMIT::makeImage( EDA_ITEM* aItem ) const
{
if( m_isLibEditor )
return new LIB_SYMBOL(
*static_cast<SYMBOL_EDIT_FRAME*>( 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<SYMBOL_EDIT_FRAME*>( m_toolMgr->GetToolHolder() );
LIB_SYMBOL* sym = static_cast<LIB_SYMBOL*>( 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<SCH_EDIT_FRAME*>( m_toolMgr->GetToolHolder() )->Schematic();
for( auto it = m_changes.rbegin(); it != m_changes.rend(); ++it )
{
COMMIT_LINE& ent = *it;
SCH_ITEM* item = static_cast<SCH_ITEM*>( ent.m_item );
SCH_ITEM* copy = static_cast<SCH_ITEM*>( ent.m_copy );
SCH_SCREEN* screen = static_cast<SCH_SCREEN*>( 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<SCH_FIELD*>( item );
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( 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<EE_SELECTION_TOOL>();
selTool->RebuildSelection();
clear();
}

View File

@ -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 <tomasz.wlostowski@cern.ch>
*
* 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 <commit.h>
class PICKED_ITEMS_LIST;
class TOOL_MANAGER;
class SCH_EDIT_FRAME;
class EDA_DRAW_FRAME;
class TOOL_BASE;
template<class T>
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<SCH_BASE_FRAME>* 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<EDA_ITEM*> &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

View File

@ -30,6 +30,7 @@
#include <tools/ee_actions.h> #include <tools/ee_actions.h>
#include <tools/ee_selection_tool.h> #include <tools/ee_selection_tool.h>
void SYMBOL_EDIT_FRAME::SaveCopyInUndoList( EDA_ITEM* aItem, UNDO_REDO aUndoType, bool aAppend ) void SYMBOL_EDIT_FRAME::SaveCopyInUndoList( EDA_ITEM* aItem, UNDO_REDO aUndoType, bool aAppend )
{ {
wxASSERT_MSG( !aAppend, "Append not needed/supported for symbol editor" ); wxASSERT_MSG( !aAppend, "Append not needed/supported for symbol editor" );

View File

@ -32,12 +32,12 @@
#include <tool/tool_menu.h> #include <tool/tool_menu.h>
#include <tool/actions.h> #include <tool/actions.h>
#include <tools/ee_selection_tool.h> #include <tools/ee_selection_tool.h>
#include <sch_view.h>
#include <sch_edit_frame.h> #include <sch_edit_frame.h>
#include <sch_view.h>
#include <schematic_commit.h>
#include <symbol_edit_frame.h> #include <symbol_edit_frame.h>
#include <undo_redo_container.h> #include <undo_redo_container.h>
class EE_SELECTION; class EE_SELECTION;
/** /**
@ -92,9 +92,20 @@ public:
m_isSymbolEditor = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame ) != nullptr; m_isSymbolEditor = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame ) != nullptr;
} }
if( aReason != RUN )
m_commit = std::make_unique<SCHEMATIC_COMMIT>( m_toolMgr );
m_view = static_cast<KIGFX::SCH_VIEW*>( getView() ); m_view = static_cast<KIGFX::SCH_VIEW*>( getView() );
} }
/**
* Returns true if the tool is running in the symbol editor
*/
bool IsSymbolEditor() const
{
return m_isSymbolEditor;
}
protected: protected:
/** /**
* Similar to getView()->Update(), but handles items that are redrawn by their parents * Similar to getView()->Update(), but handles items that are redrawn by their parents
@ -190,6 +201,8 @@ protected:
KIGFX::SCH_VIEW* m_view; KIGFX::SCH_VIEW* m_view;
EE_SELECTION_TOOL* m_selectionTool; EE_SELECTION_TOOL* m_selectionTool;
bool m_isSymbolEditor; bool m_isSymbolEditor;
std::unique_ptr<SCHEMATIC_COMMIT> m_commit;
}; };

View File

@ -34,6 +34,7 @@
#include <undo_redo_container.h> #include <undo_redo_container.h>
class EDA_ITEM; class EDA_ITEM;
class BASE_SCREEN;
///< Types of changes ///< Types of changes
enum CHANGE_TYPE { enum CHANGE_TYPE {
@ -75,39 +76,39 @@ public:
virtual ~COMMIT(); virtual ~COMMIT();
///< Add a new item to the model ///< 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 ///< 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 ///< 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 ///< 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. ///< Modify a given item in the model.
///< Must be called before modification is performed. ///< 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 ///< Create an undo entry for an item that has been already modified. Requires a copy done
///< before the modification. ///< 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 ); return createModified( aItem, aCopy );
} }
@ -123,12 +124,15 @@ public:
} }
///< Add a change of the item aItem of type aChangeType to the change list. ///< 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<EDA_ITEM*>& container, CHANGE_TYPE aChangeType ); virtual COMMIT& Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType,
BASE_SCREEN *aScreen = nullptr );
virtual COMMIT& Stage( const PICKED_ITEMS_LIST& aItems, 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. ///< Execute the changes.
virtual void Push( const wxString& aMessage = wxT( "A commit" ), int aFlags = 0 ) = 0; virtual void Push( const wxString& aMessage = wxT( "A commit" ), int aFlags = 0 ) = 0;
@ -142,7 +146,7 @@ public:
} }
///< Returns status of an item. ///< Returns status of an item.
int GetStatus( EDA_ITEM* aItem ); int GetStatus( EDA_ITEM* aItem, BASE_SCREEN *aScreen = nullptr );
protected: protected:
struct COMMIT_LINE struct COMMIT_LINE
@ -150,6 +154,7 @@ protected:
EDA_ITEM* m_item; ///< Main item that is added/deleted/modified EDA_ITEM* m_item; ///< Main item that is added/deleted/modified
EDA_ITEM* m_copy; ///< Optional copy of the item EDA_ITEM* m_copy; ///< Optional copy of the item
CHANGE_TYPE m_type; ///< Modification type CHANGE_TYPE m_type; ///< Modification type
BASE_SCREEN* m_screen;
}; };
// Should be called in Push() & Revert() methods // Should be called in Push() & Revert() methods
@ -159,16 +164,18 @@ protected:
m_changes.clear(); 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. * Search for an entry describing change for a particular item.
* *
* @return null if there is no related entry. * @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; virtual EDA_ITEM* parentObject( EDA_ITEM* aItem ) const = 0;

View File

@ -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 ); wxCHECK( aItem, *this );
@ -127,15 +127,15 @@ COMMIT& BOARD_COMMIT::Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType )
} }
COMMIT& BOARD_COMMIT::Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType ) COMMIT& BOARD_COMMIT::Stage( std::vector<EDA_ITEM*>& 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 );
} }

View File

@ -57,10 +57,13 @@ public:
int aCommitFlags = 0 ) override; int aCommitFlags = 0 ) override;
virtual void Revert() override; virtual void Revert() override;
COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ) override; COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType,
COMMIT& Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType ) override; BASE_SCREEN* aScreen = nullptr ) override;
COMMIT& Stage( std::vector<EDA_ITEM*>& container, CHANGE_TYPE aChangeType,
BASE_SCREEN* aScreen = nullptr ) override;
COMMIT& Stage( const PICKED_ITEMS_LIST& aItems, 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: private:
EDA_ITEM* parentObject( EDA_ITEM* aItem ) const override; EDA_ITEM* parentObject( EDA_ITEM* aItem ) const override;