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