549 lines
16 KiB
C
549 lines
16 KiB
C
/* 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 <stdio.h>
|
|
|
|
#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);
|
|
}
|