1 /*----------------------------------------------------------------------*/
2 /* undo.c 								*/
3 /*									*/
4 /* The comprehensive "undo" and "redo" command handler			*/
5 /*									*/
6 /* Copyright (c) 2004  Tim Edwards, Open Circuit Design, Inc., and	*/
7 /* MultiGiG, Inc.							*/
8 /*----------------------------------------------------------------------*/
9 
10 /*----------------------------------------------------------------------*/
11 /*      written by Tim Edwards, 1/29/04    				*/
12 /*----------------------------------------------------------------------*/
13 
14 #define MODE_UNDO (u_char)0
15 #define MODE_REDO (u_char)1
16 
17 #define MAX_UNDO_EVENTS 100
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <string.h>
23 
24 #ifndef XC_WIN32
25 #include <X11/Intrinsic.h>
26 #include <X11/StringDefs.h>
27 #endif
28 
29 /*----------------------------------------------------------------------*/
30 /* Local includes							*/
31 /*----------------------------------------------------------------------*/
32 
33 #ifdef TCL_WRAPPER
34 #include <tk.h>
35 #endif
36 
37 #include "xcircuit.h"
38 
39 /*----------------------------------------------------------------------*/
40 /* Function prototype declarations                                      */
41 /*----------------------------------------------------------------------*/
42 #include "prototypes.h"
43 
44 /*----------------------------------------------------------------------*/
45 /* Local structure definitions for holding undo information		*/
46 /*----------------------------------------------------------------------*/
47 
48 typedef struct {
49    float  angle1;
50    float  angle2;
51    short  radius;
52    short  yaxis;
53    XPoint position;
54 } arcinfo;
55 
56 /* Smaller data structure used when saving paths in the undo stacks.    */
57 /* We don't need the rendering data from points[].                      */
58 
59 typedef struct {
60    u_short	type;
61    int		color;
62    eparamptr	passed;
63    u_short	style;
64    float	width;
65    XPoint	ctrl[4];
66 } splineinfo;
67 
68 typedef struct {
69    int number;
70    pointlist points;
71 } pathinfo;
72 
73 typedef struct editelem {
74    genericptr element;		/* element being edited */
75    union {
76       stringpart *string;	/* original contents of string, for label */
77       pointlist points;		/* original polygon */
78       arcinfo *arcspecs;	/* original arc values */
79       pathinfo *pathspecs;	/* original parts of a path */
80       XPoint	instpos;	/* original position, for an object instance */
81    } save;
82 } editelement;
83 
84 typedef struct {
85    genericptr element;		/* element modified */
86    float scale;			/* old scale value */
87 } scaleinfo;
88 
89 typedef struct {
90    XPoint rotpos;		/* original position */
91    float rotation;		/* old rotation value */
92 } rotateinfo;
93 
94 u_char undo_collect = (u_char)0;
95 
96 /*----------------------------------------------------------------------*/
97 /* Externally declared variables					*/
98 /*----------------------------------------------------------------------*/
99 
100 extern Globaldata xobjs;
101 extern XCWindowData *areawin;
102 
103 /*----------------------------------------------------------------------*/
104 /* Attempt to set the window.  If the window exists, set it as current	*/
105 /* and return TRUE.  Otherwise, return FALSE.  This prevents the undo	*/
106 /* mechanism from crashing if we close a window and then try to undo	*/
107 /* events that were created relative to it.				*/
108 /*----------------------------------------------------------------------*/
109 
setwindow(XCWindowData * trywindow)110 Boolean setwindow(XCWindowData *trywindow)
111 {
112    XCWindowData *chkwin;
113 
114    for (chkwin = xobjs.windowlist; chkwin != NULL; chkwin = chkwin->next) {
115       if (chkwin == trywindow) {
116 	 areawin = trywindow;
117 	 return TRUE;
118       }
119    }
120    return FALSE;
121 }
122 
123 /*----------------------------------------------------------------------*/
124 /* remember_selection ---						*/
125 /*									*/
126 /*   Copy a selection list into a "uselection" record.  The uselection	*/
127 /*   record maintains the order of the elements as well as pointers to	*/
128 /*   each element, so the original element ordering can be recovered.	*/
129 /*----------------------------------------------------------------------*/
130 
remember_selection(objinstptr topinst,short * slist,int number)131 uselection *remember_selection(objinstptr topinst, short *slist, int number)
132 {
133    int i, idx;
134    uselection *newlist;
135 
136    newlist = (uselection *)malloc(sizeof(uselection));
137    if (number > 0) {
138       newlist->element = (genericptr *)malloc(number * sizeof(genericptr));
139       newlist->idx = (short *)malloc(number * sizeof(short));
140    }
141    else {
142       newlist->element = NULL;
143       newlist->idx = NULL;
144    }
145    newlist->number = number;
146    for (i = 0; i < number; i++) {
147       idx = *(slist + i);
148       *(newlist->element + i) = *(topinst->thisobject->plist + idx);
149       *(newlist->idx + i) = idx;
150    }
151    return newlist;
152 }
153 
154 /*----------------------------------------------------------------------*/
155 /* Create a selection list in areawin from the saved uselection		*/
156 /* record.								*/
157 /*----------------------------------------------------------------------*/
158 
regen_selection(objinstptr thisinst,uselection * srec)159 short *regen_selection(objinstptr thisinst, uselection *srec)
160 {
161    int i, j, k;
162    genericptr egen;
163    objectptr thisobj = thisinst->thisobject;
164    Boolean reorder = False;
165    short *slist;
166 
167    if (srec->number > 0)
168       slist = (short *)malloc(srec->number * sizeof(short));
169 
170    k = 0;
171    for (i = 0; i < srec->number; i++) {
172 
173       /* Use the element address, not the selection order. */
174       egen = *(srec->element + i);
175       if (egen == *(thisobj->plist + *(srec->idx + i)))
176 	 j = *(srec->idx + i);
177       else {
178 	 reorder = True;
179          for (j = 0; j < thisobj->parts; j++) {
180 	    if (egen == *(thisobj->plist + j))
181 	       break;
182 	 }
183       }
184       if (j < thisobj->parts) {
185 	 *(slist + k) = j;
186 	 k++;
187       }
188       else
189 	 Fprintf(stderr, "Error: element %p in select list but not object\n",
190 		egen);
191    }
192 
193    /* If the selection order is different from the order in the object,	*/
194    /* then rearrange the object's list to match the selection order.	*/
195 
196    if (reorder) {
197       /* (to be done) */
198    }
199 
200    if (k == 0) {
201       if (srec->number > 0) free(slist);
202       return NULL;
203    }
204    else
205       return slist;
206 }
207 
208 /*----------------------------------------------------------------------*/
209 /* Use the selection list in the undo record to reorder parts in the	*/
210 /* object's part list.  Invert the selection list and return it.	*/
211 /*----------------------------------------------------------------------*/
212 
reorder_selection(Undoptr thisrecord)213 void reorder_selection(Undoptr thisrecord)
214 {
215    short *slist, *newlist, snum, i;
216    genericptr *pgen, *plist, egen;
217    objinstptr thisinst = thisrecord->thisinst;
218    objectptr thisobj = thisinst->thisobject;
219 
220    snum = (short)thisrecord->idata;
221    slist = (short *)thisrecord->undodata;
222    plist = (genericptr *)malloc(snum * sizeof(genericptr));
223    newlist = (short *)malloc(snum * sizeof(short));
224 
225    i = 0;
226    for (pgen = plist; pgen < plist + snum; pgen++) {
227       egen = *(thisobj->plist + i);
228       *(plist + *(slist + i)) = egen;
229       i++;
230    }
231    i = 0;
232    for (pgen = plist; pgen < plist + snum; pgen++) {
233       *(thisobj->plist + i) = *pgen;
234       *(newlist + *(slist + i)) = i;
235       i++;
236    }
237    free(plist);
238    free(thisrecord->undodata);
239    thisrecord->undodata = (char *)newlist;
240 }
241 
242 /*----------------------------------------------------------------------*/
243 /* free_editelement ---							*/
244 /*									*/
245 /*   Free memory allocated to an undo record edit element structure.	*/
246 /*----------------------------------------------------------------------*/
247 
free_editelement(Undoptr thisrecord)248 void free_editelement(Undoptr thisrecord)
249 {
250    editelement *erec;
251    pathinfo *ppi;
252 
253    erec = (editelement *)thisrecord->undodata;
254    switch (erec->element->type) {
255       case LABEL:
256 	 freelabel(erec->save.string);
257          break;
258       case POLYGON: case SPLINE:
259 	 free(erec->save.points);
260 	 break;
261       case ARC:
262 	 free(erec->save.arcspecs);
263 	 break;
264       case PATH:
265 	 for (ppi = erec->save.pathspecs; ppi < erec->save.pathspecs +
266 		thisrecord->idata; ppi++)
267 	    free(ppi->points);
268 	 free(erec->save.pathspecs);
269 	 break;
270    }
271    free(erec);
272 }
273 
274 /*----------------------------------------------------------------------*/
275 /* free_selection ---							*/
276 /*									*/
277 /*   Free memory allocated to an undo record selection list.		*/
278 /*----------------------------------------------------------------------*/
279 
free_selection(uselection * selrec)280 void free_selection(uselection *selrec)
281 {
282    if (selrec->number > 0) {
283       free(selrec->element);
284       free(selrec->idx);
285    }
286    free(selrec);
287 }
288 
289 /*----------------------------------------------------------------------*/
290 /* get_original_string ---						*/
291 /*									*/
292 /* Find the original version of the given label.			*/
293 /*----------------------------------------------------------------------*/
294 
get_original_string(labelptr thislab)295 stringpart *get_original_string(labelptr thislab)
296 {
297    Undoptr chkrecord, thisrecord;
298    editelement *erec;
299    labelptr elab;
300 
301    thisrecord = xobjs.undostack;
302    if (thisrecord == NULL) return NULL;
303 
304    for (chkrecord = thisrecord; chkrecord != NULL; chkrecord = chkrecord->next) {
305       switch (chkrecord->type) {
306 	 case XCF_Edit:
307 	    erec = (editelement *)(chkrecord->undodata);
308 	    elab = (labelptr)(erec->element);
309 	    if (elab != thislab) return NULL;
310 	    return erec->save.string;
311 
312 	 default:
313 	    return NULL;
314       }
315    }
316    return NULL;  /* yes, this can be reached, if thisrecord->next == NULL */
317 }
318 
319 /*----------------------------------------------------------------------*/
320 /* select_previous ---							*/
321 /*									*/
322 /* Set the selection to what was previously selected in the undo list.	*/
323 /* Return 0 on success, -1 if no previous selection was found.		*/
324 /*----------------------------------------------------------------------*/
325 
select_previous(Undoptr thisrecord)326 int select_previous(Undoptr thisrecord)
327 {
328    Undoptr chkrecord;
329    uselection *srec;
330 
331    clearselects_noundo();
332    for (chkrecord = thisrecord->next; chkrecord != NULL; chkrecord = chkrecord->next) {
333 
334       /* Selections may cross page changes, but only if in the same series */
335       if ((chkrecord->thisinst != thisrecord->thisinst) &&
336 		(chkrecord->idx != thisrecord->idx))
337 	 break;
338 
339       switch (chkrecord->type) {
340 	 case XCF_Delete: case XCF_Pop: case XCF_Push:
341 	    /* Delete/Push/Pop records imply a canceled selection */
342 	    return 0;
343 	 case XCF_Copy:
344 	 case XCF_Select:
345 	    srec = (uselection *)chkrecord->undodata;
346 	    areawin->selectlist = regen_selection(thisrecord->thisinst, srec);
347 	    areawin->selects = (areawin->selectlist) ? srec->number : 0;
348 	    return 0;
349       }
350    }
351    return -1;
352 }
353 
354 /*----------------------------------------------------------------------*/
355 /* This is similar to the above routine, but we just return a pointer	*/
356 /* to the index list in the uselection record.  This lets the undelete	*/
357 /* function restore the original ordering of parts that were deleted.	*/
358 /*----------------------------------------------------------------------*/
359 
recover_selectlist(Undoptr thisrecord)360 short *recover_selectlist(Undoptr thisrecord)
361 {
362    Undoptr chkrecord;
363    uselection *srec;
364 
365    for (chkrecord = thisrecord->next; chkrecord != NULL; chkrecord = chkrecord->next) {
366 
367       /* Selections may cross page changes, but only if in the same series */
368       if ((chkrecord->thisinst != thisrecord->thisinst) &&
369 		(chkrecord->idx != thisrecord->idx))
370 	 break;
371 
372       switch (chkrecord->type) {
373 	 case XCF_Delete: case XCF_Pop: case XCF_Push:
374 	    /* Delete/Push/Pop records imply a canceled selection */
375 	    return NULL;
376 	 case XCF_Copy:
377 	    /* Copy is the same as Select, but the copied objects are	*/
378 	    /* always at the end of the element list.  Returning NULL	*/
379 	    /* is the same as declaring that elements should be		*/
380 	    /* appended to the end of the object's element list.	*/
381 	    return NULL;
382 	 case XCF_Select:
383 	    srec = (uselection *)chkrecord->undodata;
384 	    return srec->idx;
385       }
386    }
387    return NULL;
388 }
389 
390 /*----------------------------------------------------------------------*/
391 /* Put element "thiselem" back into object "thisobj".  This is only	*/
392 /* used in the case where the elements were previously at the end of	*/
393 /* the object's element list.						*/
394 /*----------------------------------------------------------------------*/
395 
undelete_one_element(objinstptr thisinst,genericptr thiselem)396 void undelete_one_element(objinstptr thisinst, genericptr thiselem)
397 {
398    objectptr thisobj = thisinst->thisobject;
399 
400    PLIST_INCR(thisobj);
401    *(thisobj->plist + thisobj->parts) = thiselem;
402    thisobj->parts++;
403 }
404 
405 /*----------------------------------------------------------------------*/
406 /* register_for_undo ---						*/
407 /*									*/
408 /*   Register an event with the undo handler.  This creates a record	*/
409 /*   based on the type, which is one of the XCF_* bindings (see		*/
410 /*   xcircuit.h for the list).  This is a variable-argument routine,	*/
411 /*   with the arguments dependent on the command.			*/
412 /*									*/
413 /*   thisinst is the instance of the object in which the event occurred	*/
414 /*   and is registered for every event type.				*/
415 /*									*/
416 /*   "mode" is UNDO_MORE if one or more undo records are expected to 	*/
417 /*   follow in a series, or UNDO_DONE if this completes a series, or is	*/
418 /*   as single undo event. The command-line command "undo series start"	*/
419 /*   forces all events to be UNDO_MORE until "undo series end" is	*/
420 /*   issued.								*/
421 /*----------------------------------------------------------------------*/
422 
register_for_undo(u_int type,u_char mode,objinstptr thisinst,...)423 void register_for_undo(u_int type, u_char mode, objinstptr thisinst, ...)
424 {
425    va_list args;
426    int drawmode, nval, oval, *idata, snum, deltax, deltay, i;
427    double dir;
428    short *slist;
429    objectptr delobj;
430    objinstptr newinst;
431    Undoptr newrecord;
432    uselection *srec;
433    editelement *erec;
434    scaleinfo *escale;
435    genericptr egen;
436    XPoint *fpoint;
437    double scale;
438 
439    /* Do not register new events while running undo/redo actions! */
440    if (eventmode == UNDO_MODE) return;
441 
442    /* This action invalidates everything in the "redo" stack, so flush it */
443    flush_redo_stack();
444 
445    /* Create the new record and push it onto the stack */
446    newrecord = (Undoptr)malloc(sizeof(Undostack));
447    newrecord->next = xobjs.undostack;
448    newrecord->last = NULL;
449    newrecord->type = type;
450    newrecord->thisinst =  thisinst;
451    newrecord->window = areawin;
452    newrecord->undodata = (char *)NULL;
453    newrecord->idata = 0;
454 
455    if (xobjs.undostack) {
456       xobjs.undostack->last = newrecord;
457       if (xobjs.undostack->idx < 0) {
458 	 xobjs.undostack->idx = -xobjs.undostack->idx;
459 	 newrecord->idx = xobjs.undostack->idx;
460       }
461       else
462 	 newrecord->idx = xobjs.undostack->idx + 1;
463    }
464    else
465       newrecord->idx = 1;
466 
467    if (mode == UNDO_MORE || undo_collect > (u_char)0)
468       newrecord->idx = -newrecord->idx;
469 
470    xobjs.undostack = newrecord;
471 
472    va_start(args, thisinst);
473 
474    switch(type) {
475       case XCF_Delete:
476 	 /* 2 args:							*/
477 	 /*    delobj = pointer to object containing deleted entries	*/
478 	 /*    drawmode = true if elements should be erased		*/
479 	 delobj = va_arg(args, objectptr);
480 	 drawmode = va_arg(args, int);
481 	 newrecord->undodata = (char *)delobj;
482 	 newrecord->idata = drawmode;
483 	 break;
484 
485       case XCF_Select_Save:
486 	 /* 1 arg:							*/
487 	 /*    newobj = pointer to instance of new object		*/
488 	 newinst = va_arg(args, objinstptr);
489 	 newrecord->undodata = (char *)newinst;
490 	 break;
491 
492       case XCF_Page:
493 	 /* 2 args:							*/
494 	 /*	oval = original integer value				*/
495 	 /*	nval = new integer value				*/
496 	 oval = va_arg(args, int);
497 	 nval = va_arg(args, int);
498 	 idata = (int *)malloc(sizeof(int));
499 	 *idata = nval;
500 	 newrecord->undodata = (char *)idata;
501 	 newrecord->idata = oval;
502 	 break;
503 
504       case XCF_Pop:
505 	 /* No args; instance to pop is the instance passed. 		*/
506 	 break;
507 
508       case XCF_Push:
509 	 /* 1 arg:							*/
510 	 /*	newinst = object instance to push			*/
511 	 newinst = va_arg(args, objinstptr);
512 	 newrecord->undodata = (char *)newinst;
513 	 break;
514 
515       case XCF_Copy:
516       case XCF_Select:
517       case XCF_Library_Pop:
518 	 /* 2 args:							*/
519 	 /*	slist = current selection list (short *)		*/
520 	 /*	snum  = number of selections (int)			*/
521 	 slist = va_arg(args, short *);
522 	 snum = va_arg(args, int);
523 	 srec = remember_selection(thisinst, slist, snum);
524 	 newrecord->undodata = (char *)srec;
525 	 /* Fprintf(stdout, "Undo: registered selection or copy action\n"); */
526 	 break;
527 
528       case XCF_Box: case XCF_Arc: case XCF_Wire: case XCF_Text:
529       case XCF_Pin_Label: case XCF_Pin_Global: case XCF_Info_Label:
530       case XCF_Spline: case XCF_Dot: case XCF_Graphic: case XCF_Join:
531 	 /* 1 arg:							*/
532 	 /*	egen = element just created (genericptr)		*/
533 	 egen = va_arg(args, genericptr);
534 	 newrecord->undodata = (char *)egen;
535 	 break;
536 
537       case XCF_Edit:
538 	 /* 1 arg:							*/
539 	 /*	egen = element to be edited (genericptr)		*/
540 	 egen = va_arg(args, genericptr);
541 
542 	 /* Create a copy of the element to save */
543 	 erec = (editelement *)malloc(sizeof(editelement));
544 	 erec->element = egen;
545 	 switch(egen->type) {
546 	    case LABEL:
547 	       erec->save.string =
548 			stringcopyall(((labelptr)egen)->string,
549 			areawin->topinstance);
550 	       newrecord->idata = ((labelptr)egen)->anchor;
551 	       break;
552 	    case POLYGON:
553 	       newrecord->idata = ((polyptr)egen)->number;
554 	       erec->save.points = copypoints(((polyptr)egen)->points,
555 			newrecord->idata);
556 	       break;
557 	    case SPLINE:
558 	       erec->save.points =
559 			copypoints((pointlist)((splineptr)egen)->ctrl, 4);
560 	       break;
561 	    case OBJINST:
562 	       erec->save.instpos = ((objinstptr)egen)->position;
563 	       break;
564 	    case ARC:
565 	       erec->save.arcspecs = (arcinfo *)malloc(sizeof(arcinfo));
566 	       erec->save.arcspecs->angle1 = ((arcptr)egen)->angle1;
567 	       erec->save.arcspecs->angle2 = ((arcptr)egen)->angle2;
568 	       erec->save.arcspecs->radius = ((arcptr)egen)->radius;
569 	       erec->save.arcspecs->yaxis = ((arcptr)egen)->yaxis;
570 	       erec->save.arcspecs->position = ((arcptr)egen)->position;
571 	       break;
572 	    case PATH:
573 	       newrecord->idata = ((pathptr)egen)->parts;  /* not needed? */
574 	       erec->save.pathspecs = (pathinfo *)malloc(newrecord->idata *
575 			sizeof(pathinfo));
576 	       for (i = 0; i < newrecord->idata; i++) {
577 		  pathinfo *ppi = erec->save.pathspecs + i;
578 		  genericptr *pgen = ((pathptr)egen)->plist + i;
579 		  switch (ELEMENTTYPE(*pgen)) {
580 		     case POLYGON:
581 			ppi->number = (TOPOLY(pgen))->number;
582 			ppi->points = copypoints((TOPOLY(pgen))->points,
583 				ppi->number);
584 			break;
585 		     case SPLINE:
586 			ppi->number = 4;
587 			ppi->points = copypoints((pointlist)
588 				(TOSPLINE(pgen))->ctrl, 4);
589 			break;
590 		  }
591 	       }
592 	       break;
593 	 }
594 	 newrecord->undodata = (char *)erec;
595 	 break;
596 
597       case XCF_ChangeStyle:
598       case XCF_Anchor:
599       case XCF_Color:
600 	 /* 2 args:							*/
601 	 /*	egen = element that was changed (with new value)	*/
602 	 /*	oval = old style value					*/
603 	 egen = va_arg(args, genericptr);
604 	 oval = va_arg(args, int);
605 	 newrecord->undodata = (char *)egen;
606 	 newrecord->idata = oval;
607 	 break;
608 
609       case XCF_Rescale:
610 	 /* 2 args:							*/
611 	 /*	egen = element that was changed (with new value)	*/
612 	 /*	ofloat = old float value				*/
613 
614 	 egen = va_arg(args, genericptr);
615 	 scale = va_arg(args, double); 		/* warning! only takes "double"! */
616 
617 	 escale = (scaleinfo *)malloc(sizeof(scaleinfo));
618 	 escale->element = egen;
619 	 escale->scale = (float)scale;
620 
621 	 newrecord->undodata = (char *)escale;
622 	 break;
623 
624       case XCF_Flip_X: case XCF_Flip_Y:
625 	 /* 1 arg:							*/
626 	 /*	fpoint = point of flip (XPoint *)			*/
627 	 fpoint = va_arg(args, XPoint *);
628 	 newrecord->undodata = (char *)malloc(sizeof(XPoint));
629 	 ((XPoint *)newrecord->undodata)->x = fpoint->x;
630 	 ((XPoint *)newrecord->undodata)->y = fpoint->y;
631 	 break;
632 
633       case XCF_Rotate:
634 	 /* 2 args:							*/
635 	 /*	fpoint = point of flip (XPoint *)			*/
636 	 /*	dir = direction and amount of rotation (float)		*/
637 	 fpoint = va_arg(args, XPoint *);
638 	 dir = va_arg(args, double);
639 	 newrecord->undodata = (char *)malloc(sizeof(rotateinfo));
640 	 ((rotateinfo *)newrecord->undodata)->rotpos.x = fpoint->x;
641 	 ((rotateinfo *)newrecord->undodata)->rotpos.y = fpoint->y;
642 	 ((rotateinfo *)newrecord->undodata)->rotation = (float)dir;
643 	 break;
644 
645       case XCF_Move:
646 	 /* 2 args:							*/
647 	 /*	deltax = change in x position (int)			*/
648 	 /*	deltay = change in y position (int)			*/
649 	 deltax = va_arg(args, int);
650 	 deltay = va_arg(args, int);
651 	 newrecord->undodata = (char *)malloc(sizeof(XPoint));
652 	 ((XPoint *)newrecord->undodata)->x = deltax;
653 	 ((XPoint *)newrecord->undodata)->y = deltay;
654 	 /* Fprintf(stdout, "Undo: registered move of delta (%d, %d)\n",
655 			deltax, deltay); */
656 	 break;
657 
658        case XCF_Reorder:
659 	 /* 2 args:							*/
660 	 /*	slist = "before" order of elements			*/
661 	 /*	snum = number of elements in list (= # parts)		*/
662 	 slist = va_arg(args, short *);
663 	 snum = va_arg(args, int);
664 	 newrecord->undodata = (char *)slist;
665 	 newrecord->idata = snum;
666    }
667 
668    va_end(args);
669 }
670 
671 /*----------------------------------------------------------------------*/
672 /* undo_one_action ---							*/
673 /*	Play undo record back one in the stack.				*/
674 /*----------------------------------------------------------------------*/
675 
undo_one_action()676 short undo_one_action()
677 {
678   Undoptr thisrecord; /* , chkrecord; (jdk) */
679    objectptr thisobj;
680    objinstptr thisinst;
681    uselection *srec;
682    editelement *erec;
683    scaleinfo *escale;
684    short *slist;
685    XPoint *delta, position;
686    int i, j, snum;
687    int savemode;
688    float fnum;
689    genericptr egen;
690    labelptr thislabel;
691    pathptr thispath;
692    polyptr thispoly;
693    arcptr thisarc;
694    splineptr thisspline;
695    graphicptr thisgraphic;
696    Boolean need_redraw;
697    XCWindowData *savewindow = areawin;
698 
699    /* Undo the recorded action and shift the undo record pointer.	*/
700 
701    thisrecord = xobjs.undostack;
702    if (thisrecord == NULL) {
703       Fprintf(stderr, "Nothing to undo!\n");
704       return 0;
705    }
706 
707    xobjs.undostack = thisrecord->next;
708    xobjs.redostack = thisrecord;
709 
710    /* Set window, if event occurred in a different window */
711    if (setwindow(thisrecord->window) == FALSE) {
712       Wprintf("Error:  Undo event in nonexistant window!  Flushing stack.\n");
713       flush_undo_stack();
714       return 0;
715    }
716 
717    /* Setting eventmode to UNDO_MODE prevents register_for_undo() from 	*/
718    /* being called again while executing the event.			*/
719 
720    savemode = eventmode;
721    eventmode = UNDO_MODE;
722 
723    /* type-dependent part */
724 
725    switch(thisrecord->type) {
726       case XCF_Delete:
727 	 unselect_all();
728 	 thisobj = (objectptr)thisrecord->undodata;
729 	 areawin->selects = thisobj->parts;
730 	 areawin->selectlist = xc_undelete(thisrecord->thisinst,
731 			thisobj, (short)thisrecord->idata,
732 			recover_selectlist(thisrecord));
733 	 srec = remember_selection(thisrecord->thisinst, areawin->selectlist,
734 			areawin->selects);
735 	 thisrecord->undodata = (char *)srec;
736 	 draw_all_selected();
737 	 break;
738 
739       /* To be finished:  Needs to remove the object & instance from	*/
740       /* the library and library page.  Should keep the empty object	*/
741       /* around so we have the name.					*/
742 
743       case XCF_Select_Save:
744 	 unselect_all();
745 	 thisinst = (objinstptr)thisrecord->undodata;
746 	 thisobj = thisinst->thisobject;
747 
748 	 /* Remove the instance */
749 	 i = thisrecord->thisinst->thisobject->parts - 1;
750 	 if ((genericptr)thisinst != *(thisrecord->thisinst->thisobject->plist + i)) {
751 	    Fprintf(stderr, "Error: No such element!\n");
752 	    thisrecord->undodata = NULL;
753 	    break;
754 	 }
755 	 else {
756 	    delete_one_element(thisrecord->thisinst, (genericptr)thisinst);
757 	 }
758 
759 	 /* Put back all the parts */
760 	 areawin->selects = thisobj->parts;
761 	 areawin->selectlist = xc_undelete(thisrecord->thisinst,
762 			thisobj, (short)thisrecord->idata,
763 			recover_selectlist(thisrecord));
764 	 srec = remember_selection(thisrecord->thisinst, areawin->selectlist,
765 			areawin->selects);
766 	 thisrecord->undodata = (char *)srec;
767 	 draw_all_selected();
768 	 break;
769 
770       case XCF_Push:
771 	 popobject(areawin->area, 0, NULL);
772 	 break;
773 
774       case XCF_Pop:
775 	 pushobject((objinstptr)thisrecord->thisinst);
776 	 break;
777 
778       case XCF_Page:
779 	 newpage(thisrecord->idata);
780 	 break;
781 
782       case XCF_Anchor:
783 	 thislabel = (labelptr)(thisrecord->undodata);
784 	 snum = thisrecord->idata;
785 	 thisrecord->idata = thislabel->anchor;
786 	 thislabel->anchor = snum;
787 	 areawin->redraw_needed = True;
788 	 drawarea(areawin->area, NULL, NULL);
789 	 break;
790 
791       case XCF_Select:
792 
793 	 /* If there was a previous selection in the undo list,	*/
794 	 /* revert to it.					*/
795 
796 	 need_redraw = (areawin->selects > 0) ? True : False;
797 	 select_previous(thisrecord);
798 	 if (need_redraw) {
799 	    areawin->redraw_needed = True;
800 	    drawarea(areawin->area, NULL, NULL);
801 	 }
802 	 else
803 	    draw_all_selected();
804 	 break;
805 
806       case XCF_Box: case XCF_Arc: case XCF_Wire: case XCF_Text:
807 		case XCF_Pin_Label: case XCF_Pin_Global: case XCF_Info_Label:
808 		case XCF_Spline: case XCF_Dot: case XCF_Graphic: case XCF_Join:
809 	 egen = (genericptr)thisrecord->undodata;
810 	 i = thisrecord->thisinst->thisobject->parts - 1;
811 	 if (egen != *(thisrecord->thisinst->thisobject->plist + i)) {
812 	    Fprintf(stderr, "Error: No such element!\n");
813 	    thisrecord->undodata = NULL;
814 	 }
815 	 else {
816 	    delete_one_element(thisrecord->thisinst, egen);
817 	    areawin->redraw_needed = True;
818 	    drawarea(areawin->area, NULL, NULL);
819 	 }
820 	 break;
821 
822       case XCF_Edit:
823 	 erec = (editelement *)thisrecord->undodata;
824 	 switch (erec->element->type) {
825 	    case LABEL: {
826 	       stringpart *tmpstr;
827 	       int tmpanchor;
828 	       labelptr elab = (labelptr)(erec->element);
829 	       undrawtext(elab);
830 	       tmpstr = elab->string;
831 	       tmpanchor = (int)elab->anchor;
832 	       elab->string = stringcopyback(erec->save.string,
833 				thisrecord->thisinst);
834 	       elab->anchor = (short)thisrecord->idata;
835 	       erec->save.string = tmpstr;
836 	       thisrecord->idata = tmpanchor;
837 	       resolveparams(thisrecord->thisinst);
838 	       redrawtext(elab);
839 	    } break;
840 	    case ARC: {
841 	       arcinfo tmpinfo;
842 	       arcptr earc = (arcptr)(erec->element);
843 	       tmpinfo.angle1 = earc->angle1;
844 	       tmpinfo.angle2 = earc->angle2;
845 	       tmpinfo.radius = earc->radius;
846 	       tmpinfo.yaxis = earc->yaxis;
847 	       tmpinfo.position = earc->position;
848 	       earc->angle1 = erec->save.arcspecs->angle1;
849 	       earc->angle2 = erec->save.arcspecs->angle2;
850 	       earc->radius = erec->save.arcspecs->radius;
851 	       earc->yaxis = erec->save.arcspecs->yaxis;
852 	       earc->position = erec->save.arcspecs->position;
853 	       *(erec->save.arcspecs) = tmpinfo;
854 	       calcarc(earc);
855 	       areawin->redraw_needed = True;
856 	       drawarea(areawin->area, NULL, NULL);
857 	    } break;
858 	    case OBJINST: {
859 	       XPoint tmppt;
860 	       objinstptr einst = (objinstptr)(erec->element);
861 	       // Swap instance and saved positions.
862 	       tmppt = einst->position;
863 	       einst->position = erec->save.instpos;
864 	       erec->save.instpos = tmppt;
865 	       areawin->redraw_needed = True;
866 	       drawarea(areawin->area, NULL, NULL);
867 	    }  break;
868 	    case POLYGON: {
869 	       pointlist tmppts;
870 	       int tmpnum;
871 	       polyptr epoly = (polyptr)(erec->element);
872 	       tmppts = epoly->points;
873 	       tmpnum = epoly->number;
874 	       epoly->points = erec->save.points;
875 	       epoly->number = thisrecord->idata;
876 	       erec->save.points = tmppts;
877 	       thisrecord->idata = tmpnum;
878 	       areawin->redraw_needed = True;
879 	       drawarea(areawin->area, NULL, NULL);
880 	    } break;
881 	    case SPLINE: {
882 	       pointlist tmppts;
883 	       splineptr espline = (splineptr)(erec->element);
884 	       tmppts = copypoints((pointlist)espline->ctrl, 4);
885 	       for (i = 0; i < 4; i++)
886 		   espline->ctrl[i] = *(erec->save.points + i);
887 	       free(erec->save.points);
888 	       erec->save.points = tmppts;
889 	       calcspline(espline);
890 	       areawin->redraw_needed = True;
891 	       drawarea(areawin->area, NULL, NULL);
892 	    } break;
893 	    case PATH: {
894 	       pointlist tmppts;
895 	       int tmpnum;
896 	       polyptr epoly;
897 	       splineptr espline;
898 	       pathptr epath = (pathptr)(erec->element);
899 	       for (i = 0; i < epath->parts; i++) {
900 		  genericptr ggen = *(epath->plist + i);
901 		  switch (ELEMENTTYPE(ggen)) {
902 		     case POLYGON:
903 			epoly = (polyptr)ggen;
904 		        tmppts = epoly->points;
905 			tmpnum = epoly->number;
906 			epoly->points = (erec->save.pathspecs + i)->points;
907 			epoly->number = (erec->save.pathspecs + i)->number;
908 			(erec->save.pathspecs + i)->points = tmppts;
909 			(erec->save.pathspecs + i)->number = tmpnum;
910 			break;
911 		     case SPLINE:
912 			espline = (splineptr)ggen;
913 			tmppts = copypoints((pointlist)espline->ctrl, 4);
914 			for (j = 0; j < 4; j++)
915 			   espline->ctrl[j] = *((erec->save.pathspecs + i)->points + j);
916 			free((erec->save.pathspecs + i)->points);
917 		        (erec->save.pathspecs + i)->points = tmppts;
918 			calcspline(espline);
919 			break;
920 		  }
921 	       }
922 	       areawin->redraw_needed = True;
923 	       drawarea(areawin->area, NULL, NULL);
924 	    }
925 	 }
926 	 break;
927 
928       case XCF_Library_Pop:
929 	 srec = (uselection *)thisrecord->undodata;
930 	 slist = regen_selection(thisrecord->thisinst, srec);
931 	 thisobj = delete_element(thisrecord->thisinst, slist,
932 			srec->number, DRAW);
933 	 free(slist);
934 	 thisrecord->undodata = (char *)thisobj;
935 	 break;
936 
937       case XCF_Copy:
938 	 clearselects_noundo();
939 	 srec = (uselection *)thisrecord->undodata;
940 	 slist = regen_selection(thisrecord->thisinst, srec);
941 	 thisobj = delete_element(thisrecord->thisinst, slist,
942 			srec->number, DRAW);
943 	 free(slist);
944 	 thisrecord->undodata = (char *)thisobj;
945 
946 	 /* Revert selection to previously selected */
947 	 select_previous(thisrecord);
948 	 areawin->redraw_needed = True;
949 	 drawarea(areawin->area, NULL, NULL);
950 	 draw_all_selected();
951 	 break;
952 
953       case XCF_ChangeStyle:
954 	 /* Style changes */
955 	 egen = (genericptr)thisrecord->undodata;
956 	 snum = thisrecord->idata;
957 	 switch(egen->type) {
958 	    case PATH:
959 	       thispath = (pathptr)(thisrecord->undodata);
960 	       thisrecord->idata = thispath->style;
961 	       thispath->style = snum;
962 	       break;
963 	    case POLYGON:
964 	       thispoly = (polyptr)(thisrecord->undodata);
965 	       thisrecord->idata = thispoly->style;
966 	       thispoly->style = snum;
967 	       break;
968 	    case ARC:
969 	       thisarc = (arcptr)(thisrecord->undodata);
970 	       thisrecord->idata = thisarc->style;
971 	       thisarc->style = snum;
972 	       break;
973 	    case SPLINE:
974 	       thisspline = (splineptr)(thisrecord->undodata);
975 	       thisrecord->idata = thisspline->style;
976 	       thisspline->style = snum;
977 	       break;
978 	 }
979 	 areawin->redraw_needed = True;
980 	 drawarea(areawin->area, NULL, NULL);
981 	 break;
982 
983       case XCF_Color:
984 	 /* Color changes */
985 	 egen = (genericptr)thisrecord->undodata;
986 	 snum = thisrecord->idata;
987 	 switch(egen->type) {
988 	    case PATH:
989 	       thispath = (pathptr)(thisrecord->undodata);
990 	       thisrecord->idata = thispath->color;
991 	       thispath->color = snum;
992 	       break;
993 	    case POLYGON:
994 	       thispoly = (polyptr)(thisrecord->undodata);
995 	       thisrecord->idata = thispoly->color;
996 	       thispoly->color = snum;
997 	       break;
998 	    case ARC:
999 	       thisarc = (arcptr)(thisrecord->undodata);
1000 	       thisrecord->idata = thisarc->color;
1001 	       thisarc->color = snum;
1002 	       break;
1003 	    case SPLINE:
1004 	       thisspline = (splineptr)(thisrecord->undodata);
1005 	       thisrecord->idata = thisspline->color;
1006 	       thisspline->color = snum;
1007 	       break;
1008 	 }
1009 	 areawin->redraw_needed = True;
1010 	 drawarea(areawin->area, NULL, NULL);
1011 	 break;
1012 
1013       case XCF_Rescale:
1014 	 escale = (scaleinfo *)thisrecord->undodata;
1015 	 egen = escale->element;
1016 	 fnum = escale->scale;
1017 	 switch(egen->type) {
1018 	    case PATH:
1019 	       thispath = (pathptr)egen;
1020 	       escale->scale = thispath->width;
1021 	       thispath->width = fnum;
1022 	       break;
1023 	    case POLYGON:
1024 	       thispoly = (polyptr)egen;
1025 	       escale->scale = thispoly->width;
1026 	       thispoly->width = fnum;
1027 	       break;
1028 	    case ARC:
1029 	       thisarc = (arcptr)egen;
1030 	       escale->scale = thisarc->width;
1031 	       thisarc->width = fnum;
1032 	       break;
1033 	    case SPLINE:
1034 	       thisspline = (splineptr)egen;
1035 	       escale->scale = thisspline->width;
1036 	       thisspline->width = fnum;
1037 	       break;
1038 	    case OBJINST:
1039 	       thisinst = (objinstptr)egen;
1040 	       escale->scale = thisinst->scale;
1041 	       thisinst->scale = fnum;
1042 	       break;
1043 	    case GRAPHIC:
1044 	       thisgraphic = (graphicptr)egen;
1045 	       escale->scale = thisgraphic->scale;
1046 	       thisgraphic->scale = fnum;
1047 #ifndef HAVE_CAIRO
1048 	       thisgraphic->valid = FALSE;
1049 #endif /* HAVE_CAIRO */
1050 	       break;
1051 	    case LABEL:
1052 	       thislabel = (labelptr)egen;
1053 	       escale->scale = thislabel->scale;
1054 	       thislabel->scale = fnum;
1055 	       break;
1056 	 }
1057 	 areawin->redraw_needed = True;
1058 	 drawarea(areawin->area, NULL, NULL);
1059 	 break;
1060 
1061       case XCF_Flip_X:
1062 	 position = *((XPoint *)thisrecord->undodata);
1063 	 elementflip(&position);
1064 	 break;
1065 
1066       case XCF_Flip_Y:
1067 	 position = *((XPoint *)thisrecord->undodata);
1068 	 elementvflip(&position);
1069 	 break;
1070 
1071       case XCF_Rotate:
1072 	 position = ((rotateinfo *)thisrecord->undodata)->rotpos;
1073 	 elementrotate(-((rotateinfo *)thisrecord->undodata)->rotation, &position);
1074 	 break;
1075 
1076       case XCF_Move:
1077 	 delta = (XPoint *)thisrecord->undodata;
1078 	 select_connected_pins();
1079 	 placeselects(-(delta->x), -(delta->y), NULL);
1080 	 reset_cycles();
1081 	 areawin->redraw_needed = True;
1082 	 drawarea(areawin->area, NULL, NULL);
1083 	 draw_all_selected();
1084 	 break;
1085 
1086        case XCF_Reorder:
1087 	 reorder_selection(thisrecord);
1088 	 areawin->redraw_needed = True;
1089 	 drawarea(areawin->area, NULL, NULL);
1090 	 break;
1091 
1092       default:
1093 	 Fprintf(stderr, "Undo not implemented for this action!\n");
1094 	 break;
1095    }
1096 
1097    /* Does this need to be set on a per-event-type basis? */
1098    switch (savemode) {
1099       case CATALOG_MODE:
1100       case CATTEXT_MODE:
1101 	 eventmode = CATALOG_MODE;
1102 	 break;
1103       default:
1104 	 eventmode = NORMAL_MODE;
1105 	 break;
1106    }
1107 
1108    /* Diagnostic, to check if all multiple-event undo series are resolved */
1109    if (thisrecord->idx < 0) {
1110       Fprintf(stderr, "Warning:  Unfinished undo series in stack!\n");
1111       thisrecord->idx = -thisrecord->idx;
1112    }
1113    areawin = savewindow;
1114    return thisrecord->idx;
1115 }
1116 
1117 /*----------------------------------------------------------------------*/
1118 /* undo_finish_series ---						*/
1119 /*	Complete a possibly incomplete undo series by forcing the	*/
1120 /*	topmost entry to have a positive index.				*/
1121 /*	Note that for "undo series start|end" to work, undo_collect	*/
1122 /*	must be set to 0 prior to calling undo_finish_series().		*/
1123 /*----------------------------------------------------------------------*/
1124 
undo_finish_series()1125 void undo_finish_series()
1126 {
1127    if (undo_collect == (u_char)0)
1128       if (xobjs.undostack && xobjs.undostack->idx < 0)
1129          xobjs.undostack->idx = -xobjs.undostack->idx;
1130 }
1131 
1132 /*----------------------------------------------------------------------*/
1133 /* undo_action ---							*/
1134 /*	Play undo record back to the completion of a series.		*/
1135 /*----------------------------------------------------------------------*/
1136 
undo_action()1137 void undo_action()
1138 {
1139    short idx;
1140 
1141    // Cannot undo while in the middle of an undo series.  Failsafe.
1142    if (undo_collect != (u_char)0) return;
1143 
1144    idx = undo_one_action();
1145    while (xobjs.undostack && xobjs.undostack->idx == idx)
1146       undo_one_action();
1147 }
1148 
1149 /*----------------------------------------------------------------------*/
1150 /* redo_one_action ---							*/
1151 /*	Play undo record forward one in the stack.			*/
1152 /*----------------------------------------------------------------------*/
1153 
redo_one_action()1154 short redo_one_action()
1155 {
1156    Undoptr thisrecord;
1157    objectptr thisobj;
1158    genericptr egen;
1159    short *slist;
1160    XPoint *delta, position;
1161    uselection *srec;
1162    editelement *erec;
1163    scaleinfo *escale;
1164    int i, j, snum;
1165    int savemode;
1166    float fnum;
1167    labelptr thislabel;
1168    arcptr thisarc;
1169    pathptr thispath;
1170    splineptr thisspline;
1171    polyptr thispoly;
1172    graphicptr thisgraphic;
1173    objinstptr thisinst;
1174    XCWindowData *savewindow = areawin;
1175 
1176    /* Undo the recorded action and shift the undo record pointer.	*/
1177 
1178    thisrecord = xobjs.redostack;
1179    if (thisrecord == NULL) {
1180       Fprintf(stderr, "Nothing to redo!\n");
1181       return 0;
1182    }
1183    xobjs.undostack = thisrecord;
1184    xobjs.redostack = thisrecord->last;
1185 
1186    /* Set window, if event occurred in a different window */
1187    if (setwindow(thisrecord->window) == FALSE) {
1188       Wprintf("Error:  Undo event in nonexistant window!  Flushing stack.\n");
1189       flush_undo_stack();
1190       return 0;
1191    }
1192 
1193    savemode = eventmode;
1194    eventmode = UNDO_MODE;
1195 
1196    /* type-dependent part */
1197 
1198    switch(thisrecord->type) {
1199       case XCF_Delete:
1200 	 srec = (uselection *)thisrecord->undodata;
1201 	 slist = regen_selection(thisrecord->thisinst, srec);
1202 	 thisobj = delete_element(thisrecord->thisinst, slist,
1203 		srec->number, DRAW);
1204 	 free(slist);
1205 	 thisrecord->undodata = (char *)thisobj;
1206 	 thisrecord->idata = (int)DRAW;
1207 	 unselect_all();
1208 	 areawin->redraw_needed = True;
1209 	 drawarea(areawin->area, NULL, NULL);
1210 	 break;
1211 
1212       /* Unfinished! */
1213       case XCF_Select_Save:
1214 	 srec = (uselection *)thisrecord->undodata;
1215 	 slist = regen_selection(thisrecord->thisinst, srec);
1216 	 break;
1217 
1218       case XCF_Page:
1219 	 newpage(*((int *)thisrecord->undodata));
1220 	 break;
1221 
1222       case XCF_Anchor:
1223 	 thislabel = (labelptr)(thisrecord->undodata);
1224 	 snum = thisrecord->idata;
1225 	 thisrecord->idata = thislabel->anchor;
1226 	 thislabel->anchor = snum;
1227 	 areawin->redraw_needed = True;
1228 	 drawarea(areawin->area, NULL, NULL);
1229 	 break;
1230 
1231       case XCF_Pop:
1232 	 popobject(areawin->area, 0, NULL);
1233 	 break;
1234 
1235       case XCF_Push:
1236 	 pushobject((objinstptr)thisrecord->undodata);
1237 	 break;
1238 
1239       case XCF_Select:
1240 	 unselect_all();
1241 	 srec = (uselection *)thisrecord->undodata;
1242 	 areawin->selectlist = regen_selection(thisrecord->thisinst, srec);
1243 	 areawin->selects = (areawin->selectlist) ? srec->number : 0;
1244 	 draw_all_selected();
1245 	 break;
1246 
1247       case XCF_Library_Pop:
1248 	 thisobj = (objectptr)thisrecord->undodata;
1249 	 if (thisobj != NULL) {
1250 	    unselect_all();
1251 	    snum = thisobj->parts;
1252 	    slist = xc_undelete(thisrecord->thisinst, thisobj, DRAW, NULL);
1253 	    thisrecord->undodata = (char *)remember_selection(thisrecord->thisinst,
1254 			slist, snum);
1255 	    free(slist);
1256 	 }
1257 	 break;
1258 
1259       case XCF_Copy:
1260 	 thisobj = (objectptr)thisrecord->undodata;
1261 	 if (thisobj != NULL) {
1262 	    unselect_all();
1263 	    areawin->selects = thisobj->parts;
1264 	    areawin->selectlist = xc_undelete(thisrecord->thisinst, thisobj, DRAW,
1265 			NULL);
1266 	    thisrecord->undodata = (char *)remember_selection(thisrecord->thisinst,
1267 			areawin->selectlist, areawin->selects);
1268 	    draw_all_selected();
1269 	 }
1270 	 break;
1271 
1272       case XCF_Box: case XCF_Arc: case XCF_Wire: case XCF_Text:
1273       case XCF_Pin_Label: case XCF_Pin_Global: case XCF_Info_Label:
1274       case XCF_Spline: case XCF_Dot: case XCF_Graphic: case XCF_Join:
1275 	 egen = (genericptr)thisrecord->undodata;
1276 	 undelete_one_element(thisrecord->thisinst, egen);
1277 	 areawin->redraw_needed = True;
1278 	 drawarea(areawin->area, NULL, NULL);
1279 	 break;
1280 
1281       case XCF_Edit:
1282 	 erec = (editelement *)thisrecord->undodata;
1283 	 switch (erec->element->type) {
1284 	    case LABEL: {
1285 	       stringpart *tmpstr;
1286 	       int tmpanchor;
1287 	       labelptr elab = (labelptr)(erec->element);
1288 	       undrawtext(elab);
1289 	       tmpstr = elab->string;
1290 	       tmpanchor = (int)elab->anchor;
1291 	       elab->string = stringcopyback(erec->save.string,
1292 				thisrecord->thisinst);
1293 	       elab->anchor = (short)thisrecord->idata;
1294 	       erec->save.string = tmpstr;
1295 	       thisrecord->idata = tmpanchor;
1296 	       resolveparams(thisrecord->thisinst);
1297 	       redrawtext(elab);
1298 	    } break;
1299 	    case OBJINST: {
1300 	       XPoint tmppt;
1301 	       objinstptr einst = (objinstptr)(erec->element);
1302 	       // Swap instance position and saved position
1303 	       tmppt = einst->position;
1304 	       einst->position = erec->save.instpos;
1305 	       erec->save.instpos = tmppt;
1306 	       areawin->redraw_needed = True;
1307 	       drawarea(areawin->area, NULL, NULL);
1308 	    }  break;
1309 	    case ARC: {
1310 	       arcinfo tmpinfo;
1311 	       arcptr earc = (arcptr)(erec->element);
1312 	       tmpinfo.angle1 = earc->angle1;
1313 	       tmpinfo.angle2 = earc->angle2;
1314 	       tmpinfo.radius = earc->radius;
1315 	       tmpinfo.yaxis = earc->yaxis;
1316 	       tmpinfo.position = earc->position;
1317 	       earc->angle1 = erec->save.arcspecs->angle1;
1318 	       earc->angle2 = erec->save.arcspecs->angle2;
1319 	       earc->radius = erec->save.arcspecs->radius;
1320 	       earc->yaxis = erec->save.arcspecs->yaxis;
1321 	       earc->position = erec->save.arcspecs->position;
1322 	       *(erec->save.arcspecs) = tmpinfo;
1323 	       calcarc(earc);
1324 	       areawin->redraw_needed = True;
1325 	       drawarea(areawin->area, NULL, NULL);
1326 	    } break;
1327 	    case POLYGON: {
1328 	       pointlist tmppts;
1329 	       int tmpnum;
1330 	       polyptr epoly = (polyptr)(erec->element);
1331 	       tmppts = epoly->points;
1332 	       tmpnum = epoly->number;
1333 	       epoly->points = erec->save.points;
1334 	       epoly->number = thisrecord->idata;
1335 	       erec->save.points = tmppts;
1336 	       thisrecord->idata = tmpnum;
1337 	       areawin->redraw_needed = True;
1338 	       drawarea(areawin->area, NULL, NULL);
1339 	    } break;
1340 	    case SPLINE: {
1341 	       pointlist tmppts;
1342 	       splineptr espline = (splineptr)(erec->element);
1343 	       tmppts = copypoints((pointlist)espline->ctrl, 4);
1344 	       for (i = 0; i < 4; i++)
1345 		   espline->ctrl[i] = *(erec->save.points + i);
1346 	       free(erec->save.points);
1347 	       erec->save.points = tmppts;
1348 	       calcspline(espline);
1349 	       areawin->redraw_needed = True;
1350 	       drawarea(areawin->area, NULL, NULL);
1351 	    } break;
1352 	    case PATH: {
1353 	       pointlist tmppts;
1354 	       int tmpnum;
1355 	       polyptr epoly;
1356 	       splineptr espline;
1357 	       pathptr epath = (pathptr)(erec->element);
1358 	       for (i = 0; i < epath->parts; i++) {
1359 		  genericptr ggen = *(epath->plist + i);
1360 		  switch (ELEMENTTYPE(ggen)) {
1361 		     case POLYGON:
1362 			epoly = (polyptr)ggen;
1363 		        tmppts = epoly->points;
1364 			tmpnum = epoly->number;
1365 			epoly->points = (erec->save.pathspecs + i)->points;
1366 			epoly->number = (erec->save.pathspecs + i)->number;
1367 			(erec->save.pathspecs + i)->points = tmppts;
1368 			(erec->save.pathspecs + i)->number = tmpnum;
1369 			break;
1370 		     case SPLINE:
1371 			espline = (splineptr)ggen;
1372 			tmppts = copypoints((pointlist)espline->ctrl, 4);
1373 			for (j = 0; j < 4; j++)
1374 			   espline->ctrl[j] = *((erec->save.pathspecs + i)->points + j);
1375 			free((erec->save.pathspecs + i)->points);
1376 		        (erec->save.pathspecs + i)->points = tmppts;
1377 			calcspline(espline);
1378 			break;
1379 		  }
1380 	       }
1381 	       areawin->redraw_needed = True;
1382 	       drawarea(areawin->area, NULL, NULL);
1383 	    }
1384 	 }
1385 	 break;
1386 
1387       case XCF_Flip_X:
1388 	 position = *((XPoint *)thisrecord->undodata);
1389 	 elementflip(&position);
1390 	 break;
1391 
1392       case XCF_Flip_Y:
1393 	 position = *((XPoint *)thisrecord->undodata);
1394 	 elementvflip(&position);
1395 	 break;
1396 
1397       case XCF_ChangeStyle:
1398 	 /* Style changes */
1399 	 egen = (genericptr)thisrecord->undodata;
1400 	 snum = thisrecord->idata;
1401 	 switch(egen->type) {
1402 	    case PATH:
1403 	       thispath = (pathptr)(thisrecord->undodata);
1404 	       thisrecord->idata = thispath->style;
1405 	       thispath->style = snum;
1406 	       break;
1407 	    case POLYGON:
1408 	       thispoly = (polyptr)(thisrecord->undodata);
1409 	       thisrecord->idata = thispoly->style;
1410 	       thispoly->style = snum;
1411 	       break;
1412 	    case ARC:
1413 	       thisarc = (arcptr)(thisrecord->undodata);
1414 	       thisrecord->idata = thisarc->style;
1415 	       thisarc->style = snum;
1416 	       break;
1417 	    case SPLINE:
1418 	       thisspline = (splineptr)(thisrecord->undodata);
1419 	       thisrecord->idata = thisspline->style;
1420 	       thisspline->style = snum;
1421 	       break;
1422 	 }
1423 	 areawin->redraw_needed = True;
1424 	 drawarea(areawin->area, NULL, NULL);
1425 	 break;
1426 
1427       case XCF_Color:
1428 	 /* Color changes */
1429 	 egen = (genericptr)thisrecord->undodata;
1430 	 snum = thisrecord->idata;
1431 	 switch(egen->type) {
1432 	    case PATH:
1433 	       thispath = (pathptr)(thisrecord->undodata);
1434 	       thisrecord->idata = thispath->color;
1435 	       thispath->color = snum;
1436 	       break;
1437 	    case POLYGON:
1438 	       thispoly = (polyptr)(thisrecord->undodata);
1439 	       thisrecord->idata = thispoly->color;
1440 	       thispoly->color = snum;
1441 	       break;
1442 	    case ARC:
1443 	       thisarc = (arcptr)(thisrecord->undodata);
1444 	       thisrecord->idata = thisarc->color;
1445 	       thisarc->color = snum;
1446 	       break;
1447 	    case SPLINE:
1448 	       thisspline = (splineptr)(thisrecord->undodata);
1449 	       thisrecord->idata = thisspline->color;
1450 	       thisspline->color = snum;
1451 	       break;
1452 	 }
1453 	 areawin->redraw_needed = True;
1454 	 drawarea(areawin->area, NULL, NULL);
1455 	 break;
1456 
1457       case XCF_Rescale:
1458 	 escale = (scaleinfo *)thisrecord->undodata;
1459 	 egen = escale->element;
1460 	 fnum = escale->scale;
1461 	 switch(egen->type) {
1462 	    case PATH:
1463 	       thispath = (pathptr)egen;
1464 	       escale->scale = thispath->width;
1465 	       thispath->width = fnum;
1466 	       break;
1467 	    case POLYGON:
1468 	       thispoly = (polyptr)egen;
1469 	       escale->scale = thispoly->width;
1470 	       thispoly->width = fnum;
1471 	       break;
1472 	    case ARC:
1473 	       thisarc = (arcptr)egen;
1474 	       escale->scale = thisarc->width;
1475 	       thisarc->width = fnum;
1476 	       break;
1477 	    case SPLINE:
1478 	       thisspline = (splineptr)egen;
1479 	       escale->scale = thisspline->width;
1480 	       thisspline->width = fnum;
1481 	       break;
1482 	    case OBJINST:
1483 	       thisinst = (objinstptr)egen;
1484 	       escale->scale = thisinst->scale;
1485 	       thisinst->scale = fnum;
1486 	       break;
1487 	    case GRAPHIC:
1488 	       thisgraphic = (graphicptr)egen;
1489 	       escale->scale = thisgraphic->scale;
1490 	       thisgraphic->scale = fnum;
1491 #ifndef HAVE_CAIRO
1492 	       thisgraphic->valid = FALSE;
1493 #endif /* HAVE_CAIRO */
1494 	       break;
1495 	    case LABEL:
1496 	       thislabel = (labelptr)egen;
1497 	       escale->scale = thislabel->scale;
1498 	       thislabel->scale = fnum;
1499 	       break;
1500 	 }
1501 	 areawin->redraw_needed = True;
1502 	 drawarea(areawin->area, NULL, NULL);
1503 	 break;
1504 
1505       case XCF_Rotate:
1506 	 position = ((rotateinfo *)thisrecord->undodata)->rotpos;
1507 	 elementrotate(((rotateinfo *)thisrecord->undodata)->rotation, &position);
1508 	 break;
1509 
1510       case XCF_Move:
1511 	 delta = (XPoint *)thisrecord->undodata;
1512 	 select_connected_pins();
1513 	 placeselects(delta->x, delta->y, NULL);
1514 	 reset_cycles();
1515 	 areawin->redraw_needed = True;
1516 	 drawarea(areawin->area, NULL, NULL);
1517 	 break;
1518 
1519       case XCF_Reorder:
1520 	 reorder_selection(thisrecord);
1521 	 areawin->redraw_needed = True;
1522 	 drawarea(areawin->area, NULL, NULL);
1523 	 break;
1524 
1525       default:
1526 	 Fprintf(stderr, "Undo not implemented for this action!\n");
1527 	 break;
1528    }
1529 
1530    /* Does this need to be set on a per-event-type basis? */
1531    switch (savemode) {
1532       case CATALOG_MODE:
1533       case CATTEXT_MODE:
1534 	 eventmode = CATALOG_MODE;
1535 	 break;
1536       default:
1537 	 eventmode = NORMAL_MODE;
1538 	 break;
1539    }
1540 
1541    areawin = savewindow;
1542 
1543    return thisrecord->idx;
1544 }
1545 
1546 /*----------------------------------------------------------------------*/
1547 /* redo_action ---							*/
1548 /*	Play undo record forward to the completion of a series.		*/
1549 /*----------------------------------------------------------------------*/
1550 
redo_action()1551 void redo_action()
1552 {
1553    short idx;
1554 
1555    // Cannot redo while in the middle of an undo series.  Failsafe.
1556    if (undo_collect != (u_char)0) return;
1557 
1558    idx = redo_one_action();
1559    while (xobjs.redostack && xobjs.redostack->idx == idx)
1560       redo_one_action();
1561 }
1562 
1563 /*----------------------------------------------------------------------*/
1564 /* flush_redo_stack ---							*/
1565 /*	Free all memory allocated to the redo stack due to the		*/
1566 /* 	insertion of a new undo record.					*/
1567 /*----------------------------------------------------------------------*/
1568 
flush_redo_stack()1569 void flush_redo_stack()
1570 {
1571    Undoptr thisrecord, nextrecord;
1572 
1573    if (xobjs.redostack == NULL) return;	/* no redo stack */
1574 
1575    thisrecord = xobjs.redostack;
1576 
1577    while (thisrecord != NULL) {
1578       nextrecord = thisrecord->last;
1579       free_redo_record(thisrecord);
1580       thisrecord = nextrecord;
1581    }
1582    xobjs.redostack = NULL;
1583 
1584    if (xobjs.undostack)
1585       xobjs.undostack->last = NULL;
1586 }
1587 
1588 /*----------------------------------------------------------------------*/
1589 /* flush_undo_stack ---							*/
1590 /*	Free all memory allocated to the undo and redo stacks.		*/
1591 /*----------------------------------------------------------------------*/
1592 
flush_undo_stack()1593 void flush_undo_stack()
1594 {
1595    Undoptr thisrecord, nextrecord;
1596 
1597    flush_redo_stack();
1598 
1599    thisrecord = xobjs.undostack;
1600 
1601    while (thisrecord != NULL) {
1602       nextrecord = thisrecord->next;
1603       free_undo_record(thisrecord);
1604       thisrecord = nextrecord;
1605    }
1606    xobjs.undostack = NULL;
1607 }
1608 
1609 /*----------------------------------------------------------------------*/
1610 /* free_undo_data ---							*/
1611 /*	Free memory allocated to the "undodata" part of the undo	*/
1612 /*	record, based on the record type.				*/
1613 /*									*/
1614 /* "mode" specifies whether this is for an "undo" or a "redo" event.	*/
1615 /*									*/
1616 /*	Note that the action taken for a specific record may *NOT* be	*/
1617 /*	the same for a record in the undo stack as it is for a record	*/
1618 /*	in the redo stack, because the data types are changed when	*/
1619 /*	moving from one record to the next.				*/
1620 /*----------------------------------------------------------------------*/
1621 
free_undo_data(Undoptr thisrecord,u_char mode)1622 void free_undo_data(Undoptr thisrecord, u_char mode)
1623 {
1624    u_int type;
1625    objectptr uobj;
1626    uselection *srec;
1627    editelement *erec;
1628 
1629    type = thisrecord->type;
1630    switch (type) {
1631       case XCF_Delete:
1632 	 if (mode == MODE_UNDO) {
1633 	    uobj = (objectptr)thisrecord->undodata;
1634 	    reset(uobj, DESTROY);
1635 	 }
1636 	 else { /* MODE_REDO */
1637 	    srec = (uselection *)thisrecord->undodata;
1638 	    free_selection(srec);
1639 	 }
1640 	 break;
1641 
1642       case XCF_Box: case XCF_Arc: case XCF_Wire: case XCF_Text:
1643       case XCF_Pin_Label: case XCF_Pin_Global: case XCF_Info_Label:
1644       case XCF_Spline: case XCF_Dot: case XCF_Graphic: case XCF_Join:
1645 	 /* if MODE_UNDO, the element is on the page, so don't destroy it! */
1646 	 if (mode == MODE_REDO)
1647 	    free(thisrecord->undodata);
1648 	 break;
1649 
1650       case XCF_Edit:
1651 	 erec = (editelement *)thisrecord->undodata;
1652 	 free_editelement(thisrecord);
1653 	 break;
1654 
1655       case XCF_Copy:
1656       case XCF_Library_Pop:
1657 	 if (mode == MODE_UNDO) {
1658 	    srec = (uselection *)thisrecord->undodata;
1659 	    free_selection(srec);
1660 	 }
1661 	 else { /* MODE_REDO */
1662 	    uobj = (objectptr)thisrecord->undodata;
1663 	    if (uobj) reset(uobj, DESTROY);
1664 	 }
1665 	 break;
1666 
1667       case XCF_Push:
1668       case XCF_ChangeStyle:
1669       case XCF_Anchor:
1670       case XCF_Color:
1671 	 /* Do nothing --- undodata points to a valid element */
1672 	 break;
1673 
1674       case XCF_Select:
1675 	 srec = (uselection *)thisrecord->undodata;
1676 	 free_selection(srec);
1677 	 break;
1678 
1679       default:
1680 	 if (thisrecord->undodata != NULL)
1681 	    free(thisrecord->undodata);
1682 	 break;
1683    }
1684    thisrecord->undodata = NULL;
1685 }
1686 
1687 
1688 /*----------------------------------------------------------------------*/
1689 /* free_undo_record ---							*/
1690 /*	Free allocated memory for one record in the undo stack.		*/
1691 /*----------------------------------------------------------------------*/
1692 
free_undo_record(Undoptr thisrecord)1693 void free_undo_record(Undoptr thisrecord)
1694 {
1695   /* Undoptr nextrecord, lastrecord; (jdk) */
1696 
1697    /* Reset the master list pointers */
1698 
1699    if (xobjs.undostack == thisrecord)
1700       xobjs.undostack = thisrecord->next;
1701 
1702    /* Relink the stack pointers */
1703 
1704    if (thisrecord->last)
1705       thisrecord->last->next = thisrecord->next;
1706 
1707    if (thisrecord->next)
1708       thisrecord->next->last = thisrecord->last;
1709 
1710    /* Free memory allocated to the record */
1711 
1712    free_undo_data(thisrecord, MODE_UNDO);
1713    free(thisrecord);
1714 }
1715 
1716 /*----------------------------------------------------------------------*/
1717 /* free_redo_record ---							*/
1718 /*	Free allocated memory for one record in the redo stack.		*/
1719 /*----------------------------------------------------------------------*/
1720 
free_redo_record(Undoptr thisrecord)1721 void free_redo_record(Undoptr thisrecord)
1722 {
1723   /* Undoptr nextrecord, lastrecord; (jdk) */
1724 
1725    /* Reset the master list pointers */
1726 
1727    if (xobjs.redostack == thisrecord)
1728       xobjs.redostack = thisrecord->last;
1729 
1730    /* Relink the stack pointers */
1731 
1732    if (thisrecord->next)
1733       thisrecord->next->last = thisrecord->last;
1734 
1735    if (thisrecord->last)
1736       thisrecord->last->next = thisrecord->next;
1737 
1738    /* Free memory allocated to the record */
1739 
1740    free_undo_data(thisrecord, MODE_REDO);
1741    free(thisrecord);
1742 }
1743 
1744 /*----------------------------------------------------------------------*/
1745 /* truncate_undo_stack ---						*/
1746 /*	If the limit MAX_UNDO_EVENTS has been reached, discard the	*/
1747 /*	last undo series on the stack (index = 1) and renumber the	*/
1748 /*	others by decrementing.						*/
1749 /*----------------------------------------------------------------------*/
1750 
truncate_undo_stack()1751 void truncate_undo_stack()
1752 {
1753    Undoptr thisrecord, nextrecord;
1754 
1755    thisrecord = xobjs.undostack;
1756    while (thisrecord != NULL) {
1757       nextrecord = thisrecord->next;
1758       if (thisrecord->idx > 1)
1759 	 thisrecord->idx--;
1760       else
1761          free_undo_record(thisrecord);
1762       thisrecord = nextrecord;
1763    }
1764 }
1765 
1766 #ifndef TCL_WRAPPER
1767 
1768 /* Calls from the Xt menus (see menus.h)				*/
1769 /* These are wrappers for undo_action and redo_action			*/
1770 
undo_call(xcWidget button,caddr_t clientdata,caddr_t calldata)1771 void undo_call(xcWidget button, caddr_t clientdata, caddr_t calldata)
1772 {
1773    undo_action();
1774 }
1775 
redo_call(xcWidget button,caddr_t clientdata,caddr_t calldata)1776 void redo_call(xcWidget button, caddr_t clientdata, caddr_t calldata)
1777 {
1778    redo_action();
1779 }
1780 
1781 #endif
1782 /*----------------------------------------------------------------------*/
1783