KiCad PCB EDA Suite
Loading...
Searching...
No Matches
schematic_undo_redo.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2004 Jean-Pierre Charras, [email protected]
5 * Copyright (C) 2004-2023 KiCad Developers, see change_log.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <ee_actions.h>
26#include <sch_edit_frame.h>
27#include <tool/tool_manager.h>
28#include <schematic.h>
29#include <sch_bus_entry.h>
30#include <sch_junction.h>
31#include <sch_line.h>
32#include <sch_bitmap.h>
35#include <tool/actions.h>
36
37
38/* Functions to undo and redo edit commands.
39 *
40 * m_UndoList and m_RedoList handle a std::vector of PICKED_ITEMS_LIST
41 * Each PICKED_ITEMS_LIST handle a std::vector of pickers (class ITEM_PICKER),
42 * that store the list of schematic items that are concerned by the command to
43 * undo or redo and is created for each command to undo (handle also a command
44 * to redo). each picker has a pointer pointing to an item to undo or redo (in
45 * fact: deleted, added or modified), and has a pointer to a copy of this item,
46 * when this item has been modified (the old values of parameters are
47 * therefore saved)
48 *
49 * there are 3 cases:
50 * - delete item(s) command
51 * - change item(s) command
52 * - add item(s) command
53 * and 2 cases for block:
54 * - move list of items
55 * - mirror (Y) list of items
56 *
57 * Undo command
58 * - delete item(s) command:
59 * => deleted items are moved in undo list
60 *
61 * - change item(s) command
62 * => A copy of item(s) is made (a DrawPickedStruct list of wrappers)
63 * the .m_Link member of each wrapper points the modified item.
64 * the .m_Item member of each wrapper points the old copy of this item.
65 *
66 * - add item(s) command
67 * =>A list of item(s) is made. The .m_Item member of each wrapper points
68 * the new item.
69 *
70 * Redo command
71 * - delete item(s) old command:
72 * => deleted items are moved into m_tree
73 *
74 * - change item(s) command
75 * => the copy of item(s) is moved in Undo list
76 *
77 * - add item(s) command
78 * => The list of item(s) is used to create a deleted list in undo
79 * list(same as a delete command)
80 *
81 * Some block operations that change items can be undone without memorized
82 * items, just the coordinates of the transform: move list of items (undo/
83 * redo is made by moving with the opposite move vector) mirror (Y) and flip
84 * list of items (undo/redo is made by mirror or flip items) so they are
85 * handled specifically.
86 *
87 * A problem is the hierarchical sheet handling.
88 * the data associated (sub-hierarchy, undo/redo list) is deleted only
89 * when the sheet is really deleted (i.e. when deleted from undo or redo list)
90 * This is handled by its destructor.
91 */
92
93
94/* Used if undo / redo command:
95 * swap data between Item and its copy, pointed by its picked item link member
96 * swapped data is data modified by editing, so not all values are swapped
97 */
98
100 UNDO_REDO aCommandType, bool aAppend,
101 bool aDirtyConnectivity )
102{
103 PICKED_ITEMS_LIST* commandToUndo = nullptr;
104
105 wxCHECK( aItem, /* void */ );
106
107 if( aDirtyConnectivity )
108 {
109 if( !aItem->IsConnectivityDirty() && aItem->Connection()
110 && ( aItem->Connection()->Name() == m_highlightedConn ) )
112
113 aItem->SetConnectivityDirty();
114 }
115
117
118 // If the last stack was empty, use that one instead of creating a new stack
119 if( lastUndo )
120 {
121 if( aAppend || !lastUndo->GetCount() )
122 commandToUndo = lastUndo;
123 else
124 PushCommandToUndoList( lastUndo );
125 }
126
127 if( !commandToUndo )
128 {
129 commandToUndo = new PICKED_ITEMS_LIST();
130 }
131
132 ITEM_PICKER itemWrapper( aScreen, aItem, aCommandType );
133 itemWrapper.SetFlags( aItem->GetFlags() );
134
135 switch( aCommandType )
136 {
137 case UNDO_REDO::CHANGED: /* Create a copy of item */
138 itemWrapper.SetLink( aItem->Duplicate( true ) );
139 commandToUndo->PushItem( itemWrapper );
140 break;
141
142 case UNDO_REDO::NEWITEM:
143 case UNDO_REDO::DELETED:
144 commandToUndo->PushItem( itemWrapper );
145 break;
146
147 default:
148 wxFAIL_MSG( wxString::Format( wxT( "SaveCopyInUndoList() error (unknown code %X)" ),
149 aCommandType ) );
150 break;
151 }
152
153 if( commandToUndo->GetCount() )
154 {
155 /* Save the copy in undo list */
156 PushCommandToUndoList( commandToUndo );
157
158 /* Clear redo list, because after new save there is no redo to do */
160 }
161 else
162 {
163 delete commandToUndo;
164 }
165}
166
167
169 UNDO_REDO aTypeCommand, bool aAppend,
170 bool aDirtyConnectivity )
171{
172 PICKED_ITEMS_LIST* commandToUndo = nullptr;
173
174 if( !aItemsList.GetCount() )
175 return;
176
178
179 // If the last stack was empty, use that one instead of creating a new stack
180 if( lastUndo )
181 {
182 if( aAppend || !lastUndo->GetCount() )
183 commandToUndo = lastUndo;
184 else
185 PushCommandToUndoList( lastUndo );
186 }
187
188 if( !commandToUndo )
189 {
190 commandToUndo = new PICKED_ITEMS_LIST();
191 commandToUndo->SetDescription( aItemsList.GetDescription() );
192 }
193
194 // Copy picker list:
195 if( !commandToUndo->GetCount() )
196 commandToUndo->CopyList( aItemsList );
197 else
198 {
199 // Unless we are appending, in which case, get the picker items
200 for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
201 commandToUndo->PushItem( aItemsList.GetItemWrapper( ii) );
202 }
203
204 // Verify list, and creates data if needed
205 for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ )
206 {
207 SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( commandToUndo->GetPickedItem( ii ) );
208
209 // Common items implemented in EDA_DRAW_FRAME will not be SCH_ITEMs.
210 if( !sch_item )
211 continue;
212
213 UNDO_REDO command = commandToUndo->GetPickedItemStatus( ii );
214
215 if( command == UNDO_REDO::UNSPECIFIED )
216 {
217 command = aTypeCommand;
218 commandToUndo->SetPickedItemStatus( command, ii );
219 }
220
221 switch( command )
222 {
223 case UNDO_REDO::CHANGED:
224
225 /* If needed, create a copy of item, and put in undo list
226 * in the picker, as link
227 * If this link is not null, the copy is already done
228 */
229 if( commandToUndo->GetPickedItemLink( ii ) == nullptr )
230 commandToUndo->SetPickedItemLink( sch_item->Duplicate( true ), ii );
231
232 wxASSERT( commandToUndo->GetPickedItemLink( ii ) );
233 break;
234
235 case UNDO_REDO::NEWITEM:
236 case UNDO_REDO::DELETED:
237 case UNDO_REDO::PAGESETTINGS:
238 break;
239
240 default:
241 wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ), command ) );
242 break;
243 }
244 }
245
246 if( commandToUndo->GetCount() )
247 {
248 /* Save the copy in undo list */
249 PushCommandToUndoList( commandToUndo );
250
251 /* Clear redo list, because after new save there is no redo to do */
253 }
254 else // Should not occur
255 {
256 delete commandToUndo;
257 }
258}
259
260
262{
263 std::vector<SCH_ITEM*> bulkAddedItems;
264 std::vector<SCH_ITEM*> bulkRemovedItems;
265 std::vector<SCH_ITEM*> bulkChangedItems;
266
267 // Undo in the reverse order of list creation: (this can allow stacked changes like the
268 // same item can be changed and deleted in the same complex command).
269 // After hitting 0, subtracting 1 will roll the value over to its max representation
270 for( unsigned ii = aList->GetCount() - 1; ii < std::numeric_limits<unsigned>::max(); ii-- )
271 {
272 UNDO_REDO status = aList->GetPickedItemStatus( ii );
273 EDA_ITEM* eda_item = aList->GetPickedItem( ii );
274 SCH_SCREEN* screen = dynamic_cast<SCH_SCREEN*>( aList->GetScreenForItem( ii ) );
275
276 wxCHECK( screen, /* void */ );
277
278 eda_item->SetFlags( aList->GetPickerFlags( ii ) );
279 eda_item->ClearEditFlags();
280 eda_item->ClearTempFlags();
281
282 if( status == UNDO_REDO::NEWITEM )
283 {
284 // If we are removing the current sheet, get out first
285 if( eda_item->Type() == SCH_SHEET_T )
286 {
287 if( static_cast<SCH_SHEET*>( eda_item )->GetScreen() == GetScreen() )
289 }
290
291 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( eda_item );
292
293 if( schItem && schItem->IsConnectable() )
295
296 RemoveFromScreen( eda_item, screen );
297 aList->SetPickedItemStatus( UNDO_REDO::DELETED, ii );
298
299 bulkRemovedItems.emplace_back( schItem );
300 }
301 else if( status == UNDO_REDO::DELETED )
302 {
303 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( eda_item );
304
305 if( schItem && schItem->IsConnectable() )
307
308 // deleted items are re-inserted on undo
309 AddToScreen( eda_item, screen );
310 aList->SetPickedItemStatus( UNDO_REDO::NEWITEM, ii );
311
312 bulkAddedItems.emplace_back( schItem );
313 }
314 else if( status == UNDO_REDO::PAGESETTINGS )
315 {
317
318 if( GetCurrentSheet() != undoSheet )
319 {
320 SetCurrentSheet( undoSheet );
322 }
323
324 // swap current settings with stored settings
325 DS_PROXY_UNDO_ITEM alt_item( this );
326 DS_PROXY_UNDO_ITEM* item = static_cast<DS_PROXY_UNDO_ITEM*>( eda_item );
327 item->Restore( this );
328 *item = alt_item;
329 }
330 else if( SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( eda_item ) )
331 {
332 if( item->IsConnectable() )
334
335 // everything else is modified in place
336 SCH_ITEM* alt_item = static_cast<SCH_ITEM*>( aList->GetPickedItemLink( ii ) );
337
338 // The root sheet is a pseudo object that owns the root screen object but is not on
339 // the root screen so do not attempt to remove it from the screen it owns.
340 if( item != &Schematic().Root() )
341 RemoveFromScreen( item, screen );
342
343 switch( status )
344 {
345 case UNDO_REDO::CHANGED:
346 item->SwapData( alt_item );
347 bulkChangedItems.emplace_back( item );
348
349 // Special cases for items which have instance data
350 if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T
351 && item->Type() == SCH_FIELD_T )
352 {
353 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
354 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item->GetParent() );
355
356 if( field->GetId() == REFERENCE_FIELD )
357 {
359 symbol->SetRef( &sheet, field->GetText() );
360 }
361
362 bulkChangedItems.emplace_back( symbol );
363 }
364
365 break;
366
367 default:
368 wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ),
369 aList->GetPickedItemStatus( ii ) ) );
370 break;
371 }
372
373 if( item->Type() == SCH_SYMBOL_T )
374 {
375 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
376 sym->UpdatePins();
377 }
378
379 if( item != &Schematic().Root() )
380 AddToScreen( item, screen );
381 }
382 }
383
385
386 // Notify our listeners
387 if( bulkAddedItems.size() > 0 )
388 Schematic().OnItemsAdded( bulkAddedItems );
389
390 if( bulkRemovedItems.size() > 0 )
391 Schematic().OnItemsRemoved( bulkRemovedItems );
392
393 if( bulkChangedItems.size() > 0 )
394 Schematic().OnItemsChanged( bulkChangedItems );
395}
396
397
399{
401
402 // Skip empty frames
403 while( undo && !undo->GetCount() )
404 {
405 delete undo;
407 }
408
409 if( undo )
410 {
412 undo->ClearListAndDeleteItems( []( EDA_ITEM* aItem )
413 {
414 delete aItem;
415 } );
416 delete undo;
417
420
422
423 m_toolManager->GetTool<EE_SELECTION_TOOL>()->RebuildSelection();
424 }
425
426 GetCanvas()->Refresh();
427}
428
429
431{
432 if( aItemCount == 0 )
433 return;
434
435 UNDO_REDO_CONTAINER& list = whichList == UNDO_LIST ? m_undoList : m_redoList;
436
437 for( PICKED_ITEMS_LIST* command : list.m_CommandsList )
438 {
439 command->ClearListAndDeleteItems( []( EDA_ITEM* aItem )
440 {
441 delete aItem;
442 } );
443 delete command;
444 }
445
446 list.m_CommandsList.clear();
447}
448
449
void Restore(EDA_DRAW_FRAME *aFrame, KIGFX::VIEW *aView=nullptr)
virtual void PushCommandToUndoList(PICKED_ITEMS_LIST *aItem)
Add a command to undo in the undo list.
UNDO_REDO_CONTAINER m_undoList
UNDO_REDO_LIST
Specifies whether we are interacting with the undo or redo stacks.
UNDO_REDO_CONTAINER m_redoList
virtual PICKED_ITEMS_LIST * PopCommandFromUndoList()
Return the last command to undo and remove it from list, nothing is deleted.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:123
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
void ClearTempFlags()
Definition: eda_item.h:149
void ClearEditFlags()
Definition: eda_item.h:137
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:126
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:95
static TOOL_ACTION leaveSheet
Definition: ee_actions.h:215
void SetLink(EDA_ITEM *aItem)
void SetFlags(EDA_ITEM_FLAGS aFlags)
void ClearHiddenFlags()
Clear the hide flag of all items in the view.
Definition: sch_view.cpp:184
A holder to handle information on schematic or board items.
bool SetPickedItemStatus(UNDO_REDO aStatus, unsigned aIdx)
Set the type of undo/redo operation for a given picked item.
EDA_ITEM_FLAGS GetPickerFlags(unsigned aIdx) const
Return the value of the picker flag.
void PushItem(const ITEM_PICKER &aItem)
Push aItem to the top of the list.
void SetDescription(const wxString &aDescription)
UNDO_REDO GetPickedItemStatus(unsigned int aIdx) const
ITEM_PICKER GetItemWrapper(unsigned int aIdx) const
EDA_ITEM * GetPickedItemLink(unsigned int aIdx) const
wxString GetDescription() const
unsigned GetCount() const
void CopyList(const PICKED_ITEMS_LIST &aSource)
Copy all data from aSource to the list.
bool SetPickedItemLink(EDA_ITEM *aLink, unsigned aIdx)
Set the link associated to a given picked item.
void ClearListAndDeleteItems(std::function< void(EDA_ITEM *)> aItemDeleter)
Delete the list of pickers AND the data pointed by #m_PickedItem or #m_PickedItemLink according to th...
BASE_SCREEN * GetScreenForItem(unsigned int aIdx) const
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
void OnItemsAdded(std::vector< SCH_ITEM * > &aNewItems)
Must be used if Add() is used using a BULK_x ADD_MODE to generate a change event for listeners.
Definition: schematic.cpp:682
void OnItemsRemoved(std::vector< SCH_ITEM * > &aRemovedItems)
Must be used if Remove() is used using a BULK_x REMOVE_MODE to generate a change event for listeners.
Definition: schematic.cpp:688
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:100
SCH_SHEET & Root() const
Definition: schematic.h:105
void OnItemsChanged(std::vector< SCH_ITEM * > &aItems)
Notify the schematic and its listeners that an item on the schematic has been modified in some way.
Definition: schematic.cpp:694
void AddToScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen=nullptr)
Add an item to the screen (and view) aScreen is the screen the item is located on,...
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void RemoveFromScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen)
Remove an item from the screen (and view) aScreen is the screen the item is located on,...
wxString Name(bool aIgnoreSheet=false) const
KIGFX::SCH_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
void RollbackSchematicFromUndo()
Perform an undo of the last edit WITHOUT logging a corresponding redo.
bool m_highlightedConnChanged
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void SaveCopyInUndoList(SCH_SCREEN *aScreen, SCH_ITEM *aItemToCopy, UNDO_REDO aTypeCommand, bool aAppend, bool aDirtyConnectivity=true)
Create a copy of the current schematic item, and put it in the undo list.
void ClearUndoORRedoList(UNDO_REDO_LIST whichList, int aItemCount=-1) override
Free the undo or redo list from aList element.
SCHEMATIC * m_schematic
The currently loaded schematic.
SCH_SHEET_PATH & GetCurrentSheet() const
SCHEMATIC & Schematic() const
void SetSheetNumberAndCount()
Set the m_ScreenNumber and m_NumberOfScreens members for screens.
void SetCurrentSheet(const SCH_SHEET_PATH &aSheet)
void DisplayCurrentSheet()
Draw the current sheet on the display.
void PutDataInPreviousState(PICKED_ITEMS_LIST *aList)
Restore an undo or redo command to put data pointed by aList in the previous state.
void UpdateHierarchyNavigator()
Update the hierarchy navigation tree and history.
wxString m_highlightedConn
The highlighted net or bus or empty string.
void TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:52
int GetId() const
Definition: sch_field.h:128
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:150
virtual bool IsConnectable() const
Definition: sch_item.h:362
void SetConnectivityDirty(bool aDirty=true)
Definition: sch_item.h:428
bool IsConnectivityDirty() const
Definition: sch_item.h:426
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:147
SCH_ITEM * Duplicate(bool doClone=false) const
Routine to create a new copy of given item.
Definition: sch_item.cpp:94
SCH_SHEET_PATH FindSheetForScreen(const SCH_SCREEN *aScreen)
Return the first SCH_SHEET_PATH object (not necessarily the only one) using a particular screen.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:57
Schematic symbol object.
Definition: sch_symbol.h:81
void UpdatePins()
Updates the cache of SCH_PIN objects for each pin.
Definition: sch_symbol.cpp:338
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
Definition: sch_symbol.cpp:762
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:165
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:230
A holder to handle a list of undo (or redo) commands.
std::vector< PICKED_ITEMS_LIST * > m_CommandsList
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ SCH_SYMBOL_T
Definition: typeinfo.h:155
@ SCH_FIELD_T
Definition: typeinfo.h:154
@ SCH_SHEET_T
Definition: typeinfo.h:157
UNDO_REDO
Undo Redo considerations: Basically we have 3 cases New item Deleted item Modified item there is also...