/* windView.c - * * Routines in this file control what is viewed in the contents * of the windows. This includes things like pan, zoom, and loading * of windows. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header$"; #endif /* not lint */ #include #include "utils/magic.h" #include "utils/geometry.h" #include "windows/windows.h" #include "graphics/glyphs.h" #include "graphics/graphics.h" #include "windows/windInt.h" #include "textio/textio.h" extern void windNewView(); /* * ---------------------------------------------------------------------------- * * windFixSurfaceArea -- * * When a window's surface or screen area has been changed, * this procedure is usually called to fix up w->w_surfaceArea and * w_origin. Before calling this procedure, w->w_origin gives the * screen location of w_surfaceArea.r_ll in SUBPIXEL of a pixel and * w->w_scale is correct, but w->w_surfaceArea may no longer be a * slight overlap of w->w_screenArea as it should be. This procedure * regenerates w->w_surfaceArea to correspond to w->w_screenArea and * changes w->w_origin to correspond to w->w_surfaceArea.r_ll again. * * Results: * None. * * Side effects: * w->w_origin and w->w_surfaceArea are modified. * * ---------------------------------------------------------------------------- */ void windFixSurfaceArea(w) MagWindow *w; /* Window to fix up. */ { Rect newArea, tmp; GEO_EXPAND(&w->w_screenArea, 1, &tmp); WindScreenToSurface(w, &tmp, &newArea); w->w_origin.p_x += (newArea.r_xbot - w->w_surfaceArea.r_xbot) * w->w_scale; w->w_origin.p_y += (newArea.r_ybot - w->w_surfaceArea.r_ybot) * w->w_scale; w->w_surfaceArea = newArea; } /* * ---------------------------------------------------------------------------- * * WindUnload -- * * Remove a client with the indicated surfaceID and reset the window * to default cell (UNKNOWN). * * Results: * None. * * Side effects: * None. * ---------------------------------------------------------------------------- */ void WindUnload(surfaceID) ClientData surfaceID; /* A unique ID for this surface */ { MagWindow *mw; for (mw = windTopWindow; mw != NULL; mw = mw->w_nextWindow) if (mw->w_surfaceID == surfaceID) DBWloadWindow(mw, (char *)NULL, TRUE, FALSE, FALSE); } /* * ---------------------------------------------------------------------------- * WindLoad -- * * Load a new client surface into a window. An initial surface area * must be specified -- this is the area that will be visible in * the window initially. * * Results: * True if everything went well, false if the client does not * own the window. * * Side effects: * None. * ---------------------------------------------------------------------------- */ bool WindLoad(w, client, surfaceID, surfaceArea) MagWindow *w; WindClient client; /* The unique identifier of the client */ ClientData surfaceID; /* A unique ID for this surface */ Rect *surfaceArea; /* The area that should appear in the window */ { if (client != w->w_client) return FALSE; w->w_surfaceID = surfaceID; WindMove(w, surfaceArea); return TRUE; } /* * ---------------------------------------------------------------------------- * WindMove -- * * Move the surface under the window so that the given area is visible * and as large as possible. * * Results: * None. * * Side effects: * The window will be now view a different portion of the clients area. * The client may be called to redisplay the areas that moved. * ---------------------------------------------------------------------------- */ void WindMove(w, surfaceArea) MagWindow *w; /* the window to be panned */ Rect *surfaceArea; /* The area to be viewed */ { int size, xscale, yscale; int halfSizePixels, halfSizeUnits; /* Compute the scale factor from world coordinates to 1/SUBPIXEL * of a pixel. To be sure that surfaceArea will all fit in the * window, compute the scale twice, once using the y-dimension * alone, and once using x alone. Then use the smaller scale factor. */ size = (surfaceArea->r_xtop - surfaceArea->r_xbot + 1); xscale = ((dlong)(w->w_screenArea.r_xtop - w->w_screenArea.r_xbot + 1) * SUBPIXEL) / size; size = (surfaceArea->r_ytop - surfaceArea->r_ybot + 1); yscale = ((w->w_screenArea.r_ytop - w->w_screenArea.r_ybot + 1) * SUBPIXEL) / size; w->w_scale = MIN(xscale, yscale); if (w->w_scale < 1) { /* If this message appears, then it is likely that the */ /* definition for SUBPIXELBITS should be increased. */ TxError("Warning: At minimum scale!\n"); w->w_scale = 1; } /* Recompute w->surfaceArea and w->w_origin, which determine the * screen-surface mapping along with w->w_scale. In order to * center surfaceArea in the window, compute the windows's half-size * in units of SUBPIXEL of a pixel and in units. Be sure to round * things up so that w->w_surfaceArea actually overlaps the window * slightly. */ halfSizePixels = (w->w_screenArea.r_xtop - w->w_screenArea.r_xbot) * HSUBPIXEL; halfSizeUnits = (halfSizePixels / w->w_scale) + 1; w->w_surfaceArea.r_xbot = (surfaceArea->r_xbot + surfaceArea->r_xtop) / 2 - halfSizeUnits; w->w_surfaceArea.r_xtop = w->w_surfaceArea.r_xbot + 2 * halfSizeUnits + 1; w->w_origin.p_x = ((w->w_screenArea.r_xtop + w->w_screenArea.r_xbot) * HSUBPIXEL) - (halfSizeUnits * w->w_scale); halfSizePixels = (w->w_screenArea.r_ytop - w->w_screenArea.r_ybot) * HSUBPIXEL; halfSizeUnits = (halfSizePixels/w->w_scale) + 1; w->w_surfaceArea.r_ybot = (surfaceArea->r_ybot + surfaceArea->r_ytop) / 2 - halfSizeUnits; w->w_surfaceArea.r_ytop = w->w_surfaceArea.r_ybot + 2 * halfSizeUnits + 1; w->w_origin.p_y = ((w->w_screenArea.r_ytop + w->w_screenArea.r_ybot) * HSUBPIXEL) - (halfSizeUnits * w->w_scale); WindAreaChanged(w, &(w->w_screenArea)); windNewView(w); } /* * ---------------------------------------------------------------------------- * WindZoom -- * * Zoom a window. The window will stay centered about the same point * as it is currently. A factor greater than 1 increases the scale * of the window (higher magnification), while a factor smaller than 1 * results in a lower scale (lower magnification). * * Results: * None. * * Side effects: * The window will be now view a different portion of the client's area. * The client may be called to redisplay part of the screen. * ---------------------------------------------------------------------------- */ void WindZoom(w, factor) MagWindow *w; /* the window to be zoomed */ float factor; /* The amount to zoom by (1 is no change), * greater than 1 is a larger magnification * (zoom in), and less than 1 is less mag. * (zoom out) ) */ { int centerx, centery; Rect newArea; centerx = (w->w_surfaceArea.r_xbot + w->w_surfaceArea.r_xtop) / 2; centery = (w->w_surfaceArea.r_ybot + w->w_surfaceArea.r_ytop) / 2; newArea.r_xbot = centerx - (centerx - w->w_surfaceArea.r_xbot) * factor; newArea.r_xtop = centerx + (w->w_surfaceArea.r_xtop - centerx) * factor; newArea.r_ybot = centery - (centery - w->w_surfaceArea.r_ybot) * factor; newArea.r_ytop = centery + (w->w_surfaceArea.r_ytop - centery) * factor; WindMove(w, &newArea); } /* * ---------------------------------------------------------------------------- * * WindScale -- * * Zoom all viewing windows by the given factor. Because this is done * in conjunction with rescaling the geometry, we don't preserve the * center position like WindZoom() does. The net effect is that the * image in the window doesn't appear to change. * * Results: * None. * * Side effects: * All windows will be now view a different portion of the client's area. * * ---------------------------------------------------------------------------- */ void WindScale(scalen, scaled) int scalen, scaled; { extern void DBScalePoint(); MagWindow *w2; Rect newArea; for (w2 = windTopWindow; w2 != NULL; w2 = w2->w_nextWindow) { newArea.r_xbot = w2->w_surfaceArea.r_xbot; newArea.r_xtop = w2->w_surfaceArea.r_xtop; newArea.r_ybot = w2->w_surfaceArea.r_ybot; newArea.r_ytop = w2->w_surfaceArea.r_ytop; DBScalePoint(&newArea.r_ll, scalen, scaled); DBScalePoint(&newArea.r_ur, scalen, scaled); WindMove(w2, &newArea); } } /* * ---------------------------------------------------------------------------- * * WindTranslate -- * * Move the viewing windows by the given delta position. Because * this is done in conjunction with repositioning the geometry * ("move origin" command), we don't preserve the center position * like WindZoom() does. The net effect is that the image in the * window doesn't appear to change. * * Results: * None. * * Side effects: * All windows will be now view a different portion of the client's area. * * ---------------------------------------------------------------------------- */ void WindTranslate(origx, origy) int origx, origy; { extern void DBMovePoint(); MagWindow *w2; Rect newArea; for (w2 = windTopWindow; w2 != NULL; w2 = w2->w_nextWindow) { newArea.r_xbot = w2->w_surfaceArea.r_xbot; newArea.r_xtop = w2->w_surfaceArea.r_xtop; newArea.r_ybot = w2->w_surfaceArea.r_ybot; newArea.r_ytop = w2->w_surfaceArea.r_ytop; DBMovePoint(&newArea.r_ll, origx, origy); DBMovePoint(&newArea.r_ur, origx, origy); WindMove(w2, &newArea); } } /* * ---------------------------------------------------------------------------- * * WindView -- * * Change the view in the selected window to just contain the * bounding box for that window. * * * Results: * None. * * Side effects: * The window underneath the cursor is changed. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ void WindView(w) MagWindow *w; { Rect bbox; #define SLOP 10 /* Amount of border (in fraction of a screenfull) * to add. */ if (w == NULL) return; if (w->w_bbox == NULL) { TxError("Can't do 'view' because w_bbox is NULL.\n"); TxError("Report this to a magic implementer.\n"); return; }; bbox = *(w->w_bbox); bbox.r_xbot -= (bbox.r_xtop - bbox.r_xbot + 1) / SLOP; bbox.r_xtop += (bbox.r_xtop - bbox.r_xbot + 1) / SLOP; bbox.r_ybot -= (bbox.r_ytop - bbox.r_ybot + 1) / SLOP; bbox.r_ytop += (bbox.r_ytop - bbox.r_ybot + 1) / SLOP; WindMove(w, &bbox); } /* * ---------------------------------------------------------------------------- * * WindScroll -- * * Scroll the view around. * * Results: * None. * * Side effects: * The window underneath the cursor is changed. The offset can * be specified either in screen coordinates or surface coordinates * or both. * * ---------------------------------------------------------------------------- */ void WindScroll(w, surfaceOffset, screenOffset) MagWindow *w; Point *surfaceOffset; /* An offset in surface coordinates. The * screen point that used to display surface * point (0,0) will now display surface point * surfaceOffset. Can be NULL to indicate * no offset. */ Point *screenOffset; /* An additional offset in screen coordinates. * Can be NULL to indicate no offset. If * non-NULL, then after scrolling according * to surfaceOffset, the view is adjusted again * so that the surface unit that used to be * displayed at (0,0) will now be displayed * at screenOffset. Be careful to make sure * the coordinates in here are relatively * small (e.g. the same order as the screen * size), or else there may be arithmetic * overflow and unexpected results. Use only * surfaceOffset if you're going to be * scrolling a long distance. */ { Rect screenorigin; bool useBackingStore = FALSE; Point moveorigin; Rect refresh, norefresh; WindSurfaceToScreenNoClip(w, &GeoNullRect, &screenorigin); if (surfaceOffset != NULL) { w->w_surfaceArea.r_xbot += surfaceOffset->p_x; w->w_surfaceArea.r_ybot += surfaceOffset->p_y; w->w_surfaceArea.r_xtop += surfaceOffset->p_x; w->w_surfaceArea.r_ytop += surfaceOffset->p_y; } /* Screen offsets are trickier. Divide up into a whole-unit part * (which is applied to w->w_surfaceArea) and a fractional-unit * part (which is applied to w->w_origin. Then readjust both to * make sure that w->w_surfaceArea still overlaps the window area * on all sides. */ if (screenOffset != NULL) { int units, pixels; pixels = screenOffset->p_x * SUBPIXEL; units = pixels/w->w_scale; w->w_surfaceArea.r_xbot -= units; w->w_surfaceArea.r_xtop -= units; w->w_origin.p_x += pixels - (w->w_scale*units); pixels = screenOffset->p_y * SUBPIXEL; units = pixels/w->w_scale; w->w_surfaceArea.r_ybot -= units; w->w_surfaceArea.r_ytop -= units; w->w_origin.p_y += pixels - (w->w_scale*units); } /* For now, we forget about using backing store in the case */ /* of diagonal scrolls. Ideally, we would want to register */ /* two rectangles for the window redraw in this case. */ if (w->w_backingStore != (ClientData)NULL) { if (surfaceOffset) if (surfaceOffset->p_x == 0 || surfaceOffset->p_y == 0) useBackingStore = TRUE; if (screenOffset) if (screenOffset->p_x == 0 || screenOffset->p_y == 0) if (useBackingStore == FALSE) useBackingStore = TRUE; } windFixSurfaceArea(w); /* Finally, if we are going to use backing store, we ought */ /* to adjust the screen movement to the nearest multiple of */ /* 8 so that stipples will remain aligned. This must be */ /* done after windFixSurfaceArea(), but cannot screw up the */ /* coordinate system, so we do windFixSurfaceArea() again. */ if (useBackingStore) { int units, pixels; WindSurfaceToScreenNoClip(w, &GeoNullRect, &refresh); moveorigin.p_x = refresh.r_xbot - screenorigin.r_xbot; moveorigin.p_y = refresh.r_ybot - screenorigin.r_ybot; pixels = (moveorigin.p_x % 8) * SUBPIXEL; units = pixels/w->w_scale; w->w_surfaceArea.r_xbot += units; w->w_surfaceArea.r_xtop += units; w->w_origin.p_x -= (pixels - (w->w_scale*units)); pixels = (moveorigin.p_y % 8) * SUBPIXEL; units = pixels/w->w_scale; w->w_surfaceArea.r_ybot += units; w->w_surfaceArea.r_ytop += units; w->w_origin.p_y -= (pixels - (w->w_scale*units)); moveorigin.p_x -= (moveorigin.p_x % 8); moveorigin.p_y -= (moveorigin.p_y % 8); windFixSurfaceArea(w); } /* If we have backing store available, shift the contents. */ if (useBackingStore) { refresh = w->w_screenArea; norefresh = w->w_screenArea; if (moveorigin.p_x > 0) { refresh.r_xtop = moveorigin.p_x + w->w_screenArea.r_xbot; norefresh.r_xbot = refresh.r_xtop; } else if (moveorigin.p_x < 0) { refresh.r_xbot = refresh.r_xtop + moveorigin.p_x; norefresh.r_xtop += moveorigin.p_x; } if (moveorigin.p_y > 0) { refresh.r_ytop = moveorigin.p_y + w->w_screenArea.r_ybot; norefresh.r_ybot = refresh.r_ytop; } else if (moveorigin.p_y < 0) { refresh.r_ybot = refresh.r_ytop + moveorigin.p_y; norefresh.r_ytop += moveorigin.p_y; } GrLock(w, FALSE); (*GrScrollBackingStorePtr)(w, &moveorigin); (*GrGetBackingStorePtr)(w, &norefresh); GrUnlock(w); WindAreaChanged(w, &refresh); /* Update highlights over entire screen area */ DBWHLRedrawPrepWindow(w, &(w->w_surfaceArea)); } else WindAreaChanged(w, &(w->w_screenArea)); windNewView(w); }