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