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, jaen-pierre.charras@gipsa-lab.inpg.com
5  * Copyright (C) 2004-2019 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 <fctsys.h>
26 #include <sch_draw_panel.h>
27 #include <sch_edit_frame.h>
28 #include <tool/tool_manager.h>
29 #include <general.h>
30 #include <sch_bus_entry.h>
31 #include <sch_marker.h>
32 #include <sch_junction.h>
33 #include <sch_line.h>
34 #include <sch_no_connect.h>
35 #include <sch_component.h>
36 #include <sch_sheet.h>
37 #include <sch_bitmap.h>
38 #include <sch_view.h>
40 
41 /* Functions to undo and redo edit commands.
42  * commands to undo are stored in CurrentScreen->m_UndoList
43  * commands to redo are stored in CurrentScreen->m_RedoList
44  *
45  * m_UndoList and m_RedoList handle a std::vector of PICKED_ITEMS_LIST
46  * Each PICKED_ITEMS_LIST handle a std::vector of pickers (class ITEM_PICKER),
47  * that store the list of schematic items that are concerned by the command to
48  * undo or redo and is created for each command to undo (handle also a command
49  * to redo). each picker has a pointer pointing to an item to undo or redo (in
50  * fact: deleted, added or modified), and has a pointer to a copy of this item,
51  * when this item has been modified (the old values of parameters are
52  * therefore saved)
53  *
54  * there are 3 cases:
55  * - delete item(s) command
56  * - change item(s) command
57  * - add item(s) command
58  * and 2 cases for block:
59  * - move list of items
60  * - mirror (Y) list of items
61  *
62  * Undo command
63  * - delete item(s) command:
64  * => deleted items are moved in undo list
65  *
66  * - change item(s) command
67  * => A copy of item(s) is made (a DrawPickedStruct list of wrappers)
68  * the .m_Link member of each wrapper points the modified item.
69  * the .m_Item member of each wrapper points the old copy of this item.
70  *
71  * - add item(s) command
72  * =>A list of item(s) is made. The .m_Item member of each wrapper points
73  * the new item.
74  *
75  * Redo command
76  * - delete item(s) old command:
77  * => deleted items are moved in GetDrawItems() list, and in
78  *
79  * - change item(s) command
80  * => the copy of item(s) is moved in Undo list
81  *
82  * - add item(s) command
83  * => The list of item(s) is used to create a deleted list in undo
84  * list(same as a delete command)
85  *
86  * Some block operations that change items can be undone without memorized
87  * items, just the coordinates of the transform: move list of items (undo/
88  * redo is made by moving with the opposite move vector) mirror (Y) and flip
89  * list of items (undo/redo is made by mirror or flip items) so they are
90  * handled specifically.
91  *
92  * A problem is the hierarchical sheet handling.
93  * the data associated (sub-hierarchy, undo/redo list) is deleted only
94  * when the sheet is really deleted (i.e. when deleted from undo or redo list)
95  * This is handled by its destructor.
96  */
97 
98 
99 /* Used if undo / redo command:
100  * swap data between Item and its copy, pointed by its picked item link member
101  * swapped data is data modified by editing, so not all values are swapped
102  */
103 
105  UNDO_REDO_T aCommandType,
106  bool aAppend,
107  const wxPoint& aTransformPoint )
108 {
109  PICKED_ITEMS_LIST* commandToUndo = NULL;
110 
111  if( aItem == NULL )
112  return;
113 
114  // Connectivity may change
115  aItem->SetConnectivityDirty();
116 
117  if( aAppend )
118  commandToUndo = GetScreen()->PopCommandFromUndoList();
119 
120  if( !commandToUndo )
121  {
122  commandToUndo = new PICKED_ITEMS_LIST();
123  commandToUndo->m_TransformPoint = aTransformPoint;
124  }
125 
126  ITEM_PICKER itemWrapper( aItem, aCommandType );
127  itemWrapper.SetFlags( aItem->GetFlags() );
128 
129  switch( aCommandType )
130  {
131  case UR_CHANGED: /* Create a copy of item */
132  itemWrapper.SetLink( aItem->Duplicate( true ) );
133  commandToUndo->PushItem( itemWrapper );
134  break;
135 
136  case UR_NEW:
137  case UR_DELETED:
138  case UR_ROTATED:
139  case UR_MOVED:
140  commandToUndo->PushItem( itemWrapper );
141  break;
142 
143  default:
144  wxFAIL_MSG( wxString::Format( wxT( "SaveCopyInUndoList() error (unknown code %X)" ),
145  aCommandType ) );
146  break;
147  }
148 
149  if( commandToUndo->GetCount() )
150  {
151  /* Save the copy in undo list */
152  GetScreen()->PushCommandToUndoList( commandToUndo );
153 
154  /* Clear redo list, because after new save there is no redo to do */
155  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
156  }
157  else
158  {
159  delete commandToUndo;
160  }
161 }
162 
163 
165  UNDO_REDO_T aTypeCommand,
166  bool aAppend,
167  const wxPoint& aTransformPoint )
168 {
169  PICKED_ITEMS_LIST* commandToUndo = NULL;
170 
171  if( !aItemsList.GetCount() )
172  return;
173 
174  // Can't append a WIRE IMAGE, so fail to a new undo point
175  if( aAppend )
176  commandToUndo = GetScreen()->PopCommandFromUndoList();
177 
178  if( !commandToUndo )
179  {
180  commandToUndo = new PICKED_ITEMS_LIST();
181  commandToUndo->m_TransformPoint = aTransformPoint;
182  commandToUndo->m_Status = aTypeCommand;
183  }
184 
185  // Copy picker list:
186  if( !commandToUndo->GetCount() )
187  commandToUndo->CopyList( aItemsList );
188  else
189  {
190  // Unless we are appending, in which case, get the picker items
191  for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
192  commandToUndo->PushItem( aItemsList.GetItemWrapper( ii) );
193  }
194 
195  // Verify list, and creates data if needed
196  for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ )
197  {
198  SCH_ITEM* item = (SCH_ITEM*) commandToUndo->GetPickedItem( ii );
199  wxASSERT( item );
200 
201  // Connectivity may change
202  item->SetConnectivityDirty();
203 
204  UNDO_REDO_T command = commandToUndo->GetPickedItemStatus( ii );
205 
206  if( command == UR_UNSPECIFIED )
207  {
208  command = aTypeCommand;
209  commandToUndo->SetPickedItemStatus( command, ii );
210  }
211 
212  switch( command )
213  {
214  case UR_CHANGED: /* Create a copy of item */
215 
216  /* If needed, create a copy of item, and put in undo list
217  * in the picker, as link
218  * If this link is not null, the copy is already done
219  */
220  if( commandToUndo->GetPickedItemLink( ii ) == NULL )
221  commandToUndo->SetPickedItemLink( item->Duplicate( true ), ii );
222 
223  wxASSERT( commandToUndo->GetPickedItemLink( ii ) );
224  break;
225 
226  case UR_MOVED:
227  case UR_MIRRORED_Y:
228  case UR_MIRRORED_X:
229  case UR_ROTATED:
230  case UR_NEW:
231  case UR_DELETED:
232  case UR_EXCHANGE_T:
233  break;
234 
235  default:
236  wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ), command ) );
237  break;
238  }
239  }
240 
241  if( commandToUndo->GetCount() )
242  {
243  /* Save the copy in undo list */
244  GetScreen()->PushCommandToUndoList( commandToUndo );
245 
246  /* Clear redo list, because after new save there is no redo to do */
247  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
248  }
249  else // Should not occur
250  {
251  delete commandToUndo;
252  }
253 }
254 
255 
257 {
258  SCH_ITEM* item;
259  SCH_ITEM* alt_item;
260 
261  // Undo in the reverse order of list creation: (this can allow stacked changes like the
262  // same item can be changed and deleted in the same complex command).
263  for( int ii = aList->GetCount() - 1; ii >= 0; ii-- )
264  {
265  UNDO_REDO_T status = aList->GetPickedItemStatus((unsigned) ii );
266  item = (SCH_ITEM*) aList->GetPickedItem( (unsigned) ii );
267  alt_item = (SCH_ITEM*) aList->GetPickedItemLink( (unsigned) ii );
268 
269  item->SetFlags( aList->GetPickerFlags( (unsigned) ii ) );
270  item->ClearEditFlags();
271  item->ClearTempFlags();
272 
273  if( status == UR_NEW )
274  {
275  // new items are deleted on undo
276  RemoveFromScreen( item );
277  aList->SetPickedItemStatus( UR_DELETED, (unsigned) ii );
278  }
279  else if (status == UR_DELETED )
280  {
281  // deleted items are re-inserted on undo
282  AddToScreen( item );
283  aList->SetPickedItemStatus( UR_NEW, (unsigned) ii );
284  }
285  else
286  {
287  // everthing else is modified in place
288  RemoveFromScreen( item );
289 
290  switch( status )
291  {
292  case UR_CHANGED:
293  item->SwapData( alt_item );
294  break;
295 
296  case UR_MOVED:
297  item->Move( aRedoCommand ? aList->m_TransformPoint : -aList->m_TransformPoint );
298  break;
299 
300  case UR_MIRRORED_Y:
301  item->MirrorY( aList->m_TransformPoint.x );
302  break;
303 
304  case UR_MIRRORED_X:
305  item->MirrorX( aList->m_TransformPoint.y );
306  break;
307 
308  case UR_ROTATED:
309  if( aRedoCommand )
310  item->Rotate( aList->m_TransformPoint );
311  else
312  {
313  // Rotate 270 deg to undo 90-deg rotate
314  item->Rotate( aList->m_TransformPoint );
315  item->Rotate( aList->m_TransformPoint );
316  item->Rotate( aList->m_TransformPoint );
317  }
318  break;
319 
320  case UR_EXCHANGE_T:
321  alt_item->SetNext( NULL );
322  alt_item->SetBack( NULL );
323  aList->SetPickedItem( alt_item, (unsigned) ii );
324  aList->SetPickedItemLink( item, (unsigned) ii );
325  item = alt_item;
326  break;
327 
328  default:
329  wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ),
330  aList->GetPickedItemStatus( (unsigned) ii ) ) );
331  break;
332  }
333 
334  AddToScreen( item );
335  }
336  }
337 
339  selTool->RebuildSelection();
340 
341  // Bitmaps are cached in Opengl: clear the cache, because
342  // the cache data can be invalid
345 }
346 
347 
349 {
351  PutDataInPreviousState( undo, false );
352 
353  undo->ClearListAndDeleteItems();
354  delete undo;
355 
357 
359 
360  SyncView();
361  GetCanvas()->Refresh();
362 }
TOOL_MANAGER * m_toolManager
Definition: draw_frame.h:130
virtual void PushCommandToUndoList(PICKED_ITEMS_LIST *aItem)
Function PushCommandToUndoList add a command to undo in undo list delete the very old commands when t...
EDA_ITEM * GetPickedItemLink(unsigned int aIdx) const
Function GetPickedItemLink.
void ClearHiddenFlags()
Clear the hide flag of all items in the view.
Definition: sch_view.cpp:179
SCH_ITEM * Duplicate(bool doClone=false)
Routine to create a new copy of given item.
Definition: sch_item.cpp:75
void ClearTempFlags()
Definition: base_struct.h:271
void SetBack(EDA_ITEM *aBack)
Definition: base_struct.h:218
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,...
UNDO_REDO_T
Undo Redo considerations: Basically we have 3 cases New item Deleted item Modified item there is also...
void RecacheAllItems()
Function RecacheAllItems() Rebuilds GAL display lists.
Definition: view.cpp:1401
STATUS_FLAGS GetPickerFlags(unsigned aIdx) const
Function GetPickerFlags returns the value of the picker flag.
void PushItem(const ITEM_PICKER &aItem)
Function PushItem pushes aItem to the top of the list.
bool TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
void RemoveFromScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen=nullptr)
Remove an item from the screen (and view) aScreen is the screen the item is located on,...
void Refresh(bool aEraseBackground=true, const wxRect *aRect=NULL) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
void SetLink(EDA_ITEM *aItem)
unsigned GetCount() const
Function GetCount.
virtual void Rotate(wxPoint aPosition)=0
Function Rotate rotates the item around aPosition 90 degrees in the clockwise direction.
virtual PICKED_ITEMS_LIST * PopCommandFromUndoList()
PopCommandFromUndoList return the last command to undo and remove it from list nothing is deleted.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void CopyList(const PICKED_ITEMS_LIST &aSource)
Function CopyList copies all data from aSource to the list.
void SetFlags(STATUS_FLAGS aMask)
Definition: base_struct.h:259
virtual void SwapData(SCH_ITEM *aItem)
Function SwapData swap the internal data structures aItem with the schematic item.
Definition: sch_item.cpp:164
bool SetPickedItem(EDA_ITEM *aItem, unsigned aIdx)
Function SetPickedItem.
void SyncView()
Mark all items for refresh.
SCH_DRAW_PANEL * GetCanvas() const override
void SaveCopyInUndoList(SCH_ITEM *aItemToCopy, UNDO_REDO_T aTypeCommand, bool aAppend=false, const wxPoint &aTransformPoint=wxPoint(0, 0))
Create a copy of the current schematic item, and put it in the undo list.
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
Function GetPickedItem.
void SetFlags(STATUS_FLAGS aFlags)
virtual void MirrorX(int aXaxis_position)=0
Function MirrorX mirrors item relative to the X axis about aXaxis_position.
Class PICKED_ITEMS_LIST is a holder to handle information on schematic or board items.
virtual void MirrorY(int aYaxis_position)=0
Function MirrorY mirrors item relative to the Y axis about aYaxis_position.
Use for changing the schematic text type where swapping data structure is insufficient to restore the...
KIGFX::SCH_VIEW * GetView() const
void PutDataInPreviousState(PICKED_ITEMS_LIST *aList, bool aRedoCommand)
Restore an undo or redo command to put data pointed by aList in the previous state.
ITEM_PICKER GetItemWrapper(unsigned int aIdx) const
Function GetItemWrapper.
void SetNext(EDA_ITEM *aNext)
Definition: base_struct.h:217
bool SetPickedItemStatus(UNDO_REDO_T aStatus, unsigned aIdx)
Function SetPickedItemStatus sets the type of undo/redo operation for a given picked item.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
void SetSheetNumberAndCount()
Set the m_ScreenNumber and m_NumberOfScreens members for screens.
void RollbackSchematicFromUndo()
Performs an undo of the last edit WITHOUT logging a corresponding redo.
UNDO_REDO_T GetPickedItemStatus(unsigned int aIdx) const
Function GetPickedItemStatus.
void RebuildSelection()
Rebuilds the selection from the EDA_ITEMs' selection flags.
bool SetPickedItemLink(EDA_ITEM *aLink, unsigned aIdx)
Function SetPickedItemLink set the link associated to a given picked item.
virtual void ClearUndoORRedoList(UNDO_REDO_CONTAINER &aList, int aItemCount=-1) override
Free the undo or redo list from aList element.
Definition: sch_screen.cpp:589
void ClearEditFlags()
Definition: base_struct.h:276
STATUS_FLAGS GetFlags() const
Definition: base_struct.h:261
Class SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container clas...
Definition: sch_item.h:114
void ClearListAndDeleteItems()
Function ClearListAndDeleteItems deletes the list of pickers, AND the data pointed by m_PickedItem or...
void SetConnectivityDirty(bool aDirty=true)
Definition: sch_item.h:358
virtual void Move(const wxPoint &aMoveVector)=0
Function Move moves the item by aMoveVector to a new position.