KiCad PCB EDA Suite
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-2021 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{
102 PushCommandToUndoList( blank );
103}
104
105
107 UNDO_REDO aCommandType, bool aAppend,
108 bool aDirtyConnectivity )
109{
110 PICKED_ITEMS_LIST* commandToUndo = nullptr;
111
112 wxCHECK( aItem, /* void */ );
113
114 if( aDirtyConnectivity )
115 aItem->SetConnectivityDirty();
116
118
119 // If the last stack was empty, use that one instead of creating a new stack
120 if( lastUndo )
121 {
122 if( aAppend || !lastUndo->GetCount() )
123 commandToUndo = lastUndo;
124 else
125 PushCommandToUndoList( lastUndo );
126 }
127
128 if( !commandToUndo )
129 {
130 commandToUndo = new PICKED_ITEMS_LIST();
131 }
132
133 ITEM_PICKER itemWrapper( aScreen, aItem, aCommandType );
134 itemWrapper.SetFlags( aItem->GetFlags() );
135
136 switch( aCommandType )
137 {
138 case UNDO_REDO::CHANGED: /* Create a copy of item */
139 itemWrapper.SetLink( aItem->Duplicate( true ) );
140 commandToUndo->PushItem( itemWrapper );
141 break;
142
145 commandToUndo->PushItem( itemWrapper );
146 break;
147
148 default:
149 wxFAIL_MSG( wxString::Format( wxT( "SaveCopyInUndoList() error (unknown code %X)" ),
150 aCommandType ) );
151 break;
152 }
153
154 if( commandToUndo->GetCount() )
155 {
156 /* Save the copy in undo list */
157 PushCommandToUndoList( commandToUndo );
158
159 /* Clear redo list, because after new save there is no redo to do */
161 }
162 else
163 {
164 delete commandToUndo;
165 }
166}
167
168
170 UNDO_REDO aTypeCommand, bool aAppend,
171 bool aDirtyConnectivity )
172{
173 PICKED_ITEMS_LIST* commandToUndo = nullptr;
174
175 if( !aItemsList.GetCount() )
176 return;
177
179
180 // If the last stack was empty, use that one instead of creating a new stack
181 if( lastUndo )
182 {
183 if( aAppend || !lastUndo->GetCount() )
184 commandToUndo = lastUndo;
185 else
186 PushCommandToUndoList( lastUndo );
187 }
188
189 if( !commandToUndo )
190 commandToUndo = new PICKED_ITEMS_LIST();
191
192 // Copy picker list:
193 if( !commandToUndo->GetCount() )
194 commandToUndo->CopyList( aItemsList );
195 else
196 {
197 // Unless we are appending, in which case, get the picker items
198 for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
199 commandToUndo->PushItem( aItemsList.GetItemWrapper( ii) );
200 }
201
202 // Verify list, and creates data if needed
203 for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ )
204 {
205 SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( commandToUndo->GetPickedItem( ii ) );
206
207 // Common items implemented in EDA_DRAW_FRAME will not be SCH_ITEMs.
208 if( !sch_item )
209 continue;
210
211 if( aDirtyConnectivity )
212 sch_item->SetConnectivityDirty();
213
214 UNDO_REDO command = commandToUndo->GetPickedItemStatus( ii );
215
216 if( command == UNDO_REDO::UNSPECIFIED )
217 {
218 command = aTypeCommand;
219 commandToUndo->SetPickedItemStatus( command, ii );
220 }
221
222 switch( command )
223 {
225
226 /* If needed, create a copy of item, and put in undo list
227 * in the picker, as link
228 * If this link is not null, the copy is already done
229 */
230 if( commandToUndo->GetPickedItemLink( ii ) == nullptr )
231 commandToUndo->SetPickedItemLink( sch_item->Duplicate( true ), ii );
232
233 wxASSERT( commandToUndo->GetPickedItemLink( ii ) );
234 break;
235
240 break;
241
242 default:
243 wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ), command ) );
244 break;
245 }
246 }
247
248 if( commandToUndo->GetCount() )
249 {
250 /* Save the copy in undo list */
251 PushCommandToUndoList( commandToUndo );
252
253 /* Clear redo list, because after new save there is no redo to do */
255 }
256 else // Should not occur
257 {
258 delete commandToUndo;
259 }
260}
261
262
264{
265 // Undo in the reverse order of list creation: (this can allow stacked changes like the
266 // same item can be changed and deleted in the same complex command).
267 // After hitting 0, subtracting 1 will roll the value over to its max representation
268 for( unsigned ii = aList->GetCount() - 1; ii < std::numeric_limits<unsigned>::max(); ii-- )
269 {
270 UNDO_REDO status = aList->GetPickedItemStatus( ii );
271 EDA_ITEM* eda_item = aList->GetPickedItem( ii );
272 SCH_SCREEN* screen = dynamic_cast<SCH_SCREEN*>( aList->GetScreenForItem( ii ) );
273
274 wxCHECK( screen, /* void */ );
275
276 eda_item->SetFlags( aList->GetPickerFlags( ii ) );
277 eda_item->ClearEditFlags();
278 eda_item->ClearTempFlags();
279
280 if( status == UNDO_REDO::NOP )
281 {
282 continue;
283 }
284 if( status == UNDO_REDO::NEWITEM )
285 {
286 // If we are removing the current sheet, get out first
287 if( SCH_SHEET* sheet = dyn_cast<SCH_SHEET*>( eda_item ) )
288 {
289 if( sheet->GetScreen() == GetScreen() )
291 }
292
293 RemoveFromScreen( eda_item, screen );
295 }
296 else if( status == UNDO_REDO::DELETED )
297 {
298 // deleted items are re-inserted on undo
299 AddToScreen( eda_item, screen );
301 }
302 else if( status == UNDO_REDO::PAGESETTINGS )
303 {
306
307 // swap current settings with stored settings
308 DS_PROXY_UNDO_ITEM alt_item( this );
309 DS_PROXY_UNDO_ITEM* item = static_cast<DS_PROXY_UNDO_ITEM*>( eda_item );
310 item->Restore( this );
311 *item = alt_item;
312 }
313 else if( SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( eda_item ) )
314 {
315 // everything else is modified in place
316 SCH_ITEM* alt_item = static_cast<SCH_ITEM*>( aList->GetPickedItemLink( ii ) );
317
318 // The root sheet is a pseudo object that owns the root screen object but is not on
319 // the root screen so do not attempt to remove it from the screen it owns.
320 if( item != &Schematic().Root() )
321 RemoveFromScreen( item, screen );
322
323 switch( status )
324 {
326 item->SwapData( alt_item );
327
328 // Special cases for items which have instance data
329 if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T
330 && item->Type() == SCH_FIELD_T )
331 {
332 SCH_FIELD* field = static_cast<SCH_FIELD*>( item );
333 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item->GetParent() );
334
335 if( field->GetId() == REFERENCE_FIELD )
336 {
337 symbol->SetRef( m_schematic->GetSheets().FindSheetForScreen( screen ),
338 field->GetText() );
339 }
340 else if( field->GetId() == VALUE_FIELD )
341 {
342 symbol->SetValue( m_schematic->GetSheets().FindSheetForScreen( screen ),
343 field->GetText() );
344 }
345 else if( field->GetId() == FOOTPRINT_FIELD )
346 {
348 field->GetText() );
349 }
350 }
351
352 break;
353
355 aList->SetPickedItem( alt_item, ii );
356 aList->SetPickedItemLink( item, ii );
357 item = alt_item;
358 break;
359
360 default:
361 wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ),
362 aList->GetPickedItemStatus( ii ) ) );
363 break;
364 }
365
366 if( item->Type() == SCH_SYMBOL_T )
367 {
368 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
369 sym->UpdatePins();
370 }
371
372 if( item != &Schematic().Root() )
373 AddToScreen( item, screen );
374 }
375 }
376
378}
379
380
382{
384
385 // Skip empty frames
386 while( undo && ( !undo->GetCount()
387 || ( undo->GetCount() == 1 && undo->GetPickedItemStatus( 0 ) == UNDO_REDO::NOP ) ) )
388 {
389 delete undo;
391 }
392
393 if( undo )
394 {
396 undo->ClearListAndDeleteItems();
397 delete undo;
398
401
403
404 m_toolManager->GetTool<EE_SELECTION_TOOL>()->RebuildSelection();
405 }
406
407 GetCanvas()->Refresh();
408}
409
410
412{
413 if( aItemCount == 0 )
414 return;
415
416 UNDO_REDO_CONTAINER& list = whichList == UNDO_LIST ? m_undoList : m_redoList;
417
418 for( PICKED_ITEMS_LIST* command : list.m_CommandsList )
419 {
420 command->ClearListAndDeleteItems();
421 delete command;
422 }
423
424 list.m_CommandsList.clear();
425}
426
427
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
Update the board display after modifying it by a python script (note: it is automatically called by a...
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:142
void ClearTempFlags()
Definition: eda_item.h:155
void ClearEditFlags()
Definition: eda_item.h:160
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:144
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:87
static TOOL_ACTION leaveSheet
Definition: ee_actions.h:202
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:181
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.
UNDO_REDO GetPickedItemStatus(unsigned int aIdx) const
ITEM_PICKER GetItemWrapper(unsigned int aIdx) const
EDA_ITEM * GetPickedItemLink(unsigned int aIdx) const
unsigned GetCount() const
bool SetPickedItem(EDA_ITEM *aItem, unsigned aIdx)
void CopyList(const PICKED_ITEMS_LIST &aSource)
Copy all data from aSource to the list.
void ClearListAndDeleteItems()
Delete the list of pickers AND the data pointed by #m_PickedItem or #m_PickedItemLink according to th...
bool SetPickedItemLink(EDA_ITEM *aLink, unsigned aIdx)
Set the link associated to a given picked item.
BASE_SCREEN * GetScreenForItem(unsigned int aIdx) const
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:85
SCH_SHEET & Root() const
Definition: schematic.h:90
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,...
void AddToScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen)
Add an item to the screen (and view) aScreen is the screen the item is located on,...
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.
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.
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.
void StartNewUndo()
Create a new, blank stack for future Undo commands to be pushed to.
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:50
int GetId() const
Definition: sch_field.h:116
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
void SetConnectivityDirty(bool aDirty=true)
Definition: sch_item.h:406
SCH_ITEM * Duplicate(bool doClone=false) const
Routine to create a new copy of given item.
Definition: sch_item.cpp:93
SCH_SHEET_PATH * FindSheetForScreen(const SCH_SCREEN *aScreen)
Return a pointer to the first SCH_SHEET_PATH object (not necessarily the only one) using a particular...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:55
Schematic symbol object.
Definition: sch_symbol.h:79
void SetValue(const SCH_SHEET_PATH *sheet, const wxString &aValue)
Definition: sch_symbol.cpp:679
void SetFootprint(const SCH_SHEET_PATH *sheet, const wxString &aFootprint)
Definition: sch_symbol.cpp:738
void UpdatePins()
Updates the cache of SCH_PIN objects for each pin.
Definition: sch_symbol.cpp:341
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:558
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:170
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
A holder to handle a list of undo (or redo) commands.
std::vector< PICKED_ITEMS_LIST * > m_CommandsList
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
@ FOOTPRINT_FIELD
Field Name Module PCB, i.e. "16DIP300".
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ SCH_SYMBOL_T
Definition: typeinfo.h:155
@ SCH_FIELD_T
Definition: typeinfo.h:154
UNDO_REDO
Undo Redo considerations: Basically we have 3 cases New item Deleted item Modified item there is also...