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