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