1 /* windView.c -
2  *
3  *	Routines in this file control what is viewed in the contents
4  *	of the windows.  This includes things like pan, zoom, and loading
5  *	of windows.
6  *
7  *     *********************************************************************
8  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
9  *     * Permission to use, copy, modify, and distribute this              *
10  *     * software and its documentation for any purpose and without        *
11  *     * fee is hereby granted, provided that the above copyright          *
12  *     * notice appear in all copies.  The University of California        *
13  *     * makes no representations about the suitability of this            *
14  *     * software for any purpose.  It is provided "as is" without         *
15  *     * express or implied warranty.  Export of this software outside     *
16  *     * of the United States of America may require an export license.    *
17  *     *********************************************************************
18  */
19 
20 #ifndef lint
21 static char rcsid[] __attribute__ ((unused)) = "$Header$";
22 #endif  /* not lint */
23 
24 #include <stdio.h>
25 
26 #include "utils/magic.h"
27 #include "utils/geometry.h"
28 #include "windows/windows.h"
29 #include "graphics/glyphs.h"
30 #include "graphics/graphics.h"
31 #include "windows/windInt.h"
32 #include "textio/textio.h"
33 #include "dbwind/dbwind.h"
34 
35 extern void windNewView();
36 
37 
38 /*
39  * ----------------------------------------------------------------------------
40  *
41  * windFixSurfaceArea --
42  *
43  * 	When a window's surface or screen area has been changed,
44  *	this procedure is usually called to fix up w->w_surfaceArea and
45  *	w_origin.  Before calling this procedure, w->w_origin gives the
46  *	screen location of w_surfaceArea.r_ll in SUBPIXEL of a pixel and
47  *	w->w_scale is correct, but w->w_surfaceArea may no longer be a
48  *	slight overlap of w->w_screenArea as it should be.  This procedure
49  *	regenerates w->w_surfaceArea to correspond to w->w_screenArea and
50  *	changes w->w_origin to correspond to w->w_surfaceArea.r_ll again.
51  *
52  * Results:
53  *	None.
54  *
55  * Side effects:
56  *	w->w_origin and w->w_surfaceArea are modified.
57  *
58  * ----------------------------------------------------------------------------
59  */
60 
61 void
windFixSurfaceArea(w)62 windFixSurfaceArea(w)
63     MagWindow *w;			/* Window to fix up. */
64 {
65     Rect newArea, tmp;
66 
67     GEO_EXPAND(&w->w_screenArea, 1, &tmp);
68     WindScreenToSurface(w, &tmp, &newArea);
69     w->w_origin.p_x += (newArea.r_xbot - w->w_surfaceArea.r_xbot) * w->w_scale;
70     w->w_origin.p_y += (newArea.r_ybot - w->w_surfaceArea.r_ybot) * w->w_scale;
71     w->w_surfaceArea = newArea;
72 }
73 
74 
75 /*
76  * ----------------------------------------------------------------------------
77  *
78  * WindUnload --
79  *
80  *	Remove a client with the indicated surfaceID and reset the window
81  *	to default cell (UNKNOWN).
82  *
83  * Results:
84  *	None.
85  *
86  * Side effects:
87  *	None.
88  * ----------------------------------------------------------------------------
89  */
90 
91 void
WindUnload(surfaceID)92 WindUnload(surfaceID)
93     ClientData surfaceID;	/* A unique ID for this surface */
94 {
95     MagWindow *mw;
96 
97     for (mw = windTopWindow; mw != NULL; mw = mw->w_nextWindow)
98 	if (mw->w_surfaceID == surfaceID)
99 	    DBWloadWindow(mw, (char *)NULL, DBW_LOAD_IGNORE_TECH);
100 }
101 
102 /*
103  * ----------------------------------------------------------------------------
104  * WindLoad --
105  *
106  *	Load a new client surface into a window.  An initial surface area
107  *	must be specified -- this is the area that will be visible in
108  *	the window initially.
109  *
110  * Results:
111  *	True if everything went well, false if the client does not
112  *	own the window.
113  *
114  * Side effects:
115  *	None.
116  * ----------------------------------------------------------------------------
117  */
118 
119 bool
WindLoad(w,client,surfaceID,surfaceArea)120 WindLoad(w, client, surfaceID, surfaceArea)
121     MagWindow *w;
122     WindClient client;		/* The unique identifier of the client */
123     ClientData surfaceID;	/* A unique ID for this surface */
124     Rect *surfaceArea;		/* The area that should appear in the window */
125 {
126    if (client != w->w_client) return FALSE;
127 
128    w->w_surfaceID = surfaceID;
129    WindMove(w, surfaceArea);
130    return TRUE;
131 }
132 
133 
134 /*
135  * ----------------------------------------------------------------------------
136  * WindMove --
137  *
138  *	Move the surface under the window so that the given area is visible
139  *	and as large as possible.
140  *
141  * Results:
142  *	None.
143  *
144  * Side effects:
145  *	The window will be now view a different portion of the clients area.
146  *	The client may be called to redisplay the areas that moved.
147  * ----------------------------------------------------------------------------
148  */
149 
150 void
WindMove(w,surfaceArea)151 WindMove(w, surfaceArea)
152     MagWindow *w;			/* the window to be panned */
153     Rect *surfaceArea;		/* The area to be viewed */
154 {
155     int size, xscale, yscale;
156     int  halfSizePixels, halfSizeUnits;
157 
158     /* Compute the scale factor from world coordinates to 1/SUBPIXEL
159      * of a pixel.  To be sure that surfaceArea will all fit in the
160      * window, compute the scale twice, once using the y-dimension
161      * alone, and once using x alone.  Then use the smaller scale factor.
162      */
163 
164     size = (surfaceArea->r_xtop - surfaceArea->r_xbot + 1);
165     xscale = ((dlong)(w->w_screenArea.r_xtop -
166 	    w->w_screenArea.r_xbot + 1) * SUBPIXEL) / size;
167 
168     size = (surfaceArea->r_ytop - surfaceArea->r_ybot + 1);
169     yscale = ((w->w_screenArea.r_ytop -
170 	    w->w_screenArea.r_ybot + 1) * SUBPIXEL) / size;
171 
172     w->w_scale = MIN(xscale, yscale);
173     if (w->w_scale < 1)
174     {
175 	/* If this message appears, then it is likely that the	*/
176 	/* definition for SUBPIXELBITS should be increased.	*/
177 
178 	TxError("Warning:  At minimum scale!\n");
179 	w->w_scale = 1;
180     }
181 
182     /* Recompute w->surfaceArea and w->w_origin, which determine the
183      * screen-surface mapping along with w->w_scale.  In order to
184      * center surfaceArea in the window, compute the windows's half-size
185      * in units of SUBPIXEL of a pixel and in units.  Be sure to round
186      * things up so that w->w_surfaceArea actually overlaps the window
187      * slightly.
188      */
189 
190     halfSizePixels = (w->w_screenArea.r_xtop - w->w_screenArea.r_xbot) * HSUBPIXEL;
191     halfSizeUnits = (halfSizePixels / w->w_scale) + 1;
192     w->w_surfaceArea.r_xbot = (surfaceArea->r_xbot + surfaceArea->r_xtop) / 2
193 	- halfSizeUnits;
194     w->w_surfaceArea.r_xtop = w->w_surfaceArea.r_xbot + 2 * halfSizeUnits + 1;
195     w->w_origin.p_x =
196 	((w->w_screenArea.r_xtop + w->w_screenArea.r_xbot) * HSUBPIXEL) -
197 	(halfSizeUnits * w->w_scale);
198 
199     halfSizePixels = (w->w_screenArea.r_ytop - w->w_screenArea.r_ybot) * HSUBPIXEL;
200     halfSizeUnits = (halfSizePixels/w->w_scale) + 1;
201     w->w_surfaceArea.r_ybot = (surfaceArea->r_ybot + surfaceArea->r_ytop) / 2
202 	- halfSizeUnits;
203     w->w_surfaceArea.r_ytop = w->w_surfaceArea.r_ybot + 2 * halfSizeUnits + 1;
204     w->w_origin.p_y =
205 	((w->w_screenArea.r_ytop + w->w_screenArea.r_ybot) * HSUBPIXEL) -
206 	(halfSizeUnits * w->w_scale);
207 
208     WindAreaChanged(w, &(w->w_screenArea));
209     windNewView(w);
210 }
211 
212 
213 /*
214  * ----------------------------------------------------------------------------
215  * WindZoom --
216  *
217  *	Zoom a window.  The window will stay centered about the same point
218  *	as it is currently.  A factor greater than 1 increases the scale
219  *	of the window (higher magnification), while a factor smaller than 1
220  *	results in a lower scale (lower magnification).
221  *
222  * Results:
223  *	None.
224  *
225  * Side effects:
226  *	The window will be now view a different portion of the client's area.
227  *	The client may be called to redisplay part of the screen.
228  * ----------------------------------------------------------------------------
229  */
230 
231 void
WindZoom(w,factor)232 WindZoom(w, factor)
233     MagWindow *w;			/* the window to be zoomed */
234     float factor;		/* The amount to zoom by (1 is no change),
235 				 * greater than 1 is a larger magnification
236 				 * (zoom in), and less than 1 is less mag.
237 				 * (zoom out) )
238 				 */
239 {
240     int centerx, centery;
241     Rect newArea;
242 
243     centerx = (w->w_surfaceArea.r_xbot + w->w_surfaceArea.r_xtop) / 2;
244     centery = (w->w_surfaceArea.r_ybot + w->w_surfaceArea.r_ytop) / 2;
245 
246     newArea.r_xbot = centerx - (centerx - w->w_surfaceArea.r_xbot) * factor;
247     newArea.r_xtop = centerx + (w->w_surfaceArea.r_xtop - centerx) * factor;
248     newArea.r_ybot = centery - (centery - w->w_surfaceArea.r_ybot) * factor;
249     newArea.r_ytop = centery + (w->w_surfaceArea.r_ytop - centery) * factor;
250 
251     WindMove(w, &newArea);
252 }
253 
254 /*
255  * ----------------------------------------------------------------------------
256  *
257  * WindScale --
258  *
259  *	Zoom all viewing windows by the given factor.  Because this is done
260  *	in conjunction with rescaling the geometry, we don't preserve the
261  *	center position like WindZoom() does.  The net effect is that the
262  *	image in the window doesn't appear to change.
263  *
264  * Results:
265  *	None.
266  *
267  * Side effects:
268  *	All windows will be now view a different portion of the client's area.
269  *
270  * ----------------------------------------------------------------------------
271  */
272 
273 void
WindScale(scalen,scaled)274 WindScale(scalen, scaled)
275     int scalen, scaled;
276 {
277     extern void DBScalePoint();
278     MagWindow *w2;
279     Rect newArea;
280 
281     for (w2 = windTopWindow; w2 != NULL; w2 = w2->w_nextWindow)
282     {
283 	newArea.r_xbot = w2->w_surfaceArea.r_xbot;
284 	newArea.r_xtop = w2->w_surfaceArea.r_xtop;
285 	newArea.r_ybot = w2->w_surfaceArea.r_ybot;
286 	newArea.r_ytop = w2->w_surfaceArea.r_ytop;
287 	DBScalePoint(&newArea.r_ll, scalen, scaled);
288 	DBScalePoint(&newArea.r_ur, scalen, scaled);
289 	WindMove(w2, &newArea);
290     }
291 }
292 
293 /*
294  * ----------------------------------------------------------------------------
295  *
296  * WindTranslate --
297  *
298  *	Move the viewing windows by the given delta position.  Because
299  *	this is done in conjunction with repositioning the geometry
300  *	("move origin" command), we don't preserve the center position
301  *	like WindZoom() does.  The net effect is that the image in the
302  *	window doesn't appear to change.
303  *
304  * Results:
305  *	None.
306  *
307  * Side effects:
308  *	All windows will be now view a different portion of the client's area.
309  *
310  * ----------------------------------------------------------------------------
311  */
312 
313 void
WindTranslate(origx,origy)314 WindTranslate(origx, origy)
315     int origx, origy;
316 {
317     extern void DBMovePoint();
318     MagWindow *w2;
319     Rect newArea;
320 
321     for (w2 = windTopWindow; w2 != NULL; w2 = w2->w_nextWindow)
322     {
323 	newArea.r_xbot = w2->w_surfaceArea.r_xbot;
324 	newArea.r_xtop = w2->w_surfaceArea.r_xtop;
325 	newArea.r_ybot = w2->w_surfaceArea.r_ybot;
326 	newArea.r_ytop = w2->w_surfaceArea.r_ytop;
327 	DBMovePoint(&newArea.r_ll, origx, origy);
328 	DBMovePoint(&newArea.r_ur, origx, origy);
329 	WindMove(w2, &newArea);
330     }
331 }
332 
333 
334 
335 /*
336  * ----------------------------------------------------------------------------
337  *
338  * WindView --
339  *
340  * Change the view in the selected window to just contain the
341  * bounding box for that window.
342  *
343  *
344  * Results:
345  *	None.
346  *
347  * Side effects:
348  *	The window underneath the cursor is changed.
349  *
350  * ----------------------------------------------------------------------------
351  */
352 
353     /* ARGSUSED */
354 
355 void
WindView(w)356 WindView(w)
357     MagWindow *w;
358 {
359     Rect bbox;
360 #define SLOP	10	/* Amount of border (in fraction of a screenfull)
361 			 * to add.
362 			 */
363     if (w == NULL)
364 	return;
365 
366     if (w->w_bbox == NULL) {
367 	TxError("Can't do 'view' because w_bbox is NULL.\n");
368 	TxError("Report this to a magic implementer.\n");
369 	return;
370     };
371 
372     bbox = *(w->w_bbox);
373     bbox.r_xbot -= (bbox.r_xtop - bbox.r_xbot + 1) / SLOP;
374     bbox.r_xtop += (bbox.r_xtop - bbox.r_xbot + 1) / SLOP;
375     bbox.r_ybot -= (bbox.r_ytop - bbox.r_ybot + 1) / SLOP;
376     bbox.r_ytop += (bbox.r_ytop - bbox.r_ybot + 1) / SLOP;
377 
378     WindMove(w, &bbox);
379 }
380 
381 /*
382  * ----------------------------------------------------------------------------
383  *
384  * WindScroll --
385  *
386  *	Scroll the view around.
387  *
388  * Results:
389  *	None.
390  *
391  * Side effects:
392  *	The window underneath the cursor is changed.  The offset can
393  *	be specified either in screen coordinates or surface coordinates
394  *	or both.
395  *
396  * ----------------------------------------------------------------------------
397  */
398 
399 void
WindScroll(w,surfaceOffset,screenOffset)400 WindScroll(w, surfaceOffset, screenOffset)
401     MagWindow *w;
402     Point *surfaceOffset;	/* An offset in surface coordinates.  The
403 				 * screen point that used to display surface
404 				 * point (0,0) will now display surface point
405 				 * surfaceOffset.  Can be NULL to indicate
406 				 * no offset.
407 				 */
408     Point *screenOffset;	/* An additional offset in screen coordinates.
409 				 * Can be NULL to indicate no offset.  If
410 				 * non-NULL, then after scrolling according
411 				 * to surfaceOffset, the view is adjusted again
412 				 * so that the surface unit that used to be
413 				 * displayed at (0,0) will now be displayed
414 				 * at screenOffset.  Be careful to make sure
415 				 * the coordinates in here are relatively
416 				 * small (e.g. the same order as the screen
417 				 * size), or else there may be arithmetic
418 				 * overflow and unexpected results.  Use only
419 				 * surfaceOffset if you're going to be
420 				 * scrolling a long distance.
421 				 */
422 {
423     Rect screenorigin;
424     bool useBackingStore = FALSE;
425     Point moveorigin;
426     Rect refresh, norefresh;
427 
428     WindSurfaceToScreenNoClip(w, &GeoNullRect, &screenorigin);
429 
430     if (surfaceOffset != NULL)
431     {
432 	w->w_surfaceArea.r_xbot += surfaceOffset->p_x;
433 	w->w_surfaceArea.r_ybot += surfaceOffset->p_y;
434 	w->w_surfaceArea.r_xtop += surfaceOffset->p_x;
435 	w->w_surfaceArea.r_ytop += surfaceOffset->p_y;
436     }
437 
438     /* Screen offsets are trickier.  Divide up into a whole-unit part
439      * (which is applied to w->w_surfaceArea) and a fractional-unit
440      * part (which is applied to w->w_origin.  Then readjust both to
441      * make sure that w->w_surfaceArea still overlaps the window area
442      * on all sides.
443      */
444 
445     if (screenOffset != NULL)
446     {
447 	int units, pixels;
448 
449 	pixels = screenOffset->p_x * SUBPIXEL;
450 	units = pixels/w->w_scale;
451 	w->w_surfaceArea.r_xbot -= units;
452 	w->w_surfaceArea.r_xtop -= units;
453 	w->w_origin.p_x += pixels - (w->w_scale*units);
454 
455 	pixels = screenOffset->p_y * SUBPIXEL;
456 	units = pixels/w->w_scale;
457 	w->w_surfaceArea.r_ybot -= units;
458 	w->w_surfaceArea.r_ytop -= units;
459 	w->w_origin.p_y += pixels - (w->w_scale*units);
460     }
461 
462     /* For now, we forget about using backing store in the case	*/
463     /* of diagonal scrolls.  Ideally, we would want to register	*/
464     /* two rectangles for the window redraw in this case.	*/
465 
466     if (w->w_backingStore != (ClientData)NULL)
467     {
468 	if (surfaceOffset)
469 	    if (surfaceOffset->p_x == 0 || surfaceOffset->p_y == 0)
470 		useBackingStore = TRUE;
471 	if (screenOffset)
472 	    if (screenOffset->p_x == 0 || screenOffset->p_y == 0)
473 		if (useBackingStore == FALSE)
474 		    useBackingStore = TRUE;
475     }
476     windFixSurfaceArea(w);
477 
478     /* Finally, if we are going to use backing store, we ought	*/
479     /* to adjust the screen movement to the nearest multiple of	*/
480     /* 8 so that stipples will remain aligned.  This must be	*/
481     /* done after windFixSurfaceArea(), but cannot screw up the	*/
482     /* coordinate system, so we do windFixSurfaceArea() again.	*/
483 
484     if (useBackingStore)
485     {
486 	int units, pixels;
487 
488 	WindSurfaceToScreenNoClip(w, &GeoNullRect, &refresh);
489 	moveorigin.p_x = refresh.r_xbot - screenorigin.r_xbot;
490 	moveorigin.p_y = refresh.r_ybot - screenorigin.r_ybot;
491 
492 	pixels = (moveorigin.p_x % 8) * SUBPIXEL;
493 	units = pixels/w->w_scale;
494 	w->w_surfaceArea.r_xbot += units;
495 	w->w_surfaceArea.r_xtop += units;
496 	w->w_origin.p_x -= (pixels - (w->w_scale*units));
497 
498 	pixels = (moveorigin.p_y % 8) * SUBPIXEL;
499 	units = pixels/w->w_scale;
500 	w->w_surfaceArea.r_ybot += units;
501 	w->w_surfaceArea.r_ytop += units;
502 	w->w_origin.p_y -= (pixels - (w->w_scale*units));
503 
504 	moveorigin.p_x -= (moveorigin.p_x % 8);
505 	moveorigin.p_y -= (moveorigin.p_y % 8);
506 
507         windFixSurfaceArea(w);
508     }
509 
510     /* If we have backing store available, shift the contents.	*/
511 
512     if (useBackingStore)
513     {
514 	refresh = w->w_screenArea;
515 	norefresh = w->w_screenArea;
516 	if (moveorigin.p_x > 0)
517 	{
518 	    refresh.r_xtop = moveorigin.p_x + w->w_screenArea.r_xbot;
519 	    norefresh.r_xbot = refresh.r_xtop;
520 	}
521 	else if (moveorigin.p_x < 0)
522 	{
523 	    refresh.r_xbot = refresh.r_xtop + moveorigin.p_x;
524 	    norefresh.r_xtop += moveorigin.p_x;
525 	}
526 	if (moveorigin.p_y > 0)
527 	{
528 	    refresh.r_ytop = moveorigin.p_y + w->w_screenArea.r_ybot;
529 	    norefresh.r_ybot = refresh.r_ytop;
530 	}
531 	else if (moveorigin.p_y < 0)
532 	{
533 	    refresh.r_ybot = refresh.r_ytop + moveorigin.p_y;
534 	    norefresh.r_ytop += moveorigin.p_y;
535 	}
536 
537 	GrLock(w, FALSE);
538 	(*GrScrollBackingStorePtr)(w, &moveorigin);
539 	(*GrGetBackingStorePtr)(w, &norefresh);
540 	GrUnlock(w);
541 	WindAreaChanged(w, &refresh);
542 	/* Update highlights over entire screen area */
543 	DBWHLRedrawPrepWindow(w, &(w->w_surfaceArea));
544     }
545     else
546 	WindAreaChanged(w, &(w->w_screenArea));
547 
548     windNewView(w);
549 }
550