magic/database/DBundo.c

1009 lines
27 KiB
C

/*
* 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 <stdio.h>
#include <string.h>
#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[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[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[up->pue_plane],
dinfo, &up->pue_rect, DBStdEraseTbl(loctype, up->pue_plane),
(PaintUndoInfo *) NULL);
loctype = (up->pue_oldtype & TT_RIGHTMASK) >> 14;
dinfo |= TT_SIDE;
DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane],
dinfo, &up->pue_rect, DBStdEraseTbl(loctype, up->pue_plane),
(PaintUndoInfo *) NULL);
}
else
DBPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], &up->pue_rect,
DBStdEraseTbl(up->pue_oldtype, 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[up->pue_plane],
dinfo, &up->pue_rect, DBStdPaintTbl(loctype, up->pue_plane),
(PaintUndoInfo *) NULL);
loctype = (up->pue_newtype & TT_RIGHTMASK) >> 14;
dinfo |= TT_SIDE;
DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane],
dinfo, &up->pue_rect, DBStdPaintTbl(loctype, up->pue_plane),
(PaintUndoInfo *) NULL);
}
else
DBPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], &up->pue_rect,
DBStdPaintTbl(up->pue_newtype, 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[up->pue_plane],
dinfo, &up->pue_rect, DBStdEraseTbl(loctype, up->pue_plane),
(PaintUndoInfo *) NULL);
loctype = (up->pue_newtype & TT_RIGHTMASK) >> 14;
dinfo |= TT_SIDE;
DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane],
dinfo, &up->pue_rect, DBStdEraseTbl(loctype, up->pue_plane),
(PaintUndoInfo *) NULL);
}
else
DBPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], &up->pue_rect,
DBStdEraseTbl(up->pue_newtype, 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[up->pue_plane], dinfo,
&up->pue_rect, DBStdPaintTbl(loctype, up->pue_plane),
(PaintUndoInfo *) NULL);
loctype = (up->pue_oldtype & TT_RIGHTMASK) >> 14;
dinfo |= TT_SIDE;
DBNMPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], dinfo,
&up->pue_rect, DBStdPaintTbl(loctype, up->pue_plane),
(PaintUndoInfo *) NULL);
DBMergeNMTiles0(dbUndoLastCell->cd_planes[up->pue_plane],
&up->pue_rect, (PaintUndoInfo *)NULL, TRUE);
}
else
DBPaintPlane(dbUndoLastCell->cd_planes[up->pue_plane], &up->pue_rect,
DBStdPaintTbl(up->pue_oldtype, 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_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_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);
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 */
(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 */
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;
}
}
/*
* ----------------------------------------------------------------------------
*
* 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;
}
}