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 #include <ws_proxy_undo_item.h>
41 #include <tool/actions.h>
42 
43 /* Functions to undo and redo edit commands.
44  * commands to undo are stored in CurrentScreen->m_UndoList
45  * commands to redo are stored in CurrentScreen->m_RedoList
46  *
47  * m_UndoList and m_RedoList handle a std::vector of PICKED_ITEMS_LIST
48  * Each PICKED_ITEMS_LIST handle a std::vector of pickers (class ITEM_PICKER),
49  * that store the list of schematic items that are concerned by the command to
50  * undo or redo and is created for each command to undo (handle also a command
51  * to redo). each picker has a pointer pointing to an item to undo or redo (in
52  * fact: deleted, added or modified), and has a pointer to a copy of this item,
53  * when this item has been modified (the old values of parameters are
54  * therefore saved)
55  *
56  * there are 3 cases:
57  * - delete item(s) command
58  * - change item(s) command
59  * - add item(s) command
60  * and 2 cases for block:
61  * - move list of items
62  * - mirror (Y) list of items
63  *
64  * Undo command
65  * - delete item(s) command:
66  * => deleted items are moved in undo list
67  *
68  * - change item(s) command
69  * => A copy of item(s) is made (a DrawPickedStruct list of wrappers)
70  * the .m_Link member of each wrapper points the modified item.
71  * the .m_Item member of each wrapper points the old copy of this item.
72  *
73  * - add item(s) command
74  * =>A list of item(s) is made. The .m_Item member of each wrapper points
75  * the new item.
76  *
77  * Redo command
78  * - delete item(s) old command:
79  * => deleted items are moved in GetDrawItems() list, and in
80  *
81  * - change item(s) command
82  * => the copy of item(s) is moved in Undo list
83  *
84  * - add item(s) command
85  * => The list of item(s) is used to create a deleted list in undo
86  * list(same as a delete command)
87  *
88  * Some block operations that change items can be undone without memorized
89  * items, just the coordinates of the transform: move list of items (undo/
90  * redo is made by moving with the opposite move vector) mirror (Y) and flip
91  * list of items (undo/redo is made by mirror or flip items) so they are
92  * handled specifically.
93  *
94  * A problem is the hierarchical sheet handling.
95  * the data associated (sub-hierarchy, undo/redo list) is deleted only
96  * when the sheet is really deleted (i.e. when deleted from undo or redo list)
97  * This is handled by its destructor.
98  */
99 
100 
101 /* Used if undo / redo command:
102  * swap data between Item and its copy, pointed by its picked item link member
103  * swapped data is data modified by editing, so not all values are swapped
104  */
105 
107  UNDO_REDO_T aCommandType,
108  bool aAppend,
109  const wxPoint& aTransformPoint )
110 {
111  PICKED_ITEMS_LIST* commandToUndo = nullptr;
112 
113  if( !aItem )
114  return;
115 
116  // Connectivity may change
117  aItem->SetConnectivityDirty();
118 
119  if( aAppend )
120  commandToUndo = GetScreen()->PopCommandFromUndoList();
121 
122  if( !commandToUndo )
123  {
124  commandToUndo = new PICKED_ITEMS_LIST();
125  commandToUndo->m_TransformPoint = aTransformPoint;
126  }
127 
128  ITEM_PICKER itemWrapper( aItem, aCommandType );
129  itemWrapper.SetFlags( aItem->GetFlags() );
130 
131  switch( aCommandType )
132  {
133  case UR_CHANGED: /* Create a copy of item */
134  itemWrapper.SetLink( aItem->Duplicate( true ) );
135  commandToUndo->PushItem( itemWrapper );
136  break;
137 
138  case UR_NEW:
139  case UR_DELETED:
140  case UR_ROTATED:
141  case UR_MOVED:
142  commandToUndo->PushItem( itemWrapper );
143  break;
144 
145  default:
146  wxFAIL_MSG( wxString::Format( wxT( "SaveCopyInUndoList() error (unknown code %X)" ),
147  aCommandType ) );
148  break;
149  }
150 
151  if( commandToUndo->GetCount() )
152  {
153  /* Save the copy in undo list */
154  GetScreen()->PushCommandToUndoList( commandToUndo );
155 
156  /* Clear redo list, because after new save there is no redo to do */
157  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
158  }
159  else
160  {
161  delete commandToUndo;
162  }
163 }
164 
165 
167  UNDO_REDO_T aTypeCommand,
168  bool aAppend,
169  const wxPoint& aTransformPoint )
170 {
171  PICKED_ITEMS_LIST* commandToUndo = nullptr;
172 
173  if( !aItemsList.GetCount() )
174  return;
175 
176  // Can't append a WIRE IMAGE, so fail to a new undo point
177  if( aAppend )
178  commandToUndo = GetScreen()->PopCommandFromUndoList();
179 
180  if( !commandToUndo )
181  {
182  commandToUndo = new PICKED_ITEMS_LIST();
183  commandToUndo->m_TransformPoint = aTransformPoint;
184  commandToUndo->m_Status = aTypeCommand;
185  }
186 
187  // Copy picker list:
188  if( !commandToUndo->GetCount() )
189  commandToUndo->CopyList( aItemsList );
190  else
191  {
192  // Unless we are appending, in which case, get the picker items
193  for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
194  commandToUndo->PushItem( aItemsList.GetItemWrapper( ii) );
195  }
196 
197  // Verify list, and creates data if needed
198  for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ )
199  {
200  SCH_ITEM* sch_item = dynamic_cast<SCH_ITEM*>( commandToUndo->GetPickedItem( ii ) );
201 
202  // Common items implemented in EDA_DRAW_FRAME will not be SCH_ITEMs.
203  if( !sch_item )
204  continue;
205 
206  // Connectivity may change
207  sch_item->SetConnectivityDirty();
208 
209  UNDO_REDO_T command = commandToUndo->GetPickedItemStatus( ii );
210 
211  if( command == UR_UNSPECIFIED )
212  {
213  command = aTypeCommand;
214  commandToUndo->SetPickedItemStatus( command, ii );
215  }
216 
217  switch( command )
218  {
219  case UR_CHANGED:
220 
221  /* If needed, create a copy of item, and put in undo list
222  * in the picker, as link
223  * If this link is not null, the copy is already done
224  */
225  if( commandToUndo->GetPickedItemLink( ii ) == nullptr )
226  commandToUndo->SetPickedItemLink( sch_item->Duplicate( true ), ii );
227 
228  wxASSERT( commandToUndo->GetPickedItemLink( ii ) );
229  break;
230 
231  case UR_MOVED:
232  case UR_MIRRORED_Y:
233  case UR_MIRRORED_X:
234  case UR_ROTATED:
235  case UR_NEW:
236  case UR_DELETED:
237  case UR_EXCHANGE_T:
238  case UR_PAGESETTINGS:
239  break;
240 
241  default:
242  wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ), command ) );
243  break;
244  }
245  }
246 
247  if( commandToUndo->GetCount() )
248  {
249  /* Save the copy in undo list */
250  GetScreen()->PushCommandToUndoList( commandToUndo );
251 
252  /* Clear redo list, because after new save there is no redo to do */
253  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
254  }
255  else // Should not occur
256  {
257  delete commandToUndo;
258  }
259 }
260 
261 
263 {
264  // Undo in the reverse order of list creation: (this can allow stacked changes like the
265  // same item can be changed and deleted in the same complex command).
266  for( int ii = aList->GetCount() - 1; ii >= 0; ii-- )
267  {
268  UNDO_REDO_T status = aList->GetPickedItemStatus((unsigned) ii );
269  EDA_ITEM* eda_item = aList->GetPickedItem( (unsigned) ii );
270 
271  eda_item->SetFlags( aList->GetPickerFlags( (unsigned) ii ) );
272  eda_item->ClearEditFlags();
273  eda_item->ClearTempFlags();
274 
275  if( status == UR_NEW )
276  {
277  // new items are deleted on undo
278  RemoveFromScreen( eda_item );
279  aList->SetPickedItemStatus( UR_DELETED, (unsigned) ii );
280  }
281  else if( status == UR_DELETED )
282  {
283  // deleted items are re-inserted on undo
284  AddToScreen( eda_item );
285  aList->SetPickedItemStatus( UR_NEW, (unsigned) ii );
286  }
287  else if( status == UR_PAGESETTINGS )
288  {
289  // swap current settings with stored settings
290  WS_PROXY_UNDO_ITEM alt_item( this );
291  WS_PROXY_UNDO_ITEM* item = (WS_PROXY_UNDO_ITEM*) eda_item;
292  item->Restore( this );
293  *item = alt_item;
295  }
296  else if( dynamic_cast<SCH_ITEM*>( eda_item ) )
297  {
298  // everthing else is modified in place
299 
300  SCH_ITEM* item = (SCH_ITEM*) eda_item;
301  SCH_ITEM* alt_item = (SCH_ITEM*) aList->GetPickedItemLink( (unsigned) ii );
302  RemoveFromScreen( item );
303 
304  switch( status )
305  {
306  case UR_CHANGED:
307  item->SwapData( alt_item );
308  break;
309 
310  case UR_MOVED:
311  item->Move( aRedoCommand ? aList->m_TransformPoint : -aList->m_TransformPoint );
312  break;
313 
314  case UR_MIRRORED_Y:
315  item->MirrorY( aList->m_TransformPoint.x );
316  break;
317 
318  case UR_MIRRORED_X:
319  item->MirrorX( aList->m_TransformPoint.y );
320  break;
321 
322  case UR_ROTATED:
323  if( aRedoCommand )
324  item->Rotate( aList->m_TransformPoint );
325  else
326  {
327  // Rotate 270 deg to undo 90-deg rotate
328  item->Rotate( aList->m_TransformPoint );
329  item->Rotate( aList->m_TransformPoint );
330  item->Rotate( aList->m_TransformPoint );
331  }
332  break;
333 
334  case UR_EXCHANGE_T:
335  alt_item->SetNext( nullptr );
336  alt_item->SetBack( nullptr );
337  aList->SetPickedItem( alt_item, (unsigned) ii );
338  aList->SetPickedItemLink( item, (unsigned) ii );
339  item = alt_item;
340  break;
341 
342  default:
343  wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ),
344  aList->GetPickedItemStatus( (unsigned) ii ) ) );
345  break;
346  }
347 
348  AddToScreen( item );
349  }
350  }
351 
353  selTool->RebuildSelection();
354 
355  // Bitmaps are cached in Opengl: clear the cache, because
356  // the cache data can be invalid
359 }
360 
361 
363 {
365 
366  if( undo )
367  {
368  PutDataInPreviousState( undo, false );
369  undo->ClearListAndDeleteItems();
370  delete undo;
371 
373 
375  }
376 
377  SyncView();
378  GetCanvas()->Refresh();
379 }
void Restore(EDA_DRAW_FRAME *aFrame, KIGFX::VIEW *aView=nullptr)
virtual void PushCommandToUndoList(PICKED_ITEMS_LIST *aItem)
Function PushCommandToUndoList add a command to undo in undo list delete the very old commands when t...
KIGFX::SCH_VIEW * GetView() const override
Function GetView() Returns a pointer to the VIEW instance used in the panel.
EDA_ITEM * GetPickedItemLink(unsigned int aIdx) const
Function GetPickedItemLink.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
void ClearHiddenFlags()
Clear the hide flag of all items in the view.
Definition: sch_view.cpp:175
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:277
void SetBack(EDA_ITEM *aBack)
Definition: base_struct.h:224
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:1402
static TOOL_ACTION zoomFitScreen
Definition: actions.h:92
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:109
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 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:265
virtual void SwapData(SCH_ITEM *aItem)
Function SwapData swap the internal data structures aItem with the schematic item.
Definition: sch_item.cpp:165
bool SetPickedItem(EDA_ITEM *aItem, unsigned aIdx)
Function SetPickedItem.
void SyncView()
Mark all items for refresh.
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
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.
virtual 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 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:223
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.
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:163
bool SetPickedItemLink(EDA_ITEM *aLink, unsigned aIdx)
Function SetPickedItemLink set the link associated to a given picked item.
TOOL_MANAGER * m_toolManager
virtual void ClearUndoORRedoList(UNDO_REDO_CONTAINER &aList, int aItemCount=-1) override
Free the undo or redo list from aList element.
Definition: sch_screen.cpp:598
void ClearEditFlags()
Definition: base_struct.h:282
STATUS_FLAGS GetFlags() const
Definition: base_struct.h:267
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:357
virtual void Move(const wxPoint &aMoveVector)=0
Function Move moves the item by aMoveVector to a new position.