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-2011 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 <protos.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  const wxPoint& aTransformPoint )
112 {
113  /* Does not save a null item or a UR_WIRE_IMAGE command type. UR_WIRE_IMAGE commands
114  * are handled by the overloaded version of SaveCopyInUndoList that takes a reference
115  * to a PICKED_ITEMS_LIST.
116  */
117  if( aItem == NULL || aCommandType == UR_WIRE_IMAGE )
118  return;
119 
120  PICKED_ITEMS_LIST* commandToUndo = new PICKED_ITEMS_LIST();
121  commandToUndo->m_TransformPoint = aTransformPoint;
122 
123  ITEM_PICKER itemWrapper( aItem, aCommandType );
124  itemWrapper.SetFlags( aItem->GetFlags() );
125 
126  switch( aCommandType )
127  {
128  case UR_CHANGED: /* Create a copy of item */
129  itemWrapper.SetLink( DuplicateStruct( aItem, true ) );
130  commandToUndo->PushItem( itemWrapper );
131  break;
132 
133  case UR_NEW:
134  case UR_DELETED:
135  case UR_ROTATED:
136  case UR_MOVED:
137  commandToUndo->PushItem( itemWrapper );
138  break;
139 
140  default:
141  wxFAIL_MSG( wxString::Format( wxT( "SaveCopyInUndoList() error (unknown code %X)" ),
142  aCommandType ) );
143  break;
144  }
145 
146  if( commandToUndo->GetCount() )
147  {
148  /* Save the copy in undo list */
149  GetScreen()->PushCommandToUndoList( commandToUndo );
150 
151  /* Clear redo list, because after new save there is no redo to do */
152  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
153  }
154  else
155  {
156  delete commandToUndo;
157  }
158 }
159 
160 
162  UNDO_REDO_T aTypeCommand,
163  const wxPoint& aTransformPoint )
164 {
165  PICKED_ITEMS_LIST* commandToUndo = new PICKED_ITEMS_LIST();
166 
167  commandToUndo->m_TransformPoint = aTransformPoint;
168  commandToUndo->m_Status = aTypeCommand;
169 
170  // Copy picker list:
171  commandToUndo->CopyList( aItemsList );
172 
173  // Verify list, and creates data if needed
174  for( unsigned ii = 0; ii < commandToUndo->GetCount(); ii++ )
175  {
176  SCH_ITEM* item = (SCH_ITEM*) commandToUndo->GetPickedItem( ii );
177  wxASSERT( item );
178 
179  UNDO_REDO_T command = commandToUndo->GetPickedItemStatus( ii );
180 
181  if( command == UR_UNSPECIFIED )
182  {
183  command = aTypeCommand;
184  commandToUndo->SetPickedItemStatus( command, ii );
185  }
186 
187  switch( command )
188  {
189  case UR_CHANGED: /* Create a copy of item */
190 
191  /* If needed, create a copy of item, and put in undo list
192  * in the picker, as link
193  * If this link is not null, the copy is already done
194  */
195  if( commandToUndo->GetPickedItemLink( ii ) == NULL )
196  commandToUndo->SetPickedItemLink( DuplicateStruct( item, true ), ii );
197 
198  wxASSERT( commandToUndo->GetPickedItemLink( ii ) );
199  break;
200 
201  case UR_MOVED:
202  case UR_MIRRORED_Y:
203  case UR_MIRRORED_X:
204  case UR_ROTATED:
205  case UR_NEW:
206  case UR_DELETED:
207  case UR_EXCHANGE_T:
208  case UR_WIRE_IMAGE:
209  break;
210 
211  default:
212  wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ), command ) );
213  break;
214  }
215  }
216 
217  if( commandToUndo->GetCount() || aTypeCommand == UR_WIRE_IMAGE )
218  {
219  /* Save the copy in undo list */
220  GetScreen()->PushCommandToUndoList( commandToUndo );
221 
222  /* Clear redo list, because after new save there is no redo to do */
223  GetScreen()->ClearUndoORRedoList( GetScreen()->m_RedoList );
224  }
225  else // Should not occur
226  {
227  delete commandToUndo;
228  }
229 }
230 
231 
233 {
234  SCH_ITEM* item;
235  SCH_ITEM* alt_item;
236 
237  // Exchange the current wires, buses, and junctions with the copy save by the last edit.
238  if( aList->m_Status == UR_WIRE_IMAGE )
239  {
240  DLIST< SCH_ITEM > oldWires;
241 
242  // Prevent items from being deleted when the DLIST goes out of scope.
243  oldWires.SetOwnership( false );
244 
245  // Remove all of the wires, buses, and junctions from the current screen.
246  GetScreen()->ExtractWires( oldWires, false );
247 
248  // Copy the saved wires, buses, and junctions to the current screen.
249  for( unsigned int i = 0; i < aList->GetCount(); i++ )
250  GetScreen()->Append( (SCH_ITEM*) aList->GetPickedItem( i ) );
251 
252  aList->ClearItemsList();
253 
254  // Copy the previous wires, buses, and junctions to the picked item list for the
255  // redo operation.
256  while( oldWires.GetCount() != 0 )
257  {
258  ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
259  aList->PushItem( picker );
260  }
261 
262  return;
263  }
264 
265  // Undo in the reverse order of list creation: (this can allow stacked changes like the
266  // same item can be changes and deleted in the same complex command.
267  for( int ii = aList->GetCount() - 1; ii >= 0; ii-- )
268  {
269  item = (SCH_ITEM*) aList->GetPickedItem( ii );
270  wxASSERT( item );
271 
272  item->ClearFlags();
273 
274  SCH_ITEM* image = (SCH_ITEM*) aList->GetPickedItemLink( ii );
275 
276  switch( aList->GetPickedItemStatus( ii ) )
277  {
278  case UR_CHANGED: /* Exchange old and new data for each item */
279  item->SwapData( image );
280  break;
281 
282  case UR_NEW: /* new items are deleted */
283  aList->SetPickedItemStatus( UR_DELETED, ii );
284  GetScreen()->Remove( item );
285  break;
286 
287  case UR_DELETED: /* deleted items are put in the draw item list, as new items */
288  aList->SetPickedItemStatus( UR_NEW, ii );
289  GetScreen()->Append( item );
290  break;
291 
292  case UR_MOVED:
293  item->ClearFlags();
294  item->SetFlags( aList->GetPickerFlags( ii ) );
295  item->Move( aRedoCommand ? aList->m_TransformPoint : -aList->m_TransformPoint );
296  item->ClearFlags();
297  break;
298 
299  case UR_MIRRORED_Y:
300  item->MirrorY( aList->m_TransformPoint.x );
301  break;
302 
303  case UR_MIRRORED_X:
304  item->MirrorX( aList->m_TransformPoint.y );
305  break;
306 
307  case UR_ROTATED:
308  // To undo a rotate 90 deg transform we must rotate 270 deg to undo
309  // and 90 deg to redo:
310  item->Rotate( aList->m_TransformPoint );
311 
312  if( aRedoCommand )
313  break; // A only one rotate transform is OK
314 
315  // Make 3 rotate 90 deg transforms is this is actually an undo command
316  item->Rotate( aList->m_TransformPoint );
317  item->Rotate( aList->m_TransformPoint );
318  break;
319 
320  case UR_EXCHANGE_T:
321  alt_item = (SCH_ITEM*) aList->GetPickedItemLink( ii );
322  alt_item->SetNext( NULL );
323  alt_item->SetBack( NULL );
324  GetScreen()->Remove( item );
325  GetScreen()->Append( alt_item );
326  aList->SetPickedItem( alt_item, ii );
327  aList->SetPickedItemLink( item, ii );
328  break;
329 
330  default:
331  wxFAIL_MSG( wxString::Format( wxT( "Unknown undo/redo command %d" ),
332  aList->GetPickedItemStatus( ii ) ) );
333  break;
334  }
335  }
336 }
337 
338 
339 void SCH_EDIT_FRAME::GetSchematicFromUndoList( wxCommandEvent& event )
340 {
341  if( GetScreen()->GetUndoCommandCount() <= 0 )
342  return;
343 
344  /* Get the old list */
346 
347  /* Undo the command */
348  PutDataInPreviousState( List, false );
349 
350  /* Put the old list in RedoList */
351  List->ReversePickersListOrder();
352  GetScreen()->PushCommandToRedoList( List );
353 
354  OnModify();
356 
358  m_canvas->Refresh();
359 }
360 
361 
362 void SCH_EDIT_FRAME::GetSchematicFromRedoList( wxCommandEvent& event )
363 {
364  if( GetScreen()->GetRedoCommandCount() == 0 )
365  return;
366 
367  /* Get the old list */
369 
370  /* Redo the command: */
371  PutDataInPreviousState( List, true );
372 
373  /* Put the old list in UndoList */
374  List->ReversePickersListOrder();
375  GetScreen()->PushCommandToUndoList( List );
376 
377  OnModify();
379 
381  m_canvas->Refresh();
382 }
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:255
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:212
void SaveCopyInUndoList(SCH_ITEM *aItemToCopy, UNDO_REDO_T aTypeCommand, const wxPoint &aTransformPoint=wxPoint(0, 0))
Function SaveCopyInUndoList.
virtual void Refresh(bool eraseBackground=true, const wxRect *rect=NULL) override
Definition: draw_panel.cpp:326
void ReversePickersListOrder()
Function ReversePickersListOrder reverses the order of pickers stored in this list.
void OnModify()
Function OnModify Must be called after a schematic change in order to set the "modify" flag of the cu...
Definition: schframe.cpp:768
void PushItem(const ITEM_PICKER &aItem)
Function PushItem pushes aItem to the top of the list.
void Remove(SCH_ITEM *aItem)
Function Remove removes aItem from the schematic associated with this screen.
Definition: sch_screen.cpp:158
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:527
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:253
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.
virtual PICKED_ITEMS_LIST * PopCommandFromRedoList()
PopCommandFromRedoList return the last command to undo and remove it from list nothing is deleted...
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)
Function GetSchematicFromRedoList Redo the last edition:
unsigned GetCount() const
Function GetCount.
void PutDataInPreviousState(PICKED_ITEMS_LIST *aList, bool aRedoCommand)
Function PutDataInPreviousState is used in undo or redo command to put data pointed by List in the pr...
EDA_DRAW_PANEL * m_canvas
The area to draw on.
Definition: draw_frame.h:92
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:211
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()
Function SetSheetNumberAndCount Set the m_ScreenNumber and m_NumberOfScreens members for screens must...
Definition: schframe.cpp:492
bool TestDanglingEnds()
Function TestDanglingEnds tests all of the connectible objects in the schematic for unused connection...
Definition: sch_screen.cpp:915
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)
Function ExtractWires extracts the old wires, junctions and buses.
Definition: sch_screen.cpp:241
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: base_struct.h:254
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
Function ClearUndoORRedoList free the undo or redo list from List element Wrappers are deleted...
Definition: sch_screen.cpp:597
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
SCH_ITEM * DuplicateStruct(SCH_ITEM *aDrawStruct, bool aClone)
Function DuplicateStruct Routine to create a new copy of given struct.
void GetSchematicFromUndoList(wxCommandEvent &event)
Function GetSchematicFromUndoList performs an undo the last edition:
virtual void Move(const wxPoint &aMoveVector)=0
Function Move moves the item by aMoveVector to a new position.