1 /*
2  * DBundo.c --
3  *
4  * Interface to the undo package for the database.
5  *
6  *     *********************************************************************
7  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
8  *     * Permission to use, copy, modify, and distribute this              *
9  *     * software and its documentation for any purpose and without        *
10  *     * fee is hereby granted, provided that the above copyright          *
11  *     * notice appear in all copies.  The University of California        *
12  *     * makes no representations about the suitability of this            *
13  *     * software for any purpose.  It is provided "as is" without         *
14  *     * express or implied warranty.  Export of this software outside     *
15  *     * of the United States of America may require an export license.    *
16  *     *********************************************************************
17  */
18 
19 #ifndef lint
20 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/database/DBundo.c,v 1.5 2010/09/24 19:53:19 tim Exp $";
21 #endif  /* not lint */
22 
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include "utils/magic.h"
27 #include "utils/malloc.h"
28 #include "utils/geometry.h"
29 #include "tiles/tile.h"
30 #include "utils/hash.h"
31 #include "database/database.h"
32 #include "database/databaseInt.h"
33 #include "utils/undo.h"
34 #include "windows/windows.h"
35 #include "dbwind/dbwind.h"
36 #include "utils/main.h"
37 #include "utils/utils.h"
38 #include "drc/drc.h"
39 #include "utils/signals.h"
40 
41 /***
42  *** Identifiers for each of the clients defined here
43  ***/
44 UndoType dbUndoIDPaint, dbUndoIDSplit, dbUndoIDJoin;
45 UndoType dbUndoIDPutLabel, dbUndoIDEraseLabel;
46 UndoType dbUndoIDOpenCell, dbUndoIDCloseCell;
47 UndoType dbUndoIDCellUse;
48 
49 /***
50  *** Functions to play events forward/backward.
51  ***/
52     /* Paint */
53 void dbUndoPaintForw(), dbUndoPaintBack();
54 
55     /* Split */
56 void dbUndoSplitForw(), dbUndoSplitBack();
57 
58     /* Labels */
59 void dbUndoLabelForw(), dbUndoLabelBack();
60 
61     /* Change in edit cell def */
62 void dbUndoOpenCell(), dbUndoCloseCell();
63 
64     /* Cell uses */
65 void dbUndoCellForw(), dbUndoCellBack();
66 
67 /***
68  *** Functions invoked at beginning and end
69  *** of an undo/redo command.
70  ***/
71 void dbUndoInit();
72 CellUse *findUse();
73 
74 /***
75  *** The following points to the CellDef specified in the most
76  *** recent database undo operation.  If, when recording the undo
77  *** information for a new database operation, the cell def being
78  *** modified is different from dbUndoLastCell, we record a special
79  *** record on the undo list.
80  ***
81  *** This strategy "differentially encodes" changes in the cell def
82  *** affected during the course of undo.
83  ***/
84 CellDef *dbUndoLastCell;
85 
86 /*
87  * Redisplay for undoing database changes:
88  * As we play the undo log backwards or forwards, we keep track
89  * of a bounding rectangle, dbUndoAreaChanged for the area changed.
90  * We rely on the fact that most database operations are over a
91  * compact local area, so keeping around a single rectangular area
92  * isn't too bad a compromise.
93  *
94  * When the edit cell changes, though, we need to call the redisplay
95  * package with what we've accumulated, recompute the bounding box of
96  * the old edit cell, and then start from scratch again.  The cell def
97  * we will pass to the redisplay package is dbUndoLastCell.
98  *
99  * The flag dbUndoUndid records whether there have been any undo
100  * events processed since the last time redisplay and bounding box
101  * recomputation were done.
102  */
103 Rect dbUndoAreaChanged;
104 bool dbUndoUndid;
105 
106 /* Forward references */
107 
108 extern void dbUndoEdit();
109 
110 
111 /*
112  * ----------------------------------------------------------------------------
113  *
114  * DBUndoInit --
115  *
116  * Initialize the database part of the undo package.
117  * Makes the functions contained in here known to the
118  * undo module.
119  *
120  * Results:
121  *	None.
122  *
123  * Side effects:
124  *	Calls the undo package.
125  *
126  * ----------------------------------------------------------------------------
127  */
128 
129 void
DBUndoInit()130 DBUndoInit()
131 {
132     void (*nullProc)() = NULL;
133 
134     /* Paint: only one client is needed since paint/erase are inverses */
135     dbUndoIDPaint = UndoAddClient(dbUndoInit, dbUndoCloseCell,
136 			(UndoEvent *(*)()) NULL, (int (*)()) NULL,
137 			dbUndoPaintForw, dbUndoPaintBack, "paint");
138 
139     /* Tile Splits */
140     dbUndoIDSplit = UndoAddClient(dbUndoInit, dbUndoCloseCell,
141 			(UndoEvent *(*)()) NULL, (int (*)()) NULL,
142 			dbUndoSplitForw, dbUndoSplitBack, "split");
143     dbUndoIDJoin = UndoAddClient(dbUndoInit, dbUndoCloseCell,
144 			(UndoEvent *(*)()) NULL, (int (*)()) NULL,
145 			dbUndoSplitBack, dbUndoSplitForw, "join");
146 
147     /* Labels */
148     dbUndoIDPutLabel = UndoAddClient(nullProc, nullProc,
149 			(UndoEvent *(*)()) NULL, (int (*)()) NULL,
150 			dbUndoLabelForw, dbUndoLabelBack, "put label");
151     dbUndoIDEraseLabel = UndoAddClient(nullProc, nullProc,
152 			(UndoEvent *(*)()) NULL, (int (*)()) NULL,
153 			dbUndoLabelBack, dbUndoLabelForw, "erase label");
154 
155     /*
156      * Changes in the current target cell of undo for paint/erase/labels.
157      * This client is used only inside this file.  Its purpose is
158      * to let us save space and time in paint, erase and label undo
159      * events.  We maintain dbUndoLastCell to be a pointer to the
160      * CellDef last passed to the database undo package when recording
161      * a paint, erase, or label undo event.  Only when this changes
162      * is it necessary to record the fact on the undo list.  Hence
163      * we avoid having to store the cell def affected with each paint,
164      * erase, and label undo event.
165      */
166     dbUndoIDOpenCell = UndoAddClient(nullProc, nullProc,
167 			(UndoEvent *(*)()) NULL, (int (*)()) NULL,
168 			dbUndoOpenCell, dbUndoCloseCell, "open cell");
169     dbUndoIDCloseCell = UndoAddClient(nullProc, nullProc,
170 			(UndoEvent *(*)()) NULL, (int (*)()) NULL,
171 			dbUndoCloseCell, dbUndoOpenCell, "close cell");
172 
173     /*
174      * Celluses: one client is used for all purposes since we store
175      * the action in the undo event.  (We let the undo client encode
176      * this information for paint and labels only because there are
177      * so many of them that saving space is important).
178      */
179     dbUndoIDCellUse = UndoAddClient(nullProc, nullProc,
180 			(UndoEvent *(*)()) NULL, (int (*)()) NULL,
181 			dbUndoCellForw, dbUndoCellBack, "modify cell use");
182 
183     dbUndoLastCell = (CellDef *) NULL;
184 }
185 
186 /*
187  * ----------------------------------------------------------------------------
188  *
189  * DBUndoReset --
190  *
191  * Set dbUndoLastCell to NULL.  Used by the cell delete function when the
192  * dbUndoLastCell points to the cell to be deleted.
193  * ----------------------------------------------------------------------------
194  */
195 
196 void
DBUndoReset(celldef)197 DBUndoReset(celldef)
198     CellDef *celldef;
199 {
200     if (celldef == dbUndoLastCell)
201     {
202 	UndoFlush();
203 	dbUndoLastCell = (CellDef *) NULL;
204     }
205 }
206 
207 
208 /*
209  * ----------------------------------------------------------------------------
210  *
211  * dbUndoInit --
212  *
213  * Initialize for playing undo events forward/backward for the
214  * database module.
215  *
216  * Results:
217  *	None.
218  *
219  * Side effects:
220  *	Resets the changed area.
221  *
222  * ----------------------------------------------------------------------------
223  */
224 
225 void
dbUndoInit()226 dbUndoInit()
227 {
228     dbUndoUndid = FALSE;
229     dbUndoAreaChanged.r_xbot = dbUndoAreaChanged.r_xtop = 0;
230     dbUndoAreaChanged.r_ybot = dbUndoAreaChanged.r_ytop = 0;
231 }
232 
233 /*
234  * ============================================================================
235  *
236  *				PAINT
237  *
238  * ============================================================================
239  */
240 
241 /*
242  * ----------------------------------------------------------------------------
243  *
244  * dbUndoSplitForw --
245  * dbUndoSplitBack --
246  *
247  * Play forward/backward a tile split event.
248  *
249  * Results:
250  *	None.
251  *
252  * Side effects:
253  *	Modifies the database.
254  *
255  * ----------------------------------------------------------------------------
256  */
257 void
dbUndoSplitForw(us)258 dbUndoSplitForw(us)
259     splitUE *us;
260 {
261     /* Create internal fracture */
262     if (dbUndoLastCell == NULL) return;
263     DBSplitTile(dbUndoLastCell->cd_planes[us->sue_plane], &us->sue_point,
264 		us->sue_splitx);
265 }
266 
267 void
dbUndoSplitBack(us)268 dbUndoSplitBack(us)
269     splitUE *us;
270 {
271     Rect srect;
272     if (dbUndoLastCell == NULL) return;
273 
274     srect.r_ll = us->sue_point;
275     srect.r_ur.p_x = us->sue_point.p_x + 1;
276     srect.r_ur.p_y = us->sue_point.p_y + 1;
277 
278     /* Remove internal fracture and restore original split tile */
279     DBMergeNMTiles0(dbUndoLastCell->cd_planes[us->sue_plane], &srect,
280 		(PaintUndoInfo *)NULL, TRUE);
281 }
282 
283 /***
284  *** The procedures to record paint undo events have been expanded
285  *** in-line in DBPaintPlane() for speed.
286  ***/
287 
288 /*
289  * ----------------------------------------------------------------------------
290  *
291  * dbUndoPaintForw --
292  * dbUndoPaintBack --
293  *
294  * Play forward/backward a paint undo event.
295  *
296  * Results:
297  *	None.
298  *
299  * Side effects:
300  *	Modifies the database.
301  *
302  * ----------------------------------------------------------------------------
303  */
304 
305 void
dbUndoPaintForw(up)306 dbUndoPaintForw(up)
307     paintUE *up;
308 {
309     TileType loctype, dinfo;
310     if (dbUndoLastCell == NULL) return;
311 
312     if (up->pue_oldtype & TT_DIAGONAL)
313     {
314 	loctype = (up->pue_oldtype & TT_LEFTMASK);
315 	dinfo = TT_DIAGONAL | (up->pue_oldtype & TT_DIRECTION);
316 
317 	DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane],
318 		dinfo, &up->pue_rect, DBStdEraseTbl(loctype, up->pue_plane),
319 		(PaintUndoInfo *) NULL);
320 
321 	loctype = (up->pue_oldtype & TT_RIGHTMASK) >> 14;
322 	dinfo |= TT_SIDE;
323 
324 	DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane],
325 		dinfo, &up->pue_rect, DBStdEraseTbl(loctype, up->pue_plane),
326 		(PaintUndoInfo *) NULL);
327     }
328     else
329 	DBPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], &up->pue_rect,
330 			DBStdEraseTbl(up->pue_oldtype, up->pue_plane),
331 			(PaintUndoInfo *) NULL);
332 
333     if (up->pue_newtype & TT_DIAGONAL)
334     {
335 	loctype = (up->pue_newtype & TT_LEFTMASK);
336 	dinfo = TT_DIAGONAL | (up->pue_newtype & TT_DIRECTION);
337 
338 	DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane],
339 		dinfo, &up->pue_rect, DBStdPaintTbl(loctype, up->pue_plane),
340 		(PaintUndoInfo *) NULL);
341 
342 	loctype = (up->pue_newtype & TT_RIGHTMASK) >> 14;
343 	dinfo |= TT_SIDE;
344 
345 	DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane],
346 		dinfo, &up->pue_rect, DBStdPaintTbl(loctype, up->pue_plane),
347 		(PaintUndoInfo *) NULL);
348     }
349     else
350 	DBPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], &up->pue_rect,
351 			DBStdPaintTbl(up->pue_newtype, up->pue_plane),
352 			(PaintUndoInfo *) NULL);
353 endPaintFor:
354     dbUndoUndid = TRUE;
355     (void) GeoInclude(&up->pue_rect, &dbUndoAreaChanged);
356     (void) DRCCheckThis(dbUndoLastCell, TT_CHECKPAINT, &up->pue_rect);
357 }
358 
359 void
dbUndoPaintBack(up)360 dbUndoPaintBack(up)
361     paintUE *up;
362 {
363     TileType loctype, dinfo;
364     if (dbUndoLastCell == NULL) return;
365 
366     if (up->pue_newtype & TT_DIAGONAL)
367     {
368 	loctype = (up->pue_newtype & TT_LEFTMASK);
369 	dinfo = TT_DIAGONAL | (up->pue_newtype & TT_DIRECTION);
370 
371 	DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane],
372 		dinfo, &up->pue_rect, DBStdEraseTbl(loctype, up->pue_plane),
373 		(PaintUndoInfo *) NULL);
374 
375 	loctype = (up->pue_newtype & TT_RIGHTMASK) >> 14;
376 	dinfo |= TT_SIDE;
377 
378 	DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane],
379 		dinfo, &up->pue_rect, DBStdEraseTbl(loctype, up->pue_plane),
380 		(PaintUndoInfo *) NULL);
381     }
382     else
383 	DBPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], &up->pue_rect,
384 			DBStdEraseTbl(up->pue_newtype, up->pue_plane),
385 			(PaintUndoInfo *) NULL);
386 
387     if (up->pue_oldtype & TT_DIAGONAL)
388     {
389 	loctype = (up->pue_oldtype & TT_LEFTMASK);
390 	dinfo = TT_DIAGONAL | (up->pue_oldtype & TT_DIRECTION);
391 
392 	DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], dinfo,
393 		&up->pue_rect, DBStdPaintTbl(loctype, up->pue_plane),
394 		(PaintUndoInfo *) NULL);
395 
396 	loctype = (up->pue_oldtype & TT_RIGHTMASK) >> 14;
397 	dinfo |= TT_SIDE;
398 
399 	DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], dinfo,
400 		&up->pue_rect, DBStdPaintTbl(loctype, up->pue_plane),
401 		(PaintUndoInfo *) NULL);
402 
403 	DBMergeNMTiles0(dbUndoLastCell->cd_planes[up->pue_plane],
404 		&up->pue_rect, (PaintUndoInfo *)NULL, TRUE);
405     }
406     else
407 	DBPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], &up->pue_rect,
408 			DBStdPaintTbl(up->pue_oldtype, up->pue_plane),
409 			(PaintUndoInfo *) NULL);
410 
411 endPaintBack:
412     dbUndoUndid = TRUE;
413     (void) GeoInclude(&up->pue_rect, &dbUndoAreaChanged);
414     (void) DRCCheckThis(dbUndoLastCell, TT_CHECKPAINT, &up->pue_rect);
415 }
416 
417 /*
418  * ============================================================================
419  *
420  *				LABELS
421  *
422  * ============================================================================
423  */
424 
425 /* Just define a labelUE to be a Label. */
426 
427 typedef  Label labelUE;
428 #define  lue_type   lab_type
429 #define  lue_rect   lab_rect
430 #define  lue_just   lab_just
431 #define  lue_font   lab_font
432 #define  lue_size   lab_size
433 #define  lue_rotate lab_rotate
434 #define  lue_offset lab_offset
435 #define  lue_flags  lab_flags
436 #define  lue_text   lab_text
437 #define  lue_port   lab_port
438 
439     /*
440      * labelSize(n) is the size of a labelUE large enough to hold
441      * a string of n characters.  Space for the trailing NULL byte
442      * is allocated automatically.
443      */
444 
445 #define	labelSize(n)	(sizeof (labelUE) - 3 + (n))
446 
447 /*
448  * ----------------------------------------------------------------------------
449  *
450  * DBUndoPutLabel --
451  *
452  * Record on the undo list the painting of a new label.
453  *
454  * Results:
455  *	None.
456  *
457  * Side effects:
458  *	Updates the undo list.
459  *
460  * ----------------------------------------------------------------------------
461  */
462 
463 void
DBUndoPutLabel(cellDef,lab)464 DBUndoPutLabel(cellDef, lab)
465     CellDef *cellDef;	/* CellDef being modified */
466     Label *lab;		/* Label being modified */
467 {
468     labelUE *lup;
469 
470     if (!UndoIsEnabled())
471 	return;
472 
473     if (cellDef != dbUndoLastCell) dbUndoEdit(cellDef);
474     lup = (labelUE *) UndoNewEvent(dbUndoIDPutLabel,
475 			(unsigned) labelSize(strlen(lab->lab_text)));
476     if (lup == (labelUE *) NULL)
477 	return;
478 
479     lup->lue_rect = lab->lab_rect;
480     lup->lue_just = lab->lab_just;
481     lup->lue_type = lab->lab_type;
482     lup->lue_flags = lab->lab_flags;
483     lup->lue_port = lab->lab_port;
484     lup->lue_font = lab->lab_font;
485     lup->lue_size = lab->lab_size;
486     lup->lue_rotate = lab->lab_rotate;
487     lup->lue_offset = lab->lab_offset;
488     strcpy(lup->lue_text, lab->lab_text);
489 }
490 
491 /*
492  * ----------------------------------------------------------------------------
493  *
494  * DBUndoEraseLabel --
495  *
496  * Record on the undo list the erasing of an existing label
497  *
498  * Results:
499  *	None.
500  *
501  * Side effects:
502  *	Updates the undo list.
503  *
504  * ----------------------------------------------------------------------------
505  */
506 
507 void
DBUndoEraseLabel(cellDef,lab)508 DBUndoEraseLabel(cellDef, lab)
509     CellDef *cellDef;	/* Cell being modified */
510     Label *lab;		/* Label being modified */
511 {
512     labelUE *lup;
513 
514     if (!UndoIsEnabled())
515 	return;
516 
517     if (cellDef != dbUndoLastCell) dbUndoEdit(cellDef);
518     lup = (labelUE *) UndoNewEvent(dbUndoIDEraseLabel,
519 			(unsigned) labelSize(strlen(lab->lab_text)));
520     if (lup == (labelUE *) NULL)
521 	return;
522 
523     lup->lue_rect = lab->lab_rect;
524     lup->lue_just = lab->lab_just;
525     lup->lue_type = lab->lab_type;
526     lup->lue_flags = lab->lab_flags;
527     lup->lue_port = lab->lab_port;
528     lup->lue_font = lab->lab_font;
529     lup->lue_size = lab->lab_size;
530     lup->lue_rotate = lab->lab_rotate;
531     lup->lue_offset = lab->lab_offset;
532     strcpy(lup->lue_text, lab->lab_text);
533 }
534 
535 /*
536  * ----------------------------------------------------------------------------
537  *
538  * dbUndoLabelForw --
539  * dbUndoLabelBack --
540  *
541  * Play forward/backward a label undo event.
542  *
543  * Results:
544  *	None.
545  *
546  * Side effects:
547  *	Modifies the database.
548  *
549  * ----------------------------------------------------------------------------
550  */
551 
552 void
dbUndoLabelForw(up)553 dbUndoLabelForw(up)
554     labelUE *up;
555 {
556     Label *lab;
557 
558     if (dbUndoLastCell == NULL) return;
559     lab = DBPutFontLabel(dbUndoLastCell, &up->lue_rect,
560 	up->lue_font, up->lue_size, up->lue_rotate,
561 	&up->lue_offset, up->lue_just, up->lue_text,
562 	up->lue_type, up->lue_flags, up->lue_port);
563     DBWLabelChanged(dbUndoLastCell, lab, DBW_ALLWINDOWS);
564 
565     /*
566      * Record that this cell def has changed, for bounding box
567      * recomputation.  This is only necessary for labels attached
568      * to space; labels attached to material will only appear or
569      * disappear during undo/redo if the material to which they
570      * were attached changes.
571      */
572     if (up->lue_type == TT_SPACE)
573 	dbUndoUndid = TRUE;
574 }
575 
576 void
dbUndoLabelBack(up)577 dbUndoLabelBack(up)
578     labelUE *up;
579 {
580     if (dbUndoLastCell == NULL) return;
581     (void) DBEraseLabelsByContent(dbUndoLastCell, &up->lue_rect,
582 		up->lue_type, up->lue_text);
583 
584     /*
585      * Record that this cell def has changed, for bounding box
586      * recomputing.  See the comments in dbUndoLabelForw above.
587      */
588     if (up->lue_type == TT_SPACE)
589 	dbUndoUndid = TRUE;
590 }
591 
592 /*
593  * ============================================================================
594  *
595  *				CELL MANIPULATION
596  *
597  * ============================================================================
598  */
599 
600     typedef struct
601     {
602 	/* Type of this event */
603 	int		 cue_action;
604 
605 	/*
606 	 * The remainder contains a copy of the important
607 	 * information from the CellUse.
608 	 */
609 	unsigned int	 cue_expandMask;
610 	Transform	 cue_transform;
611 	ArrayInfo	 cue_array;
612 	CellDef		*cue_def;
613 	CellDef		*cue_parent;
614 	Rect		 cue_bbox;
615 	Rect		 cue_extended;
616 	unsigned char	 cue_flags;
617 	char		 cue_id[4];
618     } cellUE;
619 
620     /*
621      * Compute the size of a cellUE, with sufficient space
622      * at the end to store the use id.
623      */
624 #define	cellSize(n)	(sizeof (cellUE) - 3 + (n))
625 
626 /*
627  * ----------------------------------------------------------------------------
628  *
629  * DBUndoCellUse --
630  *
631  * Record one of the following subcell actions:
632  *	UNDO_CELL_PLACE		placement in a parent
633  *	UNDO_CELL_DELETE	removal from a parent
634  *	UNDO_CELL_LOCKDOWN	setting the locked flag
635  *	UNDO_CELL_CLRID		deleting the use id
636  *	UNDO_CELL_SETID		setting the use id
637  *
638  * The last two, deleting and setting the use id, normally occur in
639  * pairs except when the name is set for the first time.
640  *
641  * Because both the parent and child cell uses are stored
642  * in the def, we don't bother to use or update dbUndoLastCell.
643  *
644  * Results:
645  *	None.
646  *
647  * Side effects:
648  *	Updates the undo list.
649  *
650  * ----------------------------------------------------------------------------
651  */
652 
653 void
DBUndoCellUse(use,action)654 DBUndoCellUse(use, action)
655     CellUse *use;
656     int action;
657 {
658     cellUE *up;
659 
660     up = (cellUE *) UndoNewEvent(dbUndoIDCellUse,
661 			(unsigned) cellSize(strlen(use->cu_id)));
662     if (up == (cellUE *) NULL)
663 	return;
664 
665     up->cue_action = action;
666     up->cue_transform = use->cu_transform;
667     up->cue_array = use->cu_array;
668     up->cue_def = use->cu_def;
669     up->cue_parent = use->cu_parent;
670     up->cue_expandMask = use->cu_expandMask;
671     up->cue_bbox = use->cu_bbox;
672     up->cue_extended = use->cu_extended;
673     up->cue_flags = use->cu_flags;
674     strcpy(up->cue_id, use->cu_id);
675 }
676 
677 /*
678  * ----------------------------------------------------------------------------
679  *
680  * dbUndoCellForw --
681  * dbUndoCellBack --
682  *
683  * Play a celluse undo event forward or backward.
684  *
685  * Results:
686  *	None.
687  *
688  * Side effects:
689  *	Modifies the database.
690  *
691  * ----------------------------------------------------------------------------
692  */
693 
694 void
dbUndoCellForw(up)695 dbUndoCellForw(up)
696     cellUE *up;
697 {
698     CellUse *use;
699 
700     switch (up->cue_action)
701     {
702 	case UNDO_CELL_PLACE:
703 	    use = DBCellNewUse(up->cue_def, (char *) NULL);
704 	    use->cu_transform = up->cue_transform;
705 	    use->cu_array = up->cue_array;
706 	    use->cu_expandMask = up->cue_expandMask;
707 	    use->cu_bbox = up->cue_bbox;
708 	    use->cu_extended = up->cue_extended;
709 	    use->cu_flags = up->cue_flags;
710 	    use->cu_id = StrDup((char **) NULL, up->cue_id);
711 	    (void) DBLinkCell(use, up->cue_parent);
712 	    DBPlaceCell(use, up->cue_parent);
713 	    DBReComputeBbox(up->cue_parent);
714 	    DBWAreaChanged(up->cue_parent, &up->cue_bbox, DBW_ALLWINDOWS,
715 		(TileTypeBitMask *) NULL);
716 	    (void) DRCCheckThis(up->cue_parent, TT_CHECKSUBCELL, &up->cue_bbox);
717 	    break;
718         case UNDO_CELL_DELETE:
719 	    use = findUse(up, TRUE);
720 	    DBUnLinkCell(use, up->cue_parent);
721 	    DBDeleteCell(use);
722 	    (void) DBCellDeleteUse(use);
723 	    DBReComputeBbox(up->cue_parent);
724 	    DBWAreaChanged(up->cue_parent, &up->cue_bbox, DBW_ALLWINDOWS,
725 		 (TileTypeBitMask *) NULL);
726 	    (void) DRCCheckThis(up->cue_parent, TT_CHECKSUBCELL, &up->cue_bbox);
727 	    break;
728 	/*
729 	 * We rely upon the fact that a UNDO_CELL_CLRID undo event is
730 	 * always followed immediately by a UNDO_CELL_SETID event.
731 	 * We also depend on the fact that no cell use ever has a
732 	 * null use id when it is linked into a parent def.
733 	 */
734         case UNDO_CELL_SETID:
735 	    use = findUse(up, FALSE);	/* Find the one with the null id */
736 	    (void) DBReLinkCell(use, up->cue_id);
737 	    DBWAreaChanged(up->cue_parent, &up->cue_bbox,
738 			(int) ~use->cu_expandMask, &DBAllButSpaceBits);
739 	    break;
740 	/*
741 	 * The following is a hack.
742 	 * We clear out the use id of the cell so that
743 	 * findUse() will find it on the next time around,
744 	 * which should be when we process a UNDO_CELL_SETID
745 	 * event.
746 	 */
747 	case UNDO_CELL_CLRID:
748 	    use = findUse(up, TRUE);	/* Find it with current id */
749 	    DBUnLinkCell(use, up->cue_parent);
750 	    freeMagic(use->cu_id);
751 	    use->cu_id = (char *) NULL;
752 	    break;
753 
754 	case UNDO_CELL_LOCKDOWN:
755 	    use = findUse(up, TRUE);
756 	    use->cu_flags = up->cue_flags;
757 	    DBWAreaChanged(up->cue_parent, &up->cue_bbox,
758 			(int) ~use->cu_expandMask, &DBAllButSpaceBits);
759 	    break;
760     }
761 }
762 
763 void
dbUndoCellBack(up)764 dbUndoCellBack(up)
765     cellUE *up;
766 {
767     CellUse *use;
768 
769     switch (up->cue_action)
770     {
771 	case UNDO_CELL_DELETE:
772 	    use = DBCellNewUse(up->cue_def, (char *) NULL);
773 	    use->cu_transform = up->cue_transform;
774 	    use->cu_array = up->cue_array;
775 	    use->cu_expandMask = up->cue_expandMask;
776 	    use->cu_bbox = up->cue_bbox;
777 	    use->cu_extended = up->cue_extended;
778 	    use->cu_flags = up->cue_flags;
779 	    use->cu_id = StrDup((char **) NULL, up->cue_id);
780 	    SigDisableInterrupts ();
781 	    (void) DBLinkCell(use, up->cue_parent);
782 	    SigEnableInterrupts ();
783 	    DBPlaceCell(use, up->cue_parent);
784 	    DBReComputeBbox(up->cue_parent);
785 	    DBWAreaChanged(up->cue_parent, &up->cue_bbox, DBW_ALLWINDOWS,
786 		(TileTypeBitMask *) NULL);
787 	    (void) DRCCheckThis(up->cue_parent, TT_CHECKSUBCELL, &up->cue_bbox);
788 	    break;
789         case UNDO_CELL_PLACE:
790 	    use = findUse(up, TRUE);
791 	    DBUnLinkCell(use, up->cue_parent);
792 	    DBDeleteCell(use);
793 	    (void) DBCellDeleteUse(use);
794 	    DBReComputeBbox(up->cue_parent);
795 	    DBWAreaChanged(up->cue_parent, &up->cue_bbox, DBW_ALLWINDOWS,
796 		(TileTypeBitMask *) NULL);
797 	    (void) DRCCheckThis(up->cue_parent, TT_CHECKSUBCELL, &up->cue_bbox);
798 	    break;
799 	/*
800 	 * We rely upon the fact that a UNDO_CELL_CLRID undo event is
801 	 * always followed immediately by a UNDO_CELL_SETID event.
802 	 * We also depend on the fact that no cell use ever has a
803 	 * null use id when it is linked into a parent def.
804 	 */
805         case UNDO_CELL_CLRID:
806 	    use = findUse(up, FALSE);	/* Find it with a NULL id */
807 	    (void) DBReLinkCell(use, up->cue_id);
808 	    DBWAreaChanged(up->cue_parent, &up->cue_bbox,
809 		(int) ~use->cu_expandMask, &DBAllButSpaceBits);
810 	    break;
811 	/*
812 	 * The following is a hack.
813 	 * We clear out the use id of the cell so that
814 	 * findUse() will find it on the next time around,
815 	 * which should be when we process a UNDO_CELL_SETID
816 	 * event.
817 	 */
818 	case UNDO_CELL_SETID:
819 	    use = findUse(up, TRUE);	/* Find it with current id */
820 	    DBUnLinkCell(use, up->cue_parent);
821 	    freeMagic(use->cu_id);
822 	    use->cu_id = (char *) NULL;
823 	    break;
824 
825 	case UNDO_CELL_LOCKDOWN:
826 	    use = findUse(up, TRUE);
827 	    use->cu_flags = up->cue_flags;
828 	    DBWAreaChanged(up->cue_parent, &up->cue_bbox,
829 			(int) ~use->cu_expandMask, &DBAllButSpaceBits);
830 	    break;
831     }
832 }
833 
834 /*
835  * ----------------------------------------------------------------------------
836  *
837  * findUse --
838  *
839  * Find the cell use corresponding to the cellUE supplied.
840  * If 'matchName' is FALSE, we search for a use with a null
841  * use ID instead of one matching the use id of the undo event.
842  * This is a clear hack that results from using two kinds of
843  * undo event to record name changes.
844  *
845  * Results:
846  *	Returns a pointer to a CellUse.
847  *
848  * Side effects:
849  *	None.
850  *	Aborts if it can't find the cell use.
851  *
852  * ----------------------------------------------------------------------------
853  */
854 
855 CellUse *
findUse(up,matchName)856 findUse(up, matchName)
857     cellUE *up;
858     bool matchName;
859 {
860     CellUse *use;
861 
862     for (use = up->cue_def->cd_parents; use; use = use->cu_nextuse)
863 	if (use->cu_parent == up->cue_parent)
864 	{
865 	    if (matchName)
866 	    {
867 		if (strcmp(use->cu_id, up->cue_id) == 0)
868 		    return use;
869 	    }
870 	    else
871 	    {
872 		if (use->cu_id == (char *) NULL)
873 		    return use;
874 	    }
875 	}
876 
877     ASSERT(FALSE, "findUse: use == NULL");
878     return (CellUse *) NULL;
879 }
880 
881 /*
882  * ============================================================================
883  *
884  *			    CHANGE IN "EDIT" CELL
885  *
886  * ============================================================================
887  */
888 
889     typedef struct
890     {
891 	char	 eue_name[4];	/* Name of cell def edited.  This is
892 				 * a place holder only, the actual
893 				 * structure is allocated to hold all
894 				 * the bytes in the def name, plus
895 				 * the null byte.
896 				 */
897     } editUE;
898 
899 /*
900  * ----------------------------------------------------------------------------
901  *
902  * dbUndoEdit --
903  *
904  * Record a change in the cell currently being modified by database
905  * operations.
906  *
907  * Results:
908  *	None.
909  *
910  * Side effects:
911  *	Updates the undo list.
912  *	Sets dbUndoLastCell to the CellDef supplied.
913  *
914  * ----------------------------------------------------------------------------
915  */
916 
917 void
dbUndoEdit(new)918 dbUndoEdit(new)
919     CellDef *new;
920 {
921     editUE *up;
922     CellDef *old = dbUndoLastCell;
923 
924     ASSERT(new != old, "dbUndoEdit");
925 
926     /*
927      * The old cell def can be NULL, eg, when we're at the beginning
928      * of the undo log.  If it is NULL, we don't want to create a close
929      * record to close the old cell.
930      */
931     if (old)
932     {
933 	up = (editUE *) UndoNewEvent(dbUndoIDCloseCell,
934 			(unsigned) strlen(old->cd_name) + 1);
935 	if (up == (editUE *) NULL)
936 	    return;
937 	strcpy(up->eue_name, old->cd_name);
938     }
939 
940     up = (editUE *) UndoNewEvent(dbUndoIDOpenCell,
941 		(unsigned) strlen(new->cd_name) + 1);
942     if (up == (editUE *) NULL)
943 	return;
944     strcpy(up->eue_name, new->cd_name);
945     dbUndoLastCell = new;
946 }
947 
948 /*
949  * ----------------------------------------------------------------------------
950  *
951  * dbUndoOpenCell --
952  *
953  * Set dbUndoLastCell
954  *
955  * Results:
956  *	None.
957  *
958  * Side effects:
959  *	Sets dbUndoLastCell
960  *
961  * ----------------------------------------------------------------------------
962  */
963 
964 void
dbUndoOpenCell(eup)965 dbUndoOpenCell(eup)
966     editUE *eup;
967 {
968     CellDef *newDef;
969 
970     newDef = DBCellLookDef(eup->eue_name);
971     ASSERT(newDef != (CellDef *) NULL, "dbUndoOpenCell");
972     dbUndoLastCell = newDef;
973 }
974 
975 /*
976  * ----------------------------------------------------------------------------
977  *
978  * dbUndoCloseCell --
979  *
980  * If any undo events have been played for dbUndoLastCell,
981  * recompute its bounding box and record the area of it to be
982  * redisplayed.
983  *
984  * Results:
985  *	None.
986  *
987  * Side effects:
988  *	Changes the bounding box on dbUndoLastCell and propagates this
989  *	information to all uses of dbUndoLastCell.  Also, marks any area
990  *	changed in dbUndoLastCell as needing redisplay.
991  *
992  *	Resets dbUndoDid to FALSE and dbUndoAreaChanged to an empty
993  *	rectangle.
994  *
995  * ----------------------------------------------------------------------------
996  */
997 
998 void
dbUndoCloseCell()999 dbUndoCloseCell()
1000 {
1001     if (dbUndoUndid && dbUndoLastCell != NULL)
1002     {
1003 	DBReComputeBbox(dbUndoLastCell);
1004 	DBWAreaChanged(dbUndoLastCell, &dbUndoAreaChanged, DBW_ALLWINDOWS,
1005 	    &DBAllButSpaceBits);
1006 	dbUndoAreaChanged.r_xbot = dbUndoAreaChanged.r_xtop = 0;
1007 	dbUndoAreaChanged.r_ybot = dbUndoAreaChanged.r_ytop = 0;
1008 	dbUndoUndid = FALSE;
1009     }
1010 }
1011