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 <class_drawpanel.h>
32 #include <schframe.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 
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 edition, 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  /* Does not save a null item or a UR_WIRE_IMAGE command type. UR_WIRE_IMAGE commands
117  * are handled by the overloaded version of SaveCopyInUndoList that takes a reference
118  * to a PICKED_ITEMS_LIST.
119  */
120  if( aItem == NULL || aCommandType == UR_WIRE_IMAGE )
121  return;
122 
123  if( aAppend )
124  commandToUndo = GetScreen()->PopCommandFromUndoList();
125 
126  if( !commandToUndo )
127  {
128  commandToUndo = new PICKED_ITEMS_LIST();
129  commandToUndo->m_TransformPoint = aTransformPoint;
130  }
131 
132  ITEM_PICKER itemWrapper( aItem, aCommandType );
133  itemWrapper.SetFlags( aItem->GetFlags() );
134 
135  switch( aCommandType )
136  {
137  case UR_CHANGED: /* Create a copy of item */
138  itemWrapper.SetLink( DuplicateStruct( aItem, true ) );
139  commandToUndo->PushItem( itemWrapper );
140  break;
141 
142  case UR_NEW:
143  case UR_DELETED:
144  case UR_ROTATED:
145  case UR_MOVED:
146  commandToUndo->PushItem( itemWrapper );
147  break;
148 
149  default:
150  wxFAIL_MSG( wxString::Format( wxT( "SaveCopyInUndoList() error (unknown code %X)" ),
151  aCommandType ) );
152  break;
153  }
154 
155  if( commandToUndo->GetCount() )
156  {
157  /* Save the copy in undo list */
158  GetScreen()->PushCommandToUndoList( commandToUndo );
159 
160  /* Clear redo list, because after new save there is no redo to do */
161  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
162  }
163  else
164  {
165  delete commandToUndo;
166  }
167 }
168 
169 
171  UNDO_REDO_T aTypeCommand,
172  bool aAppend,
173  const wxPoint& aTransformPoint )
174 {
175  PICKED_ITEMS_LIST* commandToUndo = NULL;
176 
177  if( !aItemsList.GetCount() )
178  return;
179 
180  // Can't append a WIRE IMAGE, so fail to a new undo point
181  if( aAppend && ( aTypeCommand != UR_WIRE_IMAGE ) )
182  {
183  commandToUndo = GetScreen()->PopCommandFromUndoList();
184  if( commandToUndo && commandToUndo->m_Status == UR_WIRE_IMAGE )
185  {
186  GetScreen()->PushCommandToUndoList( commandToUndo );
187  commandToUndo = NULL;
188  }
189  }
190 
191  if( !commandToUndo )
192  {
193  commandToUndo = new PICKED_ITEMS_LIST();
194  commandToUndo->m_TransformPoint = aTransformPoint;
195  commandToUndo->m_Status = aTypeCommand;
196  }
197 
198  // Copy picker list:
199  if( !commandToUndo->GetCount() )
200  commandToUndo->CopyList( aItemsList );
201  else
202  {
203  // Unless we are appending, in which case, get the picker items
204  for( unsigned ii = 0; ii < aItemsList.GetCount(); ii++ )
205  commandToUndo->PushItem( aItemsList.GetItemWrapper( ii) );
206  }
207 
208  // Verify list, and creates data if needed
209  for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ )
210  {
211  SCH_ITEM* item = (SCH_ITEM*) commandToUndo->GetPickedItem( ii );
212  wxASSERT( item );
213 
214  UNDO_REDO_T command = commandToUndo->GetPickedItemStatus( ii );
215 
216  if( command == UR_UNSPECIFIED )
217  {
218  command = aTypeCommand;
219  commandToUndo->SetPickedItemStatus( command, ii );
220  }
221 
222  switch( command )
223  {
224  case UR_CHANGED: /* Create a copy of item */
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 ) == NULL )
231  commandToUndo->SetPickedItemLink( DuplicateStruct( item, true ), ii );
232 
233  wxASSERT( commandToUndo->GetPickedItemLink( ii ) );
234  break;
235 
236  case UR_MOVED:
237  case UR_MIRRORED_Y:
238  case UR_MIRRORED_X:
239  case UR_ROTATED:
240  case UR_NEW:
241  case UR_DELETED:
242  case UR_EXCHANGE_T:
243  case UR_WIRE_IMAGE:
244  break;
245 
246  default:
247  wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ), command ) );
248  break;
249  }
250  }
251 
252  if( commandToUndo->GetCount() || aTypeCommand == UR_WIRE_IMAGE )
253  {
254  /* Save the copy in undo list */
255  GetScreen()->PushCommandToUndoList( commandToUndo );
256 
257  /* Clear redo list, because after new save there is no redo to do */
258  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
259  }
260  else // Should not occur
261  {
262  delete commandToUndo;
263  }
264 }
265 
266 
268 {
269  SCH_ITEM* item;
270  SCH_ITEM* alt_item;
271 
272  // Exchange the current wires, buses, and junctions with the copy save by the last edit.
273  if( aList->m_Status == UR_WIRE_IMAGE )
274  {
275  DLIST< SCH_ITEM > oldWires;
276 
277  // Prevent items from being deleted when the DLIST goes out of scope.
278  oldWires.SetOwnership( false );
279 
280  // Remove all of the wires, buses, and junctions from the current screen.
281  GetScreen()->ExtractWires( oldWires, false );
282 
283  // Copy the saved wires, buses, and junctions to the current screen.
284  for( unsigned int i = 0; i < aList->GetCount(); i++ )
285  GetScreen()->Append( (SCH_ITEM*) aList->GetPickedItem( i ) );
286 
287  aList->ClearItemsList();
288 
289  // Copy the previous wires, buses, and junctions to the picked item list for the
290  // redo operation.
291  while( oldWires.GetCount() != 0 )
292  {
293  ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
294  aList->PushItem( picker );
295  }
296 
297  return;
298  }
299 
300  // Undo in the reverse order of list creation: (this can allow stacked changes like the
301  // same item can be changes and deleted in the same complex command.
302  for( int ii = aList->GetCount() - 1; ii >= 0; ii-- )
303  {
304  item = (SCH_ITEM*) aList->GetPickedItem( ii );
305  wxASSERT( item );
306 
307  item->ClearFlags();
308 
309  SCH_ITEM* image = (SCH_ITEM*) aList->GetPickedItemLink( ii );
310 
311  switch( aList->GetPickedItemStatus( ii ) )
312  {
313  case UR_CHANGED: /* Exchange old and new data for each item */
314  item->SwapData( image );
315  break;
316 
317  case UR_NEW: /* new items are deleted */
318  aList->SetPickedItemStatus( UR_DELETED, ii );
319  GetScreen()->Remove( item );
320  break;
321 
322  case UR_DELETED: /* deleted items are put in the draw item list, as new items */
323  aList->SetPickedItemStatus( UR_NEW, ii );
324  GetScreen()->Append( item );
325  break;
326 
327  case UR_MOVED:
328  item->ClearFlags();
329  item->SetFlags( aList->GetPickerFlags( ii ) );
330  item->Move( aRedoCommand ? aList->m_TransformPoint : -aList->m_TransformPoint );
331  item->ClearFlags();
332  break;
333 
334  case UR_MIRRORED_Y:
335  item->MirrorY( aList->m_TransformPoint.x );
336  break;
337 
338  case UR_MIRRORED_X:
339  item->MirrorX( aList->m_TransformPoint.y );
340  break;
341 
342  case UR_ROTATED:
343  // To undo a rotate 90 deg transform we must rotate 270 deg to undo
344  // and 90 deg to redo:
345  item->Rotate( aList->m_TransformPoint );
346 
347  if( aRedoCommand )
348  break; // A only one rotate transform is OK
349 
350  // Make 3 rotate 90 deg transforms is this is actually an undo command
351  item->Rotate( aList->m_TransformPoint );
352  item->Rotate( aList->m_TransformPoint );
353  break;
354 
355  case UR_EXCHANGE_T:
356  alt_item = (SCH_ITEM*) aList->GetPickedItemLink( ii );
357  alt_item->SetNext( NULL );
358  alt_item->SetBack( NULL );
359  GetScreen()->Remove( item );
360  GetScreen()->Append( alt_item );
361  aList->SetPickedItem( alt_item, ii );
362  aList->SetPickedItemLink( item, ii );
363  break;
364 
365  default:
366  wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ),
367  aList->GetPickedItemStatus( ii ) ) );
368  break;
369  }
370  }
371 }
372 
373 
374 void SCH_EDIT_FRAME::GetSchematicFromUndoList( wxCommandEvent& event )
375 {
376  if( GetScreen()->GetUndoCommandCount() <= 0 || isBusy() )
377  return;
378 
379  /* Get the old list */
381 
382  /* Undo the command */
383  PutDataInPreviousState( List, false );
384 
385  /* Put the old list in RedoList */
386  List->ReversePickersListOrder();
387  GetScreen()->PushCommandToRedoList( List );
388 
389  OnModify();
391 
393  m_canvas->Refresh();
394 }
395 
396 
397 void SCH_EDIT_FRAME::GetSchematicFromRedoList( wxCommandEvent& event )
398 {
399  if( GetScreen()->GetRedoCommandCount() == 0 || isBusy() )
400  return;
401 
402  /* Get the old list */
404 
405  /* Redo the command: */
406  PutDataInPreviousState( List, true );
407 
408  /* Put the old list in UndoList */
409  List->ReversePickersListOrder();
410  GetScreen()->PushCommandToUndoList( List );
411 
412  OnModify();
414 
416  m_canvas->Refresh();
417 }
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...
STATUS_FLAGS GetFlags() const
Definition: base_struct.h:282
Use for changing the schematic text type where swapping data structure is insufficient to restor the ...
void SetBack(EDA_ITEM *aBack)
Definition: base_struct.h:239
virtual void Refresh(bool eraseBackground=true, const wxRect *rect=NULL) override
Update the board display after modifying it bu a python script (note: it is automatically called by a...
Definition: draw_panel.cpp:325
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 ...
Definition: schframe.cpp:773
void PushItem(const ITEM_PICKER &aItem)
Function PushItem pushes aItem to the top of the list.
void Remove(SCH_ITEM *aItem)
Remove aItem from the schematic associated with this screen.
Definition: sch_screen.cpp:171
void SetLink(EDA_ITEM *aItem)
UNDO_REDO_T GetPickedItemStatus(unsigned int aIdx) const
Function GetPickedItemStatus.
EDA_ITEM * GetPickedItemLink(unsigned int aIdx) const
Function GetPickedItemLink.
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
Function GetScreen returns a pointer to a BASE_SCREEN or one of its derivatives.
Definition: schframe.cpp:532
UNDO_REDO_T
Undo Redo considerations: Basically we have 3 cases New item Deleted item Modified item there is also...
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:280
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.
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.
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.
void GetSchematicFromRedoList(wxCommandEvent &event)
Redo the last edition.
unsigned GetCount() const
Function GetCount.
void PutDataInPreviousState(PICKED_ITEMS_LIST *aList, bool aRedoCommand)
Restore an undo or redo command to put data pointed by aList in the previous state.
EDA_DRAW_PANEL * m_canvas
The area to draw on.
Definition: draw_frame.h:93
Definition the SCH_COMPONENT class for Eeschema.
STATUS_FLAGS GetPickerFlags(unsigned aIdx) const
Function GetPickerFlags returns the value of the picker flag.
void SetNext(EDA_ITEM *aNext)
Definition: base_struct.h:238
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
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
Function GetPickedItem.
void Append(SCH_ITEM *aItem)
void SetSheetNumberAndCount()
Set the m_ScreenNumber and m_NumberOfScreens members for screens.
Definition: schframe.cpp:497
bool TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
Definition: sch_screen.cpp:879
virtual void PushCommandToRedoList(PICKED_ITEMS_LIST *aItem)
Function PushCommandToRedoList add a command to redo in redo list delete the very old commands when t...
void ClearItemsList()
Function ClearItemsList deletes only the list of pickers, NOT the picked data itself.
bool SetPickedItemLink(EDA_ITEM *aLink, unsigned aIdx)
Function SetPickedItemLink set the link associated to a given picked item.
void ExtractWires(DLIST< SCH_ITEM > &aList, bool aCreateCopy)
Extracts the old wires, junctions and buses.
Definition: sch_screen.cpp:254
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: base_struct.h:281
unsigned GetCount() const
Function GetCount returns the number of elements in the list.
Definition: dlist.h:126
void SetOwnership(bool Iown)
Function SetOwnership controls whether the list owns the objects and is responsible for deleteing the...
Definition: dlist.h:119
virtual void ClearUndoORRedoList(UNDO_REDO_CONTAINER &aList, int aItemCount=-1) override
Free the undo or redo list from aList element.
Definition: sch_screen.cpp:552
ITEM_PICKER GetItemWrapper(unsigned int aIdx) const
Function GetItemWrapper.
bool isBusy() const
Class SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container clas...
T * PopFront()
Definition: dlist.h:221
void GetSchematicFromUndoList(wxCommandEvent &event)
Perform an undo the last edition.
virtual void Move(const wxPoint &aMoveVector)=0
Function Move moves the item by aMoveVector to a new position.