/* * DBundo.c -- * * Interface to the undo package for the database. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint 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 $"; #endif /* not lint */ #include #include #include "utils/magic.h" #include "utils/malloc.h" #include "utils/geometry.h" #include "tiles/tile.h" #include "utils/hash.h" #include "database/database.h" #include "database/databaseInt.h" #include "utils/undo.h" #include "windows/windows.h" #include "dbwind/dbwind.h" #include "utils/main.h" #include "utils/utils.h" #include "drc/drc.h" #include "utils/signals.h" /*** *** Identifiers for each of the clients defined here ***/ UndoType dbUndoIDPaint, dbUndoIDSplit, dbUndoIDJoin; UndoType dbUndoIDPutLabel, dbUndoIDEraseLabel; UndoType dbUndoIDOpenCell, dbUndoIDCloseCell; UndoType dbUndoIDCellUse; /*** *** Functions to play events forward/backward. ***/ /* Paint */ void dbUndoPaintForw(), dbUndoPaintBack(); /* Split */ void dbUndoSplitForw(), dbUndoSplitBack(); /* Labels */ void dbUndoLabelForw(), dbUndoLabelBack(); /* Change in edit cell def */ void dbUndoOpenCell(), dbUndoCloseCell(); /* Cell uses */ void dbUndoCellForw(), dbUndoCellBack(); /*** *** Functions invoked at beginning and end *** of an undo/redo command. ***/ void dbUndoInit(); CellUse *findUse(); /*** *** The following points to the CellDef specified in the most *** recent database undo operation. If, when recording the undo *** information for a new database operation, the cell def being *** modified is different from dbUndoLastCell, we record a special *** record on the undo list. *** *** This strategy "differentially encodes" changes in the cell def *** affected during the course of undo. ***/ CellDef *dbUndoLastCell; /* * Redisplay for undoing database changes: * As we play the undo log backwards or forwards, we keep track * of a bounding rectangle, dbUndoAreaChanged for the area changed. * We rely on the fact that most database operations are over a * compact local area, so keeping around a single rectangular area * isn't too bad a compromise. * * When the edit cell changes, though, we need to call the redisplay * package with what we've accumulated, recompute the bounding box of * the old edit cell, and then start from scratch again. The cell def * we will pass to the redisplay package is dbUndoLastCell. * * The flag dbUndoUndid records whether there have been any undo * events processed since the last time redisplay and bounding box * recomputation were done. */ Rect dbUndoAreaChanged; bool dbUndoUndid; /* Forward references */ extern void dbUndoEdit(); /* * ---------------------------------------------------------------------------- * * DBUndoInit -- * * Initialize the database part of the undo package. * Makes the functions contained in here known to the * undo module. * * Results: * None. * * Side effects: * Calls the undo package. * * ---------------------------------------------------------------------------- */ void DBUndoInit() { void (*nullProc)() = NULL; /* Paint: only one client is needed since paint/erase are inverses */ dbUndoIDPaint = UndoAddClient(dbUndoInit, dbUndoCloseCell, (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbUndoPaintForw, dbUndoPaintBack, "paint"); /* Tile Splits */ dbUndoIDSplit = UndoAddClient(dbUndoInit, dbUndoCloseCell, (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbUndoSplitForw, dbUndoSplitBack, "split"); dbUndoIDJoin = UndoAddClient(dbUndoInit, dbUndoCloseCell, (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbUndoSplitBack, dbUndoSplitForw, "join"); /* Labels */ dbUndoIDPutLabel = UndoAddClient(nullProc, nullProc, (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbUndoLabelForw, dbUndoLabelBack, "put label"); dbUndoIDEraseLabel = UndoAddClient(nullProc, nullProc, (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbUndoLabelBack, dbUndoLabelForw, "erase label"); /* * Changes in the current target cell of undo for paint/erase/labels. * This client is used only inside this file. Its purpose is * to let us save space and time in paint, erase and label undo * events. We maintain dbUndoLastCell to be a pointer to the * CellDef last passed to the database undo package when recording * a paint, erase, or label undo event. Only when this changes * is it necessary to record the fact on the undo list. Hence * we avoid having to store the cell def affected with each paint, * erase, and label undo event. */ dbUndoIDOpenCell = UndoAddClient(nullProc, nullProc, (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbUndoOpenCell, dbUndoCloseCell, "open cell"); dbUndoIDCloseCell = UndoAddClient(nullProc, nullProc, (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbUndoCloseCell, dbUndoOpenCell, "close cell"); /* * Celluses: one client is used for all purposes since we store * the action in the undo event. (We let the undo client encode * this information for paint and labels only because there are * so many of them that saving space is important). */ dbUndoIDCellUse = UndoAddClient(nullProc, nullProc, (UndoEvent *(*)()) NULL, (int (*)()) NULL, dbUndoCellForw, dbUndoCellBack, "modify cell use"); dbUndoLastCell = (CellDef *) NULL; } /* * ---------------------------------------------------------------------------- * * DBUndoReset -- * * Set dbUndoLastCell to NULL. Used by the cell delete function when the * dbUndoLastCell points to the cell to be deleted. * ---------------------------------------------------------------------------- */ void DBUndoReset(celldef) CellDef *celldef; { if (celldef == dbUndoLastCell) { UndoFlush(); dbUndoLastCell = (CellDef *) NULL; } } /* * ---------------------------------------------------------------------------- * * dbUndoInit -- * * Initialize for playing undo events forward/backward for the * database module. * * Results: * None. * * Side effects: * Resets the changed area. * * ---------------------------------------------------------------------------- */ void dbUndoInit() { dbUndoUndid = FALSE; dbUndoAreaChanged.r_xbot = dbUndoAreaChanged.r_xtop = 0; dbUndoAreaChanged.r_ybot = dbUndoAreaChanged.r_ytop = 0; } /* * ============================================================================ * * PAINT * * ============================================================================ */ /* * ---------------------------------------------------------------------------- * * dbUndoSplitForw -- * dbUndoSplitBack -- * * Play forward/backward a tile split event. * * Results: * None. * * Side effects: * Modifies the database. * * ---------------------------------------------------------------------------- */ void dbUndoSplitForw(us) splitUE *us; { /* Create internal fracture */ if (dbUndoLastCell == NULL) return; DBSplitTile(dbUndoLastCell->cd_planes[(unsigned char)us->sue_plane], &us->sue_point, us->sue_splitx); } void dbUndoSplitBack(us) splitUE *us; { Rect srect; if (dbUndoLastCell == NULL) return; srect.r_ll = us->sue_point; srect.r_ur.p_x = us->sue_point.p_x + 1; srect.r_ur.p_y = us->sue_point.p_y + 1; /* Remove internal fracture and restore original split tile */ DBMergeNMTiles0(dbUndoLastCell->cd_planes[(unsigned char)us->sue_plane], &srect, (PaintUndoInfo *)NULL, TRUE); } /*** *** The procedures to record paint undo events have been expanded *** in-line in DBPaintPlane() for speed. ***/ /* * ---------------------------------------------------------------------------- * * dbUndoPaintForw -- * dbUndoPaintBack -- * * Play forward/backward a paint undo event. * * Results: * None. * * Side effects: * Modifies the database. * * ---------------------------------------------------------------------------- */ void dbUndoPaintForw(up) paintUE *up; { TileType loctype, dinfo; if (dbUndoLastCell == NULL) return; if (up->pue_oldtype & TT_DIAGONAL) { loctype = (up->pue_oldtype & TT_LEFTMASK); dinfo = TT_DIAGONAL | (up->pue_oldtype & TT_DIRECTION); DBNMPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], dinfo, &up->pue_rect, DBStdEraseTbl(loctype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); loctype = (up->pue_oldtype & TT_RIGHTMASK) >> 14; dinfo |= TT_SIDE; DBNMPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], dinfo, &up->pue_rect, DBStdEraseTbl(loctype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); } else DBPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], &up->pue_rect, DBStdEraseTbl(up->pue_oldtype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); if (up->pue_newtype & TT_DIAGONAL) { loctype = (up->pue_newtype & TT_LEFTMASK); dinfo = TT_DIAGONAL | (up->pue_newtype & TT_DIRECTION); DBNMPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], dinfo, &up->pue_rect, DBStdPaintTbl(loctype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); loctype = (up->pue_newtype & TT_RIGHTMASK) >> 14; dinfo |= TT_SIDE; DBNMPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], dinfo, &up->pue_rect, DBStdPaintTbl(loctype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); } else DBPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], &up->pue_rect, DBStdPaintTbl(up->pue_newtype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); endPaintFor: dbUndoUndid = TRUE; (void) GeoInclude(&up->pue_rect, &dbUndoAreaChanged); (void) DRCCheckThis(dbUndoLastCell, TT_CHECKPAINT, &up->pue_rect); } void dbUndoPaintBack(up) paintUE *up; { TileType loctype, dinfo; if (dbUndoLastCell == NULL) return; if (up->pue_newtype & TT_DIAGONAL) { loctype = (up->pue_newtype & TT_LEFTMASK); dinfo = TT_DIAGONAL | (up->pue_newtype & TT_DIRECTION); DBNMPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], dinfo, &up->pue_rect, DBStdEraseTbl(loctype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); loctype = (up->pue_newtype & TT_RIGHTMASK) >> 14; dinfo |= TT_SIDE; DBNMPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], dinfo, &up->pue_rect, DBStdEraseTbl(loctype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); } else DBPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], &up->pue_rect, DBStdEraseTbl(up->pue_newtype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); if (up->pue_oldtype & TT_DIAGONAL) { loctype = (up->pue_oldtype & TT_LEFTMASK); dinfo = TT_DIAGONAL | (up->pue_oldtype & TT_DIRECTION); DBNMPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], dinfo, &up->pue_rect, DBStdPaintTbl(loctype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); loctype = (up->pue_oldtype & TT_RIGHTMASK) >> 14; dinfo |= TT_SIDE; DBNMPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], dinfo, &up->pue_rect, DBStdPaintTbl(loctype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); DBMergeNMTiles0(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], &up->pue_rect, (PaintUndoInfo *)NULL, TRUE); } else DBPaintPlane(dbUndoLastCell->cd_planes[(unsigned char)up->pue_plane], &up->pue_rect, DBStdPaintTbl(up->pue_oldtype, (unsigned char)up->pue_plane), (PaintUndoInfo *) NULL); endPaintBack: dbUndoUndid = TRUE; (void) GeoInclude(&up->pue_rect, &dbUndoAreaChanged); (void) DRCCheckThis(dbUndoLastCell, TT_CHECKPAINT, &up->pue_rect); } /* * ============================================================================ * * LABELS * * ============================================================================ */ /* Just define a labelUE to be a Label. */ typedef Label labelUE; #define lue_type lab_type #define lue_rect lab_rect #define lue_just lab_just #define lue_font lab_font #define lue_size lab_size #define lue_rotate lab_rotate #define lue_offset lab_offset #define lue_flags lab_flags #define lue_text lab_text #define lue_port lab_port /* * labelSize(n) is the size of a labelUE large enough to hold * a string of n characters. Space for the trailing NULL byte * is allocated automatically. */ #define labelSize(n) (sizeof (labelUE) - 3 + (n)) /* * ---------------------------------------------------------------------------- * * DBUndoPutLabel -- * * Record on the undo list the painting of a new label. * * Results: * None. * * Side effects: * Updates the undo list. * * ---------------------------------------------------------------------------- */ void DBUndoPutLabel(cellDef, lab) CellDef *cellDef; /* CellDef being modified */ Label *lab; /* Label being modified */ { labelUE *lup; if (!UndoIsEnabled()) return; if (cellDef != dbUndoLastCell) dbUndoEdit(cellDef); lup = (labelUE *) UndoNewEvent(dbUndoIDPutLabel, (unsigned) labelSize(strlen(lab->lab_text))); if (lup == (labelUE *) NULL) return; lup->lue_rect = lab->lab_rect; lup->lue_just = lab->lab_just; lup->lue_type = lab->lab_type; lup->lue_flags = lab->lab_flags; lup->lue_port = lab->lab_port; lup->lue_font = lab->lab_font; lup->lue_size = lab->lab_size; lup->lue_rotate = lab->lab_rotate; lup->lue_offset = lab->lab_offset; strcpy(lup->lue_text, lab->lab_text); } /* * ---------------------------------------------------------------------------- * * DBUndoEraseLabel -- * * Record on the undo list the erasing of an existing label * * Results: * None. * * Side effects: * Updates the undo list. * * ---------------------------------------------------------------------------- */ void DBUndoEraseLabel(cellDef, lab) CellDef *cellDef; /* Cell being modified */ Label *lab; /* Label being modified */ { labelUE *lup; if (!UndoIsEnabled()) return; if (cellDef != dbUndoLastCell) dbUndoEdit(cellDef); lup = (labelUE *) UndoNewEvent(dbUndoIDEraseLabel, (unsigned) labelSize(strlen(lab->lab_text))); if (lup == (labelUE *) NULL) return; lup->lue_rect = lab->lab_rect; lup->lue_just = lab->lab_just; lup->lue_type = lab->lab_type; lup->lue_flags = lab->lab_flags; lup->lue_port = lab->lab_port; lup->lue_font = lab->lab_font; lup->lue_size = lab->lab_size; lup->lue_rotate = lab->lab_rotate; lup->lue_offset = lab->lab_offset; strcpy(lup->lue_text, lab->lab_text); } /* * ---------------------------------------------------------------------------- * * dbUndoLabelForw -- * dbUndoLabelBack -- * * Play forward/backward a label undo event. * * Results: * None. * * Side effects: * Modifies the database. * * ---------------------------------------------------------------------------- */ void dbUndoLabelForw(up) labelUE *up; { Label *lab; if (dbUndoLastCell == NULL) return; lab = DBPutFontLabel(dbUndoLastCell, &up->lue_rect, up->lue_font, up->lue_size, up->lue_rotate, &up->lue_offset, up->lue_just, up->lue_text, up->lue_type, up->lue_flags, up->lue_port); DBWLabelChanged(dbUndoLastCell, lab, DBW_ALLWINDOWS); /* * Record that this cell def has changed, for bounding box * recomputation. This is only necessary for labels attached * to space; labels attached to material will only appear or * disappear during undo/redo if the material to which they * were attached changes. */ if (up->lue_type == TT_SPACE) dbUndoUndid = TRUE; } void dbUndoLabelBack(up) labelUE *up; { if (dbUndoLastCell == NULL) return; (void) DBEraseLabelsByContent(dbUndoLastCell, &up->lue_rect, up->lue_type, up->lue_text); /* * Record that this cell def has changed, for bounding box * recomputing. See the comments in dbUndoLabelForw above. */ if (up->lue_type == TT_SPACE) dbUndoUndid = TRUE; } /* * ============================================================================ * * CELL MANIPULATION * * ============================================================================ */ typedef struct { /* Type of this event */ int cue_action; /* * The remainder contains a copy of the important * information from the CellUse. */ unsigned int cue_expandMask; Transform cue_transform; ArrayInfo cue_array; CellDef *cue_def; CellDef *cue_parent; Rect cue_bbox; Rect cue_extended; unsigned char cue_flags; char cue_id[4]; } cellUE; /* * Compute the size of a cellUE, with sufficient space * at the end to store the use id. */ #define cellSize(n) (sizeof (cellUE) - 3 + (n)) /* * ---------------------------------------------------------------------------- * * DBUndoCellUse -- * * Record one of the following subcell actions: * UNDO_CELL_PLACE placement in a parent * UNDO_CELL_DELETE removal from a parent * UNDO_CELL_LOCKDOWN setting the locked flag * UNDO_CELL_CLRID deleting the use id * UNDO_CELL_SETID setting the use id * * The last two, deleting and setting the use id, normally occur in * pairs except when the name is set for the first time. * * Because both the parent and child cell uses are stored * in the def, we don't bother to use or update dbUndoLastCell. * * Results: * None. * * Side effects: * Updates the undo list. * * ---------------------------------------------------------------------------- */ void DBUndoCellUse(use, action) CellUse *use; int action; { cellUE *up; up = (cellUE *) UndoNewEvent(dbUndoIDCellUse, (unsigned) cellSize(strlen(use->cu_id))); if (up == (cellUE *) NULL) return; up->cue_action = action; up->cue_transform = use->cu_transform; up->cue_array = use->cu_array; up->cue_def = use->cu_def; up->cue_parent = use->cu_parent; up->cue_expandMask = use->cu_expandMask; up->cue_bbox = use->cu_bbox; up->cue_extended = use->cu_extended; up->cue_flags = use->cu_flags; strcpy(up->cue_id, use->cu_id); } /* * ---------------------------------------------------------------------------- * * dbUndoCellForw -- * dbUndoCellBack -- * * Play a celluse undo event forward or backward. * * Results: * None. * * Side effects: * Modifies the database. * * ---------------------------------------------------------------------------- */ void dbUndoCellForw(up) cellUE *up; { CellUse *use; switch (up->cue_action) { case UNDO_CELL_PLACE: use = DBCellNewUse(up->cue_def, (char *) NULL); use->cu_transform = up->cue_transform; use->cu_array = up->cue_array; use->cu_expandMask = up->cue_expandMask; use->cu_bbox = up->cue_bbox; use->cu_extended = up->cue_extended; use->cu_flags = up->cue_flags; use->cu_id = StrDup((char **) NULL, up->cue_id); (void) DBLinkCell(use, up->cue_parent); DBPlaceCell(use, up->cue_parent); DBReComputeBbox(up->cue_parent); DBWAreaChanged(up->cue_parent, &up->cue_bbox, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); (void) DRCCheckThis(up->cue_parent, TT_CHECKSUBCELL, &up->cue_bbox); break; case UNDO_CELL_DELETE: use = findUse(up, TRUE); DBUnLinkCell(use, up->cue_parent); DBDeleteCell(use); (void) DBCellDeleteUse(use); DBReComputeBbox(up->cue_parent); DBWAreaChanged(up->cue_parent, &up->cue_bbox, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); (void) DRCCheckThis(up->cue_parent, TT_CHECKSUBCELL, &up->cue_bbox); break; /* * We rely upon the fact that a UNDO_CELL_CLRID undo event is * always followed immediately by a UNDO_CELL_SETID event. * We also depend on the fact that no cell use ever has a * null use id when it is linked into a parent def. */ case UNDO_CELL_SETID: use = findUse(up, FALSE); /* Find the one with the null id */ (void) DBReLinkCell(use, up->cue_id); DBWAreaChanged(up->cue_parent, &up->cue_bbox, (int) ~use->cu_expandMask, &DBAllButSpaceBits); break; /* * The following is a hack. * We clear out the use id of the cell so that * findUse() will find it on the next time around, * which should be when we process a UNDO_CELL_SETID * event. */ case UNDO_CELL_CLRID: use = findUse(up, TRUE); /* Find it with current id */ DBUnLinkCell(use, up->cue_parent); freeMagic(use->cu_id); use->cu_id = (char *) NULL; break; case UNDO_CELL_LOCKDOWN: use = findUse(up, TRUE); use->cu_flags = up->cue_flags; DBWAreaChanged(up->cue_parent, &up->cue_bbox, (int) ~use->cu_expandMask, &DBAllButSpaceBits); break; } } void dbUndoCellBack(up) cellUE *up; { CellUse *use; switch (up->cue_action) { case UNDO_CELL_DELETE: use = DBCellNewUse(up->cue_def, (char *) NULL); use->cu_transform = up->cue_transform; use->cu_array = up->cue_array; use->cu_expandMask = up->cue_expandMask; use->cu_bbox = up->cue_bbox; use->cu_extended = up->cue_extended; use->cu_flags = up->cue_flags; use->cu_id = StrDup((char **) NULL, up->cue_id); SigDisableInterrupts (); (void) DBLinkCell(use, up->cue_parent); SigEnableInterrupts (); DBPlaceCell(use, up->cue_parent); DBReComputeBbox(up->cue_parent); DBWAreaChanged(up->cue_parent, &up->cue_bbox, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); (void) DRCCheckThis(up->cue_parent, TT_CHECKSUBCELL, &up->cue_bbox); break; case UNDO_CELL_PLACE: use = findUse(up, TRUE); if (use) { DBUnLinkCell(use, up->cue_parent); DBDeleteCell(use); (void) DBCellDeleteUse(use); } DBReComputeBbox(up->cue_parent); DBWAreaChanged(up->cue_parent, &up->cue_bbox, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL); (void) DRCCheckThis(up->cue_parent, TT_CHECKSUBCELL, &up->cue_bbox); break; /* * We rely upon the fact that a UNDO_CELL_CLRID undo event is * always followed immediately by a UNDO_CELL_SETID event. * We also depend on the fact that no cell use ever has a * null use id when it is linked into a parent def. */ case UNDO_CELL_CLRID: use = findUse(up, FALSE); /* Find it with a NULL id */ if (use) { (void) DBReLinkCell(use, up->cue_id); DBWAreaChanged(up->cue_parent, &up->cue_bbox, (int) ~use->cu_expandMask, &DBAllButSpaceBits); } break; /* * The following is a hack. * We clear out the use id of the cell so that * findUse() will find it on the next time around, * which should be when we process a UNDO_CELL_SETID * event. */ case UNDO_CELL_SETID: use = findUse(up, TRUE); /* Find it with current id */ if (use) { DBUnLinkCell(use, up->cue_parent); freeMagic(use->cu_id); use->cu_id = (char *) NULL; } break; case UNDO_CELL_LOCKDOWN: use = findUse(up, TRUE); if (use) { use->cu_flags = up->cue_flags; DBWAreaChanged(up->cue_parent, &up->cue_bbox, (int) ~use->cu_expandMask, &DBAllButSpaceBits); } break; } } /* * ---------------------------------------------------------------------------- * * findUse -- * * Find the cell use corresponding to the cellUE supplied. * If 'matchName' is FALSE, we search for a use with a null * use ID instead of one matching the use id of the undo event. * This is a clear hack that results from using two kinds of * undo event to record name changes. * * Results: * Returns a pointer to a CellUse. * * Side effects: * None. * Aborts if it can't find the cell use. * * ---------------------------------------------------------------------------- */ CellUse * findUse(up, matchName) cellUE *up; bool matchName; { CellUse *use; for (use = up->cue_def->cd_parents; use; use = use->cu_nextuse) if (use->cu_parent == up->cue_parent) { if (matchName) { if (strcmp(use->cu_id, up->cue_id) == 0) return use; } else { if (use->cu_id == (char *) NULL) return use; } } ASSERT(FALSE, "findUse: use == NULL"); return (CellUse *) NULL; } /* * ============================================================================ * * CHANGE IN "EDIT" CELL * * ============================================================================ */ typedef struct { char eue_name[4]; /* Name of cell def edited. This is * a place holder only, the actual * structure is allocated to hold all * the bytes in the def name, plus * the null byte. */ } editUE; /* * ---------------------------------------------------------------------------- * * dbUndoEdit -- * * Record a change in the cell currently being modified by database * operations. * * Results: * None. * * Side effects: * Updates the undo list. * Sets dbUndoLastCell to the CellDef supplied. * * ---------------------------------------------------------------------------- */ void dbUndoEdit(new) CellDef *new; { editUE *up; CellDef *old = dbUndoLastCell; ASSERT(new != old, "dbUndoEdit"); /* * The old cell def can be NULL, eg, when we're at the beginning * of the undo log. If it is NULL, we don't want to create a close * record to close the old cell. */ if (old) { up = (editUE *) UndoNewEvent(dbUndoIDCloseCell, (unsigned) strlen(old->cd_name) + 1); if (up == (editUE *) NULL) return; strcpy(up->eue_name, old->cd_name); } up = (editUE *) UndoNewEvent(dbUndoIDOpenCell, (unsigned) strlen(new->cd_name) + 1); if (up == (editUE *) NULL) return; strcpy(up->eue_name, new->cd_name); dbUndoLastCell = new; } /* * ---------------------------------------------------------------------------- * * dbUndoOpenCell -- * * Set dbUndoLastCell * * Results: * None. * * Side effects: * Sets dbUndoLastCell * * ---------------------------------------------------------------------------- */ void dbUndoOpenCell(eup) editUE *eup; { CellDef *newDef; newDef = DBCellLookDef(eup->eue_name); ASSERT(newDef != (CellDef *) NULL, "dbUndoOpenCell"); dbUndoLastCell = newDef; } /* * ---------------------------------------------------------------------------- * * dbUndoCloseCell -- * * If any undo events have been played for dbUndoLastCell, * recompute its bounding box and record the area of it to be * redisplayed. * * Results: * None. * * Side effects: * Changes the bounding box on dbUndoLastCell and propagates this * information to all uses of dbUndoLastCell. Also, marks any area * changed in dbUndoLastCell as needing redisplay. * * Resets dbUndoDid to FALSE and dbUndoAreaChanged to an empty * rectangle. * * ---------------------------------------------------------------------------- */ void dbUndoCloseCell() { if (dbUndoUndid && dbUndoLastCell != NULL) { DBReComputeBbox(dbUndoLastCell); DBWAreaChanged(dbUndoLastCell, &dbUndoAreaChanged, DBW_ALLWINDOWS, &DBAllButSpaceBits); dbUndoAreaChanged.r_xbot = dbUndoAreaChanged.r_xtop = 0; dbUndoAreaChanged.r_ybot = dbUndoAreaChanged.r_ytop = 0; dbUndoUndid = FALSE; } }