Files
magic/select/selEnum.c
T
Tim Edwards 6d99e5326e Added a command option "setlabel box" to modify the attachment box
of a label from the command line.  Also fixed a long-standing
irritation that "setlabel" would change the properties of a label
in the edit cell but not in the selection itself, which would
cause the label to be drawn both with the original properties
in the selection and the new properties in the edit cell.  Now
both views will track the same changes.
2023-01-04 14:08:39 -05:00

975 lines
30 KiB
C

/*
* selEnum.c --
*
* This file contains routines to enumerate various pieces of the
* selection, e.g. find all subcells that are in the selection and
* also in the edit cell. The procedures here are used as basic
* building blocks for the selection commands like copy or delete.
*
* *********************************************************************
* * 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/select/selEnum.c,v 1.13 2010/06/24 12:37:56 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <string.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "database/database.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "windows/windows.h"
#include "utils/main.h"
#include "select/select.h"
#include "select/selInt.h"
#include "utils/malloc.h"
#include "textio/textio.h"
/* C99 compat */
#include "graphics/graphics.h"
/* Structure passed from top-level enumeration procedures to lower-level
* ones:
*/
struct searg
{
int (*sea_func)(); /* Client function to call. */
ClientData sea_cdarg; /* Client data to pass to sea_func. */
char sea_flags; /* See flag definitions below */
bool *sea_nonEdit; /* Word to set if non-edit stuff is found. */
int sea_plane; /* Index of plane currently being searched. */
Rect sea_rect; /* Area of tile being searched */
TileType sea_type; /* Type of current piece of selected paint. */
ExtRectList *sea_rectList; /* List of rectangles found in edit cell. */
CellUse *sea_use; /* Use that we're looking for an identical
* copy of in the layout.
*/
CellUse *sea_foundUse; /* Use that was found to match sea_use, or else
* NULL. Also used to hold use for foundLabel.
*/
Transform sea_foundTrans; /* Transform from coords of foundUse to root. */
Label *sea_label; /* Label that we're trying to match in the
* layout.
*/
Label *sea_foundLabel; /* Matching label that was found, or NULL.
* If non_NULL, foundUse and foundTrans
* describe its containing cell.
*/
};
/* sea_flags bit definitions */
#define SEA_EDIT_ONLY 0x01 /* When set, only consider stuff that is in
* the edit cell.
*/
#define SEA_CHUNK 0x02 /* When set, treat the selection as a chunk
* (affects behavior of SelEnumPaint())
*/
#define SEA_AREA 0x04 /* When set, treat the selection as an area
* selection with a possibly limited number
* of named types.
*/
/*
* ----------------------------------------------------------------------------
*
* SelEnumPaint --
*
* Find all selected paint, and call the client's procedure for
* all the areas of paint that are found. Only consider paint
* on "layers", and if "editOnly" is TRUE, then only consider
* paint that it is the edit cell. The client procedure must
* be of the form
*
* int
* func(rect, type, clientData)
* Rect *rect;
* TileType type;
* ClientData clientData;
* {
* }
*
* The rect and type parameters identify the paint that was found,
* in root coordinates, and clientData is just the clientData
* argument passed to this procedure. Func should normally return
* 0. If it returns a non-zero return value, then the search
* will be aborted.
*
* Note that the "type" value passed to func() always contains a
* tiletype in the TT_LEFTMASK field regardless of the upper bits
* describing the diagonal of a non-Manhattan tile. The function
* is responsible for shifting the type field up into TT_RIGHTMASK
* if the TT_SIDE bit is set. This method is obscure but usually
* simplifies processing of non-Manhattan tiles in func().
*
* Results:
* Returns 0 if the search finished normally. Returns 1 if the
* search was aborted.
*
* Side effects:
* If foundNonEdit is non-NULL, its target is set to indicate
* whether there was selected paint from outside the edit cell.
* Otherwise, the only side effects are those of func.
*
* ----------------------------------------------------------------------------
*/
int
SelEnumPaint(layers, editOnly, foundNonEdit, func, clientData)
TileTypeBitMask *layers; /* Mask layers to find. */
bool editOnly; /* TRUE means only find material that is
* both selected and in the edit cell.
*/
bool *foundNonEdit; /* If non-NULL, this word is set to TRUE
* if there's selected paint that's not in
* the edit cell, FALSE otherwise.
*/
int (*func)(); /* Function to call for paint that's found. */
ClientData clientData; /* Argument to pass through to func. */
{
int plane;
struct searg arg;
extern int selEnumPFunc1(); /* Forward declaration. */
arg.sea_func = func;
arg.sea_cdarg = clientData;
arg.sea_flags = (editOnly) ? SEA_EDIT_ONLY : 0;
arg.sea_nonEdit = foundNonEdit;
arg.sea_rectList = NULL;
if (foundNonEdit != NULL) *foundNonEdit = FALSE;
/* EditCellUse is referenced in selEnumPFunc1() and must be non-NULL */
if (EditCellUse == NULL) return 1;
/* First, find all the paint in the selection that has the right
* layers. Use the same procedure as "dbCellUniqueTileSrFunc()"
* so that contacts are not double-counted.
*/
for (plane = PL_SELECTBASE; plane < DBNumPlanes; plane++)
{
arg.sea_plane = plane;
if (DBSrPaintArea((Tile *) NULL, SelectDef->cd_planes[plane],
&TiPlaneRect, layers, selEnumPFunc1,
(ClientData) &arg) != 0)
return 1;
}
return 0;
}
/* Search function invoked for each piece of paint on the right layers
* in the select cell. Collect all of the sub-areas of this piece that
* are also in the edit cell, then call the client function for each
* one of them. It's important to collect the pieces first, then call
* the client: if we call the client while the edit cell search is
* underway, the client might trash the tile plane underneath us.
*/
int
selEnumPFunc1(tile, arg)
Tile *tile; /* Tile of matching type. */
struct searg *arg; /* Describes the current search. */
{
Rect editRect, rootRect;
TileType loctype;
TileTypeBitMask uMask;
extern int selEnumPFunc2();
TiToRect(tile, &arg->sea_rect);
if (IsSplit(tile))
{
arg->sea_type = TiGetTypeExact(tile) & (TT_DIAGONAL | TT_SIDE | TT_DIRECTION);
if (SplitSide(tile))
loctype = SplitRightType(tile);
else
loctype = SplitLeftType(tile);
}
else
loctype = TiGetType(tile);
if (IsSplit(tile))
arg->sea_type |= loctype;
else
arg->sea_type = loctype;
/* If the paint doesn't have to be in the edit cell, life's pretty
* simple: just call the client and quit.
*/
if (!(arg->sea_flags & SEA_EDIT_ONLY))
{
if ((*arg->sea_func)(&arg->sea_rect, arg->sea_type, arg->sea_cdarg) != 0)
return 1;
return 0;
}
/* From here to the end of the routine gets tricky. To apply the */
/* function only to what's in the edit cell, we use the selection */
/* paint to compare against the edit cell, and apply the function */
/* to matching paint, ignoring paint from non-edit cells. Because */
/* the function may alter the edit cell, we must collect a list of */
/* areas to paint, and apply them one by one without reference to */
/* any actual tiles. */
GeoTransRect(&RootToEditTransform, &arg->sea_rect, &editRect);
arg->sea_rectList = NULL;
/* If we have selected a chunk, set sea_flags to mark */
/* this condition (see comments in selEnumPFunc2, below) */
if (SelectUse->cu_flags & CU_SELECT_CHUNK)
arg->sea_flags |= SEA_CHUNK;
if (!TTMaskIsZero(&SelectDef->cd_types))
arg->sea_flags |= SEA_AREA;
if (IsSplit(tile))
DBSrPaintNMArea((Tile *)NULL,
EditCellUse->cu_def->cd_planes[arg->sea_plane],
arg->sea_type, &editRect, &DBAllTypeBits, selEnumPFunc2,
(ClientData)arg);
else
DBSrPaintArea((Tile *)NULL,
EditCellUse->cu_def->cd_planes[arg->sea_plane],
&editRect, &DBAllTypeBits, selEnumPFunc2,
(ClientData)arg);
/* Each rectangle found represents paint that is both in */
/* the selection and also in the edit cell. Call the */
/* client for each such area. */
while (arg->sea_rectList != NULL)
{
GeoTransRect(&EditToRootTransform, &arg->sea_rectList->r_r, &rootRect);
GeoClip(&rootRect, &arg->sea_rect);
if ((*arg->sea_func)(&rootRect, arg->sea_rectList->r_type, arg->sea_cdarg) != 0)
return 1;
freeMagic((char *)arg->sea_rectList);
arg->sea_rectList = arg->sea_rectList->r_next;
}
return 0;
}
/* Second-level paint search function: save around (in edit coords)
* each tile that has the same type as requested in arg. Record if
* any wrong-type tiles are found.
*/
int
selEnumPFunc2(tile, arg)
Tile *tile; /* Tile found in the edit cell */
struct searg *arg; /* Describes our search */
{
ExtRectList *lr;
int ttype;
TileType seltype;
if (IsSplit(tile))
ttype = SplitSide(tile) ? SplitRightType(tile) : SplitLeftType(tile);
else
ttype = TiGetTypeExact(tile);
seltype = arg->sea_type & TT_LEFTMASK;
/* Handle stacked contact types */
if ((ttype != seltype) && (ttype >= DBNumUserLayers))
{
TileTypeBitMask *rmask = DBResidueMask(ttype);
TileType rtype;
for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++)
if (TTMaskHasType(rmask, rtype))
if (arg->sea_plane == DBPlane(rtype))
{
ttype = rtype;
break;
}
}
/* For chunk selection, the types have to match exactly, so we don't
* pick up bits of compatible material in another cell that were not
* intended (a chunk can only exist in one cell)
*/
if (arg->sea_flags & SEA_CHUNK)
if (ttype != seltype)
return 0;
/* If we selected with "select area <types>", then the type */
/* must belong to <types> (after decomposing stacked types). */
if (arg->sea_flags & SEA_AREA)
if (ttype != seltype)
if (!TTMaskHasType(&SelectDef->cd_types, ttype))
return 0;
/* Check for compatible types. That is: paint X (in use) over Y */
/* (in select) yields Y means that it is okay to add X to the list. */
/* Also okay if the X over Y yields a stacking type whose residues */
/* are X and Y. */
if ((ttype != seltype) && (((ttype == TT_SPACE) &&
TTMaskHasType(&DBHomePlaneTypes[arg->sea_plane], seltype))) ||
(DBPaintResultTbl[arg->sea_plane][ttype][seltype] != seltype))
{
TileType chktype = DBPaintResultTbl[arg->sea_plane][ttype][seltype];
TileTypeBitMask *cmask = DBResidueMask(chktype);
if (chktype < DBNumUserLayers || ((ttype != chktype)
&& !TTMaskHasType(cmask, ttype))
|| !TTMaskHasType(cmask, seltype))
{
if (arg->sea_nonEdit != NULL) *(arg->sea_nonEdit) = TRUE;
}
return 0;
}
/* Only process contacts once: only process the home plane */
/* image of any contact type. Stacked contacts are treated */
/* as whichever residue (contact) is in its home plane. */
if (DBIsContact(ttype))
{
if (ttype < DBNumUserLayers)
{
if (arg->sea_plane != DBPlane(ttype)) return 0;
}
else /* stacked contact type */
{
TileTypeBitMask *rmask = DBResidueMask(ttype);
TileType rtype;
for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++)
if (TTMaskHasType(rmask, rtype) &&
arg->sea_plane == DBPlane(rtype))
break;
if (rtype == DBNumUserLayers) return 0;
/* Continue, with the tile type recast as rtype */
ttype = rtype;
}
}
lr = (ExtRectList *)mallocMagic(sizeof(ExtRectList));
TiToRect(tile, &lr->r_r);
if (arg->sea_type & TT_DIAGONAL)
{
Point points[5];
int np, i, j;
Rect area, orect;
if (!IsSplit(tile))
{
/* Things get messy here. Clip against the select triangle */
/* and decompose the result into rectangles and triangles. */
GrClipTriangle(&arg->sea_rect, &lr->r_r, TRUE, arg->sea_type, points, &np);
if (np < 3)
{
freeMagic((char *)lr);
return 0; /* Clipped out of existence */
}
else
{
for (i = 0; i < np; i++)
{
j = (i + 1) % np;
if (points[i].p_x != points[j].p_x && points[i].p_y !=
points[j].p_y)
{
/* Break out the triangle */
lr->r_r.r_xbot = points[i].p_x;
lr->r_r.r_xtop = points[j].p_x;
lr->r_r.r_ybot = points[i].p_y;
lr->r_r.r_ytop = points[j].p_y;
GeoCanonicalRect(&lr->r_r, &area);
break;
}
}
if (i == np) /* Exactly one Manhattan rectangle */
{
lr->r_r.r_xbot = points[0].p_x;
lr->r_r.r_xtop = points[2].p_x;
lr->r_r.r_ybot = points[0].p_y;
lr->r_r.r_ytop = points[2].p_y;
GeoCanonicalRect(&lr->r_r, &area);
lr->r_type = ttype;
}
else if (np >= 4) /* Process extra rectangles */
{
orect.r_xtop = orect.r_xbot = points[0].p_x;
orect.r_ytop = orect.r_ybot = points[0].p_y;
for (i = 0; i < np; i++)
GeoIncludePoint(&points[i], &orect);
/* Rectangle to right or left */
lr->r_r.r_ybot = orect.r_ybot;
lr->r_r.r_ytop = orect.r_ytop;
if (area.r_xbot > orect.r_xbot)
{
lr->r_r.r_xbot = orect.r_xbot;
lr->r_r.r_xtop = area.r_xbot;
}
else if (area.r_xtop < orect.r_xtop)
{
lr->r_r.r_xtop = orect.r_xtop;
lr->r_r.r_xbot = area.r_xtop;
}
else
goto topbottom;
lr->r_type = ttype;
lr->r_next = arg->sea_rectList;
arg->sea_rectList = lr;
lr = (ExtRectList *)mallocMagic(sizeof(ExtRectList));
lr->r_r = arg->sea_rectList->r_r;
topbottom:
/* Rectangle to top or bottom */
lr->r_r.r_xbot = area.r_xbot;
lr->r_r.r_xtop = area.r_xtop;
if (area.r_ybot > orect.r_ybot)
{
lr->r_r.r_ybot = orect.r_ybot;
lr->r_r.r_ytop = area.r_ybot;
}
else if (area.r_ytop < orect.r_ytop)
{
lr->r_r.r_ytop = orect.r_ytop;
lr->r_r.r_ybot = area.r_ytop;
}
else
{
lr->r_type = ttype;
goto splitdone;
}
lr->r_type = ttype;
lr->r_next = arg->sea_rectList;
arg->sea_rectList = lr;
lr = (ExtRectList *)mallocMagic(sizeof(ExtRectList));
lr->r_r = area;
lr->r_type = arg->sea_type;
}
else
{
lr->r_r = area;
lr->r_type = arg->sea_type;
}
}
}
else
{
/* NOTE: Need general-purpose triangle-triangle */
/* intersection routine here! However, for most */
/* purposes, it suffices to copy the tile if the */
/* triangles are similar and to compute the */
/* rectangular union are if opposite. */
if ((arg->sea_type & TT_SIDE) == (TiGetTypeExact(tile) & TT_SIDE))
lr->r_type = ttype | (arg->sea_type &
(TT_DIAGONAL | TT_SIDE | TT_DIRECTION));
else
lr->r_type = ttype;
}
}
else
lr->r_type = ttype;
splitdone:
lr->r_next = arg->sea_rectList;
arg->sea_rectList = lr;
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* SelEnumCells --
*
* Call a client-supplied procedure for each selected subcell.
* If "editOnly" is TRUE, then only consider selected subcells
* that are children of the edit cell. The client procedure
* must be of the form
*
* int
* func(selUse, realUse, transform, clientData)
* CellUse *selUse;
* CellUse *realUse;
* Transform *transform;
* ClientData clientData;
* {
* }
*
* SelUse is a pointer to a cellUse that's in the selection cell.
* RealUse is a pointer to the corresponding cell that's part of
* the layout. Transform is a transform from the coordinates of
* RealUse to root coordinates. If the cell is an array, only one
* call is made for the entire array, and transform is the transform
* for the root element of the array (array[xlo, ylo]). Func should
* normally return 0. If it returns a non-zero return value, then
* the search will be aborted.
*
* Results:
* Returns 0 if the search finished normally. Returns 1 if the
* search was aborted.
*
* Side effects:
* If foundNonEdit is non-NULL, its target is set to indicate
* whether there were selected cells that weren't children of
* the edit cell. Otherwise, the only side effects are those
* of func.
*
* ----------------------------------------------------------------------------
*/
int
SelEnumCells(editOnly, foundNonEdit, scx, func, clientData)
bool editOnly; /* TRUE means only find material that is
* both selected and in the edit cell.
*/
bool *foundNonEdit; /* If non-NULL, this word is set to TRUE
* if there are one or more selected cells
* that aren't children of the edit cell,
* FALSE otherwise.
*/
SearchContext *scx; /* Most clients will provide a NULL value
* here, in which case all the subcells in
* the selection are enumerated. If this
* is non-NULL, it describes a different
* area in which to enumerate subcells. This
* feature is intended primarily for internal
* use within this module.
*/
int (*func)(); /* Function to call for subcells found. */
ClientData clientData; /* Argument to pass through to func. */
{
struct searg arg;
SearchContext scx2;
extern int selEnumCFunc1(); /* Forward reference. */
arg.sea_func = func;
arg.sea_cdarg = clientData;
arg.sea_flags = (editOnly) ? SEA_EDIT_ONLY : 0;
arg.sea_nonEdit = foundNonEdit;
if (foundNonEdit != NULL) *foundNonEdit = FALSE;
/* Find all the subcells that are in the selection. */
if (scx != NULL)
scx2 = *scx;
else
{
scx2.scx_use = SelectUse;
scx2.scx_area = TiPlaneRect;
scx2.scx_trans = GeoIdentityTransform;
}
if (DBCellSrArea(&scx2, selEnumCFunc1, (ClientData) &arg) != 0)
return 1;
return 0;
}
/* The first-level search function: called for each subcell in the
* selection.
*/
int
selEnumCFunc1(scx, arg)
SearchContext *scx; /* Describes cell that was found. */
struct searg *arg; /* Describes our search. */
{
SearchContext scx2;
extern int selEnumCFunc2(); /* Forward reference. */
CellUse dummy;
/* If this cell is the top-level one in its window, we have to
* handle it specially: just look for any use that's a top-level
* use, then call the client for it.
*/
if (scx->scx_use->cu_def == SelectRootDef)
{
CellUse *parent;
/* A root use can't ever be a child of the edit cell. */
if (arg->sea_flags & SEA_EDIT_ONLY)
{
if (arg->sea_nonEdit != NULL) *(arg->sea_nonEdit) = TRUE;
return 2;
}
/* Find a top-level use (one with no parent). */
for (parent = SelectRootDef->cd_parents;
parent != NULL;
parent = parent->cu_nextuse)
{
if (parent->cu_parent == NULL) break;
}
if (parent == NULL)
{
TxError("Internal error: couldn't find selected root cell %s.\n",
SelectRootDef->cd_name);
return 2;
}
/* Call the client. */
if ((*arg->sea_func)(scx->scx_use, parent, &GeoIdentityTransform,
arg->sea_cdarg) != 0)
return 1;
return 2;
}
/* This isn't a top-level cell. Find the instance corresponding
* to this one in the layout. Only search a 1-unit square at the
* cell's lower-left corner in order to cut down the work that
* has to be done. Unfortunately
* we can't use DBTreeSrCells for this, because we don't want to
* look at expanded/unexpanded information.
*/
scx2.scx_use = &dummy;
dummy.cu_def = SelectRootDef;
dummy.cu_id = NULL;
GeoTransRect(&scx->scx_use->cu_transform, &scx->scx_use->cu_def->cd_bbox,
&scx2.scx_area);
scx2.scx_area.r_xtop = scx2.scx_area.r_xbot + 1;
scx2.scx_area.r_ytop = scx2.scx_area.r_ybot + 1;
scx2.scx_trans = GeoIdentityTransform;
arg->sea_use = scx->scx_use;
arg->sea_foundUse = NULL;
(void) DBCellSrArea(&scx2, selEnumCFunc2, (ClientData) arg);
if (arg->sea_foundUse == NULL)
{
TxError("The selected cell %s is not in the layout.\n",
arg->sea_use->cu_id);
return 2;
}
/* See whether the cell is a child of the edit cell and
* call the client's procedure if everything's OK. We do the
* call here rather than in selEnumCFunc2 because the client
* could modify the edit cell in a way that would cause the
* search in progress to core dump. By the time we get back
* here, the search is complete so there's no danger.
*/
if (arg->sea_flags & SEA_EDIT_ONLY)
{
if (!EditCellUse) return 1;
if (arg->sea_foundUse->cu_parent != EditCellUse->cu_def)
{
if (arg->sea_nonEdit != NULL) *(arg->sea_nonEdit) = TRUE;
return 2;
}
}
if ((*arg->sea_func)(scx->scx_use, arg->sea_foundUse,
&arg->sea_foundTrans, arg->sea_cdarg) != 0)
return 1;
return 2;
}
/* Second-level cell search function: called for each cell in the
* tree of SelectRootDef that touches the lower-left corner of
* the subcell in the selection that we're trying to match. If
* this use is for the same subcell, and has the same transformation
* and array structure, then remember the cell use for the caller
* and abort the search.
*/
int
selEnumCFunc2(scx, arg)
SearchContext *scx; /* Describes child of edit cell. */
struct searg *arg; /* Describes what we're looking for. */
{
CellUse *use, *selUse;
use = scx->scx_use;
selUse = arg->sea_use;
if (use->cu_def != selUse->cu_def) goto checkChildren;
if ((scx->scx_trans.t_a != selUse->cu_transform.t_a)
|| (scx->scx_trans.t_b != selUse->cu_transform.t_b)
|| (scx->scx_trans.t_c != selUse->cu_transform.t_c)
|| (scx->scx_trans.t_d != selUse->cu_transform.t_d)
|| (scx->scx_trans.t_e != selUse->cu_transform.t_e)
|| (scx->scx_trans.t_f != selUse->cu_transform.t_f))
goto checkChildren;
if ((use->cu_array.ar_xlo != selUse->cu_array.ar_xlo)
|| (use->cu_array.ar_ylo != selUse->cu_array.ar_ylo)
|| (use->cu_array.ar_xhi != selUse->cu_array.ar_xhi)
|| (use->cu_array.ar_yhi != selUse->cu_array.ar_yhi)
|| (use->cu_array.ar_xsep != selUse->cu_array.ar_xsep)
|| (use->cu_array.ar_ysep != selUse->cu_array.ar_ysep))
goto checkChildren;
arg->sea_foundUse = use;
arg->sea_foundTrans = scx->scx_trans;
return 1;
/* This cell didn't match... see if any of its children do. */
checkChildren:
if (DBCellSrArea(scx, selEnumCFunc2, (ClientData) arg) != 0)
return 1;
else return 0;
}
/*
* ----------------------------------------------------------------------------
*
* SelEnumLabelsBase --
*
* Find all selected labels, and call the client's procedure for
* each label found. Only consider labels attached to "layers",
* and if "editOnly" is TRUE, then only consider labels that
* are in the edit cell. The client procedure must be of the
* form
*
* int
* func(label, cellUse, transform, clientData)
* Label *label;
* CellUse *cellUse;
* Transform *transform;
* ClientData clientData;
* {
* }
*
* Label is a pointer to a selected label. It refers to the label
* in cellUse, and transform gives the transform from that
* cell's coordinates to root coordinates. ClientData is just
* the clientData argument passed to this procedure. Func
* should normally return 0. If it returns a non-zero return
* value, then the search will be aborted.
*
* Results:
* Returns 0 if the search finished normally. Returns 1 if the
* search was aborted.
*
* Side effects:
* If foundNonEdit is non-NULL, its target is set to indicate
* whether there was at least one selected label that was not
* in the edit cell. Otherwise, the only side effects are
* those of func.
*
* ----------------------------------------------------------------------------
*/
int
SelEnumLabelsBase(layers, editOnly, doMirror, foundNonEdit, func, clientData)
TileTypeBitMask *layers; /* Find labels on these layers. */
bool editOnly; /* TRUE means only find labels that are
* both selected and in the edit cell.
*/
bool doMirror; /* If TRUE, apply the function to both the
* label in the selection and in the edit cell.
*/
bool *foundNonEdit; /* If non-NULL, this word is set to TRUE
* if there are selected labels that aren't
* in the edit cell, FALSE otherwise.
*/
int (*func)(); /* Function to call for each label found. */
ClientData clientData; /* Argument to pass through to func. */
{
Label *selLabel;
CellUse dummy;
SearchContext scx;
struct searg arg;
extern int selEnumLFunc(); /* Forward reference. */
extern int selEnumLFunc2();
if (foundNonEdit != NULL) *foundNonEdit = FALSE;
/* First of all, search through all of the selected labels. */
for (selLabel = SelectDef->cd_labels; selLabel != NULL;
selLabel = selLabel->lab_next)
{
if (!TTMaskHasType(layers, selLabel->lab_type)) continue;
/* Find the label corresponding to this one in the design. */
scx.scx_use = &dummy;
dummy.cu_def = SelectRootDef;
dummy.cu_id = NULL;
GEO_EXPAND(&selLabel->lab_rect, 1, &scx.scx_area);
scx.scx_trans = GeoIdentityTransform;
arg.sea_label = selLabel;
arg.sea_foundLabel = NULL;
(void) DBTreeSrLabels(&scx, &DBAllTypeBits, 0, (TerminalPath *) NULL,
TF_LABEL_ATTACH, selEnumLFunc, (ClientData) &arg);
if (arg.sea_foundLabel == NULL)
{
/* Take a 2nd try with relaxed criteria. We may have */
/* altered the text or label justification. */
DBTreeSrLabels(&scx, &DBAllTypeBits, 0, (TerminalPath *) NULL,
TF_LABEL_ATTACH, selEnumLFunc2, (ClientData) &arg);
if (arg.sea_foundLabel == NULL)
{
TxError("The selected label %s is not in the layout.\n",
selLabel->lab_text);
continue;
}
}
/* If only edit-cell labels are wanted, check this label's
* parentage.
*/
if (editOnly && (arg.sea_foundUse->cu_def != EditCellUse->cu_def))
{
if (foundNonEdit != NULL) *foundNonEdit = TRUE;
continue;
}
if ((*func)(arg.sea_foundLabel, arg.sea_foundUse,
&arg.sea_foundTrans, clientData) != 0) return 1;
if (doMirror)
(*func)(selLabel, SelectUse, &SelectUse->cu_transform, clientData);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
* SelEnumLabels --
*
* Wrapper for SelEnumLabelsBase() with doMirror set to FALSE.
*
* ----------------------------------------------------------------------------
*/
int
SelEnumLabels(layers, editOnly, foundNonEdit, func, clientData)
TileTypeBitMask *layers; /* Find labels on these layers. */
bool editOnly; /* TRUE means only find labels that are
* both selected and in the edit cell.
*/
bool *foundNonEdit; /* If non-NULL, this word is set to TRUE
* if there are selected labels that aren't
* in the edit cell, FALSE otherwise.
*/
int (*func)(); /* Function to call for each label found. */
ClientData clientData; /* Argument to pass through to func. */
{
return SelEnumLabelsBase(layers, editOnly, FALSE, foundNonEdit, func, clientData);
}
/*
* ----------------------------------------------------------------------------
* SelEnumLabelsMirror --
*
* Wrapper for SelEnumLabelsBase() with doMirror set to TRUE. Function
* "func" is applied to both the label in the edit cell and in the
* selection. Used when modifying labels so that the selection view
* tracks the changes made to each label.
*
* ----------------------------------------------------------------------------
*/
int
SelEnumLabelsMirror(layers, editOnly, foundNonEdit, func, clientData)
TileTypeBitMask *layers; /* Find labels on these layers. */
bool editOnly; /* TRUE means only find labels that are
* both selected and in the edit cell.
*/
bool *foundNonEdit; /* If non-NULL, this word is set to TRUE
* if there are selected labels that aren't
* in the edit cell, FALSE otherwise.
*/
int (*func)(); /* Function to call for each label found. */
ClientData clientData; /* Argument to pass through to func. */
{
return SelEnumLabelsBase(layers, editOnly, TRUE, foundNonEdit, func, clientData);
}
/* Search function for label enumeration: make sure that this label
* matches the one we're looking for. If it does, then record
* information about it and return right away.
*/
/* ARGSUSED */
int
selEnumLFunc(scx, label, tpath, arg)
SearchContext *scx; /* Describes current cell for search. */
Label *label; /* Describes label that is in right area
* and has right type.
*/
TerminalPath *tpath; /* Ignored. */
struct searg *arg; /* Indicates what we're looking for. */
{
Rect *want, got;
GeoTransRect(&scx->scx_trans, &label->lab_rect, &got);
want = &arg->sea_label->lab_rect;
if (want->r_xbot != got.r_xbot) return 0;
if (want->r_ybot != got.r_ybot) return 0;
if (want->r_xtop != got.r_xtop) return 0;
if (want->r_ytop != got.r_ytop) return 0;
if (arg->sea_label->lab_just
!= GeoTransPos(&scx->scx_trans, label->lab_just)) return 0;
if (strcmp(label->lab_text, arg->sea_label->lab_text) != 0) return 0;
arg->sea_foundLabel = label;
arg->sea_foundUse = scx->scx_use;
arg->sea_foundTrans = scx->scx_trans;
return 1;
}
/*
* selEnumLFunc2() is like selEnumLFunc(), but it allows one of the justification
* or the text string to differ between the source and target labels. This
* prevents magic from failing to find the label if "setlabel text" or
* "setlabel just" is applied to the selection. Both functions are a lousy
* substitute for what ought to be an identifier tag. . .
*/
int
selEnumLFunc2(scx, label, tpath, arg)
SearchContext *scx; /* Describes current cell for search. */
Label *label; /* Describes label that is in right area
* and has right type.
*/
TerminalPath *tpath; /* Ignored. */
struct searg *arg; /* Indicates what we're looking for. */
{
Rect *want, got;
int mismatch = 0;
GeoTransRect(&scx->scx_trans, &label->lab_rect, &got);
want = &arg->sea_label->lab_rect;
if (want->r_xbot != got.r_xbot) return 0;
if (want->r_ybot != got.r_ybot) return 0;
if (want->r_xtop != got.r_xtop) return 0;
if (want->r_ytop != got.r_ytop) return 0;
if (arg->sea_label->lab_just != GeoTransPos(&scx->scx_trans, label->lab_just))
mismatch++;
if (strcmp(label->lab_text, arg->sea_label->lab_text) != 0)
mismatch++;
if (mismatch == 2) return 0;
arg->sea_foundLabel = label;
arg->sea_foundUse = scx->scx_use;
arg->sea_foundTrans = scx->scx_trans;
return 1;
}