magic/dbwind/DBWtools.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;
}