Continuing to track down the issue with a crash on startup on
[xcircuit.git] / undo.c
blobe1c2c01ad66edca9553a5f338adbcc154ee907b4
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 /*----------------------------------------------------------------------*/
10 /*----------------------------------------------------------------------*/
11 /* written by Tim Edwards, 1/29/04 */
12 /*----------------------------------------------------------------------*/
14 #define MODE_UNDO (u_char)0
15 #define MODE_REDO (u_char)1
17 #define MAX_UNDO_EVENTS 100
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <string.h>
24 #ifndef XC_WIN32
25 #include <X11/Intrinsic.h>
26 #include <X11/StringDefs.h>
27 #endif
29 /*----------------------------------------------------------------------*/
30 /* Local includes */
31 /*----------------------------------------------------------------------*/
33 #ifdef TCL_WRAPPER
34 #include <tk.h>
35 #endif
37 #include "xcircuit.h"
39 /*----------------------------------------------------------------------*/
40 /* Function prototype declarations */
41 /*----------------------------------------------------------------------*/
42 #include "prototypes.h"
44 /*----------------------------------------------------------------------*/
45 /* Local structure definitions for holding undo information */
46 /*----------------------------------------------------------------------*/
48 typedef struct {
49 float angle1;
50 float angle2;
51 short radius;
52 short yaxis;
53 XPoint position;
54 } arcinfo;
56 /* Smaller data structure used when saving paths in the undo stacks. */
57 /* We don't need the rendering data from points[]. */
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;
68 typedef struct {
69 int number;
70 pointlist points;
71 } pathinfo;
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;
84 typedef struct {
85 genericptr element; /* element modified */
86 float scale; /* old scale value */
87 } scaleinfo;
89 typedef struct {
90 XPoint rotpos; /* original position */
91 float rotation; /* old rotation value */
92 } rotateinfo;
94 u_char undo_collect = (u_char)0;
96 /*----------------------------------------------------------------------*/
97 /* Externally declared variables */
98 /*----------------------------------------------------------------------*/
100 extern Globaldata xobjs;
101 extern XCWindowData *areawin;
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 /*----------------------------------------------------------------------*/
110 Boolean setwindow(XCWindowData *trywindow)
112 XCWindowData *chkwin;
114 for (chkwin = xobjs.windowlist; chkwin != NULL; chkwin = chkwin->next) {
115 if (chkwin == trywindow) {
116 areawin = trywindow;
117 return TRUE;
120 return FALSE;
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 /*----------------------------------------------------------------------*/
131 uselection *remember_selection(objinstptr topinst, short *slist, int number)
133 int i, idx;
134 uselection *newlist;
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));
141 else {
142 newlist->element = NULL;
143 newlist->idx = NULL;
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;
151 return newlist;
154 /*----------------------------------------------------------------------*/
155 /* Create a selection list in areawin from the saved uselection */
156 /* record. */
157 /*----------------------------------------------------------------------*/
159 short *regen_selection(objinstptr thisinst, uselection *srec)
161 int i, j, k;
162 genericptr egen;
163 objectptr thisobj = thisinst->thisobject;
164 Boolean reorder = False;
165 short *slist;
167 if (srec->number > 0)
168 slist = (short *)malloc(srec->number * sizeof(short));
170 k = 0;
171 for (i = 0; i < srec->number; i++) {
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;
184 if (j < thisobj->parts) {
185 *(slist + k) = j;
186 k++;
188 else
189 Fprintf(stderr, "Error: element %p in select list but not object\n",
190 egen);
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. */
196 if (reorder) {
197 /* (to be done) */
200 if (k == 0) {
201 if (srec->number > 0) free(slist);
202 return NULL;
204 else
205 return slist;
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 /*----------------------------------------------------------------------*/
213 void reorder_selection(Undoptr thisrecord)
215 short *slist, *newlist, snum, i;
216 genericptr *pgen, *plist, egen;
217 objinstptr thisinst = thisrecord->thisinst;
218 objectptr thisobj = thisinst->thisobject;
220 snum = (short)thisrecord->idata;
221 slist = (short *)thisrecord->undodata;
222 plist = (genericptr *)malloc(snum * sizeof(genericptr));
223 newlist = (short *)malloc(snum * sizeof(short));
225 i = 0;
226 for (pgen = plist; pgen < plist + snum; pgen++) {
227 egen = *(thisobj->plist + i);
228 *(plist + *(slist + i)) = egen;
229 i++;
231 i = 0;
232 for (pgen = plist; pgen < plist + snum; pgen++) {
233 *(thisobj->plist + i) = *pgen;
234 *(newlist + *(slist + i)) = i;
235 i++;
237 free(plist);
238 free(thisrecord->undodata);
239 thisrecord->undodata = (char *)newlist;
242 /*----------------------------------------------------------------------*/
243 /* free_editelement --- */
244 /* */
245 /* Free memory allocated to an undo record edit element structure. */
246 /*----------------------------------------------------------------------*/
248 void free_editelement(Undoptr thisrecord)
250 editelement *erec;
251 pathinfo *ppi;
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;
271 free(erec);
274 /*----------------------------------------------------------------------*/
275 /* free_selection --- */
276 /* */
277 /* Free memory allocated to an undo record selection list. */
278 /*----------------------------------------------------------------------*/
280 void free_selection(uselection *selrec)
282 if (selrec->number > 0) {
283 free(selrec->element);
284 free(selrec->idx);
286 free(selrec);
289 /*----------------------------------------------------------------------*/
290 /* get_original_string --- */
291 /* */
292 /* Find the original version of the given label. */
293 /*----------------------------------------------------------------------*/
295 stringpart *get_original_string(labelptr thislab)
297 Undoptr chkrecord, thisrecord;
298 editelement *erec;
299 labelptr elab;
301 thisrecord = xobjs.undostack;
302 if (thisrecord == NULL) return NULL;
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;
312 default:
313 return NULL;
316 return NULL; /* yes, this can be reached, if thisrecord->next == NULL */
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 /*----------------------------------------------------------------------*/
326 int select_previous(Undoptr thisrecord)
328 Undoptr chkrecord;
329 uselection *srec;
331 clearselects_noundo();
332 for (chkrecord = thisrecord->next; chkrecord != NULL; chkrecord = chkrecord->next) {
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;
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;
351 return -1;
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 /*----------------------------------------------------------------------*/
360 short *recover_selectlist(Undoptr thisrecord)
362 Undoptr chkrecord;
363 uselection *srec;
365 for (chkrecord = thisrecord->next; chkrecord != NULL; chkrecord = chkrecord->next) {
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;
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;
387 return NULL;
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 /*----------------------------------------------------------------------*/
396 void undelete_one_element(objinstptr thisinst, genericptr thiselem)
398 objectptr thisobj = thisinst->thisobject;
400 PLIST_INCR(thisobj);
401 *(thisobj->plist + thisobj->parts) = thiselem;
402 thisobj->parts++;
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 /*----------------------------------------------------------------------*/
423 void register_for_undo(u_int type, u_char mode, objinstptr thisinst, ...)
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;
439 /* Do not register new events while running undo/redo actions! */
440 if (eventmode == UNDO_MODE) return;
442 /* This action invalidates everything in the "redo" stack, so flush it */
443 flush_redo_stack();
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;
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;
461 else
462 newrecord->idx = xobjs.undostack->idx + 1;
464 else
465 newrecord->idx = 1;
467 if (mode == UNDO_MORE || undo_collect > (u_char)0)
468 newrecord->idx = -newrecord->idx;
470 xobjs.undostack = newrecord;
472 va_start(args, thisinst);
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;
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;
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;
504 case XCF_Pop:
505 /* No args; instance to pop is the instance passed. */
506 break;
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;
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;
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;
537 case XCF_Edit:
538 /* 1 arg: */
539 /* egen = element to be edited (genericptr) */
540 egen = va_arg(args, genericptr);
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;
592 break;
594 newrecord->undodata = (char *)erec;
595 break;
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;
609 case XCF_Rescale:
610 /* 2 args: */
611 /* egen = element that was changed (with new value) */
612 /* ofloat = old float value */
614 egen = va_arg(args, genericptr);
615 scale = va_arg(args, double); /* warning! only takes "double"! */
617 escale = (scaleinfo *)malloc(sizeof(scaleinfo));
618 escale->element = egen;
619 escale->scale = (float)scale;
621 newrecord->undodata = (char *)escale;
622 break;
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;
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;
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;
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;
668 va_end(args);
671 /*----------------------------------------------------------------------*/
672 /* undo_one_action --- */
673 /* Play undo record back one in the stack. */
674 /*----------------------------------------------------------------------*/
676 short undo_one_action()
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;
699 /* Undo the recorded action and shift the undo record pointer. */
701 thisrecord = xobjs.undostack;
702 if (thisrecord == NULL) {
703 Fprintf(stderr, "Nothing to undo!\n");
704 return 0;
707 xobjs.undostack = thisrecord->next;
708 xobjs.redostack = thisrecord;
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;
717 /* Setting eventmode to UNDO_MODE prevents register_for_undo() from */
718 /* being called again while executing the event. */
720 savemode = eventmode;
721 eventmode = UNDO_MODE;
723 /* type-dependent part */
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;
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. */
743 case XCF_Select_Save:
744 unselect_all();
745 thisinst = (objinstptr)thisrecord->undodata;
746 thisobj = thisinst->thisobject;
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;
755 else {
756 delete_one_element(thisrecord->thisinst, (genericptr)thisinst);
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;
770 case XCF_Push:
771 popobject(areawin->area, 0, NULL);
772 break;
774 case XCF_Pop:
775 pushobject((objinstptr)thisrecord->thisinst);
776 break;
778 case XCF_Page:
779 newpage(thisrecord->idata);
780 break;
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;
791 case XCF_Select:
793 /* If there was a previous selection in the undo list, */
794 /* revert to it. */
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);
802 else
803 draw_all_selected();
804 break;
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;
815 else {
816 delete_one_element(thisrecord->thisinst, egen);
817 areawin->redraw_needed = True;
818 drawarea(areawin->area, NULL, NULL);
820 break;
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;
922 areawin->redraw_needed = True;
923 drawarea(areawin->area, NULL, NULL);
926 break;
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;
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;
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;
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;
979 areawin->redraw_needed = True;
980 drawarea(areawin->area, NULL, NULL);
981 break;
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;
1009 areawin->redraw_needed = True;
1010 drawarea(areawin->area, NULL, NULL);
1011 break;
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;
1057 areawin->redraw_needed = True;
1058 drawarea(areawin->area, NULL, NULL);
1059 break;
1061 case XCF_Flip_X:
1062 position = *((XPoint *)thisrecord->undodata);
1063 elementflip(&position);
1064 break;
1066 case XCF_Flip_Y:
1067 position = *((XPoint *)thisrecord->undodata);
1068 elementvflip(&position);
1069 break;
1071 case XCF_Rotate:
1072 position = ((rotateinfo *)thisrecord->undodata)->rotpos;
1073 elementrotate(-((rotateinfo *)thisrecord->undodata)->rotation, &position);
1074 break;
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;
1086 case XCF_Reorder:
1087 reorder_selection(thisrecord);
1088 areawin->redraw_needed = True;
1089 drawarea(areawin->area, NULL, NULL);
1090 break;
1092 default:
1093 Fprintf(stderr, "Undo not implemented for this action!\n");
1094 break;
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;
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;
1113 areawin = savewindow;
1114 return thisrecord->idx;
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 /*----------------------------------------------------------------------*/
1125 void undo_finish_series()
1127 if (undo_collect == (u_char)0)
1128 if (xobjs.undostack && xobjs.undostack->idx < 0)
1129 xobjs.undostack->idx = -xobjs.undostack->idx;
1132 /*----------------------------------------------------------------------*/
1133 /* undo_action --- */
1134 /* Play undo record back to the completion of a series. */
1135 /*----------------------------------------------------------------------*/
1137 void undo_action()
1139 short idx;
1141 // Cannot undo while in the middle of an undo series. Failsafe.
1142 if (undo_collect != (u_char)0) return;
1144 idx = undo_one_action();
1145 while (xobjs.undostack && xobjs.undostack->idx == idx)
1146 undo_one_action();
1149 /*----------------------------------------------------------------------*/
1150 /* redo_one_action --- */
1151 /* Play undo record forward one in the stack. */
1152 /*----------------------------------------------------------------------*/
1154 short redo_one_action()
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;
1176 /* Undo the recorded action and shift the undo record pointer. */
1178 thisrecord = xobjs.redostack;
1179 if (thisrecord == NULL) {
1180 Fprintf(stderr, "Nothing to redo!\n");
1181 return 0;
1183 xobjs.undostack = thisrecord;
1184 xobjs.redostack = thisrecord->last;
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;
1193 savemode = eventmode;
1194 eventmode = UNDO_MODE;
1196 /* type-dependent part */
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;
1212 /* Unfinished! */
1213 case XCF_Select_Save:
1214 srec = (uselection *)thisrecord->undodata;
1215 slist = regen_selection(thisrecord->thisinst, srec);
1216 break;
1218 case XCF_Page:
1219 newpage(*((int *)thisrecord->undodata));
1220 break;
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;
1231 case XCF_Pop:
1232 popobject(areawin->area, 0, NULL);
1233 break;
1235 case XCF_Push:
1236 pushobject((objinstptr)thisrecord->undodata);
1237 break;
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;
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);
1257 break;
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();
1270 break;
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;
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;
1381 areawin->redraw_needed = True;
1382 drawarea(areawin->area, NULL, NULL);
1385 break;
1387 case XCF_Flip_X:
1388 position = *((XPoint *)thisrecord->undodata);
1389 elementflip(&position);
1390 break;
1392 case XCF_Flip_Y:
1393 position = *((XPoint *)thisrecord->undodata);
1394 elementvflip(&position);
1395 break;
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;
1423 areawin->redraw_needed = True;
1424 drawarea(areawin->area, NULL, NULL);
1425 break;
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;
1453 areawin->redraw_needed = True;
1454 drawarea(areawin->area, NULL, NULL);
1455 break;
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;
1501 areawin->redraw_needed = True;
1502 drawarea(areawin->area, NULL, NULL);
1503 break;
1505 case XCF_Rotate:
1506 position = ((rotateinfo *)thisrecord->undodata)->rotpos;
1507 elementrotate(((rotateinfo *)thisrecord->undodata)->rotation, &position);
1508 break;
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;
1519 case XCF_Reorder:
1520 reorder_selection(thisrecord);
1521 areawin->redraw_needed = True;
1522 drawarea(areawin->area, NULL, NULL);
1523 break;
1525 default:
1526 Fprintf(stderr, "Undo not implemented for this action!\n");
1527 break;
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;
1541 areawin = savewindow;
1543 return thisrecord->idx;
1546 /*----------------------------------------------------------------------*/
1547 /* redo_action --- */
1548 /* Play undo record forward to the completion of a series. */
1549 /*----------------------------------------------------------------------*/
1551 void redo_action()
1553 short idx;
1555 // Cannot redo while in the middle of an undo series. Failsafe.
1556 if (undo_collect != (u_char)0) return;
1558 idx = redo_one_action();
1559 while (xobjs.redostack && xobjs.redostack->idx == idx)
1560 redo_one_action();
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 /*----------------------------------------------------------------------*/
1569 void flush_redo_stack()
1571 Undoptr thisrecord, nextrecord;
1573 if (xobjs.redostack == NULL) return; /* no redo stack */
1575 thisrecord = xobjs.redostack;
1577 while (thisrecord != NULL) {
1578 nextrecord = thisrecord->last;
1579 free_redo_record(thisrecord);
1580 thisrecord = nextrecord;
1582 xobjs.redostack = NULL;
1584 if (xobjs.undostack)
1585 xobjs.undostack->last = NULL;
1588 /*----------------------------------------------------------------------*/
1589 /* flush_undo_stack --- */
1590 /* Free all memory allocated to the undo and redo stacks. */
1591 /*----------------------------------------------------------------------*/
1593 void flush_undo_stack()
1595 Undoptr thisrecord, nextrecord;
1597 flush_redo_stack();
1599 thisrecord = xobjs.undostack;
1601 while (thisrecord != NULL) {
1602 nextrecord = thisrecord->next;
1603 free_undo_record(thisrecord);
1604 thisrecord = nextrecord;
1606 xobjs.undostack = NULL;
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 /*----------------------------------------------------------------------*/
1622 void free_undo_data(Undoptr thisrecord, u_char mode)
1624 u_int type;
1625 objectptr uobj;
1626 uselection *srec;
1627 editelement *erec;
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);
1636 else { /* MODE_REDO */
1637 srec = (uselection *)thisrecord->undodata;
1638 free_selection(srec);
1640 break;
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;
1650 case XCF_Edit:
1651 erec = (editelement *)thisrecord->undodata;
1652 free_editelement(thisrecord);
1653 break;
1655 case XCF_Copy:
1656 case XCF_Library_Pop:
1657 if (mode == MODE_UNDO) {
1658 srec = (uselection *)thisrecord->undodata;
1659 free_selection(srec);
1661 else { /* MODE_REDO */
1662 uobj = (objectptr)thisrecord->undodata;
1663 if (uobj) reset(uobj, DESTROY);
1665 break;
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;
1674 case XCF_Select:
1675 srec = (uselection *)thisrecord->undodata;
1676 free_selection(srec);
1677 break;
1679 default:
1680 if (thisrecord->undodata != NULL)
1681 free(thisrecord->undodata);
1682 break;
1684 thisrecord->undodata = NULL;
1688 /*----------------------------------------------------------------------*/
1689 /* free_undo_record --- */
1690 /* Free allocated memory for one record in the undo stack. */
1691 /*----------------------------------------------------------------------*/
1693 void free_undo_record(Undoptr thisrecord)
1695 /* Undoptr nextrecord, lastrecord; (jdk) */
1697 /* Reset the master list pointers */
1699 if (xobjs.undostack == thisrecord)
1700 xobjs.undostack = thisrecord->next;
1702 /* Relink the stack pointers */
1704 if (thisrecord->last)
1705 thisrecord->last->next = thisrecord->next;
1707 if (thisrecord->next)
1708 thisrecord->next->last = thisrecord->last;
1710 /* Free memory allocated to the record */
1712 free_undo_data(thisrecord, MODE_UNDO);
1713 free(thisrecord);
1716 /*----------------------------------------------------------------------*/
1717 /* free_redo_record --- */
1718 /* Free allocated memory for one record in the redo stack. */
1719 /*----------------------------------------------------------------------*/
1721 void free_redo_record(Undoptr thisrecord)
1723 /* Undoptr nextrecord, lastrecord; (jdk) */
1725 /* Reset the master list pointers */
1727 if (xobjs.redostack == thisrecord)
1728 xobjs.redostack = thisrecord->last;
1730 /* Relink the stack pointers */
1732 if (thisrecord->next)
1733 thisrecord->next->last = thisrecord->last;
1735 if (thisrecord->last)
1736 thisrecord->last->next = thisrecord->next;
1738 /* Free memory allocated to the record */
1740 free_undo_data(thisrecord, MODE_REDO);
1741 free(thisrecord);
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 /*----------------------------------------------------------------------*/
1751 void truncate_undo_stack()
1753 Undoptr thisrecord, nextrecord;
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;
1766 #ifndef TCL_WRAPPER
1768 /* Calls from the Xt menus (see menus.h) */
1769 /* These are wrappers for undo_action and redo_action */
1771 void undo_call(xcWidget button, caddr_t clientdata, caddr_t calldata)
1773 undo_action();
1776 void redo_call(xcWidget button, caddr_t clientdata, caddr_t calldata)
1778 redo_action();
1781 #endif
1782 /*----------------------------------------------------------------------*/