1 /*
2  * DBWtools.c --
3  *
4  * Implements tools for Magic.  Two tools are
5  * provided:  a box and a point.  This module is a sort of
6  * broker between the low-level display routines and the
7  * high-level command routines.  It provides two kinds of
8  * routines:  those invoked by the screen handler to change
9  * the tool location, and those invoked by the command interpreter
10  * to get information about the current tool location and relocate
11  * the box.
12  *
13  *     *********************************************************************
14  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
15  *     * Permission to use, copy, modify, and distribute this              *
16  *     * software and its documentation for any purpose and without        *
17  *     * fee is hereby granted, provided that the above copyright          *
18  *     * notice appear in all copies.  The University of California        *
19  *     * makes no representations about the suitability of this            *
20  *     * software for any purpose.  It is provided "as is" without         *
21  *     * express or implied warranty.  Export of this software outside     *
22  *     * of the United States of America may require an export license.    *
23  *     *********************************************************************
24  */
25 
26 #ifndef lint
27 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 $";
28 #endif  /* not lint */
29 
30 #include <stdio.h>
31 #include "utils/magic.h"
32 #include "utils/geometry.h"
33 #include "utils/styles.h"
34 #include "tiles/tile.h"
35 #include "utils/hash.h"
36 #include "database/database.h"
37 #include "windows/windows.h"
38 #include "graphics/graphics.h"
39 #include "dbwind/dbwind.h"
40 #include "textio/textio.h"
41 #include "utils/main.h"
42 #include "textio/txcommands.h"
43 
44 #define WINDOW_DEF(w)	(((CellUse *)(w->w_surfaceID))->cu_def)
45 
46 /* The following rectangle defines the current box location.
47  * The box is defined in terms of a particular root cell def
48  * and an area within that def.  Note:  there is a notion
49  * of "compatibility" between windows, as far as tools are
50  * concerned.  Two windows are compatible if they have the same
51  * root cell def.  If the box is placed in one window, it will
52  * be displayed in all compatible windows.  The box may not have
53  * one corner placed in one window and other corners in
54  * incompatible windows.
55  */
56 
57 static CellDef *boxRootDef = NULL;	/* CellDef for the box */
58 static Rect boxRootArea;		/* Root def coords */
59 
60 typedef struct _crosshairRec {
61     Point    pos;	/* Crosshair position */
62     CellDef *def;	/* CellDef that crosshair position references */
63 } CrosshairRec;
64 
65 static CrosshairRec curCrosshair;		/* Crosshair position */
66 
67 /*
68  * If the following is DBW_SNAP_USER, the box gets snapped to the user's
69  * grid always, instead of snapping to the usual 1x1 grid.  If the value
70  * is DBW_SNAP_INTERNAL, the box gets snapped to the internal grid.
71  */
72 int DBWSnapToGrid = DBW_SNAP_LAMBDA;
73 
74 /* Forward reference: */
75 
76 extern int DBWToolDraw();
77 
78 
79 /*
80  * ----------------------------------------------------------------------------
81  *
82  *	toolFindPoint --
83  *
84  * 	Returns the point in root coordinates.
85  *	If DBWSnapToGrid is DBW_SNAP_USER, pick the nearest point that is
86  *	aligned with the window's grid.  If DBWSnapToGrid is DBW_SNAP_LAMBDA,
87  *	pick the nearest point that is an integer lambda value.
88  *
89  * Results:
90  *	The return value is a pointer to the window containing the
91  *	point, or NULL if no window contains the point.
92  *
93  * Side effects:
94  *	The parameters rootPoint and rootArea are modified
95  *	to contain information about the point's location.  RootPoint
96  *	is the nearest lambda grid point to the point's actual
97  *	location, while rootArea is a one-lambda-square box surrounding
98  *	the point.  If rootPoint or rootArea is NULL, then that structure
99  *	isn't filled in.
100  * ----------------------------------------------------------------------------
101  */
102 
103 MagWindow *
toolFindPoint(p,rootPoint,rootArea)104 toolFindPoint(p, rootPoint, rootArea)
105     Point *p;			/* The point to find, in the current window. */
106     Point *rootPoint;		/* Modified to contain coordinates of point
107 				 * in root cell coordinates.  Is unchanged
108 				 * if NULL is returned.
109 				 */
110     Rect *rootArea;		/* Modified to contain box around point.  Is
111 				 * unchanged when NULL is returned.
112 				 */
113 {
114     extern MagWindow *WindCurrentWindow;
115 
116     if (WindCurrentWindow == NULL) return NULL;
117 
118     if (WindCurrentWindow->w_client != DBWclientID) return NULL;
119 
120     if (!GEO_ENCLOSE(p, &WindCurrentWindow->w_screenArea)) return NULL;
121 
122     WindPointToSurface(WindCurrentWindow, p, rootPoint, rootArea);
123     if (DBWSnapToGrid != DBW_SNAP_INTERNAL)
124 	ToolSnapToGrid(WindCurrentWindow, rootPoint, rootArea);
125     return WindCurrentWindow;
126 
127 }
128 
129 
130 /*
131  * ----------------------------------------------------------------------------
132  *	ToolGetPoint --
133  *
134  * 	Returns information about the point.  Used by command processors.
135  *
136  * Results:
137  *	The return value is a pointer to the window containing the
138  *	point, or NULL if no window contains the point.
139  *
140  * Side effects:
141  *	The parameters rootPoint and rootArea are modified
142  *	to contain information about the point's location.  RootPoint
143  *	is the nearest lambda grid point to the point's actual
144  *	location, while rootArea is a one-lambda-square box surrounding
145  *	the point.  If rootPoint or rootArea is NULL, then that structure
146  *	isn't filled in.
147  * ----------------------------------------------------------------------------
148  */
149 
150 MagWindow *
ToolGetPoint(rootPoint,rootArea)151 ToolGetPoint(rootPoint, rootArea)
152     Point *rootPoint;		/* Modified to contain coordinates of point
153 				 * in root cell coordinates.  Is unchanged
154 				 * if NULL is returned.
155 				 */
156     Rect *rootArea;		/* Modified to contain box around point.  Is
157 				 * unchanged when NULL is returned.
158 				 */
159 {
160     extern TxCommand *WindCurrentCmd;
161 
162     if (WindCurrentCmd == NULL)
163 	return NULL;
164     else
165 	return toolFindPoint(&WindCurrentCmd->tx_p, rootPoint, rootArea);
166 }
167 
168 
169 /*
170  * ----------------------------------------------------------------------------
171  * ToolGetBox --
172  *
173  *	Returns the box CellDef and location in CellDef coords.
174  *
175  * Results:
176  *	TRUE if the box exists.
177  *
178  * Side effects:
179  *	The rootArea parameter is modified to contain the area
180  *	of the box.  If rootArea is NULL, it is ignored.
181  *	Same with rootDef.
182  * ----------------------------------------------------------------------------
183  */
184 
185 bool
ToolGetBox(rootDef,rootArea)186 ToolGetBox(rootDef, rootArea)
187     CellDef **rootDef;		/* Filled in with the root def of the box */
188     Rect *rootArea;		/* Filled in with area of box.  Will be
189 				 * unchanged when NULL is returned.
190 				 */
191 {
192     if (boxRootDef == NULL) return FALSE;
193     if (rootDef != NULL)
194 	*rootDef = boxRootDef;
195     if (rootArea != NULL)
196 	*rootArea = boxRootArea;
197     return TRUE;
198 }
199 
200 /* ToolScaleBox ---  Simple scaling of the root area box, no update needed */
201 
202 void
ToolScaleBox(scalen,scaled)203 ToolScaleBox(scalen, scaled)
204     int scalen;
205     int scaled;
206 {
207     DBScalePoint(&boxRootArea.r_ll, scalen, scaled);
208     DBScalePoint(&boxRootArea.r_ur, scalen, scaled);
209 
210 }
211 
212 
213 /*
214  * ----------------------------------------------------------------------------
215  * ToolGetBoxWindow --
216  *
217  * 	Returns information about the current box location.  Used by
218  *	command processing routines.
219  *
220  * Results:
221  *	The return value is a pointer to a window containing the
222  *	box, or NULL if the box doesn't exist in any window.  Note:
223  *	the box may actually be in more than one window, so this
224  *	isn't necessarily the only window containing the box.
225  *
226  * Side effects:
227  *	The rootArea parameter is modified to contain the area
228  *	of the box.  If rootArea is NULL, it is ignored.  The
229  *	integer pointed to by pMask is modified to contain a
230  *	mask of all windows containing the box (there may be more
231  *	than one).  If pMask is NULL, it is ignored.
232  * ----------------------------------------------------------------------------
233  */
234 
235 static int toolMask;		/* Shared between these two routines. */
236 
237 MagWindow *
ToolGetBoxWindow(rootArea,pMask)238 ToolGetBoxWindow(rootArea, pMask)
239     Rect *rootArea;		/* Filled in with area of box.  Will be
240 				 * unchanged when NULL is returned.
241 				 */
242     int *pMask;			/* Filled in with mask of all windows
243 				 * containing box.
244 				 */
245 {
246     MagWindow *window;
247     extern int toolWindowSave();
248 
249     /* Search through the windows and remember a window that has
250      * the right root cell.  It's important to NOT search on the
251      * area of the box (i.e. take any window with the box's root
252      * definition, even if the box isn't visible in the window).
253      * Otherwise, some commands won't work when the box goes
254      * off-screen.  Also accumulate the mask bits.
255      */
256 
257     toolMask = 0;
258     window = NULL;
259     if (boxRootDef != NULL)
260 	(void) WindSearch(DBWclientID, (ClientData) NULL, (Rect *) NULL,
261 	    toolWindowSave, (ClientData) &window);
262     if ((window != NULL) && (rootArea != NULL)) *rootArea = boxRootArea;
263     if (pMask != NULL) *pMask = toolMask;
264     return window;
265 }
266 
267 int
toolWindowSave(window,clientData)268 toolWindowSave(window, clientData)
269     MagWindow *window;		/* Window that matched in some search. */
270     ClientData clientData;	/* Contains the address of a location
271 				 * to be filled in with the window address.
272 				 */
273 {
274     if (WINDOW_DEF(window) == boxRootDef)
275     {
276 	*((MagWindow **) clientData) = window;
277         toolMask |= ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
278     }
279     return 0;
280 }
281 
282 /*
283  * ----------------------------------------------------------------------------
284  *
285  * ToolGetEditBox --
286  *
287  *	Fill in the location of the box in edit cell coordinates.
288  *
289  * Results:
290  *	TRUE if the box can indeed be put into edit cell coordinates.
291  *	FALSE and an error message otherwise.
292  *
293  * Side effects:
294  *	Sets *rect to be the coordinates of the box tool in edit cell
295  *	coordinates, if TRUE was returned.
296  *
297  *	Prints an error message if the box is not found or the box
298  *	is not in the edit cell coordinate system.
299  *
300  * ----------------------------------------------------------------------------
301  */
302 
303 bool
ToolGetEditBox(rect)304 ToolGetEditBox(rect)
305     Rect *rect;
306 {
307     CellDef *editDef;
308 
309     if (boxRootDef == NULL)
310     {
311 	TxError("Box must be present\n");
312 	return FALSE;
313     }
314 
315     /* Added 8/4/2021---Don't require that EditRootDef be non-NULL. */
316 
317     if (EditRootDef == NULL)
318     {
319 	MagWindow *w;
320 	w = ToolGetBoxWindow(rect, NULL);
321 
322 	windCheckOnlyWindow(&w, DBWclientID);
323 	if (w == (MagWindow *) NULL)
324 	    editDef = EditCellUse->cu_def;
325 	else
326 	    editDef = ((CellUse *) w->w_surfaceID)->cu_def;
327     }
328     else
329 	editDef = EditRootDef;
330 
331     if (editDef != boxRootDef)
332     {
333 	TxError("The box isn't in a window on the edit cell.\n");
334 	return FALSE;
335     }
336     if (rect != NULL)
337 	GeoTransRect(&RootToEditTransform, &boxRootArea, rect);
338     return TRUE;
339 }
340 
341 
342 /*
343  * ----------------------------------------------------------------------------
344  *	ToolGetCorner --
345  *
346  * 	Returns the corner of the box closest to a given screen location.
347  *
348  * Results:
349  *	An integer value is returned, indicating the corner closest to
350  *	the given screen location.  The point must be in a window
351  *	containing a root cell use, and the box must currently be in
352  *	that window, or in another window with the same root cell use.
353  *	If these conditions aren't satisfied, then the lower-left corner
354  *	is returned.  Note:  "closeness" is determined not by screen
355  *	closeness, but by closeness in root cell coordinates.
356  *
357  * Side effects:
358  *	None.
359  * ----------------------------------------------------------------------------
360  */
361 
362 int
ToolGetCorner(screenPoint)363 ToolGetCorner(screenPoint)
364     Point *screenPoint;
365 
366 {
367     Point p;
368     MagWindow *w;
369     Rect r;
370 
371     /* Make sure that the cursor is in a valid window. */
372 
373     w = toolFindPoint(screenPoint, &p, (Rect *) NULL);
374     if (w == NULL) return TOOL_BL;
375     if (WINDOW_DEF(w) != boxRootDef) return TOOL_BL;
376 
377     /* Find out which corner is closest.  Consider only the
378      * intersection of the box with the window (otherwise it
379      * may not be possible to select off-screen corners).
380      */
381 
382     WindSurfaceToScreen(w, &boxRootArea, &r);
383     GeoClip(&r, &w->w_screenArea);
384     if (screenPoint->p_x < ((r.r_xbot + r.r_xtop)/2))
385     {
386 	if (screenPoint->p_y < ((r.r_ybot + r.r_ytop)/2))
387 	    return TOOL_BL;
388 	else return TOOL_TL;
389     }
390     else
391     {
392 	if (screenPoint->p_y < ((r.r_ybot + r.r_ytop)/2))
393 	    return TOOL_BR;
394 	else return TOOL_TR;
395     }
396 }
397 
398 
399 /*
400  * ----------------------------------------------------------------------------
401  * dbwCrosshairInit --
402  *
403  *	Set the initial crosshair position to MINFINITY, where it will
404  *	not display until explicitly set by a command.
405  *
406  * Results:
407  *	None.
408  *
409  * Side effects:
410  *	Initializes static memory.
411  *
412  * ----------------------------------------------------------------------------
413  */
414 
415 void
dbwCrosshairInit()416 dbwCrosshairInit()
417 {
418     curCrosshair.pos.p_x = MINFINITY;
419     curCrosshair.pos.p_y = MINFINITY;
420     curCrosshair.def = (CellDef *)NULL;
421     DBWHLAddClient(DBWDrawCrosshair);
422 }
423 
424 /*
425  * ----------------------------------------------------------------------------
426  * dbwRecordCrosshair[X,Y]Pos --
427  *
428  * 	This procedure tells the highlight manager that the crosshair's
429  *	current position needs to be erased or redisplayed.
430  *
431  * Results:
432  *	None.
433  *
434  * Side effects:
435  *	Highlight redisplay information is logged.
436  * ----------------------------------------------------------------------------
437  */
438 
439 void
dbwRecordCrosshairYPos(def,erase)440 dbwRecordCrosshairYPos(def, erase)
441     CellDef *def;
442     bool erase;			/* TRUE means crossair is being erased from its
443 				 * current position.  FALSE means the crosshair
444 				 * is being added at a new position.
445 				 */
446 {
447     Rect xwire;
448 
449     xwire.r_xbot = MINFINITY;
450     xwire.r_xtop = INFINITY;
451     xwire.r_ybot = xwire.r_ytop = curCrosshair.pos.p_y;
452     DBWHLRedraw(def, &xwire, erase);
453 }
454 
455 void
dbwRecordCrosshairXPos(def,erase)456 dbwRecordCrosshairXPos(def, erase)
457     CellDef *def;
458     bool erase;			/* TRUE means crossair is being erased from its
459 				 * current position.  FALSE means the crosshair
460 				 * is being added at a new position.
461 				 */
462 {
463     Rect xwire;
464 
465     xwire.r_ybot = MINFINITY;
466     xwire.r_ytop = INFINITY;
467     xwire.r_xbot = xwire.r_xtop = curCrosshair.pos.p_x;
468     DBWHLRedraw(def, &xwire, erase);
469 }
470 
471 /*
472  * ----------------------------------------------------------------------------
473  *
474  * DBWDrawCrosshair --
475  *
476  * 	This procedure will redraw the crosshair in a given window, if
477  *	the crosshair is to appear in that window.  It is called only by
478  *	the highlight code.  The caller must lock the window.
479  *
480  * Results:
481  *	None.
482  *
483  * Side effects:
484  *	The crosshair is redrawn, unless it isn't supposed to appear.
485  *
486  * ----------------------------------------------------------------------------
487  */
488 
489 void
DBWDrawCrosshair(window,plane)490 DBWDrawCrosshair(window, plane)
491     MagWindow *window;		/* Window in which to redraw box. */
492     Plane *plane;		/* Non-space tiles on this plane indicate
493 				 * which highlight areas must be redrawn.
494 				 */
495 {
496     Point p;
497 
498     /* Unlike most highlights, which are related to the database and	*/
499     /* therefore drawn on all windows, the crosshair only exists in	*/
500     /* the cell where the pointer is active, and in any other window	*/
501     /* with the same root cell.  It is therefore necessary to check	*/
502     /* if the root def of the window matches the root def of the window	*/
503     /* with active focus.  Otherwise, translating the crosshair		*/
504     /* position to window coordinates makes no sense.			*/
505 
506     if (WINDOW_DEF(window) != curCrosshair.def) return;
507 
508     WindPointToScreen(window, &(curCrosshair.pos), &p);
509 
510     GrSetStuff(STYLE_YELLOW1);
511     if (p.p_x > window->w_screenArea.r_xbot &&
512 		p.p_x < window->w_screenArea.r_xtop)
513 	GrClipLine(p.p_x, window->w_screenArea.r_ybot,
514 		   p.p_x, window->w_screenArea.r_ytop);
515 
516     if (p.p_y > window->w_screenArea.r_ybot &&
517 		p.p_y < window->w_screenArea.r_ytop)
518 	GrClipLine(window->w_screenArea.r_xbot, p.p_y,
519 		   window->w_screenArea.r_xtop, p.p_y);
520 }
521 
522 /* DBWScaleCrosshair ---  Simple scaling of the crosshair point, no update needed */
523 
524 void
DBWScaleCrosshair(scalen,scaled)525 DBWScaleCrosshair(scalen, scaled)
526     int scalen;
527     int scaled;
528 {
529     DBScalePoint(&(curCrosshair.pos), scalen, scaled);
530 }
531 
532 /*
533  * ----------------------------------------------------------------------------
534  *	DBWSetCrosshair --
535  *
536  * 	Change the location of the crosshair.
537  *
538  * Results:
539  *	None.
540  *
541  * Side effects:
542  *	Information is recorded so that the crosshair will be redrawn.
543  * ----------------------------------------------------------------------------
544  */
545 
546 void
DBWSetCrosshair(window,pos)547 DBWSetCrosshair(window, pos)
548     MagWindow *window;
549     Point *pos;			/* New crosshair location in coords of rootDef. */
550 {
551     bool needUpdate = FALSE;
552 
553     if (WINDOW_DEF(window) != curCrosshair.def) needUpdate = TRUE;
554 
555     /* Erase */
556 
557     if (needUpdate || (curCrosshair.pos.p_x != pos->p_x))
558     {
559 	/* Record the old and area of the vertical line for redisplay. */
560 	dbwRecordCrosshairXPos(curCrosshair.def, TRUE);
561     }
562 
563     /* Do the same thing for the horizontal crosshair line */
564     if (needUpdate || (curCrosshair.pos.p_y != pos->p_y))
565     {
566 	dbwRecordCrosshairYPos(curCrosshair.def, TRUE);
567     }
568 
569     /* Record the CellDef that this position is referenced to. */
570     if (needUpdate) curCrosshair.def = WINDOW_DEF(window);
571 
572     /* Draw */
573 
574     if (curCrosshair.pos.p_x != pos->p_x)
575     {
576 	/* Update the crosshair location. */
577 	curCrosshair.pos.p_x = pos->p_x;
578 
579 	/* Record the new area for redisplay. */
580 	dbwRecordCrosshairXPos(curCrosshair.def, FALSE);
581     }
582 
583     /* Do the same thing for the horizontal crosshair line */
584     if (curCrosshair.pos.p_y != pos->p_y)
585     {
586 	curCrosshair.pos.p_y = pos->p_y;
587 	dbwRecordCrosshairYPos(curCrosshair.def, FALSE);
588     }
589 }
590 
591 /*
592  * ----------------------------------------------------------------------------
593  * dbwRecordBoxArea --
594  *
595  * 	This procedure tells the highlight manager that the box's current
596  *	area needs to be redisplayed.  It contains a special optimization
597  *	for when the box is big:  record four separate areas, one along
598  *	each side, instead of one huge area.  This means stuff inside
599  *	the box doesn't have to be redisplayed.
600  *
601  * Results:
602  *	None.
603  *
604  * Side effects:
605  *	Highlight redisplay information is logged.
606  * ----------------------------------------------------------------------------
607  */
608 
609 void
dbwRecordBoxArea(erase)610 dbwRecordBoxArea(erase)
611     bool erase;			/* TRUE means box is being erased from its
612 				 * current area.  FALSE means box is being
613 				 * added to a new area.
614 				 */
615 {
616     Rect side;
617 
618     if (((boxRootArea.r_xtop - boxRootArea.r_xbot) < 20)
619 	    || ((boxRootArea.r_ytop - boxRootArea.r_ybot) < 20))
620     {
621 	DBWHLRedraw(boxRootDef, &boxRootArea, erase);
622     }
623     else
624     {
625 	side = boxRootArea;
626 	side.r_xtop = side.r_xbot + 1;
627 	DBWHLRedraw(boxRootDef, &side, erase);
628 	side = boxRootArea;
629 	side.r_ytop = side.r_ybot + 1;
630 	DBWHLRedraw(boxRootDef, &side, erase);
631 	side = boxRootArea;
632 	side.r_xbot = side.r_xtop - 1;
633 	DBWHLRedraw(boxRootDef, &side, erase);
634 	side = boxRootArea;
635 	side.r_ybot = side.r_ytop - 1;
636 	DBWHLRedraw(boxRootDef, &side, erase);
637     }
638 }
639 
640 /*
641  * ----------------------------------------------------------------------------
642  *
643  * DBWDrawBox --
644  *
645  * 	This procedure will redraw the box in a given window, if the
646  *	box is to appear in that window.  It is called only by the
647  *	highlight code.  The caller must lock the window.
648  *
649  * Results:
650  *	None.
651  *
652  * Side effects:
653  *	The box is redrawn, unless it isn't supposed to appear.
654  *
655  * ----------------------------------------------------------------------------
656  */
657 
658 void
DBWDrawBox(window,plane)659 DBWDrawBox(window, plane)
660     MagWindow *window;		/* Window in which to redraw box. */
661     Plane *plane;		/* Non-space tiles on this plane indicate
662 				 * which highlight areas must be redrawn.
663 				 */
664 {
665     Rect screenArea;
666     Rect side;
667     int boxStyle = STYLE_SOLIDHIGHLIGHTS;
668     extern int dbwBoxAlways1();	/* Forward reference. */
669 
670     /* Return if the window is not compatible with the box location
671      * or if the box's area doesn't need to be redrawn.
672      */
673 
674     if (boxRootDef != WINDOW_DEF(window)) return;
675     if (!DBSrPaintArea((Tile *) NULL, plane, &boxRootArea,
676 	    &DBAllButSpaceBits, dbwBoxAlways1, (ClientData) NULL))
677 	return;
678 
679     /* Get the box coordinates in the coordinate system of the	*/
680     /* edit cell.  If the box is outside of the edit cell, then	*/
681     /* we draw it with style "medium_highlights" instead of	*/
682     /* "solid_highlights".					*/
683 
684     if (EditRootDef == boxRootDef)
685     {
686 	Rect editbox;
687         GeoTransRect(&RootToEditTransform, &boxRootArea, &editbox);
688 	if (!GEO_OVERLAP(&editbox, &EditCellUse->cu_def->cd_bbox))
689 	    boxStyle = STYLE_MEDIUMHIGHLIGHTS;
690     }
691 
692     /* Transform the box into screen coordinates, then draw it.  If the
693      * box is a point, draw it as a cross (GrFastBox does this automatically)
694      * and add a slightly solid center.  Otherwise, draw the box several
695      * pixels wide so it will stand out from other highlights.
696      */
697 
698     WindSurfaceToScreen(window, &boxRootArea, &screenArea);
699 
700     if ((screenArea.r_xbot == screenArea.r_xtop) &&
701 	    (screenArea.r_ybot == screenArea.r_ytop))
702     {
703 	GrSetStuff(STYLE_OUTLINEHIGHLIGHTS);
704 	GrFastBox(&screenArea);
705 	GEO_EXPAND(&screenArea, 1, &screenArea);
706 	GrFastBox(&screenArea);
707 	return;
708     }
709 
710     /* One more optimization here:  if the box is not flattened to
711      * a line, but is so skinny that widening it will make it a solid
712      * blob, then don't do the widening.  This is to make the box more
713      * useable when features are very small.
714      */
715 
716     if (((screenArea.r_xtop != screenArea.r_xbot) &&
717 	    (screenArea.r_xtop < screenArea.r_xbot + 4))
718 	    || ((screenArea.r_ytop != screenArea.r_ybot) &&
719 	    (screenArea.r_ytop < screenArea.r_ybot + 4)))
720     {
721 	GrClipBox(&screenArea, STYLE_OUTLINEHIGHLIGHTS);
722 	return;
723     }
724 
725     GrSetStuff(boxStyle);
726     if ((screenArea.r_xbot >= window->w_screenArea.r_xbot) &&
727 	(screenArea.r_xbot <= window->w_screenArea.r_xtop))
728     {
729 	side = screenArea;
730 	side.r_xtop = side.r_xbot + 2 - GrPixelCorrect;
731 	if (side.r_ytop != side.r_ybot) {
732 	    GrFastBox(&side);
733 	}
734     }
735     if ((screenArea.r_ybot >= window->w_screenArea.r_ybot) &&
736 	(screenArea.r_ybot <= window->w_screenArea.r_ytop))
737     {
738 	side = screenArea;
739 	side.r_ytop = side.r_ybot + 1;
740 	if (!GrPixelCorrect) side.r_ybot--;
741 	if (side.r_xtop != side.r_xbot) {
742 	    GrFastBox(&side);
743 	}
744     }
745     if ((screenArea.r_xtop >= window->w_screenArea.r_xbot) &&
746 	(screenArea.r_xtop <= window->w_screenArea.r_xtop))
747     {
748 	side = screenArea;
749 	side.r_xbot = side.r_xtop - 1;
750 	if (!GrPixelCorrect) side.r_xtop++;
751 	if (side.r_ytop != side.r_ybot) {
752 	    GrFastBox(&side);
753 	}
754     }
755     if ((screenArea.r_ytop >= window->w_screenArea.r_ybot) &&
756 	(screenArea.r_ytop <= window->w_screenArea.r_ytop))
757     {
758 	side = screenArea;
759 	side.r_ybot = side.r_ytop - 2 + GrPixelCorrect;
760 	if (side.r_xtop != side.r_xbot) {
761 	    GrFastBox(&side);
762 	}
763     }
764 }
765 
766 int
dbwBoxAlways1()767 dbwBoxAlways1()
768 {
769     return 1;
770 }
771 
772 /*
773  * ----------------------------------------------------------------------------
774  *	DBWSetBox --
775  *
776  * 	Change the location and/or size of the box.
777  *
778  * Results:
779  *	None.
780  *
781  * Side effects:
782  *	Information is recorded so that the box will be redrawn.
783  * ----------------------------------------------------------------------------
784  */
785 
786 void
DBWSetBox(rootDef,rect)787 DBWSetBox(rootDef, rect)
788     CellDef *rootDef;		/* Root definition in whose coordinate system
789 				 * the box is defined.  It will appear in all
790 				 * windows with this as root cell.
791 				 */
792     Rect *rect;			/* New box location in coords of rootDef. */
793 {
794     /* Record the old and area of the box for redisplay. */
795 
796     dbwRecordBoxArea(TRUE);
797 
798     /* Save information for undo-ing. */
799 
800     DBWUndoBox(boxRootDef, &boxRootArea, rootDef, rect);
801 
802     /* Update the box location. */
803 
804     boxRootDef = rootDef;
805     boxRootArea = *rect;
806 
807     /* Record the new area for redisplay. */
808 
809     dbwRecordBoxArea(FALSE);
810 }
811 
812 /*
813  * ----------------------------------------------------------------------------
814  * DBWResetBox() ---
815  *
816  *	Make sure that boxRootDef is set to NULL if it is equal to the
817  *	specified CellDef.  This is used by the cell delete function to
818  *	make sure that if an edit cell is deleted, the boxRootDef is not
819  *	pointing to an invalid area of memory.
820  *
821  * Results:
822  *	None.
823  *
824  * Side effects:
825  *	Global variable boxRootDef may be set to NULL.
826  *
827  * ----------------------------------------------------------------------------
828  */
829 
830 void
DBWResetBox(CellDef * def)831 DBWResetBox(CellDef *def)
832 {
833     if (def == boxRootDef)
834 	boxRootDef = NULL;
835 }
836 
837 /*
838  * ----------------------------------------------------------------------------
839  *	ToolMoveBox --
840  *
841  * 	Repositions the box by one of its corners.
842  *	If the point given to reposition the box is in screen coordinates,
843  *	the box corner is snapped to the user's grid (set with the :grid
844  *	command) if DBWSnapToGrid is DBW_SNAP_USER.  If DBWSnapToGrid is
845  *	DBW_SNAP_LAMBDA, the box corner is snapped to the nearest integer
846  *	lambda value.
847  *
848  * Results:
849  *	None.
850  *
851  * Side effects:
852  *	The box is repositioned so that the given corner is at the
853  *	given screen or root cell position.  If the point is given
854  *	in screen coordinates, it must fall within the active area
855  *	of a window with a non-NULL root cell use.  An error message
856  *	is output if this can't be done.  The size of the box isn't
857  *	changed.  The box is marked for redisplay, but isn't actually
858  *	redisplayed on the screen.
859  * ----------------------------------------------------------------------------
860  */
861 
862 void
ToolMoveBox(corner,point,screenCoords,rootDef)863 ToolMoveBox(corner, point, screenCoords, rootDef)
864     int corner;			/* Specifies a corner in the format
865 				 * returned by ToolGetCorner.
866 				 */
867     Point *point;		/* New position of corner, in screen
868 				 * coordinates.
869 				 */
870     int screenCoords;		/* TRUE means point is in screen coordinates,
871 				 * FALSE means root cell coordinates.
872 				 */
873     CellDef *rootDef;		/* Used only when screenCoords = FALSE, to
874 				 * give root cell def.
875 				 */
876 {
877     Point p;
878     MagWindow *w;
879     int x, y;
880     CellDef *newDef;
881     Rect newArea;
882 
883     /* Find the current point location. */
884 
885     if (screenCoords)
886     {
887 	w = toolFindPoint(point, &p, (Rect *) NULL);
888 	if ((w == NULL) || (w->w_client != DBWclientID))
889 	{
890 	    TxError("Can't put box there.\n");
891 	    return;
892 	}
893 	newDef = WINDOW_DEF(w);
894     }
895     else
896     {
897 	p = *point;
898 	newDef = rootDef;
899     }
900 
901     /* Move the box.  If an illegal corner is specified, then
902      * move by the bottom-left corner.
903      */
904 
905     switch (corner)
906     {
907 	case TOOL_BL:
908 	    x = p.p_x - boxRootArea.r_xbot;
909 	    y = p.p_y - boxRootArea.r_ybot;
910 	    break;
911 	case TOOL_BR:
912 	    x = p.p_x - boxRootArea.r_xtop;
913 	    y = p.p_y - boxRootArea.r_ybot;
914 	    break;
915 	case TOOL_TR:
916 	    x = p.p_x - boxRootArea.r_xtop;
917 	    y = p.p_y - boxRootArea.r_ytop;
918 	    break;
919 	case TOOL_TL:
920 	    x = p.p_x - boxRootArea.r_xbot;
921 	    y = p.p_y - boxRootArea.r_ytop;
922 	    break;
923 	default:
924 	    x = p.p_x - boxRootArea.r_xbot;
925 	    y = p.p_y - boxRootArea.r_ybot;
926 	    break;
927     }
928     newArea = boxRootArea;
929     newArea.r_xbot += x;
930     newArea.r_ybot += y;
931     newArea.r_xtop += x;
932     newArea.r_ytop += y;
933 
934     DBWSetBox(newDef, &newArea);
935 }
936 
937 
938 /*
939  * ----------------------------------------------------------------------------
940  *	ToolMoveCorner --
941  *
942  * 	This procedure moves one corner of the box, leaving the other
943  *	corners as fixed as possible.  Invoked by low-level screen
944  *	handler.
945  *
946  *	If the point given to reposition the box is in screen coordinates,
947  *	the box corner is snapped to the user's grid (set with the :grid
948  *	command) if DBWSnapToGrid is DBW_SNAP_USER.  If DBWSnapToGrid is
949  *	DBW_SNAP_LAMBDA, the box corner is snapped to the nearest integer
950  *	lambda value.
951  *
952  * Results:
953  *	None.
954  *
955  * Side effects:
956  *	The box is modified so that the given corner is at the given
957  *	point.  As with ToolMoveBox, the point may either be given as
958  *	a screen location, or as a location in the coordinates of a
959  *	root cell.  If the new corner location is indicated in a window
960  *	that isn't compatible with the current box window, then the
961  *	whole box is moved.  Otherwise, the corner diagonally opposite
962  *	the given corner isn't moved at all.
963  * ----------------------------------------------------------------------------
964  */
965 
966 void
ToolMoveCorner(corner,point,screenCoords,rootDef)967 ToolMoveCorner(corner, point, screenCoords, rootDef)
968     int corner;			/* The corner to be moved, in format
969 				 * returned by ToolGetCorner.
970 				 */
971     Point *point;		/* Destination of corner. */
972     int screenCoords;		/* TRUE means point is in screen coordinates,
973 				 * we look up window and translate to root
974 				 * cell coordinates.  FALSE means point is in
975 				 * coordinates of rootDef.
976 				 */
977     CellDef *rootDef;		/* Root cell Def if screenCoords = FALSE,
978 				 * unused otherwise.
979 				 */
980 {
981     Point p;
982     MagWindow *w;
983     CellDef *oldDef, *newDef;
984     int tmp;
985     Rect newArea;
986 
987     /* Find the current point location. */
988 
989     oldDef = boxRootDef;
990     if (screenCoords)
991     {
992 	w = toolFindPoint(point, &p, (Rect *) NULL);
993 	if ((w == NULL) || (w->w_client != DBWclientID))
994 	{
995 	    TxError("Can't put box there.\n");
996 	    return;
997 	}
998 	newDef = WINDOW_DEF(w);
999     }
1000     else
1001     {
1002 	p = *point;
1003 	newDef = rootDef;
1004     }
1005 
1006     /* If the root def for the moved corner isn't the same as the
1007      * current box root def, then just move the whole durned box.
1008      * Also move the whole box if a weird corner is specified.
1009      */
1010 
1011     if ((newDef != oldDef) || (corner < 0) || (corner > TOOL_TL))
1012     {
1013 	ToolMoveBox(corner, &p, FALSE, newDef);
1014 	return;
1015     }
1016 
1017     /* Move the requested corner. */
1018 
1019     newArea = boxRootArea;
1020     switch (corner)
1021     {
1022 	case TOOL_BL:
1023 	    newArea.r_xbot = p.p_x;
1024 	    newArea.r_ybot = p.p_y;
1025 	    break;
1026 	case TOOL_BR:
1027 	    newArea.r_xtop = p.p_x;
1028 	    newArea.r_ybot = p.p_y;
1029 	    break;
1030 	case TOOL_TR:
1031 	    newArea.r_xtop = p.p_x;
1032 	    newArea.r_ytop = p.p_y;
1033 	    break;
1034 	case TOOL_TL:
1035 	    newArea.r_xbot = p.p_x;
1036 	    newArea.r_ytop = p.p_y;
1037 	    break;
1038     }
1039 
1040     /* If the movement turned the box inside out, turn it right
1041      * side out again.
1042      */
1043 
1044     if (newArea.r_xbot > newArea.r_xtop)
1045     {
1046 	tmp = newArea.r_xtop;
1047 	newArea.r_xtop = newArea.r_xbot;
1048 	newArea.r_xbot = tmp;
1049     }
1050     if (newArea.r_ybot > newArea.r_ytop)
1051     {
1052 	tmp = newArea.r_ytop;
1053 	newArea.r_ytop = newArea.r_ybot;
1054 	newArea.r_ybot = tmp;
1055     }
1056 
1057     DBWSetBox(newDef, &newArea);
1058 }
1059 
1060 /*
1061  * ----------------------------------------------------------------------------
1062  *
1063  * ToolSnapToGrid --
1064  *
1065  * Snap the point *p (in root cell coordinates) to the nearest
1066  * point in the user-defined grid or the nearest integer lambda value,
1067  * according to the setting of DBWSnapToGrid.  Also translates the rectangle
1068  * *rEnclose by the same amount by which the point *p was snapped.
1069  *
1070  * Results:
1071  *	None.
1072  *
1073  * Side effects:
1074  *	Modifies *p.
1075  * ----------------------------------------------------------------------------
1076  */
1077 
1078 void
ToolSnapToGrid(w,p,rEnclose)1079 ToolSnapToGrid(w, p, rEnclose)
1080     MagWindow *w;
1081     Point *p;
1082     Rect *rEnclose;
1083 {
1084     DBWclientRec *crec = (DBWclientRec *) w->w_clientData;
1085     Rect *r;
1086     Rect lr;
1087     int xd, yd, xlo, xhi, ylo, yhi, xtmp, ytmp;
1088 
1089     if (crec == NULL || p == NULL)
1090 	return;
1091 
1092     if (DBWSnapToGrid == DBW_SNAP_LAMBDA)
1093     {
1094 	lr.r_xbot = lr.r_ybot = 0;
1095 	lr.r_xtop = DBLambda[1] / DBLambda[0];
1096 	if (lr.r_xtop < 1) lr.r_xtop = 1;
1097 	lr.r_ytop = lr.r_xtop;
1098 	r = &lr;
1099     }
1100     else
1101         r = &crec->dbw_gridRect;
1102 
1103     xd = r->r_xtop - r->r_xbot;
1104     yd = r->r_ytop - r->r_ybot;
1105 
1106     /*
1107      * The following is tricky because we want to ensure we bracket
1108      * the point p.
1109      */
1110     xtmp = p->p_x - r->r_xbot;
1111     if (xtmp < 0)
1112     {
1113 	xhi = xd * (xtmp / xd) + r->r_xbot;
1114 	xlo = xhi - xd;
1115     }
1116     else
1117     {
1118 	xlo = xd * (xtmp / xd) + r->r_xbot;
1119 	xhi = xlo + xd;
1120     }
1121 
1122     ytmp = p->p_y - r->r_ybot;
1123     if (ytmp < 0)
1124     {
1125 	yhi = yd * (ytmp / yd) + r->r_ybot;
1126 	ylo = yhi - yd;
1127     }
1128     else
1129     {
1130 	ylo = yd * (ytmp / yd) + r->r_ybot;
1131 	yhi = ylo + yd;
1132     }
1133 
1134     xtmp = (ABSDIFF(p->p_x, xlo) < ABSDIFF(p->p_x, xhi)) ? xlo : xhi;
1135     ytmp = (ABSDIFF(p->p_y, ylo) < ABSDIFF(p->p_y, yhi)) ? ylo : yhi;
1136 
1137     if (rEnclose)
1138     {
1139 	rEnclose->r_xbot += xtmp - p->p_x;
1140 	rEnclose->r_ybot += ytmp - p->p_y;
1141 	rEnclose->r_xtop += xtmp - p->p_x;
1142 	rEnclose->r_ytop += ytmp - p->p_y;
1143     }
1144     p->p_x = xtmp;
1145     p->p_y = ytmp;
1146 }
1147