1147 lines
31 KiB
C
1147 lines
31 KiB
C
/*
|
|
* DBWtools.c --
|
|
*
|
|
* Implements tools for Magic. Two tools are
|
|
* provided: a box and a point. This module is a sort of
|
|
* broker between the low-level display routines and the
|
|
* high-level command routines. It provides two kinds of
|
|
* routines: those invoked by the screen handler to change
|
|
* the tool location, and those invoked by the command interpreter
|
|
* to get information about the current tool location and relocate
|
|
* the box.
|
|
*
|
|
* *********************************************************************
|
|
* * 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/dbwind/DBWtools.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "utils/styles.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "database/database.h"
|
|
#include "windows/windows.h"
|
|
#include "graphics/graphics.h"
|
|
#include "dbwind/dbwind.h"
|
|
#include "textio/textio.h"
|
|
#include "utils/main.h"
|
|
#include "textio/txcommands.h"
|
|
|
|
#define WINDOW_DEF(w) (((CellUse *)(w->w_surfaceID))->cu_def)
|
|
|
|
/* The following rectangle defines the current box location.
|
|
* The box is defined in terms of a particular root cell def
|
|
* and an area within that def. Note: there is a notion
|
|
* of "compatibility" between windows, as far as tools are
|
|
* concerned. Two windows are compatible if they have the same
|
|
* root cell def. If the box is placed in one window, it will
|
|
* be displayed in all compatible windows. The box may not have
|
|
* one corner placed in one window and other corners in
|
|
* incompatible windows.
|
|
*/
|
|
|
|
static CellDef *boxRootDef = NULL; /* CellDef for the box */
|
|
static Rect boxRootArea; /* Root def coords */
|
|
|
|
typedef struct _crosshairRec {
|
|
Point pos; /* Crosshair position */
|
|
CellDef *def; /* CellDef that crosshair position references */
|
|
} CrosshairRec;
|
|
|
|
static CrosshairRec curCrosshair; /* Crosshair position */
|
|
|
|
/*
|
|
* If the following is DBW_SNAP_USER, the box gets snapped to the user's
|
|
* grid always, instead of snapping to the usual 1x1 grid. If the value
|
|
* is DBW_SNAP_INTERNAL, the box gets snapped to the internal grid.
|
|
*/
|
|
int DBWSnapToGrid = DBW_SNAP_LAMBDA;
|
|
|
|
/* Forward reference: */
|
|
|
|
extern int DBWToolDraw();
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* toolFindPoint --
|
|
*
|
|
* Returns the point in root coordinates.
|
|
* If DBWSnapToGrid is DBW_SNAP_USER, pick the nearest point that is
|
|
* aligned with the window's grid. If DBWSnapToGrid is DBW_SNAP_LAMBDA,
|
|
* pick the nearest point that is an integer lambda value.
|
|
*
|
|
* Results:
|
|
* The return value is a pointer to the window containing the
|
|
* point, or NULL if no window contains the point.
|
|
*
|
|
* Side effects:
|
|
* The parameters rootPoint and rootArea are modified
|
|
* to contain information about the point's location. RootPoint
|
|
* is the nearest lambda grid point to the point's actual
|
|
* location, while rootArea is a one-lambda-square box surrounding
|
|
* the point. If rootPoint or rootArea is NULL, then that structure
|
|
* isn't filled in.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
MagWindow *
|
|
toolFindPoint(p, rootPoint, rootArea)
|
|
Point *p; /* The point to find, in the current window. */
|
|
Point *rootPoint; /* Modified to contain coordinates of point
|
|
* in root cell coordinates. Is unchanged
|
|
* if NULL is returned.
|
|
*/
|
|
Rect *rootArea; /* Modified to contain box around point. Is
|
|
* unchanged when NULL is returned.
|
|
*/
|
|
{
|
|
extern MagWindow *WindCurrentWindow;
|
|
|
|
if (WindCurrentWindow == NULL) return NULL;
|
|
|
|
if (WindCurrentWindow->w_client != DBWclientID) return NULL;
|
|
|
|
if (!GEO_ENCLOSE(p, &WindCurrentWindow->w_screenArea)) return NULL;
|
|
|
|
WindPointToSurface(WindCurrentWindow, p, rootPoint, rootArea);
|
|
if (DBWSnapToGrid != DBW_SNAP_INTERNAL)
|
|
ToolSnapToGrid(WindCurrentWindow, rootPoint, rootArea);
|
|
return WindCurrentWindow;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* ToolGetPoint --
|
|
*
|
|
* Returns information about the point. Used by command processors.
|
|
*
|
|
* Results:
|
|
* The return value is a pointer to the window containing the
|
|
* point, or NULL if no window contains the point.
|
|
*
|
|
* Side effects:
|
|
* The parameters rootPoint and rootArea are modified
|
|
* to contain information about the point's location. RootPoint
|
|
* is the nearest lambda grid point to the point's actual
|
|
* location, while rootArea is a one-lambda-square box surrounding
|
|
* the point. If rootPoint or rootArea is NULL, then that structure
|
|
* isn't filled in.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
MagWindow *
|
|
ToolGetPoint(rootPoint, rootArea)
|
|
Point *rootPoint; /* Modified to contain coordinates of point
|
|
* in root cell coordinates. Is unchanged
|
|
* if NULL is returned.
|
|
*/
|
|
Rect *rootArea; /* Modified to contain box around point. Is
|
|
* unchanged when NULL is returned.
|
|
*/
|
|
{
|
|
extern TxCommand *WindCurrentCmd;
|
|
|
|
if (WindCurrentCmd == NULL)
|
|
return NULL;
|
|
else
|
|
return toolFindPoint(&WindCurrentCmd->tx_p, rootPoint, rootArea);
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* ToolGetBox --
|
|
*
|
|
* Returns the box CellDef and location in CellDef coords.
|
|
*
|
|
* Results:
|
|
* TRUE if the box exists.
|
|
*
|
|
* Side effects:
|
|
* The rootArea parameter is modified to contain the area
|
|
* of the box. If rootArea is NULL, it is ignored.
|
|
* Same with rootDef.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
ToolGetBox(rootDef, rootArea)
|
|
CellDef **rootDef; /* Filled in with the root def of the box */
|
|
Rect *rootArea; /* Filled in with area of box. Will be
|
|
* unchanged when NULL is returned.
|
|
*/
|
|
{
|
|
if (boxRootDef == NULL) return FALSE;
|
|
if (rootDef != NULL)
|
|
*rootDef = boxRootDef;
|
|
if (rootArea != NULL)
|
|
*rootArea = boxRootArea;
|
|
return TRUE;
|
|
}
|
|
|
|
/* ToolScaleBox --- Simple scaling of the root area box, no update needed */
|
|
|
|
void
|
|
ToolScaleBox(scalen, scaled)
|
|
int scalen;
|
|
int scaled;
|
|
{
|
|
DBScalePoint(&boxRootArea.r_ll, scalen, scaled);
|
|
DBScalePoint(&boxRootArea.r_ur, scalen, scaled);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* ToolGetBoxWindow --
|
|
*
|
|
* Returns information about the current box location. Used by
|
|
* command processing routines.
|
|
*
|
|
* Results:
|
|
* The return value is a pointer to a window containing the
|
|
* box, or NULL if the box doesn't exist in any window. Note:
|
|
* the box may actually be in more than one window, so this
|
|
* isn't necessarily the only window containing the box.
|
|
*
|
|
* Side effects:
|
|
* The rootArea parameter is modified to contain the area
|
|
* of the box. If rootArea is NULL, it is ignored. The
|
|
* integer pointed to by pMask is modified to contain a
|
|
* mask of all windows containing the box (there may be more
|
|
* than one). If pMask is NULL, it is ignored.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static int toolMask; /* Shared between these two routines. */
|
|
|
|
MagWindow *
|
|
ToolGetBoxWindow(rootArea, pMask)
|
|
Rect *rootArea; /* Filled in with area of box. Will be
|
|
* unchanged when NULL is returned.
|
|
*/
|
|
int *pMask; /* Filled in with mask of all windows
|
|
* containing box.
|
|
*/
|
|
{
|
|
MagWindow *window;
|
|
extern int toolWindowSave();
|
|
|
|
/* Search through the windows and remember a window that has
|
|
* the right root cell. It's important to NOT search on the
|
|
* area of the box (i.e. take any window with the box's root
|
|
* definition, even if the box isn't visible in the window).
|
|
* Otherwise, some commands won't work when the box goes
|
|
* off-screen. Also accumulate the mask bits.
|
|
*/
|
|
|
|
toolMask = 0;
|
|
window = NULL;
|
|
if (boxRootDef != NULL)
|
|
(void) WindSearch(DBWclientID, (ClientData) NULL, (Rect *) NULL,
|
|
toolWindowSave, (ClientData) &window);
|
|
if ((window != NULL) && (rootArea != NULL)) *rootArea = boxRootArea;
|
|
if (pMask != NULL) *pMask = toolMask;
|
|
return window;
|
|
}
|
|
|
|
int
|
|
toolWindowSave(window, clientData)
|
|
MagWindow *window; /* Window that matched in some search. */
|
|
ClientData clientData; /* Contains the address of a location
|
|
* to be filled in with the window address.
|
|
*/
|
|
{
|
|
if (WINDOW_DEF(window) == boxRootDef)
|
|
{
|
|
*((MagWindow **) clientData) = window;
|
|
toolMask |= ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* ToolGetEditBox --
|
|
*
|
|
* Fill in the location of the box in edit cell coordinates.
|
|
*
|
|
* Results:
|
|
* TRUE if the box can indeed be put into edit cell coordinates.
|
|
* FALSE and an error message otherwise.
|
|
*
|
|
* Side effects:
|
|
* Sets *rect to be the coordinates of the box tool in edit cell
|
|
* coordinates, if TRUE was returned.
|
|
*
|
|
* Prints an error message if the box is not found or the box
|
|
* is not in the edit cell coordinate system.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
ToolGetEditBox(rect)
|
|
Rect *rect;
|
|
{
|
|
CellDef *editDef;
|
|
|
|
if (boxRootDef == NULL)
|
|
{
|
|
TxError("Box must be present\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Added 8/4/2021---Don't require that EditRootDef be non-NULL. */
|
|
|
|
if (EditRootDef == NULL)
|
|
{
|
|
MagWindow *w;
|
|
w = ToolGetBoxWindow(rect, NULL);
|
|
|
|
windCheckOnlyWindow(&w, DBWclientID);
|
|
if (w == (MagWindow *) NULL)
|
|
editDef = EditCellUse->cu_def;
|
|
else
|
|
editDef = ((CellUse *) w->w_surfaceID)->cu_def;
|
|
}
|
|
else
|
|
editDef = EditRootDef;
|
|
|
|
if (editDef != boxRootDef)
|
|
{
|
|
TxError("The box isn't in a window on the edit cell.\n");
|
|
return FALSE;
|
|
}
|
|
if (rect != NULL)
|
|
GeoTransRect(&RootToEditTransform, &boxRootArea, rect);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* ToolGetCorner --
|
|
*
|
|
* Returns the corner of the box closest to a given screen location.
|
|
*
|
|
* Results:
|
|
* An integer value is returned, indicating the corner closest to
|
|
* the given screen location. The point must be in a window
|
|
* containing a root cell use, and the box must currently be in
|
|
* that window, or in another window with the same root cell use.
|
|
* If these conditions aren't satisfied, then the lower-left corner
|
|
* is returned. Note: "closeness" is determined not by screen
|
|
* closeness, but by closeness in root cell coordinates.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
ToolGetCorner(screenPoint)
|
|
Point *screenPoint;
|
|
|
|
{
|
|
Point p;
|
|
MagWindow *w;
|
|
Rect r;
|
|
|
|
/* Make sure that the cursor is in a valid window. */
|
|
|
|
w = toolFindPoint(screenPoint, &p, (Rect *) NULL);
|
|
if (w == NULL) return TOOL_BL;
|
|
if (WINDOW_DEF(w) != boxRootDef) return TOOL_BL;
|
|
|
|
/* Find out which corner is closest. Consider only the
|
|
* intersection of the box with the window (otherwise it
|
|
* may not be possible to select off-screen corners).
|
|
*/
|
|
|
|
WindSurfaceToScreen(w, &boxRootArea, &r);
|
|
GeoClip(&r, &w->w_screenArea);
|
|
if (screenPoint->p_x < ((r.r_xbot + r.r_xtop)/2))
|
|
{
|
|
if (screenPoint->p_y < ((r.r_ybot + r.r_ytop)/2))
|
|
return TOOL_BL;
|
|
else return TOOL_TL;
|
|
}
|
|
else
|
|
{
|
|
if (screenPoint->p_y < ((r.r_ybot + r.r_ytop)/2))
|
|
return TOOL_BR;
|
|
else return TOOL_TR;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* dbwCrosshairInit --
|
|
*
|
|
* Set the initial crosshair position to MINFINITY, where it will
|
|
* not display until explicitly set by a command.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Initializes static memory.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
dbwCrosshairInit()
|
|
{
|
|
curCrosshair.pos.p_x = MINFINITY;
|
|
curCrosshair.pos.p_y = MINFINITY;
|
|
curCrosshair.def = (CellDef *)NULL;
|
|
DBWHLAddClient(DBWDrawCrosshair);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* dbwRecordCrosshair[X,Y]Pos --
|
|
*
|
|
* This procedure tells the highlight manager that the crosshair's
|
|
* current position needs to be erased or redisplayed.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Highlight redisplay information is logged.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
dbwRecordCrosshairYPos(def, erase)
|
|
CellDef *def;
|
|
bool erase; /* TRUE means crossair is being erased from its
|
|
* current position. FALSE means the crosshair
|
|
* is being added at a new position.
|
|
*/
|
|
{
|
|
Rect xwire;
|
|
|
|
xwire.r_xbot = MINFINITY;
|
|
xwire.r_xtop = INFINITY;
|
|
xwire.r_ybot = xwire.r_ytop = curCrosshair.pos.p_y;
|
|
DBWHLRedraw(def, &xwire, erase);
|
|
}
|
|
|
|
void
|
|
dbwRecordCrosshairXPos(def, erase)
|
|
CellDef *def;
|
|
bool erase; /* TRUE means crossair is being erased from its
|
|
* current position. FALSE means the crosshair
|
|
* is being added at a new position.
|
|
*/
|
|
{
|
|
Rect xwire;
|
|
|
|
xwire.r_ybot = MINFINITY;
|
|
xwire.r_ytop = INFINITY;
|
|
xwire.r_xbot = xwire.r_xtop = curCrosshair.pos.p_x;
|
|
DBWHLRedraw(def, &xwire, erase);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* DBWDrawCrosshair --
|
|
*
|
|
* This procedure will redraw the crosshair in a given window, if
|
|
* the crosshair is to appear in that window. It is called only by
|
|
* the highlight code. The caller must lock the window.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* The crosshair is redrawn, unless it isn't supposed to appear.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
DBWDrawCrosshair(window, plane)
|
|
MagWindow *window; /* Window in which to redraw box. */
|
|
Plane *plane; /* Non-space tiles on this plane indicate
|
|
* which highlight areas must be redrawn.
|
|
*/
|
|
{
|
|
Point p;
|
|
|
|
/* Unlike most highlights, which are related to the database and */
|
|
/* therefore drawn on all windows, the crosshair only exists in */
|
|
/* the cell where the pointer is active, and in any other window */
|
|
/* with the same root cell. It is therefore necessary to check */
|
|
/* if the root def of the window matches the root def of the window */
|
|
/* with active focus. Otherwise, translating the crosshair */
|
|
/* position to window coordinates makes no sense. */
|
|
|
|
if (WINDOW_DEF(window) != curCrosshair.def) return;
|
|
|
|
WindPointToScreen(window, &(curCrosshair.pos), &p);
|
|
|
|
GrSetStuff(STYLE_YELLOW1);
|
|
if (p.p_x > window->w_screenArea.r_xbot &&
|
|
p.p_x < window->w_screenArea.r_xtop)
|
|
GrClipLine(p.p_x, window->w_screenArea.r_ybot,
|
|
p.p_x, window->w_screenArea.r_ytop);
|
|
|
|
if (p.p_y > window->w_screenArea.r_ybot &&
|
|
p.p_y < window->w_screenArea.r_ytop)
|
|
GrClipLine(window->w_screenArea.r_xbot, p.p_y,
|
|
window->w_screenArea.r_xtop, p.p_y);
|
|
}
|
|
|
|
/* DBWScaleCrosshair --- Simple scaling of the crosshair point, no update needed */
|
|
|
|
void
|
|
DBWScaleCrosshair(scalen, scaled)
|
|
int scalen;
|
|
int scaled;
|
|
{
|
|
DBScalePoint(&(curCrosshair.pos), scalen, scaled);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* DBWSetCrosshair --
|
|
*
|
|
* Change the location of the crosshair.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Information is recorded so that the crosshair will be redrawn.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
DBWSetCrosshair(window, pos)
|
|
MagWindow *window;
|
|
Point *pos; /* New crosshair location in coords of rootDef. */
|
|
{
|
|
bool needUpdate = FALSE;
|
|
|
|
if (WINDOW_DEF(window) != curCrosshair.def) needUpdate = TRUE;
|
|
|
|
/* Erase */
|
|
|
|
if (needUpdate || (curCrosshair.pos.p_x != pos->p_x))
|
|
{
|
|
/* Record the old and area of the vertical line for redisplay. */
|
|
dbwRecordCrosshairXPos(curCrosshair.def, TRUE);
|
|
}
|
|
|
|
/* Do the same thing for the horizontal crosshair line */
|
|
if (needUpdate || (curCrosshair.pos.p_y != pos->p_y))
|
|
{
|
|
dbwRecordCrosshairYPos(curCrosshair.def, TRUE);
|
|
}
|
|
|
|
/* Record the CellDef that this position is referenced to. */
|
|
if (needUpdate) curCrosshair.def = WINDOW_DEF(window);
|
|
|
|
/* Draw */
|
|
|
|
if (curCrosshair.pos.p_x != pos->p_x)
|
|
{
|
|
/* Update the crosshair location. */
|
|
curCrosshair.pos.p_x = pos->p_x;
|
|
|
|
/* Record the new area for redisplay. */
|
|
dbwRecordCrosshairXPos(curCrosshair.def, FALSE);
|
|
}
|
|
|
|
/* Do the same thing for the horizontal crosshair line */
|
|
if (curCrosshair.pos.p_y != pos->p_y)
|
|
{
|
|
curCrosshair.pos.p_y = pos->p_y;
|
|
dbwRecordCrosshairYPos(curCrosshair.def, FALSE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* dbwRecordBoxArea --
|
|
*
|
|
* This procedure tells the highlight manager that the box's current
|
|
* area needs to be redisplayed. It contains a special optimization
|
|
* for when the box is big: record four separate areas, one along
|
|
* each side, instead of one huge area. This means stuff inside
|
|
* the box doesn't have to be redisplayed.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Highlight redisplay information is logged.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
dbwRecordBoxArea(erase)
|
|
bool erase; /* TRUE means box is being erased from its
|
|
* current area. FALSE means box is being
|
|
* added to a new area.
|
|
*/
|
|
{
|
|
Rect side;
|
|
|
|
if (((boxRootArea.r_xtop - boxRootArea.r_xbot) < 20)
|
|
|| ((boxRootArea.r_ytop - boxRootArea.r_ybot) < 20))
|
|
{
|
|
DBWHLRedraw(boxRootDef, &boxRootArea, erase);
|
|
}
|
|
else
|
|
{
|
|
side = boxRootArea;
|
|
side.r_xtop = side.r_xbot + 1;
|
|
DBWHLRedraw(boxRootDef, &side, erase);
|
|
side = boxRootArea;
|
|
side.r_ytop = side.r_ybot + 1;
|
|
DBWHLRedraw(boxRootDef, &side, erase);
|
|
side = boxRootArea;
|
|
side.r_xbot = side.r_xtop - 1;
|
|
DBWHLRedraw(boxRootDef, &side, erase);
|
|
side = boxRootArea;
|
|
side.r_ybot = side.r_ytop - 1;
|
|
DBWHLRedraw(boxRootDef, &side, erase);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* DBWDrawBox --
|
|
*
|
|
* This procedure will redraw the box in a given window, if the
|
|
* box is to appear in that window. It is called only by the
|
|
* highlight code. The caller must lock the window.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* The box is redrawn, unless it isn't supposed to appear.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
DBWDrawBox(window, plane)
|
|
MagWindow *window; /* Window in which to redraw box. */
|
|
Plane *plane; /* Non-space tiles on this plane indicate
|
|
* which highlight areas must be redrawn.
|
|
*/
|
|
{
|
|
Rect screenArea;
|
|
Rect side;
|
|
int boxStyle = STYLE_SOLIDHIGHLIGHTS;
|
|
extern int dbwBoxAlways1(); /* Forward reference. */
|
|
|
|
/* Return if the window is not compatible with the box location
|
|
* or if the box's area doesn't need to be redrawn.
|
|
*/
|
|
|
|
if (boxRootDef != WINDOW_DEF(window)) return;
|
|
if (!DBSrPaintArea((Tile *) NULL, plane, &boxRootArea,
|
|
&DBAllButSpaceBits, dbwBoxAlways1, (ClientData) NULL))
|
|
return;
|
|
|
|
/* Get the box coordinates in the coordinate system of the */
|
|
/* edit cell. If the box is outside of the edit cell, then */
|
|
/* we draw it with style "medium_highlights" instead of */
|
|
/* "solid_highlights". */
|
|
|
|
if (EditRootDef == boxRootDef)
|
|
{
|
|
Rect editbox;
|
|
GeoTransRect(&RootToEditTransform, &boxRootArea, &editbox);
|
|
if (!GEO_OVERLAP(&editbox, &EditCellUse->cu_def->cd_bbox))
|
|
boxStyle = STYLE_MEDIUMHIGHLIGHTS;
|
|
}
|
|
|
|
/* Transform the box into screen coordinates, then draw it. If the
|
|
* box is a point, draw it as a cross (GrFastBox does this automatically)
|
|
* and add a slightly solid center. Otherwise, draw the box several
|
|
* pixels wide so it will stand out from other highlights.
|
|
*/
|
|
|
|
WindSurfaceToScreen(window, &boxRootArea, &screenArea);
|
|
|
|
if ((screenArea.r_xbot == screenArea.r_xtop) &&
|
|
(screenArea.r_ybot == screenArea.r_ytop))
|
|
{
|
|
GrSetStuff(STYLE_OUTLINEHIGHLIGHTS);
|
|
GrFastBox(&screenArea);
|
|
GEO_EXPAND(&screenArea, 1, &screenArea);
|
|
GrFastBox(&screenArea);
|
|
return;
|
|
}
|
|
|
|
/* One more optimization here: if the box is not flattened to
|
|
* a line, but is so skinny that widening it will make it a solid
|
|
* blob, then don't do the widening. This is to make the box more
|
|
* useable when features are very small.
|
|
*/
|
|
|
|
if (((screenArea.r_xtop != screenArea.r_xbot) &&
|
|
(screenArea.r_xtop < screenArea.r_xbot + 4))
|
|
|| ((screenArea.r_ytop != screenArea.r_ybot) &&
|
|
(screenArea.r_ytop < screenArea.r_ybot + 4)))
|
|
{
|
|
GrClipBox(&screenArea, STYLE_OUTLINEHIGHLIGHTS);
|
|
return;
|
|
}
|
|
|
|
GrSetStuff(boxStyle);
|
|
if ((screenArea.r_xbot >= window->w_screenArea.r_xbot) &&
|
|
(screenArea.r_xbot <= window->w_screenArea.r_xtop))
|
|
{
|
|
side = screenArea;
|
|
side.r_xtop = side.r_xbot + 2 - GrPixelCorrect;
|
|
if (side.r_ytop != side.r_ybot) {
|
|
GrFastBox(&side);
|
|
}
|
|
}
|
|
if ((screenArea.r_ybot >= window->w_screenArea.r_ybot) &&
|
|
(screenArea.r_ybot <= window->w_screenArea.r_ytop))
|
|
{
|
|
side = screenArea;
|
|
side.r_ytop = side.r_ybot + 1;
|
|
if (!GrPixelCorrect) side.r_ybot--;
|
|
if (side.r_xtop != side.r_xbot) {
|
|
GrFastBox(&side);
|
|
}
|
|
}
|
|
if ((screenArea.r_xtop >= window->w_screenArea.r_xbot) &&
|
|
(screenArea.r_xtop <= window->w_screenArea.r_xtop))
|
|
{
|
|
side = screenArea;
|
|
side.r_xbot = side.r_xtop - 1;
|
|
if (!GrPixelCorrect) side.r_xtop++;
|
|
if (side.r_ytop != side.r_ybot) {
|
|
GrFastBox(&side);
|
|
}
|
|
}
|
|
if ((screenArea.r_ytop >= window->w_screenArea.r_ybot) &&
|
|
(screenArea.r_ytop <= window->w_screenArea.r_ytop))
|
|
{
|
|
side = screenArea;
|
|
side.r_ybot = side.r_ytop - 2 + GrPixelCorrect;
|
|
if (side.r_xtop != side.r_xbot) {
|
|
GrFastBox(&side);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
dbwBoxAlways1()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* DBWSetBox --
|
|
*
|
|
* Change the location and/or size of the box.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Information is recorded so that the box will be redrawn.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
DBWSetBox(rootDef, rect)
|
|
CellDef *rootDef; /* Root definition in whose coordinate system
|
|
* the box is defined. It will appear in all
|
|
* windows with this as root cell.
|
|
*/
|
|
Rect *rect; /* New box location in coords of rootDef. */
|
|
{
|
|
/* Record the old and area of the box for redisplay. */
|
|
|
|
dbwRecordBoxArea(TRUE);
|
|
|
|
/* Save information for undo-ing. */
|
|
|
|
DBWUndoBox(boxRootDef, &boxRootArea, rootDef, rect);
|
|
|
|
/* Update the box location. */
|
|
|
|
boxRootDef = rootDef;
|
|
boxRootArea = *rect;
|
|
|
|
/* Record the new area for redisplay. */
|
|
|
|
dbwRecordBoxArea(FALSE);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* DBWResetBox() ---
|
|
*
|
|
* Make sure that boxRootDef is set to NULL if it is equal to the
|
|
* specified CellDef. This is used by the cell delete function to
|
|
* make sure that if an edit cell is deleted, the boxRootDef is not
|
|
* pointing to an invalid area of memory.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Global variable boxRootDef may be set to NULL.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
DBWResetBox(CellDef *def)
|
|
{
|
|
if (def == boxRootDef)
|
|
boxRootDef = NULL;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* ToolMoveBox --
|
|
*
|
|
* Repositions the box by one of its corners.
|
|
* If the point given to reposition the box is in screen coordinates,
|
|
* the box corner is snapped to the user's grid (set with the :grid
|
|
* command) if DBWSnapToGrid is DBW_SNAP_USER. If DBWSnapToGrid is
|
|
* DBW_SNAP_LAMBDA, the box corner is snapped to the nearest integer
|
|
* lambda value.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* The box is repositioned so that the given corner is at the
|
|
* given screen or root cell position. If the point is given
|
|
* in screen coordinates, it must fall within the active area
|
|
* of a window with a non-NULL root cell use. An error message
|
|
* is output if this can't be done. The size of the box isn't
|
|
* changed. The box is marked for redisplay, but isn't actually
|
|
* redisplayed on the screen.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ToolMoveBox(corner, point, screenCoords, rootDef)
|
|
int corner; /* Specifies a corner in the format
|
|
* returned by ToolGetCorner.
|
|
*/
|
|
Point *point; /* New position of corner, in screen
|
|
* coordinates.
|
|
*/
|
|
int screenCoords; /* TRUE means point is in screen coordinates,
|
|
* FALSE means root cell coordinates.
|
|
*/
|
|
CellDef *rootDef; /* Used only when screenCoords = FALSE, to
|
|
* give root cell def.
|
|
*/
|
|
{
|
|
Point p;
|
|
MagWindow *w;
|
|
int x, y;
|
|
CellDef *newDef;
|
|
Rect newArea;
|
|
|
|
/* Find the current point location. */
|
|
|
|
if (screenCoords)
|
|
{
|
|
w = toolFindPoint(point, &p, (Rect *) NULL);
|
|
if ((w == NULL) || (w->w_client != DBWclientID))
|
|
{
|
|
TxError("Can't put box there.\n");
|
|
return;
|
|
}
|
|
newDef = WINDOW_DEF(w);
|
|
}
|
|
else
|
|
{
|
|
p = *point;
|
|
newDef = rootDef;
|
|
}
|
|
|
|
/* Move the box. If an illegal corner is specified, then
|
|
* move by the bottom-left corner.
|
|
*/
|
|
|
|
switch (corner)
|
|
{
|
|
case TOOL_BL:
|
|
x = p.p_x - boxRootArea.r_xbot;
|
|
y = p.p_y - boxRootArea.r_ybot;
|
|
break;
|
|
case TOOL_BR:
|
|
x = p.p_x - boxRootArea.r_xtop;
|
|
y = p.p_y - boxRootArea.r_ybot;
|
|
break;
|
|
case TOOL_TR:
|
|
x = p.p_x - boxRootArea.r_xtop;
|
|
y = p.p_y - boxRootArea.r_ytop;
|
|
break;
|
|
case TOOL_TL:
|
|
x = p.p_x - boxRootArea.r_xbot;
|
|
y = p.p_y - boxRootArea.r_ytop;
|
|
break;
|
|
default:
|
|
x = p.p_x - boxRootArea.r_xbot;
|
|
y = p.p_y - boxRootArea.r_ybot;
|
|
break;
|
|
}
|
|
newArea = boxRootArea;
|
|
newArea.r_xbot += x;
|
|
newArea.r_ybot += y;
|
|
newArea.r_xtop += x;
|
|
newArea.r_ytop += y;
|
|
|
|
DBWSetBox(newDef, &newArea);
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* ToolMoveCorner --
|
|
*
|
|
* This procedure moves one corner of the box, leaving the other
|
|
* corners as fixed as possible. Invoked by low-level screen
|
|
* handler.
|
|
*
|
|
* If the point given to reposition the box is in screen coordinates,
|
|
* the box corner is snapped to the user's grid (set with the :grid
|
|
* command) if DBWSnapToGrid is DBW_SNAP_USER. If DBWSnapToGrid is
|
|
* DBW_SNAP_LAMBDA, the box corner is snapped to the nearest integer
|
|
* lambda value.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* The box is modified so that the given corner is at the given
|
|
* point. As with ToolMoveBox, the point may either be given as
|
|
* a screen location, or as a location in the coordinates of a
|
|
* root cell. If the new corner location is indicated in a window
|
|
* that isn't compatible with the current box window, then the
|
|
* whole box is moved. Otherwise, the corner diagonally opposite
|
|
* the given corner isn't moved at all.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ToolMoveCorner(corner, point, screenCoords, rootDef)
|
|
int corner; /* The corner to be moved, in format
|
|
* returned by ToolGetCorner.
|
|
*/
|
|
Point *point; /* Destination of corner. */
|
|
int screenCoords; /* TRUE means point is in screen coordinates,
|
|
* we look up window and translate to root
|
|
* cell coordinates. FALSE means point is in
|
|
* coordinates of rootDef.
|
|
*/
|
|
CellDef *rootDef; /* Root cell Def if screenCoords = FALSE,
|
|
* unused otherwise.
|
|
*/
|
|
{
|
|
Point p;
|
|
MagWindow *w;
|
|
CellDef *oldDef, *newDef;
|
|
int tmp;
|
|
Rect newArea;
|
|
|
|
/* Find the current point location. */
|
|
|
|
oldDef = boxRootDef;
|
|
if (screenCoords)
|
|
{
|
|
w = toolFindPoint(point, &p, (Rect *) NULL);
|
|
if ((w == NULL) || (w->w_client != DBWclientID))
|
|
{
|
|
TxError("Can't put box there.\n");
|
|
return;
|
|
}
|
|
newDef = WINDOW_DEF(w);
|
|
}
|
|
else
|
|
{
|
|
p = *point;
|
|
newDef = rootDef;
|
|
}
|
|
|
|
/* If the root def for the moved corner isn't the same as the
|
|
* current box root def, then just move the whole durned box.
|
|
* Also move the whole box if a weird corner is specified.
|
|
*/
|
|
|
|
if ((newDef != oldDef) || (corner < 0) || (corner > TOOL_TL))
|
|
{
|
|
ToolMoveBox(corner, &p, FALSE, newDef);
|
|
return;
|
|
}
|
|
|
|
/* Move the requested corner. */
|
|
|
|
newArea = boxRootArea;
|
|
switch (corner)
|
|
{
|
|
case TOOL_BL:
|
|
newArea.r_xbot = p.p_x;
|
|
newArea.r_ybot = p.p_y;
|
|
break;
|
|
case TOOL_BR:
|
|
newArea.r_xtop = p.p_x;
|
|
newArea.r_ybot = p.p_y;
|
|
break;
|
|
case TOOL_TR:
|
|
newArea.r_xtop = p.p_x;
|
|
newArea.r_ytop = p.p_y;
|
|
break;
|
|
case TOOL_TL:
|
|
newArea.r_xbot = p.p_x;
|
|
newArea.r_ytop = p.p_y;
|
|
break;
|
|
}
|
|
|
|
/* If the movement turned the box inside out, turn it right
|
|
* side out again.
|
|
*/
|
|
|
|
if (newArea.r_xbot > newArea.r_xtop)
|
|
{
|
|
tmp = newArea.r_xtop;
|
|
newArea.r_xtop = newArea.r_xbot;
|
|
newArea.r_xbot = tmp;
|
|
}
|
|
if (newArea.r_ybot > newArea.r_ytop)
|
|
{
|
|
tmp = newArea.r_ytop;
|
|
newArea.r_ytop = newArea.r_ybot;
|
|
newArea.r_ybot = tmp;
|
|
}
|
|
|
|
DBWSetBox(newDef, &newArea);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* ToolSnapToGrid --
|
|
*
|
|
* Snap the point *p (in root cell coordinates) to the nearest
|
|
* point in the user-defined grid or the nearest integer lambda value,
|
|
* according to the setting of DBWSnapToGrid. Also translates the rectangle
|
|
* *rEnclose by the same amount by which the point *p was snapped.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Modifies *p.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
ToolSnapToGrid(w, p, rEnclose)
|
|
MagWindow *w;
|
|
Point *p;
|
|
Rect *rEnclose;
|
|
{
|
|
DBWclientRec *crec = (DBWclientRec *) w->w_clientData;
|
|
Rect *r;
|
|
Rect lr;
|
|
int xd, yd, xlo, xhi, ylo, yhi, xtmp, ytmp;
|
|
|
|
if (crec == NULL || p == NULL)
|
|
return;
|
|
|
|
if (DBWSnapToGrid == DBW_SNAP_LAMBDA)
|
|
{
|
|
lr.r_xbot = lr.r_ybot = 0;
|
|
lr.r_xtop = DBLambda[1] / DBLambda[0];
|
|
if (lr.r_xtop < 1) lr.r_xtop = 1;
|
|
lr.r_ytop = lr.r_xtop;
|
|
r = &lr;
|
|
}
|
|
else
|
|
r = &crec->dbw_gridRect;
|
|
|
|
xd = r->r_xtop - r->r_xbot;
|
|
yd = r->r_ytop - r->r_ybot;
|
|
|
|
/*
|
|
* The following is tricky because we want to ensure we bracket
|
|
* the point p.
|
|
*/
|
|
xtmp = p->p_x - r->r_xbot;
|
|
if (xtmp < 0)
|
|
{
|
|
xhi = xd * (xtmp / xd) + r->r_xbot;
|
|
xlo = xhi - xd;
|
|
}
|
|
else
|
|
{
|
|
xlo = xd * (xtmp / xd) + r->r_xbot;
|
|
xhi = xlo + xd;
|
|
}
|
|
|
|
ytmp = p->p_y - r->r_ybot;
|
|
if (ytmp < 0)
|
|
{
|
|
yhi = yd * (ytmp / yd) + r->r_ybot;
|
|
ylo = yhi - yd;
|
|
}
|
|
else
|
|
{
|
|
ylo = yd * (ytmp / yd) + r->r_ybot;
|
|
yhi = ylo + yd;
|
|
}
|
|
|
|
xtmp = (ABSDIFF(p->p_x, xlo) < ABSDIFF(p->p_x, xhi)) ? xlo : xhi;
|
|
ytmp = (ABSDIFF(p->p_y, ylo) < ABSDIFF(p->p_y, yhi)) ? ylo : yhi;
|
|
|
|
if (rEnclose)
|
|
{
|
|
rEnclose->r_xbot += xtmp - p->p_x;
|
|
rEnclose->r_ybot += ytmp - p->p_y;
|
|
rEnclose->r_xtop += xtmp - p->p_x;
|
|
rEnclose->r_ytop += ytmp - p->p_y;
|
|
}
|
|
p->p_x = xtmp;
|
|
p->p_y = ytmp;
|
|
}
|