/*
 * 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, dinfo, arg)
    Tile *tile;			/* Tile of matching type. */
    TileType dinfo;		/* Split tile information */
    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) | dinfo) &
			(TT_DIAGONAL | TT_SIDE | TT_DIRECTION);
	if (dinfo & TT_SIDE)
	    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;
	free_magic1_t mm1 = freeMagic1_init();
	freeMagic1(&mm1, (char *)arg->sea_rectList);
	arg->sea_rectList = arg->sea_rectList->r_next;
	freeMagic1_end(&mm1);
    }
    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, dinfo, arg)
    Tile *tile;			/* Tile found in the edit cell */
    TileType dinfo;		/* Split tile information */
    struct searg *arg;		/* Describes our search */
{
    ExtRectList *lr;
    int ttype;
    TileType seltype;

    if (IsSplit(tile))
	ttype = (dinfo & TT_SIDE) ? 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) == (dinfo  & 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;
}
