1 /*!
2  * \file src/undo.c
3  *
4  * \brief Functions used to undo operations.
5  *
6  * Description:
7  *
8  * There are two lists which hold:
9  * - information about a command
10  * - data of removed objects
11  *
12  * Both lists are organized as first-in-last-out which means that the undo
13  * list can always use the last entry of the remove list.
14  *
15  * A serial number is incremented whenever an operation is completed.
16  *
17  * An operation itself may consist of several basic instructions.
18  *
19  * E.g.: removing all selected objects is one operation with exactly one
20  * serial number even if the remove function is called several times.
21  *
22  * \note A lock flag ensures that no infinite loops occur.
23  *
24  * <hr>
25  *
26  * <h1><b>Copyright.</b></h1>\n
27  *
28  * PCB, interactive printed circuit board design
29  *
30  * Copyright (C) 1994,1995,1996 Thomas Nau
31  *
32  * This program is free software; you can redistribute it and/or modify
33  * it under the terms of the GNU General Public License as published by
34  * the Free Software Foundation; either version 2 of the License, or
35  * (at your option) any later version.
36  *
37  * This program is distributed in the hope that it will be useful,
38  * but WITHOUT ANY WARRANTY; without even the implied warranty of
39  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
40  * GNU General Public License for more details.
41  *
42  * You should have received a copy of the GNU General Public License along
43  * with this program; if not, write to the Free Software Foundation, Inc.,
44  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
45  *
46  * Contact addresses for paper mail and Email:
47  *
48  * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
49  *
50  * Thomas.Nau@rz.uni-ulm.de
51  */
52 
53 
54 #ifdef HAVE_CONFIG_H
55 #include "config.h"
56 #endif
57 
58 #include <assert.h>
59 #include <memory.h>
60 #include "global.h"
61 
62 #include "buffer.h"
63 #include "change.h"
64 #include "create.h"
65 #include "data.h"
66 #include "draw.h"
67 #include "error.h"
68 #include "flags.h"
69 #include "insert.h"
70 #include "misc.h"
71 #include "mirror.h"
72 #include "move.h"
73 #include "mymem.h"
74 #include "polygon.h"
75 #include "remove.h"
76 #include "rotate.h"
77 #include "rtree.h"
78 #include "search.h"
79 #include "set.h"
80 #include "undo.h"
81 #include "strflags.h"
82 
83 #ifdef HAVE_LIBDMALLOC
84 #include <dmalloc.h>
85 #endif
86 
87 static bool between_increment_and_restore = false;
88 static bool added_undo_between_increment_and_restore = false;
89 
90 /* ---------------------------------------------------------------------------
91  * some local data types
92  */
93 
94 /*!
95  * \brief Information about a change command.
96  */
97 typedef struct
98 {
99   char *Name;
100 } ChangeNameType;
101 
102 /*!
103  * \brief Information about a move command.
104  */
105 typedef struct
106 {
107   Coord DX; /*!< movement vector. */
108   Coord DY; /*!< movement vector. */
109 } MoveType;
110 
111 /*!
112  * \brief Information about removed polygon points.
113  */
114 typedef struct
115 {
116   Coord X; /*!< Data. */
117   Coord Y; /*!< Data. */
118   int ID;
119   Cardinal Index; /*!< Index in a polygons array of points. */
120   bool last_in_contour; /*!< Whether the point was the last in its contour. */
121 } RemovedPointType;
122 
123 /*!
124  * \brief Information about rotation.
125  */
126 typedef struct
127 {
128   Coord CenterX; /*!< Center of rotation. */
129   Coord CenterY; /*!< Center of rotation. */
130   Cardinal Steps; /*!< Number of steps. */
131 } RotateType;
132 
133 /*!
134  * \brief Information about moves between layers.
135  */
136 typedef struct
137 {
138   Cardinal OriginalLayer; /*!< The index of the original layer. */
139 } MoveToLayerType;
140 
141 /*!
142  * \brief Information about layer changes.
143  */
144 typedef struct
145 {
146   int old_index;
147   int new_index;
148 } LayerChangeType;
149 
150 /*!
151  * \brief Information about layer changes.
152  */
153 typedef struct
154 {
155   int from;
156   int to;
157 } SetViaLayersChangeType;
158 
159 /*!
160  * \brief Information about poly clear/restore.
161  */
162 typedef struct
163 {
164   bool Clear; /*!< true was clear, false was restore. */
165   LayerType *Layer;
166 } ClearPolyType;
167 
168 /*!
169  * \brief Information about netlist lib changes.
170  */
171 typedef struct
172 {
173   LibraryType *old;
174   LibraryType *lib;
175 } NetlistChangeType;
176 
177 /*!
178  * \brief Holds information about an operation.
179  */
180 typedef struct
181 {
182   int Serial, /*!< Serial number of operation. */
183     Type, /*!< Type of operation. */
184     Kind, /*!< Type of object with given ID. */
185     ID; /*!< Object ID. */
186   union /* Some additional information. */
187   {
188     ChangeNameType ChangeName;
189     MoveType Move;
190     RemovedPointType RemovedPoint;
191     RotateType Rotate;
192     MoveToLayerType MoveToLayer;
193     FlagType Flags;
194     Coord Size;
195     int Scale;
196     LayerChangeType LayerChange;
197     ClearPolyType ClearPoly;
198     NetlistChangeType NetlistChange;
199     SetViaLayersChangeType SetViaLayersChange;
200     long int CopyID;
201   }
202   Data;
203 } UndoListType;
204 
205 /* ---------------------------------------------------------------------------
206  * some local variables
207  */
208 static DataType *RemoveList = NULL; /*!< List of removed objects. */
209 static UndoListType *UndoList = NULL; /*!< List of operations. */
210 static int Serial = 1; /*!< Serial number. */
211 static int SavedSerial;
212 static size_t UndoN; /*!< Number of entries. */
213 static size_t RedoN; /*!< Number of entries. */
214 static size_t UndoMax; /*!< Number of entries. */
215 static bool Locked = false; /*!< Do not add entries if. */
216 static bool andDraw = true;
217 										/* flag is set; prevents from */
218 										/* infinite loops */
219 
220 /* ---------------------------------------------------------------------------
221  * some local prototypes
222  */
223 static UndoListType *GetUndoSlot (int, int, int);
224 static void DrawRecoveredObject (int, void *, void *, void *);
225 static bool UndoRotate (UndoListType *);
226 static bool UndoChangeName (UndoListType *);
227 static bool UndoCopyOrCreate (UndoListType *);
228 static bool UndoMove (UndoListType *);
229 static bool UndoRemove (UndoListType *);
230 static bool UndoRemovePoint (UndoListType *);
231 static bool UndoInsertPoint (UndoListType *);
232 static bool UndoRemoveContour (UndoListType *);
233 static bool UndoInsertContour (UndoListType *);
234 static bool UndoMoveToLayer (UndoListType *);
235 static bool UndoFlag (UndoListType *);
236 static bool UndoMirror (UndoListType *);
237 static bool UndoChangeSize (UndoListType *);
238 static bool UndoChange2ndSize (UndoListType *);
239 static bool UndoChangeAngles (UndoListType *);
240 static bool UndoChangeClearSize (UndoListType *);
241 static bool UndoChangeMaskSize (UndoListType *);
242 static bool UndoClearPoly (UndoListType *);
243 static bool UndoSetViaLayers (UndoListType *);
244 static int PerformUndo (UndoListType *);
245 
246 /*!
247  * \brief Adds a command plus some data to the undo list.
248  */
249 static UndoListType *
GetUndoSlot(int CommandType,int ID,int Kind)250 GetUndoSlot (int CommandType, int ID, int Kind)
251 {
252   UndoListType *ptr;
253   void *ptr1, *ptr2, *ptr3;
254   int type;
255   static size_t limit = UNDO_WARNING_SIZE;
256 
257 #ifdef DEBUG_ID
258   if (SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, ID, Kind) == NO_TYPE)
259     Message ("hace: ID (%d) and Type (%x) mismatch in AddObject...\n", ID,
260 	     Kind);
261 #endif
262 
263   /* allocate memory */
264   if (UndoN >= UndoMax)
265     {
266       size_t size;
267 
268       UndoMax += STEP_UNDOLIST;
269       size = UndoMax * sizeof (UndoListType);
270       UndoList = (UndoListType *) realloc (UndoList, size);
271       memset (&UndoList[UndoN], 0, STEP_REMOVELIST * sizeof (UndoListType));
272 
273       /* ask user to flush the table because of it's size */
274       if (size > limit)
275 	{
276 	  limit = (size / UNDO_WARNING_SIZE + 1) * UNDO_WARNING_SIZE;
277 	  Message (_("Size of 'undo-list' exceeds %li kb\n"),
278 		   (long) (size >> 10));
279 	}
280     }
281 
282   /* free structures from the pruned redo list */
283 
284   for (ptr = &UndoList[UndoN]; RedoN; ptr++, RedoN--)
285     switch (ptr->Type)
286       {
287       case UNDO_CHANGENAME:
288 	free (ptr->Data.ChangeName.Name);
289 	break;
290       case UNDO_REMOVE:
291 	type =
292 	  SearchObjectByID (RemoveList, &ptr1, &ptr2, &ptr3, ptr->ID,
293 			    ptr->Kind);
294 	if (type != NO_TYPE)
295 	  {
296 	    DestroyObject (RemoveList, type, ptr1, ptr2, ptr3);
297 	  }
298 	break;
299       default:
300 	break;
301       }
302 
303   if (between_increment_and_restore)
304     added_undo_between_increment_and_restore = true;
305 
306   /* copy typefield and serial number to the list */
307   ptr = &UndoList[UndoN++];
308   ptr->Type = CommandType;
309   ptr->Kind = Kind;
310   ptr->ID = ID;
311   ptr->Serial = Serial;
312   return (ptr);
313 }
314 
315 /*!
316  * \brief Redraws the recovered object.
317  */
318 static void
DrawRecoveredObject(int Type,void * Ptr1,void * Ptr2,void * Ptr3)319 DrawRecoveredObject (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
320 {
321   if (Type & (LINE_TYPE | TEXT_TYPE | POLYGON_TYPE | ARC_TYPE))
322     {
323       LayerType *layer;
324 
325       layer = LAYER_PTR (GetLayerNumber (RemoveList, (LayerType *) Ptr1));
326       DrawObject (Type, (void *) layer, Ptr2);
327     }
328   else
329     DrawObject (Type, Ptr1, Ptr2);
330 }
331 
332 /*!
333  * \brief Recovers an object from a 'rotate' operation.
334  *
335  * \return true if anything has been recovered.
336  */
337 static bool
UndoRotate(UndoListType * Entry)338 UndoRotate (UndoListType *Entry)
339 {
340   void *ptr1, *ptr2, *ptr3;
341   int type;
342 
343   /* lookup entry by it's ID */
344   type =
345     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
346   if (type != NO_TYPE)
347     {
348       RotateObject (type, ptr1, ptr2, ptr3,
349 		    Entry->Data.Rotate.CenterX, Entry->Data.Rotate.CenterY,
350 		    (4 - Entry->Data.Rotate.Steps) & 0x03);
351       Entry->Data.Rotate.Steps = (4 - Entry->Data.Rotate.Steps) & 0x03;
352       return (true);
353     }
354   return (false);
355 }
356 
357 /*!
358  * \brief Recovers an object from a clear/restore poly operation.
359  *
360  * \return true if anything has been recovered.
361  */
362 static bool
UndoClearPoly(UndoListType * Entry)363 UndoClearPoly (UndoListType *Entry)
364 {
365   void *ptr1, *ptr2, *ptr3;
366   int type;
367 
368   type =
369     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
370   if (type != NO_TYPE)
371     {
372       if (Entry->Data.ClearPoly.Clear)
373 	RestoreToPolygon (PCB->Data, type, Entry->Data.ClearPoly.Layer, ptr3);
374       else
375 	ClearFromPolygon (PCB->Data, type, Entry->Data.ClearPoly.Layer, ptr3);
376       Entry->Data.ClearPoly.Clear = !Entry->Data.ClearPoly.Clear;
377       return true;
378     }
379   return false;
380 }
381 
382 /*!
383  * \brief Recovers an object from a 'change name' operation.
384  *
385  * \return true if anything has been recovered.
386  */
387 static bool
UndoChangeName(UndoListType * Entry)388 UndoChangeName (UndoListType *Entry)
389 {
390   void *ptr1, *ptr2, *ptr3;
391   int type;
392 
393   /* lookup entry by it's ID */
394   type =
395     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
396   if (type != NO_TYPE)
397     {
398       Entry->Data.ChangeName.Name =
399 	(char *)(ChangeObjectName (type, ptr1, ptr2, ptr3,
400 			   Entry->Data.ChangeName.Name));
401       return (true);
402     }
403   return (false);
404 }
405 
406 /*!
407  * \brief Recovers an object from a 2ndSize change operation.
408  */
409 static bool
UndoChange2ndSize(UndoListType * Entry)410 UndoChange2ndSize (UndoListType *Entry)
411 {
412   void *ptr1, *ptr2, *ptr3;
413   int type;
414   Coord swap;
415 
416   /* lookup entry by ID */
417   type =
418     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
419   if (type != NO_TYPE)
420     {
421       swap = ((PinType *) ptr2)->DrillingHole;
422       if (andDraw)
423 	EraseObject (type, ptr1, ptr2);
424       ((PinType *) ptr2)->DrillingHole = Entry->Data.Size;
425       Entry->Data.Size = swap;
426       DrawObject (type, ptr1, ptr2);
427       return (true);
428     }
429   return (false);
430 }
431 
432 /*!
433  * \brief Recovers an object from a ChangeAngles change operation.
434  */
435 static bool
UndoChangeAngles(UndoListType * Entry)436 UndoChangeAngles (UndoListType *Entry)
437 {
438   void *ptr1, *ptr2, *ptr3;
439   int type;
440   long int old_sa, old_da;
441 
442   /* lookup entry by ID */
443   type =
444     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
445   if (type == ARC_TYPE)
446     {
447       LayerType *Layer = (LayerType *) ptr1;
448       ArcType *a = (ArcType *) ptr2;
449       r_delete_entry (Layer->arc_tree, (BoxType *) a);
450       old_sa = a->StartAngle;
451       old_da = a->Delta;
452       if (andDraw)
453 	EraseObject (type, Layer, a);
454       a->StartAngle = Entry->Data.Move.DX;
455       a->Delta = Entry->Data.Move.DY;
456       SetArcBoundingBox (a);
457       r_insert_entry (Layer->arc_tree, (BoxType *) a, 0);
458       Entry->Data.Move.DX = old_sa;
459       Entry->Data.Move.DY = old_da;;
460       DrawObject (type, ptr1, a);
461       return (true);
462     }
463   return (false);
464 }
465 
466 /*!
467  * \brief Recovers an object from a clearance size change operation.
468  */
469 static bool
UndoChangeClearSize(UndoListType * Entry)470 UndoChangeClearSize (UndoListType *Entry)
471 {
472   void *ptr1, *ptr2, *ptr3;
473   int type;
474   Coord swap;
475 
476   /* lookup entry by ID */
477   type =
478     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
479   if (type != NO_TYPE)
480     {
481       swap = ((PinType *) ptr2)->Clearance;
482       RestoreToPolygon (PCB->Data, type, ptr1, ptr2);
483       if (andDraw)
484 	EraseObject (type, ptr1, ptr2);
485       ((PinType *) ptr2)->Clearance = Entry->Data.Size;
486       ClearFromPolygon (PCB->Data, type, ptr1, ptr2);
487       Entry->Data.Size = swap;
488       if (andDraw)
489 	DrawObject (type, ptr1, ptr2);
490       return (true);
491     }
492   return (false);
493 }
494 
495 /*!
496  * \brief Recovers an object from a mask size change operation.
497  */
498 static bool
UndoChangeMaskSize(UndoListType * Entry)499 UndoChangeMaskSize (UndoListType *Entry)
500 {
501   void *ptr1, *ptr2, *ptr3;
502   int type;
503   Coord swap;
504 
505   /* lookup entry by ID */
506   type =
507     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
508   if (type & (VIA_TYPE | PIN_TYPE | PAD_TYPE))
509     {
510       swap =
511 	(type ==
512 	 PAD_TYPE ? ((PadType *) ptr2)->Mask : ((PinType *) ptr2)->Mask);
513       if (andDraw)
514 	EraseObject (type, ptr1, ptr2);
515       if (type == PAD_TYPE)
516 	((PadType *) ptr2)->Mask = Entry->Data.Size;
517       else
518 	((PinType *) ptr2)->Mask = Entry->Data.Size;
519       Entry->Data.Size = swap;
520       if (andDraw)
521 	DrawObject (type, ptr1, ptr2);
522       return (true);
523     }
524   return (false);
525 }
526 
527 
528 /*!
529  * \brief Recovers an object from a Size change operation.
530  */
531 static bool
UndoChangeSize(UndoListType * Entry)532 UndoChangeSize (UndoListType *Entry)
533 {
534   void *ptr1, *ptr2, *ptr3;
535   int type, iswap = 0;
536   Coord swap = 0;
537 
538   /* lookup entry by ID */
539   type =
540     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
541   if (type != NO_TYPE)
542     {
543       /* Wow! can any object be treated as a pin type for size change?? */
544       /* pins, vias, lines, and arcs can. Text can't but it has it's own mechanism */
545       switch (type)
546 	{
547 	case PIN_TYPE:
548 	case VIA_TYPE:
549 	  swap = ((PinType *) ptr2)->Thickness;
550 	  break;
551 	case LINE_TYPE:
552 	case ELEMENTLINE_TYPE:
553 	  swap = ((LineType *) ptr2)->Thickness;
554 	  break;
555 	case TEXT_TYPE:
556 	case ELEMENTNAME_TYPE:
557 	  iswap = ((TextType *) ptr2)->Scale;
558 	  break;
559 	case PAD_TYPE:
560 	  swap = ((PadType *) ptr2)->Thickness;
561 	  break;
562 	case ARC_TYPE:
563 	case ELEMENTARC_TYPE:
564 	  swap = ((ArcType *) ptr2)->Thickness;
565 	  break;
566 	}
567 
568       RestoreToPolygon (PCB->Data, type, ptr1, ptr2);
569       if (andDraw)
570 	EraseObject (type, ptr1, ptr2);
571 
572       switch (type)
573 	{
574 	case PIN_TYPE:
575 	case VIA_TYPE:
576 	  ((PinType *) ptr2)->Thickness = Entry->Data.Size;
577 	  Entry->Data.Size = swap;
578 	  break;
579 	case LINE_TYPE:
580 	case ELEMENTLINE_TYPE:
581 	  ((LineType *) ptr2)->Thickness = Entry->Data.Size;
582 	  Entry->Data.Size = swap;
583 	  break;
584 	case TEXT_TYPE:
585 	case ELEMENTNAME_TYPE:
586 	  ((TextType *) ptr2)->Scale = Entry->Data.Scale;
587 	  Entry->Data.Scale = iswap;
588 	  break;
589 	case PAD_TYPE:
590 	  ((PadType *) ptr2)->Thickness = Entry->Data.Size;
591 	  Entry->Data.Size = swap;
592 	  break;
593 	case ARC_TYPE:
594 	case ELEMENTARC_TYPE:
595 	  ((ArcType *) ptr2)->Thickness = Entry->Data.Size;
596 	  Entry->Data.Size = swap;
597 	  break;
598 	}
599 
600       ClearFromPolygon (PCB->Data, type, ptr1, ptr2);
601       if (andDraw)
602 	DrawObject (type, ptr1, ptr2);
603       return (true);
604     }
605   return (false);
606 }
607 
608 /*!
609  * \brief Recovers an object from a FLAG change operation.
610  */
611 static bool
UndoFlag(UndoListType * Entry)612 UndoFlag (UndoListType *Entry)
613 {
614   void *ptr1, *ptr2, *ptr3;
615   int type;
616   FlagType swap;
617   int must_redraw;
618 
619   /* lookup entry by ID */
620   type =
621     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
622   if (type != NO_TYPE)
623     {
624       FlagType f1, f2;
625       PinType *pin = (PinType *) ptr2;
626 
627       swap = pin->Flags;
628 
629       must_redraw = 0;
630       f1 = MaskFlags (pin->Flags, ~DRAW_FLAGS);
631       f2 = MaskFlags (Entry->Data.Flags, ~DRAW_FLAGS);
632 
633       if (!FLAGS_EQUAL (f1, f2))
634 	must_redraw = 1;
635 
636       if (andDraw && must_redraw)
637 	EraseObject (type, ptr1, ptr2);
638 
639       pin->Flags = Entry->Data.Flags;
640 
641       Entry->Data.Flags = swap;
642 
643       if (andDraw && must_redraw)
644 	DrawObject (type, ptr1, ptr2);
645       return (true);
646     }
647   Message ("hace Internal error: Can't find ID %d type %08x\n", Entry->ID,
648 	   Entry->Kind);
649   Message ("for UndoFlag Operation. Previous flags: %s\n",
650 	   flags_to_string (Entry->Data.Flags, 0));
651   return (false);
652 }
653 
654 /*!
655  * \brief Recovers an object from a mirror operation.
656  *
657  * \return true if anything has been recovered.
658  */
659 static bool
UndoMirror(UndoListType * Entry)660 UndoMirror (UndoListType *Entry)
661 {
662   void *ptr1, *ptr2, *ptr3;
663   int type;
664 
665   /* lookup entry by ID */
666   type =
667     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
668   if (type == ELEMENT_TYPE)
669     {
670       ElementType *element = (ElementType *) ptr3;
671       if (andDraw)
672 	EraseElement (element);
673       MirrorElementCoordinates (PCB->Data, element, Entry->Data.Move.DY);
674       if (andDraw)
675 	DrawElement (element);
676       return (true);
677     }
678   Message ("hace Internal error: UndoMirror on object type %d\n", type);
679   return (false);
680 }
681 
682 /*!
683  * \brief Recovers an object from a 'copy' or 'create' operation.
684  *
685  * \return true if anything has been recovered.
686  */
687 static bool
UndoCopyOrCreate(UndoListType * Entry)688 UndoCopyOrCreate (UndoListType *Entry)
689 {
690   void *ptr1, *ptr2, *ptr3;
691   int type;
692 
693   /* lookup entry by it's ID */
694   type =
695     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
696   if (type != NO_TYPE)
697     {
698       if (!RemoveList)
699 	RemoveList = CreateNewBuffer ();
700       if (andDraw)
701 	EraseObject (type, ptr1, ptr2);
702       /* in order to make this re-doable we move it to the RemoveList */
703       MoveObjectToBuffer (RemoveList, PCB->Data, type, ptr1, ptr2, ptr3);
704       Entry->Type = UNDO_REMOVE;
705       return (true);
706     }
707   return (false);
708 }
709 
710 /*!
711  * \brief Recovers an object from a 'move' operation.
712  *
713  * \return true if anything has been recovered.
714  */
715 static bool
UndoMove(UndoListType * Entry)716 UndoMove (UndoListType *Entry)
717 {
718   void *ptr1, *ptr2, *ptr3;
719   int type;
720 
721   /* lookup entry by it's ID */
722   type =
723     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
724   if (type != NO_TYPE)
725     {
726       MoveObject (type, ptr1, ptr2, ptr3,
727 		  -Entry->Data.Move.DX, -Entry->Data.Move.DY);
728       Entry->Data.Move.DX *= -1;
729       Entry->Data.Move.DY *= -1;
730       return (true);
731     }
732   return (false);
733 }
734 
735 /*!
736  * \brief Recovers an object from a 'remove' operation.
737  *
738  * \return true if anything has been recovered.
739  */
740 static bool
UndoRemove(UndoListType * Entry)741 UndoRemove (UndoListType *Entry)
742 {
743   void *ptr1, *ptr2, *ptr3;
744   int type;
745 
746   /* lookup entry by it's ID */
747   type =
748     SearchObjectByID (RemoveList, &ptr1, &ptr2, &ptr3, Entry->ID,
749 		      Entry->Kind);
750   if (type != NO_TYPE)
751     {
752       if (andDraw)
753 	DrawRecoveredObject (type, ptr1, ptr2, ptr3);
754       MoveObjectToBuffer (PCB->Data, RemoveList, type, ptr1, ptr2, ptr3);
755       Entry->Type = UNDO_CREATE;
756       return (true);
757     }
758   return (false);
759 }
760 
761 /*!
762  * \brief Recovers an object from a 'move to another layer' operation.
763  *
764  * \return true if anything has been recovered.
765  */
766 static bool
UndoMoveToLayer(UndoListType * Entry)767 UndoMoveToLayer (UndoListType *Entry)
768 {
769   void *ptr1, *ptr2, *ptr3;
770   int type, swap;
771 
772   /* lookup entry by it's ID */
773   type =
774     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
775   if (type != NO_TYPE)
776     {
777       swap = GetLayerNumber (PCB->Data, (LayerType *) ptr1);
778       MoveObjectToLayer (type, ptr1, ptr2, ptr3,
779 			 LAYER_PTR (Entry->Data.
780 				    MoveToLayer.OriginalLayer), true);
781       Entry->Data.MoveToLayer.OriginalLayer = swap;
782       return (true);
783     }
784   return (false);
785 }
786 
787 /*!
788  * \brief Recovers a removed polygon point.
789  *
790  * \return true on success.
791  */
792 static bool
UndoRemovePoint(UndoListType * Entry)793 UndoRemovePoint (UndoListType *Entry)
794 {
795   LayerType *layer;
796   PolygonType *polygon;
797   void *ptr3;
798   int type;
799 
800   /* lookup entry (polygon not point was saved) by it's ID */
801   assert (Entry->Kind == POLYGON_TYPE);
802   type =
803     SearchObjectByID (PCB->Data, (void **) &layer, (void **) &polygon, &ptr3,
804 		      Entry->ID, Entry->Kind);
805   switch (type)
806     {
807     case POLYGON_TYPE:		/* restore the removed point */
808       {
809 	/* recover the point */
810 	if (andDraw && layer->On)
811 	  ErasePolygon (polygon);
812 	InsertPointIntoObject (POLYGON_TYPE, layer, polygon,
813 			       &Entry->Data.RemovedPoint.Index,
814 			       Entry->Data.RemovedPoint.X,
815 			       Entry->Data.RemovedPoint.Y, true,
816 			       Entry->Data.RemovedPoint.last_in_contour);
817 
818 	polygon->Points[Entry->Data.RemovedPoint.Index].ID =
819 	  Entry->Data.RemovedPoint.ID;
820 	if (andDraw && layer->On)
821 	  DrawPolygon (layer, polygon);
822 	Entry->Type = UNDO_INSERT_POINT;
823 	Entry->ID = Entry->Data.RemovedPoint.ID;
824 	Entry->Kind = POLYGONPOINT_TYPE;
825 	return (true);
826       }
827 
828     default:
829       return (false);
830     }
831 }
832 
833 /*!
834  * \brief Recovers an inserted polygon point.
835  *
836  * \return true on success.
837  */
838 static bool
UndoInsertPoint(UndoListType * Entry)839 UndoInsertPoint (UndoListType *Entry)
840 {
841   LayerType *layer;
842   PolygonType *polygon;
843   PointType *pnt;
844   int type;
845   Cardinal point_idx;
846   Cardinal hole;
847   bool last_in_contour = false;
848 
849   assert (Entry->Kind == POLYGONPOINT_TYPE);
850   /* lookup entry by it's ID */
851   type =
852     SearchObjectByID (PCB->Data, (void **) &layer, (void **) &polygon,
853 		      (void **) &pnt, Entry->ID, Entry->Kind);
854   switch (type)
855     {
856     case POLYGONPOINT_TYPE:	/* removes an inserted polygon point */
857       {
858 	if (andDraw && layer->On)
859 	  ErasePolygon (polygon);
860 
861 	/* Check whether this point was at the end of its contour.
862 	 * If so, we need to flag as such when re-adding the point
863 	 * so it goes back in the correct place
864 	 */
865 	point_idx = polygon_point_idx (polygon, pnt);
866 	for (hole = 0; hole < polygon->HoleIndexN; hole++)
867 	  if (point_idx == polygon->HoleIndex[hole] - 1)
868 	    last_in_contour = true;
869 	if (point_idx == polygon->PointN - 1)
870 	  last_in_contour = true;
871 	Entry->Data.RemovedPoint.last_in_contour = last_in_contour;
872 
873 	Entry->Data.RemovedPoint.X = pnt->X;
874 	Entry->Data.RemovedPoint.Y = pnt->Y;
875 	Entry->Data.RemovedPoint.ID = pnt->ID;
876 	Entry->ID = polygon->ID;
877 	Entry->Kind = POLYGON_TYPE;
878 	Entry->Type = UNDO_REMOVE_POINT;
879 	Entry->Data.RemovedPoint.Index = point_idx;
880 	DestroyObject (PCB->Data, POLYGONPOINT_TYPE, layer, polygon, pnt);
881 	if (andDraw && layer->On)
882 	  DrawPolygon (layer, polygon);
883 	return (true);
884       }
885 
886     default:
887       return (false);
888     }
889 }
890 
891 static bool
UndoSwapCopiedObject(UndoListType * Entry)892 UndoSwapCopiedObject (UndoListType *Entry)
893 {
894   void *ptr1, *ptr2, *ptr3;
895   void *ptr1b, *ptr2b, *ptr3b;
896   AnyObjectType *obj, *obj2;
897   int type;
898   long int swap_id;
899 
900   /* lookup entry by it's ID */
901   type =
902     SearchObjectByID (RemoveList, &ptr1, &ptr2, &ptr3, Entry->Data.CopyID,
903 		      Entry->Kind);
904   if (type == NO_TYPE)
905     return false;
906 
907   type =
908     SearchObjectByID (PCB->Data, &ptr1b, &ptr2b, &ptr3b, Entry->ID,
909 		      Entry->Kind);
910   if (type == NO_TYPE)
911     return FALSE;
912 
913   obj = (AnyObjectType *)ptr2;
914   obj2 = (AnyObjectType *)ptr2b;
915 
916   swap_id = obj->ID;
917   obj->ID = obj2->ID;
918   obj2->ID = swap_id;
919 
920   MoveObjectToBuffer (RemoveList, PCB->Data, type, ptr1b, ptr2b, ptr3b);
921 
922   if (andDraw)
923     DrawRecoveredObject (Entry->Kind, ptr1, ptr2, ptr3);
924 
925   obj = (AnyObjectType *)MoveObjectToBuffer (PCB->Data, RemoveList, type, ptr1, ptr2, ptr3);
926   if (Entry->Kind == POLYGON_TYPE)
927     InitClip (PCB->Data, (LayerType *)ptr1b, (PolygonType *)obj);
928   return (true);
929 }
930 
931 /*!
932  * \brief Recovers an removed polygon point.
933  *
934  * \return true on success.
935  */
936 static bool
UndoRemoveContour(UndoListType * Entry)937 UndoRemoveContour (UndoListType *Entry)
938 {
939   assert (Entry->Kind == POLYGON_TYPE);
940   return UndoSwapCopiedObject (Entry);
941 }
942 
943 /*!
944  * \brief Recovers an inserted polygon point.
945  *
946  * \return true on success.
947  */
948 static bool
UndoInsertContour(UndoListType * Entry)949 UndoInsertContour (UndoListType *Entry)
950 {
951   assert (Entry->Kind == POLYGON_TYPE);
952   return UndoSwapCopiedObject (Entry);
953 }
954 
955 /*!
956  * \brief Undo a layer change.
957  *
958  * \return true on success.
959  */
960 static bool
UndoLayerChange(UndoListType * Entry)961 UndoLayerChange (UndoListType *Entry)
962 {
963   LayerChangeType *l = &Entry->Data.LayerChange;
964   int tmp;
965 
966   tmp = l->new_index;
967   l->new_index = l->old_index;
968   l->old_index = tmp;
969 
970   if (MoveLayer (l->old_index, l->new_index))
971     return false;
972   else
973     return true;
974 }
975 
976 /*!
977  * \brief Undo a netlist change.
978  *
979  * \return true on success.
980  */
981 static bool
UndoNetlistChange(UndoListType * Entry)982 UndoNetlistChange (UndoListType *Entry)
983 {
984   NetlistChangeType *l = & Entry->Data.NetlistChange;
985   unsigned int i, j;
986   LibraryType *lib, *saved;
987 
988   lib = l->lib;
989   saved = l->old;
990 
991   /* iterate over each net */
992   for (i = 0 ; i < lib->MenuN; i++)
993     {
994       if (lib->Menu[i].Name)
995 	free (lib->Menu[i].Name);
996 
997       if (lib->Menu[i].directory)
998 	free (lib->Menu[i].directory);
999 
1000       if (lib->Menu[i].Style)
1001 	free (lib->Menu[i].Style);
1002 
1003       /* iterate over each pin on the net */
1004       for (j = 0; j < lib->Menu[i].EntryN; j++) {
1005 
1006 	if (lib->Menu[i].Entry[j].ListEntry)
1007 	  free (lib->Menu[i].Entry[j].ListEntry);
1008 
1009 	if (lib->Menu[i].Entry[j].AllocatedMemory)
1010 	  free (lib->Menu[i].Entry[j].AllocatedMemory);
1011 
1012 	if (lib->Menu[i].Entry[j].Template)
1013 	  free (lib->Menu[i].Entry[j].Template);
1014 
1015 	if (lib->Menu[i].Entry[j].Package)
1016 	  free (lib->Menu[i].Entry[j].Package);
1017 
1018 	if (lib->Menu[i].Entry[j].Value)
1019 	  free (lib->Menu[i].Entry[j].Value);
1020 
1021 	if (lib->Menu[i].Entry[j].Description)
1022 	  free (lib->Menu[i].Entry[j].Description);
1023 
1024       }
1025     }
1026 
1027   if (lib->Menu)
1028     free (lib->Menu);
1029 
1030   *lib = *saved;
1031 
1032   NetlistChanged (0);
1033   return true;
1034 }
1035 
1036 /*!
1037  * \brief Recovers an object from a Size change operation.
1038  */
1039 static bool
UndoSetViaLayers(UndoListType * Entry)1040 UndoSetViaLayers (UndoListType *Entry)
1041 {
1042   void *ptr1, *ptr2, *ptr3;
1043   int type;
1044   int from, to;
1045 
1046   /* lookup entry by ID */
1047   type =
1048     SearchObjectByID (PCB->Data, &ptr1, &ptr2, &ptr3, Entry->ID, Entry->Kind);
1049   if (type == VIA_TYPE)
1050     {
1051       from = ((PinType *) ptr2)->BuriedFrom;
1052       to = ((PinType *) ptr2)->BuriedTo;
1053       RestoreToPolygon (PCB->Data, type, ptr1, ptr2);
1054       if (andDraw)
1055 	EraseObject (type, ptr1, ptr2);
1056       ((PinType *) ptr2)->BuriedFrom = Entry->Data.SetViaLayersChange.from;
1057       ((PinType *) ptr2)->BuriedTo = Entry->Data.SetViaLayersChange.to;
1058       Entry->Data.SetViaLayersChange.from = from;
1059       Entry->Data.SetViaLayersChange.to = to;
1060       ClearFromPolygon (PCB->Data, type, ptr1, ptr2);
1061       if (andDraw)
1062 	DrawObject (type, ptr1, ptr2);
1063       return (true);
1064     }
1065   return (false);
1066 }
1067 
1068 /*!
1069  * \brief Undo of any 'hard to recover' operation.
1070  *
1071  * \return The bitfield for the types of operations that were undone.
1072  */
1073 int
Undo(bool draw)1074 Undo (bool draw)
1075 {
1076   UndoListType *ptr;
1077   int Types = 0;
1078   int unique;
1079   bool error_undoing = false;
1080 
1081   unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
1082   CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
1083 
1084   andDraw = draw;
1085 
1086   if (Serial == 0)
1087     {
1088       Message (_("ERROR: Attempt to Undo() with Serial == 0\n"
1089                  "       Please save your work and report this bug.\n"));
1090       return 0;
1091     }
1092 
1093   if (UndoN == 0)
1094     {
1095       Message (_("Nothing to undo - buffer is empty\n"));
1096       return 0;
1097     }
1098 
1099   Serial --;
1100 
1101   ptr = &UndoList[UndoN - 1];
1102 
1103   if (ptr->Serial > Serial)
1104     {
1105       Message (_("ERROR: Bad undo serial number %d in undo stack - expecting %d or lower\n"
1106                  "       Please save your work and report this bug.\n"),
1107                ptr->Serial, Serial);
1108 
1109       /* It is likely that the serial number got corrupted through some bad
1110        * use of the SaveUndoSerialNumber() / RestoreUndoSerialNumber() APIs.
1111        *
1112        * Reset the serial number to be consistent with that of the last
1113        * operation on the undo stack in the hope that this might clear
1114        * the problem and  allow the user to hit Undo again.
1115        */
1116       Serial = ptr->Serial + 1;
1117       return 0;
1118     }
1119 
1120   LockUndo (); /* lock undo module to prevent from loops */
1121 
1122   /* Loop over all entries with the correct serial number */
1123   for (; UndoN && ptr->Serial == Serial; ptr--, UndoN--, RedoN++)
1124     {
1125       int undid = PerformUndo (ptr);
1126       if (undid == 0)
1127         error_undoing = true;
1128       Types |= undid;
1129     }
1130 
1131   UnlockUndo ();
1132 
1133   if (error_undoing)
1134     Message (_("ERROR: Failed to undo some operations\n"));
1135 
1136   if (Types && andDraw)
1137     Draw ();
1138 
1139   /* restore the unique flag setting */
1140   if (unique)
1141     SET_FLAG (UNIQUENAMEFLAG, PCB);
1142 
1143   return Types;
1144 }
1145 
1146 static int
PerformUndo(UndoListType * ptr)1147 PerformUndo (UndoListType *ptr)
1148 {
1149   switch (ptr->Type)
1150     {
1151     case UNDO_CHANGENAME:
1152       if (UndoChangeName (ptr))
1153 	return (UNDO_CHANGENAME);
1154       break;
1155 
1156     case UNDO_CREATE:
1157       if (UndoCopyOrCreate (ptr))
1158 	return (UNDO_CREATE);
1159       break;
1160 
1161     case UNDO_MOVE:
1162       if (UndoMove (ptr))
1163 	return (UNDO_MOVE);
1164       break;
1165 
1166     case UNDO_REMOVE:
1167       if (UndoRemove (ptr))
1168 	return (UNDO_REMOVE);
1169       break;
1170 
1171     case UNDO_REMOVE_POINT:
1172       if (UndoRemovePoint (ptr))
1173 	return (UNDO_REMOVE_POINT);
1174       break;
1175 
1176     case UNDO_INSERT_POINT:
1177       if (UndoInsertPoint (ptr))
1178 	return (UNDO_INSERT_POINT);
1179       break;
1180 
1181     case UNDO_REMOVE_CONTOUR:
1182       if (UndoRemoveContour (ptr))
1183 	return (UNDO_REMOVE_CONTOUR);
1184       break;
1185 
1186     case UNDO_INSERT_CONTOUR:
1187       if (UndoInsertContour (ptr))
1188 	return (UNDO_INSERT_CONTOUR);
1189       break;
1190 
1191     case UNDO_ROTATE:
1192       if (UndoRotate (ptr))
1193 	return (UNDO_ROTATE);
1194       break;
1195 
1196     case UNDO_CLEAR:
1197       if (UndoClearPoly (ptr))
1198 	return (UNDO_CLEAR);
1199       break;
1200 
1201     case UNDO_MOVETOLAYER:
1202       if (UndoMoveToLayer (ptr))
1203 	return (UNDO_MOVETOLAYER);
1204       break;
1205 
1206     case UNDO_FLAG:
1207       if (UndoFlag (ptr))
1208 	return (UNDO_FLAG);
1209       break;
1210 
1211     case UNDO_CHANGESIZE:
1212       if (UndoChangeSize (ptr))
1213 	return (UNDO_CHANGESIZE);
1214       break;
1215 
1216     case UNDO_CHANGECLEARSIZE:
1217       if (UndoChangeClearSize (ptr))
1218 	return (UNDO_CHANGECLEARSIZE);
1219       break;
1220 
1221     case UNDO_CHANGEMASKSIZE:
1222       if (UndoChangeMaskSize (ptr))
1223 	return (UNDO_CHANGEMASKSIZE);
1224       break;
1225 
1226     case UNDO_CHANGE2NDSIZE:
1227       if (UndoChange2ndSize (ptr))
1228 	return (UNDO_CHANGE2NDSIZE);
1229       break;
1230 
1231     case UNDO_CHANGEANGLES:
1232       if (UndoChangeAngles (ptr))
1233 	return (UNDO_CHANGEANGLES);
1234       break;
1235 
1236     case UNDO_LAYERCHANGE:
1237       if (UndoLayerChange (ptr))
1238 	return (UNDO_LAYERCHANGE);
1239       break;
1240 
1241     case UNDO_NETLISTCHANGE:
1242       if (UndoNetlistChange (ptr))
1243 	return (UNDO_NETLISTCHANGE);
1244       break;
1245 
1246     case UNDO_MIRROR:
1247       if (UndoMirror (ptr))
1248 	return (UNDO_MIRROR);
1249       break;
1250 
1251     case UNDO_CHANGESETVIALAYERS:
1252       if (UndoSetViaLayers (ptr))
1253         return (UNDO_CHANGESETVIALAYERS);
1254       break;
1255     }
1256   return 0;
1257 }
1258 
1259 /*!
1260  * \brief Redo of any 'hard to recover' operation.
1261  *
1262  * \return The number of operations redone.
1263  */
1264 int
Redo(bool draw)1265 Redo (bool draw)
1266 {
1267   UndoListType *ptr;
1268   int Types = 0;
1269   bool error_undoing = false;
1270 
1271   andDraw = draw;
1272 
1273   if (RedoN == 0)
1274     {
1275       Message (_("Nothing to redo. Perhaps changes have been made since last undo\n"));
1276       return 0;
1277     }
1278 
1279   ptr = &UndoList[UndoN];
1280 
1281   if (ptr->Serial < Serial)
1282     {
1283       Message (_("ERROR: Bad undo serial number %d in redo stack - expecting %d or higher\n"
1284                  "       Please save your work and report this bug.\n"),
1285                ptr->Serial, Serial);
1286 
1287       /* It is likely that the serial number got corrupted through some bad
1288        * use of the SaveUndoSerialNumber() / RestoreUndoSerialNumber() APIs.
1289        *
1290        * Reset the serial number to be consistent with that of the first
1291        * operation on the redo stack in the hope that this might clear
1292        * the problem and  allow the user to hit Redo again.
1293        */
1294       Serial = ptr->Serial;
1295       return 0;
1296     }
1297 
1298   LockUndo (); /* lock undo module to prevent from loops */
1299 
1300   /* and loop over all entries with the correct serial number */
1301   for (; RedoN && ptr->Serial == Serial; ptr++, UndoN++, RedoN--)
1302     {
1303       int undid = PerformUndo (ptr);
1304       if (undid == 0)
1305         error_undoing = true;
1306       Types |= undid;
1307     }
1308 
1309   /* Make next serial number current */
1310   Serial++;
1311 
1312   UnlockUndo ();
1313 
1314   if (error_undoing)
1315     Message (_("ERROR: Failed to redo some operations\n"));
1316 
1317   if (Types && andDraw)
1318     Draw ();
1319 
1320   return Types;
1321 }
1322 
1323 /*!
1324  * \brief Restores the serial number of the undo list.
1325  *
1326  * Returns the current undo serial number, after the restore.
1327  */
1328 int
RestoreUndoSerialNumber(void)1329 RestoreUndoSerialNumber (void)
1330 {
1331   if (added_undo_between_increment_and_restore)
1332     Message (_("ERROR: Operations were added to the Undo stack with an incorrect serial number\n"));
1333   between_increment_and_restore = false;
1334   added_undo_between_increment_and_restore = false;
1335   Serial = SavedSerial;
1336   return Serial;
1337 }
1338 
1339 /*!
1340  * \brief Saves the serial number of the undo list.
1341  *
1342  * Returns the current undo serial number.
1343  */
1344 int
SaveUndoSerialNumber(void)1345 SaveUndoSerialNumber (void)
1346 {
1347   Bumped = false;
1348   between_increment_and_restore = false;
1349   added_undo_between_increment_and_restore = false;
1350   SavedSerial = Serial;
1351   return Serial;
1352 }
1353 
1354 /*!
1355  * \brief Increments the serial number of the undo list.
1356  *
1357  * It's not done automatically because some operations perform more
1358  * than one request with the same serial #.
1359  *
1360  * Returns the undo serial number after the increment.
1361  */
1362 int
IncrementUndoSerialNumber(void)1363 IncrementUndoSerialNumber (void)
1364 {
1365   if (!Locked)
1366     {
1367       /* Set the changed flag if anything was added prior to this bump */
1368       if (UndoN > 0 && UndoList[UndoN - 1].Serial == Serial)
1369         SetChangedFlag (true);
1370       Serial++;
1371       Bumped = true;
1372       between_increment_and_restore = true;
1373       return Serial;
1374     }
1375   return -1;
1376 }
1377 
1378 /*!
1379  * \brief Merge range of undo serial numbers into one.
1380  *
1381  * The purpose of this is to combine multiple undo operations into a
1382  * single one.
1383  *
1384  * Any undo operation that has a serial number inclusively in the
1385  * range (min, max) gets reassigned the number min. Everything > max is
1386  * shifted down so that the serial numbers remain contiguous.
1387  *
1388  * This should not interfere with operations that have added things to the
1389  * undo list but not yet incremented the serial number.
1390  *
1391  * Returns the current undo serial number, after the operations.
1392  */
1393 int
MergeUndoSerialRange(int min,int max)1394 MergeUndoSerialRange(int min, int max)
1395 {
1396   size_t n;
1397   int dsn = max - min; /* delta serial number */
1398   for(n = 0; n < UndoN; n++)
1399   {
1400     if (UndoList[n].Serial < min) continue;
1401     else if (UndoList[n].Serial <= max) UndoList[n].Serial = min;
1402     /* greater than max */
1403     else UndoList[n].Serial -= dsn;
1404   }
1405   Serial -= dsn;
1406   return Serial;
1407 }
1408 
1409 /*!
1410  * \brief Releases memory of the undo- and remove list.
1411  */
1412 void
ClearUndoList(bool Force)1413 ClearUndoList (bool Force)
1414 {
1415   UndoListType *undo;
1416 
1417   if (UndoN
1418       && (Force || gui->confirm_dialog ("OK to clear 'undo' buffer?", 0)))
1419     {
1420       /* release memory allocated by objects in undo list */
1421       for (undo = UndoList; UndoN; undo++, UndoN--)
1422 	{
1423 	  if (undo->Type == UNDO_CHANGENAME)
1424 	    free (undo->Data.ChangeName.Name);
1425 	}
1426       free (UndoList);
1427       UndoList = NULL;
1428       if (RemoveList)
1429 	{
1430           FreeDataMemory (RemoveList);
1431 	  free (RemoveList);
1432 	  RemoveList = NULL;
1433         }
1434 
1435       /* reset some counters */
1436       UndoN = UndoMax = RedoN = 0;
1437     }
1438 
1439   /* reset counter in any case */
1440   Serial = 1;
1441 }
1442 
1443 /*!
1444  * \brief Adds an object to the list of clearpoly objects.
1445  */
1446 void
AddObjectToClearPolyUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3,bool clear)1447 AddObjectToClearPolyUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3,
1448 			      bool clear)
1449 {
1450   UndoListType *undo;
1451 
1452   if (!Locked)
1453     {
1454       undo = GetUndoSlot (UNDO_CLEAR, OBJECT_ID (Ptr3), Type);
1455       undo->Data.ClearPoly.Clear = clear;
1456       undo->Data.ClearPoly.Layer = (LayerType *) Ptr1;
1457     }
1458 }
1459 
1460 /*!
1461  * \brief Adds an object to the list of mirrored objects.
1462  */
1463 void
AddObjectToMirrorUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3,Coord yoff)1464 AddObjectToMirrorUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3,
1465 			   Coord yoff)
1466 {
1467   UndoListType *undo;
1468 
1469   if (!Locked)
1470     {
1471       undo = GetUndoSlot (UNDO_MIRROR, OBJECT_ID (Ptr3), Type);
1472       undo->Data.Move.DY = yoff;
1473     }
1474 }
1475 
1476 /*!
1477  * \brief Adds an object to the list of rotated objects.
1478  */
1479 void
AddObjectToRotateUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3,Coord CenterX,Coord CenterY,BYTE Steps)1480 AddObjectToRotateUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3,
1481 			   Coord CenterX, Coord CenterY,
1482 			   BYTE Steps)
1483 {
1484   UndoListType *undo;
1485 
1486   if (!Locked)
1487     {
1488       undo = GetUndoSlot (UNDO_ROTATE, OBJECT_ID (Ptr3), Type);
1489       undo->Data.Rotate.CenterX = CenterX;
1490       undo->Data.Rotate.CenterY = CenterY;
1491       undo->Data.Rotate.Steps = Steps;
1492     }
1493 }
1494 
1495 /*!
1496  * \brief Adds an object to the list of removed objects and removes it
1497  * from the current PCB.
1498  */
1499 void
MoveObjectToRemoveUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3)1500 MoveObjectToRemoveUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1501 {
1502   if (Locked)
1503     return;
1504 
1505   if (!RemoveList)
1506     RemoveList = CreateNewBuffer ();
1507 
1508   GetUndoSlot (UNDO_REMOVE, OBJECT_ID (Ptr3), Type);
1509   MoveObjectToBuffer (RemoveList, PCB->Data, Type, Ptr1, Ptr2, Ptr3);
1510 }
1511 
1512 /*!
1513  * \brief Adds an object to the list of removed polygon/... points.
1514  */
1515 void
AddObjectToRemovePointUndoList(int Type,void * Ptr1,void * Ptr2,Cardinal index)1516 AddObjectToRemovePointUndoList (int Type,
1517 				void *Ptr1, void *Ptr2, Cardinal index)
1518 {
1519   UndoListType *undo;
1520   PolygonType *polygon = (PolygonType *) Ptr2;
1521   Cardinal hole;
1522   bool last_in_contour = false;
1523 
1524   if (!Locked)
1525     {
1526       switch (Type)
1527 	{
1528 	case POLYGONPOINT_TYPE:
1529 	  {
1530 	    /* save the ID of the parent object; else it will be
1531 	     * impossible to recover the point
1532 	     */
1533 	    undo =
1534 	      GetUndoSlot (UNDO_REMOVE_POINT, OBJECT_ID (polygon),
1535 			   POLYGON_TYPE);
1536 	    undo->Data.RemovedPoint.X = polygon->Points[index].X;
1537 	    undo->Data.RemovedPoint.Y = polygon->Points[index].Y;
1538 	    undo->Data.RemovedPoint.ID = polygon->Points[index].ID;
1539 	    undo->Data.RemovedPoint.Index = index;
1540 
1541 	    /* Check whether this point was at the end of its contour.
1542 	     * If so, we need to flag as such when re-adding the point
1543 	     * so it goes back in the correct place
1544 	     */
1545 	    for (hole = 0; hole < polygon->HoleIndexN; hole++)
1546 	      if (index == polygon->HoleIndex[hole] - 1)
1547 		last_in_contour = true;
1548 	    if (index == polygon->PointN - 1)
1549 	      last_in_contour = true;
1550 	    undo->Data.RemovedPoint.last_in_contour = last_in_contour;
1551 	  }
1552 	  break;
1553 	}
1554     }
1555 }
1556 
1557 /*!
1558  * \brief Adds an object to the list of inserted polygon/... points.
1559  */
1560 void
AddObjectToInsertPointUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3)1561 AddObjectToInsertPointUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1562 {
1563   if (!Locked)
1564     GetUndoSlot (UNDO_INSERT_POINT, OBJECT_ID (Ptr3), Type);
1565 }
1566 
1567 static void
CopyObjectToUndoList(int undo_type,int Type,void * Ptr1,void * Ptr2,void * Ptr3)1568 CopyObjectToUndoList (int undo_type, int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1569 {
1570   UndoListType *undo;
1571   AnyObjectType *copy;
1572 
1573   if (Locked)
1574     return;
1575 
1576   if (!RemoveList)
1577     RemoveList = CreateNewBuffer ();
1578 
1579   undo = GetUndoSlot (undo_type, OBJECT_ID (Ptr2), Type);
1580   copy = (AnyObjectType *)CopyObjectToBuffer (RemoveList, PCB->Data, Type, Ptr1, Ptr2, Ptr3);
1581   undo->Data.CopyID = copy->ID;
1582 }
1583 
1584 /*!
1585  * \brief Adds an object to the list of removed contours.
1586  *
1587  * Actually just takes a copy of the whole polygon to restore.
1588  */
1589 void
AddObjectToRemoveContourUndoList(int Type,LayerType * Layer,PolygonType * Polygon)1590 AddObjectToRemoveContourUndoList (int Type,
1591 				  LayerType *Layer, PolygonType *Polygon)
1592 {
1593   CopyObjectToUndoList (UNDO_REMOVE_CONTOUR, Type, Layer, Polygon, NULL);
1594 }
1595 
1596 /*!
1597  * \brief Adds an object to the list of insert contours.
1598  *
1599  * Actually just takes a copy of the whole polygon to restore.
1600  */
1601 void
AddObjectToInsertContourUndoList(int Type,LayerType * Layer,PolygonType * Polygon)1602 AddObjectToInsertContourUndoList (int Type,
1603 				  LayerType *Layer, PolygonType *Polygon)
1604 {
1605   CopyObjectToUndoList (UNDO_INSERT_CONTOUR, Type, Layer, Polygon, NULL);
1606 }
1607 
1608 /*!
1609  * \brief Adds an object to the list of moved objects.
1610  */
1611 void
AddObjectToMoveUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3,Coord DX,Coord DY)1612 AddObjectToMoveUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3,
1613 			 Coord DX, Coord DY)
1614 {
1615   UndoListType *undo;
1616 
1617   if (!Locked)
1618     {
1619       undo = GetUndoSlot (UNDO_MOVE, OBJECT_ID (Ptr3), Type);
1620       undo->Data.Move.DX = DX;
1621       undo->Data.Move.DY = DY;
1622     }
1623 }
1624 
1625 /*!
1626  * \brief Adds an object to the list of objects with changed names.
1627  */
1628 void
AddObjectToChangeNameUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3,char * OldName)1629 AddObjectToChangeNameUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3,
1630 			       char *OldName)
1631 {
1632   UndoListType *undo;
1633 
1634   if (!Locked)
1635     {
1636       undo = GetUndoSlot (UNDO_CHANGENAME, OBJECT_ID (Ptr3), Type);
1637       undo->Data.ChangeName.Name = OldName;
1638     }
1639 }
1640 
1641 /*!
1642  * \brief Adds an object to the list of objects moved to another layer.
1643  */
1644 void
AddObjectToMoveToLayerUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3)1645 AddObjectToMoveToLayerUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1646 {
1647   UndoListType *undo;
1648 
1649   if (!Locked)
1650     {
1651       undo = GetUndoSlot (UNDO_MOVETOLAYER, OBJECT_ID (Ptr3), Type);
1652       undo->Data.MoveToLayer.OriginalLayer =
1653 	GetLayerNumber (PCB->Data, (LayerType *) Ptr1);
1654     }
1655 }
1656 
1657 /*!
1658  * \brief Adds an object to the list of created objects.
1659  */
1660 void
AddObjectToCreateUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3)1661 AddObjectToCreateUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1662 {
1663   if (!Locked)
1664     GetUndoSlot (UNDO_CREATE, OBJECT_ID (Ptr3), Type);
1665   ClearFromPolygon (PCB->Data, Type, Ptr1, Ptr2);
1666 }
1667 
1668 /*!
1669  * \brief Adds an object to the list of objects with flags changed.
1670  */
1671 void
AddObjectToFlagUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3)1672 AddObjectToFlagUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1673 {
1674   UndoListType *undo;
1675 
1676   if (!Locked)
1677     {
1678       undo = GetUndoSlot (UNDO_FLAG, OBJECT_ID (Ptr2), Type);
1679       undo->Data.Flags = ((PinType *) Ptr2)->Flags;
1680     }
1681 }
1682 
1683 /*!
1684  * \brief Adds an object to the list of objects with Size changes.
1685  */
1686 void
AddObjectToSizeUndoList(int Type,void * ptr1,void * ptr2,void * ptr3)1687 AddObjectToSizeUndoList (int Type, void *ptr1, void *ptr2, void *ptr3)
1688 {
1689   UndoListType *undo;
1690 
1691   if (!Locked)
1692     {
1693       undo = GetUndoSlot (UNDO_CHANGESIZE, OBJECT_ID (ptr2), Type);
1694       switch (Type)
1695 	{
1696 	case PIN_TYPE:
1697 	case VIA_TYPE:
1698 	  undo->Data.Size = ((PinType *) ptr2)->Thickness;
1699 	  break;
1700 	case LINE_TYPE:
1701 	case ELEMENTLINE_TYPE:
1702 	  undo->Data.Size = ((LineType *) ptr2)->Thickness;
1703 	  break;
1704 	case TEXT_TYPE:
1705 	case ELEMENTNAME_TYPE:
1706 	  undo->Data.Scale = ((TextType *) ptr2)->Scale;
1707 	  break;
1708 	case PAD_TYPE:
1709 	  undo->Data.Size = ((PadType *) ptr2)->Thickness;
1710 	  break;
1711 	case ARC_TYPE:
1712 	case ELEMENTARC_TYPE:
1713 	  undo->Data.Size = ((ArcType *) ptr2)->Thickness;
1714 	  break;
1715 	}
1716     }
1717 }
1718 
1719 /*!
1720  * \brief Adds an object to the list of objects with Size changes.
1721  */
1722 void
AddObjectToClearSizeUndoList(int Type,void * ptr1,void * ptr2,void * ptr3)1723 AddObjectToClearSizeUndoList (int Type, void *ptr1, void *ptr2, void *ptr3)
1724 {
1725   UndoListType *undo;
1726 
1727   if (!Locked)
1728     {
1729       undo = GetUndoSlot (UNDO_CHANGECLEARSIZE, OBJECT_ID (ptr2), Type);
1730       switch (Type)
1731 	{
1732 	case PIN_TYPE:
1733 	case VIA_TYPE:
1734 	  undo->Data.Size = ((PinType *) ptr2)->Clearance;
1735 	  break;
1736 	case LINE_TYPE:
1737 	  undo->Data.Size = ((LineType *) ptr2)->Clearance;
1738 	  break;
1739 	case PAD_TYPE:
1740 	  undo->Data.Size = ((PadType *) ptr2)->Clearance;
1741 	  break;
1742 	case ARC_TYPE:
1743 	  undo->Data.Size = ((ArcType *) ptr2)->Clearance;
1744 	  break;
1745 	}
1746     }
1747 }
1748 
1749 /*!
1750  * \brief Adds an object to the list of objects with Size changes.
1751  */
1752 void
AddObjectToMaskSizeUndoList(int Type,void * ptr1,void * ptr2,void * ptr3)1753 AddObjectToMaskSizeUndoList (int Type, void *ptr1, void *ptr2, void *ptr3)
1754 {
1755   UndoListType *undo;
1756 
1757   if (!Locked)
1758     {
1759       undo = GetUndoSlot (UNDO_CHANGEMASKSIZE, OBJECT_ID (ptr2), Type);
1760       switch (Type)
1761 	{
1762 	case PIN_TYPE:
1763 	case VIA_TYPE:
1764 	  undo->Data.Size = ((PinType *) ptr2)->Mask;
1765 	  break;
1766 	case PAD_TYPE:
1767 	  undo->Data.Size = ((PadType *) ptr2)->Mask;
1768 	  break;
1769 	}
1770     }
1771 }
1772 
1773 /*!
1774  * \brief Adds an object to the list of objects with 2ndSize changes.
1775  */
1776 void
AddObjectTo2ndSizeUndoList(int Type,void * ptr1,void * ptr2,void * ptr3)1777 AddObjectTo2ndSizeUndoList (int Type, void *ptr1, void *ptr2, void *ptr3)
1778 {
1779   UndoListType *undo;
1780 
1781   if (!Locked)
1782     {
1783       undo = GetUndoSlot (UNDO_CHANGE2NDSIZE, OBJECT_ID (ptr2), Type);
1784       if (Type == PIN_TYPE || Type == VIA_TYPE)
1785 	undo->Data.Size = ((PinType *) ptr2)->DrillingHole;
1786     }
1787 }
1788 
1789 /*!
1790  * \brief Adds an object to the list of changed angles.
1791  *
1792  * \note You must call this before changing the angles, passing the new
1793  * start/delta.
1794  */
1795 void
AddObjectToChangeAnglesUndoList(int Type,void * Ptr1,void * Ptr2,void * Ptr3)1796 AddObjectToChangeAnglesUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1797 {
1798   UndoListType *undo;
1799   ArcType *a = (ArcType *) Ptr3;
1800 
1801   if (!Locked)
1802     {
1803       undo = GetUndoSlot (UNDO_CHANGEANGLES, OBJECT_ID (Ptr3), Type);
1804       undo->Data.Move.DX = a->StartAngle;
1805       undo->Data.Move.DY = a->Delta;
1806     }
1807 }
1808 
1809 /*!
1810  * \brief Adds a layer change (new, delete, move) to the undo list.
1811  */
1812 void
AddLayerChangeToUndoList(int old_index,int new_index)1813 AddLayerChangeToUndoList (int old_index, int new_index)
1814 {
1815   UndoListType *undo;
1816 
1817   if (!Locked)
1818     {
1819       undo = GetUndoSlot (UNDO_LAYERCHANGE, 0, 0);
1820       undo->Data.LayerChange.old_index = old_index;
1821       undo->Data.LayerChange.new_index = new_index;
1822     }
1823 }
1824 
1825 /*!
1826  * \brief Adds a netlist change to the undo list.
1827  */
1828 void
AddNetlistLibToUndoList(LibraryType * lib)1829 AddNetlistLibToUndoList (LibraryType *lib)
1830 {
1831   UndoListType *undo;
1832   unsigned int i, j;
1833   LibraryType *old;
1834 
1835   if (!Locked)
1836     {
1837       undo = GetUndoSlot (UNDO_NETLISTCHANGE, 0, 0);
1838       /* keep track of where the data needs to go */
1839       undo->Data.NetlistChange.lib = lib;
1840 
1841       /* and what the old data is that we'll need to restore */
1842       undo->Data.NetlistChange.old = (LibraryType *)malloc (sizeof (LibraryType));
1843       old = undo->Data.NetlistChange.old;
1844       old->MenuN = lib->MenuN;
1845       old->MenuMax = lib->MenuMax;
1846       old->Menu = (LibraryMenuType *)malloc (old->MenuMax * sizeof (LibraryMenuType));
1847       if (old->Menu == NULL)
1848 	{
1849 	  fprintf (stderr, "malloc() failed in %s\n", __FUNCTION__);
1850 	  exit (1);
1851 	}
1852 
1853       /* iterate over each net */
1854       for (i = 0 ; i < lib->MenuN; i++)
1855 	{
1856 	  old->Menu[i].EntryN = lib->Menu[i].EntryN;
1857 	  old->Menu[i].EntryMax = lib->Menu[i].EntryMax;
1858 
1859 	  old->Menu[i].Name =
1860 	    lib->Menu[i].Name ? strdup (lib->Menu[i].Name) : NULL;
1861 
1862 	  old->Menu[i].directory =
1863 	    lib->Menu[i].directory ? strdup (lib->Menu[i].directory) : NULL;
1864 
1865 	  old->Menu[i].Style =
1866 	    lib->Menu[i].Style ? strdup (lib->Menu[i].Style) : NULL;
1867 
1868 
1869 	  old->Menu[i].Entry =
1870 	    (LibraryEntryType *)malloc (old->Menu[i].EntryMax * sizeof (LibraryEntryType));
1871 	  if (old->Menu[i].Entry == NULL)
1872 	    {
1873 	      fprintf (stderr, "malloc() failed in %s\n", __FUNCTION__);
1874 	      exit (1);
1875 	    }
1876 
1877 	  /* iterate over each pin on the net */
1878 	  for (j = 0; j < lib->Menu[i].EntryN; j++) {
1879 
1880 	    old->Menu[i].Entry[j].ListEntry =
1881 	      lib->Menu[i].Entry[j].ListEntry ?
1882 	      strdup (lib->Menu[i].Entry[j].ListEntry) :
1883 	      NULL;
1884 
1885 	    old->Menu[i].Entry[j].AllocatedMemory =
1886 	      lib->Menu[i].Entry[j].AllocatedMemory ?
1887 	      strdup (lib->Menu[i].Entry[j].AllocatedMemory) :
1888 	      NULL;
1889 
1890 	    old->Menu[i].Entry[j].Template =
1891 	      lib->Menu[i].Entry[j].Template ?
1892 	      strdup (lib->Menu[i].Entry[j].Template) :
1893 	      NULL;
1894 
1895 	    old->Menu[i].Entry[j].Package =
1896 	      lib->Menu[i].Entry[j].Package ?
1897 	      strdup (lib->Menu[i].Entry[j].Package) :
1898 	      NULL;
1899 
1900 	    old->Menu[i].Entry[j].Value =
1901 	      lib->Menu[i].Entry[j].Value ?
1902 	      strdup (lib->Menu[i].Entry[j].Value) :
1903 	      NULL;
1904 
1905 	    old->Menu[i].Entry[j].Description =
1906 	      lib->Menu[i].Entry[j].Description ?
1907 	      strdup (lib->Menu[i].Entry[j].Description) :
1908 	      NULL;
1909 
1910 
1911 	  }
1912 	}
1913 
1914     }
1915 }
1916 
1917 /*!
1918  * \brief Adds an object to the list of objects with buried info data.
1919  */
1920 void
AddObjectToSetViaLayersUndoList(void * ptr1,void * ptr2,void * ptr3)1921 AddObjectToSetViaLayersUndoList (void *ptr1, void *ptr2, void *ptr3)
1922 {
1923   UndoListType *undo;
1924 
1925   if (!Locked)
1926     {
1927       undo = GetUndoSlot (UNDO_CHANGESETVIALAYERS, OBJECT_ID (ptr2), VIA_TYPE);
1928       undo->Data.SetViaLayersChange.from = ((PinType *) ptr2)->BuriedFrom;
1929       undo->Data.SetViaLayersChange.to = ((PinType *) ptr2)->BuriedTo;
1930     }
1931 }
1932 
1933 
1934 /*!
1935  * \brief Set lock flag
1936  */
1937 void
LockUndo(void)1938 LockUndo (void)
1939 {
1940   Locked = true;
1941 }
1942 
1943 /*!
1944  * \brief Reset lock flag.
1945  */
1946 void
UnlockUndo(void)1947 UnlockUndo (void)
1948 {
1949   Locked = false;
1950 }
1951 
1952 /*!
1953  * \brief Return undo lock state.
1954  */
1955 bool
Undoing(void)1956 Undoing (void)
1957 {
1958   return (Locked);
1959 }
1960