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-2017 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 
30 #include <fctsys.h>
31 #include <sch_draw_panel.h>
32 #include <sch_edit_frame.h>
33 
34 #include <general.h>
35 #include <list_operations.h>
36 #include <sch_bus_entry.h>
37 #include <sch_marker.h>
38 #include <sch_junction.h>
39 #include <sch_line.h>
40 #include <sch_no_connect.h>
41 #include <sch_component.h>
42 #include <sch_sheet.h>
43 #include <sch_bitmap.h>
44 #include <sch_view.h>
45 
46 /* Functions to undo and redo edit commands.
47  * commands to undo are stored in CurrentScreen->m_UndoList
48  * commands to redo are stored in CurrentScreen->m_RedoList
49  *
50  * m_UndoList and m_RedoList handle a std::vector of PICKED_ITEMS_LIST
51  * Each PICKED_ITEMS_LIST handle a std::vector of pickers (class ITEM_PICKER),
52  * that store the list of schematic items that are concerned by the command to
53  * undo or redo and is created for each command to undo (handle also a command
54  * to redo). each picker has a pointer pointing to an item to undo or redo (in
55  * fact: deleted, added or modified), and has a pointer to a copy of this item,
56  * when this item has been modified (the old values of parameters are
57  * therefore saved)
58  *
59  * there are 3 cases:
60  * - delete item(s) command
61  * - change item(s) command
62  * - add item(s) command
63  * and 2 cases for block:
64  * - move list of items
65  * - mirror (Y) list of items
66  *
67  * Undo command
68  * - delete item(s) command:
69  * => deleted items are moved in undo list
70  *
71  * - change item(s) command
72  * => A copy of item(s) is made (a DrawPickedStruct list of wrappers)
73  * the .m_Link member of each wrapper points the modified item.
74  * the .m_Item member of each wrapper points the old copy of this item.
75  *
76  * - add item(s) command
77  * =>A list of item(s) is made. The .m_Item member of each wrapper points
78  * the new item.
79  *
80  * Redo command
81  * - delete item(s) old command:
82  * => deleted items are moved in GetDrawItems() list, and in
83  *
84  * - change item(s) command
85  * => the copy of item(s) is moved in Undo list
86  *
87  * - add item(s) command
88  * => The list of item(s) is used to create a deleted list in undo
89  * list(same as a delete command)
90  *
91  * Some block operations that change items can be undone without memorized
92  * items, just the coordinates of the transform: move list of items (undo/
93  * redo is made by moving with the opposite move vector) mirror (Y) and flip
94  * list of items (undo/redo is made by mirror or flip items) so they are
95  * handled specifically.
96  *
97  * A problem is the hierarchical sheet handling.
98  * the data associated (sub-hierarchy, undo/redo list) is deleted only
99  * when the sheet is really deleted (i.e. when deleted from undo or redo list)
100  * This is handled by its destructor.
101  */
102 
103 
104 /* Used if undo / redo command:
105  * swap data between Item and its copy, pointed by its picked item link member
106  * swapped data is data modified by editing, so not all values are swapped
107  */
108 
110  UNDO_REDO_T aCommandType,
111  bool aAppend,
112  const wxPoint& aTransformPoint )
113 {
114  PICKED_ITEMS_LIST* commandToUndo = NULL;
115 
116  if( aItem == NULL )
117  return;
118 
119  // Connectivity may change
120  aItem->SetConnectivityDirty();
121 
122  if( aAppend )
123  commandToUndo = GetScreen()->PopCommandFromUndoList();
124 
125  if( !commandToUndo )
126  {
127  commandToUndo = new PICKED_ITEMS_LIST();
128  commandToUndo->m_TransformPoint = aTransformPoint;
129  }
130 
131  ITEM_PICKER itemWrapper( aItem, aCommandType );
132  itemWrapper.SetFlags( aItem->GetFlags() );
133 
134  switch( aCommandType )
135  {
136  case UR_CHANGED: /* Create a copy of item */
137  itemWrapper.SetLink( DuplicateStruct( aItem, true ) );
138  commandToUndo->PushItem( itemWrapper );
139  break;
140 
141  case UR_NEW:
142  case UR_DELETED:
143  case UR_ROTATED:
144  case UR_MOVED:
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  GetScreen()->PushCommandToUndoList( commandToUndo );
158 
159  /* Clear redo list, because after new save there is no redo to do */
160  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
161  }
162  else
163  {
164  delete commandToUndo;
165  }
166 }
167 
168 
170  UNDO_REDO_T aTypeCommand,
171  bool aAppend,
172  const wxPoint& aTransformPoint )
173 {
174  PICKED_ITEMS_LIST* commandToUndo = NULL;
175 
176  if( !aItemsList.GetCount() )
177  return;
178 
179  // Can't append a WIRE IMAGE, so fail to a new undo point
180  if( aAppend )
181  commandToUndo = GetScreen()->PopCommandFromUndoList();
182 
183  if( !commandToUndo )
184  {
185  commandToUndo = new PICKED_ITEMS_LIST();
186  commandToUndo->m_TransformPoint = aTransformPoint;
187  commandToUndo->m_Status = aTypeCommand;
188  }
189 
190  // Copy picker list:
191  if( !commandToUndo->GetCount() )
192  commandToUndo->CopyList( aItemsList );
193  else
194  {
195  // Unless we are appending, in which case, get the picker items
196  for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
197  commandToUndo->PushItem( aItemsList.GetItemWrapper( ii) );
198  }
199 
200  // Verify list, and creates data if needed
201  for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ )
202  {
203  SCH_ITEM* item = (SCH_ITEM*) commandToUndo->GetPickedItem( ii );
204  wxASSERT( item );
205 
206  // Connectivity may change
207  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: /* Create a copy of item */
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 ) == NULL )
226  commandToUndo->SetPickedItemLink( DuplicateStruct( item, 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  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  GetScreen()->PushCommandToUndoList( commandToUndo );
250 
251  /* Clear redo list, because after new save there is no redo to do */
252  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
253  }
254  else // Should not occur
255  {
256  delete commandToUndo;
257  }
258 }
259 
260 
262 {
263  SCH_ITEM* item;
264  SCH_ITEM* alt_item;
265 
266  // Undo in the reverse order of list creation: (this can allow stacked changes like the
267  // same item can be changed and deleted in the same complex command).
268  for( int ii = aList->GetCount() - 1; ii >= 0; ii-- )
269  {
270  UNDO_REDO_T status = aList->GetPickedItemStatus((unsigned) ii );
271  item = (SCH_ITEM*) aList->GetPickedItem( (unsigned) ii );
272  alt_item = (SCH_ITEM*) aList->GetPickedItemLink( (unsigned) ii );
273 
274  item->ClearFlags();
275 
276  if( status == UR_NEW )
277  {
278  // new items are deleted on undo
279  RemoveFromScreen( item );
280  aList->SetPickedItemStatus( UR_DELETED, (unsigned) ii );
281  }
282  else if (status == UR_DELETED )
283  {
284  // deleted items are re-inserted on undo
285  AddToScreen( item );
286  aList->SetPickedItemStatus( UR_NEW, (unsigned) ii );
287  }
288  else
289  {
290  // everthing else is modified in place
291  RemoveFromScreen( item );
292 
293  switch( status )
294  {
295  case UR_CHANGED:
296  item->SwapData( alt_item );
297  break;
298 
299  case UR_MOVED:
300  item->SetFlags( aList->GetPickerFlags( (unsigned) ii ) );
301  item->Move( aRedoCommand ? aList->m_TransformPoint : -aList->m_TransformPoint );
302  item->ClearFlags();
303  break;
304 
305  case UR_MIRRORED_Y:
306  item->MirrorY( aList->m_TransformPoint.x );
307  break;
308 
309  case UR_MIRRORED_X:
310  item->MirrorX( aList->m_TransformPoint.y );
311  break;
312 
313  case UR_ROTATED:
314  if( aRedoCommand )
315  item->Rotate( aList->m_TransformPoint );
316  else
317  {
318  // Rotate 270 deg to undo 90-deg rotate
319  item->Rotate( aList->m_TransformPoint );
320  item->Rotate( aList->m_TransformPoint );
321  item->Rotate( aList->m_TransformPoint );
322  }
323  break;
324 
325  case UR_EXCHANGE_T:
326  alt_item->SetNext( NULL );
327  alt_item->SetBack( NULL );
328  aList->SetPickedItem( alt_item, (unsigned) ii );
329  aList->SetPickedItemLink( item, (unsigned) ii );
330  item = alt_item;
331  break;
332 
333  default:
334  wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ),
335  aList->GetPickedItemStatus( (unsigned) ii ) ) );
336  break;
337  }
338 
339  AddToScreen( item );
340  }
341  }
342 
343  // Bitmaps are cached in Opengl: clear the cache, because
344  // the cache data can be invalid
347 }
348 
349 
350 void SCH_EDIT_FRAME::GetSchematicFromUndoList( wxCommandEvent& event )
351 {
352  if( GetScreen()->GetUndoCommandCount() <= 0 || isBusy() )
353  return;
354 
355  /* Get the old list */
357 
358  /* Undo the command */
359  PutDataInPreviousState( List, false );
360 
361  /* Put the old list in RedoList */
362  List->ReversePickersListOrder();
363  GetScreen()->PushCommandToRedoList( List );
364 
367 
368  SyncView();
369  GetCanvas()->Refresh();
370  OnModify();
371 }
372 
373 
374 void SCH_EDIT_FRAME::GetSchematicFromRedoList( wxCommandEvent& event )
375 {
376  if( GetScreen()->GetRedoCommandCount() == 0 || isBusy() )
377  return;
378 
379  /* Get the old list */
381 
382  /* Redo the command: */
383  PutDataInPreviousState( List, true );
384 
385  /* Put the old list in UndoList */
386  List->ReversePickersListOrder();
387  GetScreen()->PushCommandToUndoList( List );
388 
390 
392 
393  SyncView();
394  GetCanvas()->Refresh();
395  OnModify();
396 }
Definition of the SCH_SHEET class for Eeschema.
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
void SetBack(EDA_ITEM *aBack)
Definition: base_struct.h:218
void ReversePickersListOrder()
Function ReversePickersListOrder reverses the order of pickers stored in this list.
void OnModify()
Must be called after a schematic change in order to set the "modify" flag of the current screen* and ...
UNDO_REDO_T
Undo Redo considerations: Basically we have 3 cases New item Deleted item Modified item there is also...
SCH_MARKER class definition.
void RecacheAllItems()
Function RecacheAllItems() Rebuilds GAL display lists.
Definition: view.cpp:1401
void AddToScreen(SCH_ITEM *aItem, SCH_SCREEN *aScreen=nullptr)
Add an item to the screen (and view) aScreen is the screen the item is located on,...
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 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.
bool SetPickedItem(EDA_ITEM *aItem, unsigned aIdx)
Function SetPickedItem.
SCH_ITEM * DuplicateStruct(SCH_ITEM *aDrawStruct, bool aClone=false)
Routine to create a new copy of given struct.
void SyncView()
Mark all items for refresh.
SCH_DRAW_PANEL * GetCanvas() const override
void RemoveFromScreen(SCH_ITEM *aItem, SCH_SCREEN *aScreen=nullptr)
Remove an item from the screen (and view) aScreen is the screen the item is located on,...
virtual PICKED_ITEMS_LIST * PopCommandFromRedoList()
PopCommandFromRedoList return the last command to undo and remove it from list nothing is deleted.
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...
void GetSchematicFromRedoList(wxCommandEvent &event)
Redo the last edit.
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.
UNDO_REDO_T GetPickedItemStatus(unsigned int aIdx) const
Function GetPickedItemStatus.
virtual void PushCommandToRedoList(PICKED_ITEMS_LIST *aItem)
Function PushCommandToRedoList add a command to redo in redo list delete the very old commands when t...
bool SetPickedItemLink(EDA_ITEM *aLink, unsigned aIdx)
Function SetPickedItemLink set the link associated to a given picked item.
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: base_struct.h:260
virtual void ClearUndoORRedoList(UNDO_REDO_CONTAINER &aList, int aItemCount=-1) override
Free the undo or redo list from aList element.
Definition: sch_screen.cpp:579
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...
void SetConnectivityDirty(bool aDirty=true)
void GetSchematicFromUndoList(wxCommandEvent &event)
Perform an undo the last edit.
virtual void Move(const wxPoint &aMoveVector)=0
Function Move moves the item by aMoveVector to a new position.