mirror of
https://github.com/RTimothyEdwards/magic.git
synced 2026-08-30 01:48:43 +02:00
had numerous problems, the main one being that each of the three commands was inconsistent: "expand toggle" inverts the expansion of the selection and syncs with the layout. But "expand" expands the layout where instances overlap the box and does not sync to the selection, and "unexpand" unexpands the layout where the box completely surrounds instances. Added a set of options to "expand" and "unexpand" so that these functions can be made consistent with each other. All varieties of the function now always sync the selection and the layout.
342 lines
10 KiB
C
342 lines
10 KiB
C
/*
|
|
* DBexpand.c --
|
|
*
|
|
* Expansion and unexpansion of cells
|
|
*
|
|
* *********************************************************************
|
|
* * 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/DBexpand.c,v 1.3 2010/08/15 14:35:47 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "database/database.h"
|
|
#include "database/databaseInt.h"
|
|
#include "textio/textio.h"
|
|
#include "utils/utils.h"
|
|
#include "utils/stack.h"
|
|
|
|
/*
|
|
* Argument passed down to search functions when searching for
|
|
* cells to expand or unexpand.
|
|
*/
|
|
struct expandArg
|
|
{
|
|
bool ea_deref; /* TRUE if root def dereference flag is set */
|
|
int ea_xmask; /* Expand mask. */
|
|
int ea_type; /* Expand, unexpand, or toggle */
|
|
int (*ea_func)(); /* Function to call for each cell whose
|
|
* status is changed.
|
|
*/
|
|
ClientData ea_arg; /* Argument to pass to func. */
|
|
};
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* DBExpand --
|
|
*
|
|
* Expand/unexpand a single CellUse.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* If expandFlag is TRUE, sets all the bits of expandMask in
|
|
* the flags of the given cellUse expandFlag is FALSE, clears
|
|
* all bits of expandMask.
|
|
*
|
|
* If expandFlag is TRUE and the cell being expanded has not
|
|
* been read in, reads it in from disk.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
DBExpand(cellUse, expandMask, expandType)
|
|
CellUse *cellUse;
|
|
int expandMask;
|
|
int expandType;
|
|
{
|
|
CellDef *def;
|
|
bool expandFlag, expandTest;
|
|
|
|
expandTest = DBDescendSubcell(cellUse, expandMask);
|
|
if ((expandType & DB_EXPAND_MASK) == DB_EXPAND_TOGGLE)
|
|
expandFlag = expandTest;
|
|
else
|
|
{
|
|
expandFlag = ((expandType & DB_EXPAND_MASK) == DB_EXPAND) ? TRUE : FALSE;
|
|
if (expandFlag == expandTest) return;
|
|
}
|
|
|
|
if (expandFlag)
|
|
{
|
|
def = cellUse->cu_def;
|
|
if ((def->cd_flags & CDAVAILABLE) == 0)
|
|
{
|
|
if (!DBCellRead(def, TRUE, TRUE, NULL))
|
|
return;
|
|
/* Note: we don't have to recompute the bbox here, because
|
|
* if it changed, then a timestamp violation must have occurred
|
|
* and the timestamp manager will take care of recomputing
|
|
* the bbox.
|
|
*/
|
|
}
|
|
cellUse->cu_expandMask |= expandMask;
|
|
}
|
|
else
|
|
cellUse->cu_expandMask &= ~expandMask;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* DBExpandAll --
|
|
*
|
|
* Recursively expand/unexpand all cells which intersect or are
|
|
* contained within the given rectangle. Furthermore, if func is
|
|
* non-NULL, it is invoked for each cell whose status has changed,
|
|
* just after the change has been made. The calling sequence is
|
|
*
|
|
* int
|
|
* func(cellUse, cdarg)
|
|
* CellUse *cellUse;
|
|
* ClientData cdarg;
|
|
* {
|
|
* }
|
|
*
|
|
* In the calls to func, cellUse is the use whose expand bit has just
|
|
* been changed, and cdarg is the argument that the caller gave to us.
|
|
* Func should normally return 0. If it returns a non-zero value, then
|
|
* the call terminates immediately and no more cells are expanded.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* If expandFlag is TRUE, sets all the bits specified by
|
|
* expandMask in the flags of each CellUse found to intersect
|
|
* the given rectangle. If expandFlag is FALSE, clears all bits
|
|
* of expandMask.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
DBExpandAll(rootUse, rootRect, expandMask, expandType, func, cdarg)
|
|
CellUse *rootUse; /* Root cell use from which search begins */
|
|
Rect *rootRect; /* Area to be expanded, in root coordinates */
|
|
int expandMask; /* Window mask in which cell is to be expanded */
|
|
int expandType; /* DB_EXPAND, DB_UNEXPAND, DB_EXPAND_TOGGLE */
|
|
int (*func)(); /* Function to call for each cell whose expansion
|
|
* status is modified. NULL means don't call anyone.
|
|
*/
|
|
ClientData cdarg; /* Argument to pass to func. */
|
|
{
|
|
int dbExpandFunc();
|
|
SearchContext scontext;
|
|
struct expandArg arg;
|
|
|
|
if ((rootUse->cu_def->cd_flags & CDAVAILABLE) == 0)
|
|
(void) DBCellRead(rootUse->cu_def, TRUE, TRUE, NULL);
|
|
|
|
/*
|
|
* Walk through the area and set the expansion state appropriately.
|
|
*/
|
|
|
|
arg.ea_xmask = expandMask;
|
|
arg.ea_func = func;
|
|
arg.ea_arg = cdarg;
|
|
arg.ea_type = expandType;
|
|
arg.ea_deref = (rootUse->cu_def->cd_flags & CDDEREFERENCE) ? TRUE : FALSE;
|
|
|
|
scontext.scx_use = rootUse;
|
|
scontext.scx_trans = GeoIdentityTransform;
|
|
scontext.scx_area = *rootRect;
|
|
DBCellSrArea(&scontext, dbExpandFunc, (ClientData) &arg);
|
|
}
|
|
|
|
/*
|
|
* dbExpandFunc --
|
|
*
|
|
* Filter function called by DBCellSrArea on behalf of DBExpandAll above
|
|
* when cells are being expanded, unexpanded, or toggled.
|
|
*/
|
|
|
|
int
|
|
dbExpandFunc(scx, arg)
|
|
SearchContext *scx; /* Pointer to search context containing
|
|
* child use, search area in coor-
|
|
* dinates of the child use, and
|
|
* transform back to "root".
|
|
*/
|
|
struct expandArg *arg; /* Client data from caller */
|
|
{
|
|
CellUse *childUse = scx->scx_use;
|
|
int n = DBLambda[1];
|
|
int expandTest;
|
|
int expandType = (arg->ea_type & DB_EXPAND_MASK);
|
|
int expandSurround = (arg->ea_type & DB_EXPAND_SURROUND_MASK);
|
|
bool surround;
|
|
|
|
expandTest = DBDescendSubcell(childUse, arg->ea_xmask);
|
|
|
|
/*
|
|
* Change the expansion status of this cell if necessary. Call the
|
|
* client's function if the expansion status has changed.
|
|
*/
|
|
|
|
if (!expandTest && ((expandType == DB_EXPAND) || (expandType == DB_EXPAND_TOGGLE)))
|
|
{
|
|
surround = (!GEO_SURROUND(&childUse->cu_def->cd_bbox, &scx->scx_area)
|
|
|| GEO_SURROUND(&scx->scx_area, &childUse->cu_def->cd_bbox));
|
|
if (surround || (expandSurround == DB_EXPAND_OVERLAP))
|
|
{
|
|
/* If the cell is unavailable, then don't expand it.
|
|
*/
|
|
if ((childUse->cu_def->cd_flags & CDAVAILABLE) == 0)
|
|
{
|
|
/* If the parent is dereferenced, then the child should be, too */
|
|
if (arg->ea_deref) childUse->cu_def->cd_flags |= CDDEREFERENCE;
|
|
if (!DBCellRead(childUse->cu_def, TRUE, TRUE, NULL))
|
|
{
|
|
TxError("Cell %s is unavailable. It could not be expanded.\n",
|
|
childUse->cu_def->cd_name);
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
childUse->cu_expandMask |= arg->ea_xmask;
|
|
expandTest = TRUE;
|
|
if (arg->ea_func != NULL)
|
|
{
|
|
if ((*arg->ea_func)(childUse, arg->ea_arg) != 0) return 1;
|
|
}
|
|
}
|
|
}
|
|
else if (expandTest && ((expandType == DB_UNEXPAND) ||
|
|
(expandType == DB_EXPAND_TOGGLE)))
|
|
{
|
|
surround = (!GEO_SURROUND(&childUse->cu_def->cd_bbox, &scx->scx_area)
|
|
|| GEO_SURROUND(&scx->scx_area, &childUse->cu_def->cd_bbox));
|
|
if (surround || (expandSurround == DB_EXPAND_OVERLAP))
|
|
{
|
|
childUse->cu_expandMask &= ~arg->ea_xmask;
|
|
expandTest = FALSE;
|
|
|
|
/* Call the client's function, if there is one. */
|
|
|
|
if (arg->ea_func != NULL)
|
|
{
|
|
if ((*arg->ea_func)(childUse, arg->ea_arg) != 0) return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DBCellSrArea(scx, dbExpandFunc, (ClientData) arg))
|
|
return 1;
|
|
return 2;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* DBCellReadArea --
|
|
*
|
|
* Recursively read all cells which intersect or are contained within
|
|
* the given rectangle.
|
|
*
|
|
* Results:
|
|
* If "halt_on_error" is TRUE, then return a pointer to the first
|
|
* subcell that could not be read. Otherwise, return NULL.
|
|
*
|
|
* Side effects:
|
|
* May make new cells known to the database. Sets the CDAVAILABLE
|
|
* bit in all cells intersecting the search area.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
CellDef *
|
|
DBCellReadArea(rootUse, rootRect, halt_on_error)
|
|
CellUse *rootUse; /* Root cell use from which search begins */
|
|
Rect *rootRect; /* Area to be read, in root coordinates */
|
|
bool halt_on_error; /* If TRUE, failure to find a cell causes a halt */
|
|
{
|
|
int dbReadAreaFunc();
|
|
SearchContext scontext;
|
|
CellDef *err_def = NULL;
|
|
|
|
scontext.scx_use = rootUse;
|
|
scontext.scx_trans = GeoIdentityTransform;
|
|
scontext.scx_area = *rootRect;
|
|
if (dbReadAreaFunc(&scontext, ((halt_on_error == TRUE) ? &err_def : NULL)) == 1)
|
|
return err_def;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
dbReadAreaFunc(scx, err_ptr)
|
|
SearchContext *scx; /* Pointer to context specifying
|
|
* the cell use to be read in, and
|
|
* an area to be recursively read in
|
|
* coordinates of the cell use's def.
|
|
*/
|
|
CellDef **err_ptr; /* If non-NULL, failure to find a cell causes a halt
|
|
* and the CellDef in error is returned in err_def.
|
|
* If NULL, failure to find a cell still causes a
|
|
* halt but no information is passed back to the
|
|
* calling routine.
|
|
*/
|
|
{
|
|
CellDef *def = scx->scx_use->cu_def;
|
|
|
|
if ((def->cd_flags & CDAVAILABLE) == 0)
|
|
{
|
|
if (DBCellRead(def, TRUE, TRUE, NULL) == FALSE)
|
|
{
|
|
if (err_ptr != NULL) {
|
|
*err_ptr = def;
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Note: we don't have to invoke DBReComputeBbox here because
|
|
* if the bbox changed then there was a timestamp mismatch and
|
|
* the timestamp code will take care of the bounding box later.
|
|
*/
|
|
}
|
|
|
|
if (DBCellSrArea(scx, dbReadAreaFunc, (ClientData)err_ptr))
|
|
return 1;
|
|
|
|
/* Be clever about handling arrays: if the search area covers this
|
|
* whole definition, then there's no need to look at any other
|
|
* array elements, since we've already expanded the entire area
|
|
* of the definition.
|
|
*/
|
|
|
|
if (GEO_SURROUND(&scx->scx_area, &scx->scx_use->cu_def->cd_bbox))
|
|
return 2;
|
|
|
|
return 0;
|
|
}
|