magic/windows/windMove.c

858 lines
22 KiB
C
Raw Normal View History

/* windMove.c -
*
* This file contains the functions which move windows around on
* the screen. It does not contain the functions that change the
* contents of the 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
2020-05-25 21:46:59 +02:00
static char rcsid[] __attribute__ ((unused)) = "$Header$";
#endif /* not lint */
#include <stdio.h>
#include <string.h>
#include "utils/magic.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "graphics/glyphs.h"
#include "windows/windInt.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/malloc.h"
/* The following own variable is used to pass information between
* WindReframe and windFindUnobscured.
*/
static MagWindow *sharedW;
/*
* By default, new windows have scroll bars, border, a title caption,
* and allow standard window commands.
*/
int WindDefaultFlags = WIND_SCROLLBARS | WIND_CAPTION |
WIND_BORDER | WIND_COMMANDS |
WIND_SCROLLABLE;
/*
* A mask of the current window IDs, as well as a limit on the number of
* windows we can create.
*/
int windWindowMask = 0; /* One bit per window ID */
int windMaxWindows = 32; /* May be decreased via the WIND_MAX_WINDOWS() macro */
int windCurNumWindows = 0;
/*
* ----------------------------------------------------------------------------
* windUnlink --
*
* Unlink a window from the doubly linked list of windows.
*
* Results:
* None.
*
* Side effects:
* The window is unlinked.
* ----------------------------------------------------------------------------
*/
void
windUnlink(w)
MagWindow *w;
{
ASSERT(w != (MagWindow *) NULL, "windUnlink");
ASSERT(windTopWindow != (MagWindow *) NULL, "windUnlink");
ASSERT(windBottomWindow != (MagWindow *) NULL, "windUnlink");
ASSERT(windTopWindow->w_prevWindow == (MagWindow *) NULL, "windUnlink");
ASSERT(windBottomWindow->w_nextWindow == (MagWindow *) NULL, "windUnlink");
if ( (windTopWindow == w) || (windBottomWindow == w) )
{
if (windTopWindow == w)
{
windTopWindow = w->w_nextWindow;
if (windTopWindow != (MagWindow *) NULL)
windTopWindow->w_prevWindow = (MagWindow *) NULL;
}
if (windBottomWindow == w)
{
windBottomWindow = w->w_prevWindow;
if (windBottomWindow != (MagWindow *) NULL)
windBottomWindow->w_nextWindow = (MagWindow *) NULL;
}
}
else
{
w->w_nextWindow->w_prevWindow = w->w_prevWindow;
w->w_prevWindow->w_nextWindow = w->w_nextWindow;
}
w->w_nextWindow = (MagWindow *) NULL;
w->w_prevWindow = (MagWindow *) NULL;
}
/*
* ----------------------------------------------------------------------------
*
* windFree --
*
* This local procedure does the dirty work of freeing up
* memory in a window.
*
* Results:
* None.
*
* Side effects:
* All storage associated with w is returned to the free pool.
*
* ----------------------------------------------------------------------------
*/
void
windFree(w)
MagWindow *w;
{
windWindowMask &= ~(1 << w->w_wid);
windCurNumWindows--;
if (w->w_caption != (char *) NULL) freeMagic(w->w_caption);
if (w->w_iconname != (char *) NULL) freeMagic(w->w_iconname);
if (GrFreeBackingStorePtr != NULL) (*GrFreeBackingStorePtr)(w);
if (w->w_redrawAreas != (ClientData) NULL) {
DBFreePaintPlane( (Plane *) w->w_redrawAreas);
TiFreePlane( (Plane *) w->w_redrawAreas);
}
freeMagic( (char *) w);
}
/*
* ----------------------------------------------------------------------------
* WindSetWindowAreas --
*
* Given the location of the window on the screen, compute w->w_allArea
* and w->w_screenArea in the window's own coordinate system.
*
* Results:
* None.
*
* Side effects:
* Modifies w->w_allArea and w->w_screenArea.
* ----------------------------------------------------------------------------
*/
void
WindSetWindowAreas(w)
MagWindow *w;
{
switch ( WindPackageType )
{
case WIND_X_WINDOWS:
/* Windows have origin at lower-left corner */
w->w_allArea.r_xbot = w->w_allArea.r_ybot = 0;
w->w_allArea.r_xtop = w->w_frameArea.r_xtop - w->w_frameArea.r_xbot;
w->w_allArea.r_ytop = w->w_frameArea.r_ytop - w->w_frameArea.r_ybot;
break;
default:
/* Windows are all in the same coordinate system */
w->w_allArea = w->w_frameArea;
}
WindOutToIn(w, &w->w_allArea, &w->w_screenArea);
}
/*
* ----------------------------------------------------------------------------
* windSetWindowPosition --
*
* (deprecated function)
*
* Results:
* None.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
void
windSetWindowPosition(w)
MagWindow *w;
{
}
/*
* ----------------------------------------------------------------------------
* WindDelete --
*
* Delete a window.
*
* Results:
* TRUE if the window was deleted, FALSE otherwise.
*
* Side effects:
* The window disappears from the sreen. The window's client is notified
* that this is about to happen, and it may refuse to let it happen.
* ----------------------------------------------------------------------------
*/
bool
WindDelete(w)
MagWindow *w;
{
clientRec *cr;
ASSERT(w != (MagWindow *) NULL, "WindDelete");
cr = (clientRec *) w->w_client;
if ( (cr->w_delete == NULL) || (*(cr->w_delete))(w) )
{
WindAreaChanged(w, &(w->w_allArea) );
if (GrDeleteWindowPtr != NULL) (*GrDeleteWindowPtr)(w);
windUnlink(w);
windReClip();
windFree(w);
return TRUE;
}
else
return FALSE;
}
/*
* ----------------------------------------------------------------------------
* WindCreate --
*
* Create a new window for the specified client.
*
* Results:
* A pointer to the new window, or NULL if one couldn't be created.
*
* Side effects:
* An empty window is created, and it is displayed.
* The new window is place on top of all the other
* windows.
* ----------------------------------------------------------------------------
*/
MagWindow *
WindCreate(client, frameArea, isHint, argc, argv)
WindClient client; /* The client that will control this window */
Rect *frameArea; /* The area that the window is to occupy */
bool isHint; /* TRUE if the above rectangle is only a
* hint and it is OK for a window package to
* override it to maintain a consistent
* user interface.
*/
int argc; /* Passed to the client */
char *argv[];
{
MagWindow *w;
clientRec *cr;
bool OK;
int id;
if (windCurNumWindows + 1 > windMaxWindows) {
TxError("Can't have more than %d windows.\n", windMaxWindows);
return NULL;
}
windCurNumWindows++;
cr = (clientRec *) client;
/* initialize the window */
w = (MagWindow *) mallocMagic( sizeof(MagWindow) );
w->w_client = client;
w->w_flags = WindDefaultFlags;
w->w_clipAgainst = (LinkedRect *) NULL;
w->w_caption = (char *) NULL;
w->w_stippleOrigin.p_x = 0;
w->w_stippleOrigin.p_y = 0;
w->w_bbox = NULL;
w->w_grdata = (ClientData) NULL;
w->w_grdata2 = (ClientData) NULL;
w->w_backingStore = (ClientData)NULL;
w->w_redrawAreas = (ClientData) NULL;
w->w_surfaceID = (ClientData) NULL;
w->w_iconname = NULL;
for (id = 0; ((1 << id) & windWindowMask) != 0; id++) /* advance id */ ;
windWindowMask |= (1 << id);
w->w_wid = id;
/* locate window on the screen */
if (frameArea == (Rect *) NULL)
{
switch ( WindPackageType )
{
case WIND_X_WINDOWS:
/*
* Create default size window in upper left corner
* of screen.
*/
w->w_frameArea.r_xbot = GrScreenRect.r_xbot;
w->w_frameArea.r_ytop = GrScreenRect.r_ytop;
w->w_frameArea.r_xtop =
(GrScreenRect.r_xtop - GrScreenRect.r_xbot) / 2;
w->w_frameArea.r_ybot =
(GrScreenRect.r_ytop - GrScreenRect.r_ybot) / 2;
break;
default:
w->w_frameArea = GrScreenRect;
}
}
else
w->w_frameArea = *frameArea;
WindSetWindowAreas(w);
/* now link the window in on top */
w->w_nextWindow = windTopWindow;
w->w_prevWindow = (MagWindow *) NULL;
if (windTopWindow == (MagWindow *) NULL)
windBottomWindow = w;
else
windTopWindow->w_prevWindow = w;
windTopWindow = w;
/* notify the client */
OK = ((cr->w_create == NULL) || (*(cr->w_create))(w, argc, argv));
#ifdef THREE_D
if (strcmp(cr->w_clientName, "wind3d"))
#endif
if (OK && (GrCreateWindowPtr != NULL))
OK = (*GrCreateWindowPtr)(w, (argc > 1) ? argv[1] : NULL);
if (OK)
{
WindSetWindowAreas(w);
windSetWindowPosition(w);
WindAreaChanged(w, &(w->w_allArea));
}
else
{
/* the client refused the new window */
windUnlink(w);
windFree(w);
w = (MagWindow *) NULL;
}
windReClip();
if ((GrCreateBackingStorePtr != NULL) && (w != NULL) &&
(!(w->w_flags & WIND_OBSCURED)))
(*GrCreateBackingStorePtr)(w);
return w;
}
/*
* ----------------------------------------------------------------------------
*
* WindOutToIn and WindInToOut --
*
* The two procedures on this page translate from window inside
* area (the area used to display the surface) to window
* outside area (the total area of the window including caption),
* and vice versa.
*
* Results:
* None.
*
* Side effects:
* Each procedure modifies its third parameter. WindOutToIn
* fills in the third parameter with the inside area of the
* window whose outside area is out, and WindInToOut does the
* opposite.
*
* ----------------------------------------------------------------------------
*/
void
WindOutToIn(w, out, in)
MagWindow *w; /* Window under consideration */
Rect *out; /* Pointer to rectangle of outside area of
* a window.
*/
Rect *in; /* Pointer to rectangle to be filled in with
* inside area corresponding to out.
*/
{
*in = *out;
in->r_xbot += LEFT_BORDER(w);
in->r_xtop -= RIGHT_BORDER(w);
in->r_ybot += BOT_BORDER(w);
in->r_ytop -= TOP_BORDER(w);
}
void WindInToOut(w, in, out)
MagWindow *w; /* Window under consideration */
Rect *in; /* Pointer to rectangle of outside area of
* a window.
*/
Rect *out; /* Pointer to rectangle to be filled in with
* inside area corresponding to out.
*/
{
*out = *in;
out->r_xbot -= LEFT_BORDER(w);
out->r_xtop += RIGHT_BORDER(w);
out->r_ybot -= BOT_BORDER(w);
out->r_ytop += TOP_BORDER(w);
}
/*
* ----------------------------------------------------------------------------
* WindUnder --
*
* Move a window underneath the rest.
*
* Results:
* None.
*
* Side effects:
* The window is moved so that it is underneath the rest. This will
* cause portions of uncovered windows to be redisplayed.
* ----------------------------------------------------------------------------
*/
void
WindUnder(w)
MagWindow *w; /* the window to be moved */
{
Rect area;
MagWindow *w2;
switch ( WindPackageType )
{
case WIND_X_WINDOWS:
if ( GrUnderWindowPtr )
(*GrUnderWindowPtr)(w);
break;
default:
/* Mark for redisplay all the areas that this window
* currently obscures.
*/
for (w2 = w->w_nextWindow; w2 != NULL; w2 = w2->w_nextWindow)
{
area = w2->w_allArea;
GeoClip(&area, &w->w_allArea);
if ((area.r_xbot <= area.r_xtop) && (area.r_ybot <= area.r_ytop))
WindAreaChanged(w, &area);
}
/* take the window out of the linked list */
windUnlink(w);
/* now link it back in at the bottom */
w->w_prevWindow = windBottomWindow;
if (windBottomWindow != (MagWindow *) NULL)
windBottomWindow->w_nextWindow = w;
else
windTopWindow = w;
windBottomWindow = w;
windReClip();
}
}
/*
* ----------------------------------------------------------------------------
* WindOver --
*
* Move a window so that it is over (on top of) the other windows.
*
* Results:
* None.
*
* Side effects:
* The window is moved to the top. This may obscure some other windows.
* The window that is moved will be redisplayed.
* ----------------------------------------------------------------------------
*/
void
WindOver(w)
MagWindow *w; /* the window to be moved */
{
LinkedRect *r;
Rect area;
switch ( WindPackageType )
{
case WIND_X_WINDOWS:
if ( GrOverWindowPtr )
(*GrOverWindowPtr)(w);
break;
default:
/* Mark for redisplay all of the areas of the screen that
* currently obscure this window.
*/
for (r = w->w_clipAgainst; r != NULL; r = r->r_next)
{
area = r->r_r;
GeoClip(&area, &w->w_frameArea);
if ((area.r_xbot <= area.r_xtop) && (area.r_ybot <= area.r_ytop))
WindAreaChanged((MagWindow *) NULL, &area);
}
/* take the window out of the linked list */
windUnlink(w);
/* now link it back in at the top */
w->w_nextWindow = windTopWindow;
if (windTopWindow != (MagWindow *) NULL)
windTopWindow->w_prevWindow = w;
else
windBottomWindow = w;
windTopWindow = w;
windReClip();
}
}
/*
* ----------------------------------------------------------------------------
*
* windFindUnobscured --
*
* Locates one portion of a rectangle that is unobscured, if
* any part of the rectangle is unobscured. Used only by
* WindReframe.
*
* Results:
* Always returns TRUE.
*
* Side effects:
* The caller must place in the shared variable sharedW the
* name of a window, or NULL. That window, and all windows
* above it, are checked to see if any obscure area. If
* there is any unobscured part of area, it is placed in
* okArea. If several distinct parts of area are unobscured,
* one, but only one, of them will be placed in okArea.
*
* ----------------------------------------------------------------------------
*/
bool
windFindUnobscured(area, okArea)
Rect *area; /* Area that may be obscured. */
Rect *okArea; /* Modified to contain one of the
* unobscured areas.
*/
{
MagWindow *w;
w = sharedW;
if (w == NULL)
{
*okArea = *area;
return FALSE;
}
sharedW = w->w_prevWindow;
(void) GeoDisjoint(area, &w->w_frameArea,
windFindUnobscured, (ClientData) okArea);
return FALSE;
}
/*
* ----------------------------------------------------------------------------
* WindReframe --
*
* Change the size or location of a window.
*
* Results:
* None.
*
* Side effects:
* The window is moved, and areas of the screen are marked for
* redisplay. This routine tries to be tricky in order to avoid
* massive redisplay for small changes.
* ----------------------------------------------------------------------------
*/
void
WindReframe(w, r, inside, move)
MagWindow *w; /* the window to be reframed */
Rect *r; /* the new location in screen coordinates */
bool inside; /* TRUE if the rectangle is the screen location of
* the inside of the window, FALSE if the above
* rectangle includes border areas.
*/
bool move; /* Move the coordinate system of the window the same
* amount as the lower left corner of the window?
*/
{
Rect newFrameArea; /* New w_frameArea. */
Rect dontRedisplay; /* Used to record an area that does
* not have to be redisplayed.
*/
int xmove, ymove; /* Distance window is moving. */
extern bool windReframeFunc(); /* Forward declaration. */
clientRec *cr;
cr = (clientRec *) w->w_client;
/* Ensure that the new window size is not inside out and has some size to
* it. Compute the new w_frameArea (in newFrameArea).
*/
GeoCanonicalRect(r, &newFrameArea);
if (inside) WindInToOut(w, &newFrameArea, &newFrameArea);
if ((w->w_flags & WIND_ISICONIC) == 0) {
/* Not iconic -- enforce a minimum size */
newFrameArea.r_xtop = MAX(newFrameArea.r_xtop,
newFrameArea.r_xbot + WIND_MIN_WIDTH);
newFrameArea.r_ytop = MAX(newFrameArea.r_ytop,
newFrameArea.r_ybot + WIND_MIN_HEIGHT);
}
/* Give the client a chance to modify the change. */
if (cr->w_reposition != NULL)
(*(cr->w_reposition))(w, &newFrameArea, FALSE);
/* If the window coordinates are moving, update the transform
* so that the lower-left corner of the window remains at the
* same location in surface coordinates.
*/
if (move)
{
xmove = newFrameArea.r_xbot - w->w_frameArea.r_xbot;
w->w_origin.p_x += xmove << SUBPIXELBITS;
ymove = newFrameArea.r_ybot - w->w_frameArea.r_ybot;
w->w_origin.p_y += ymove << SUBPIXELBITS;
w->w_stippleOrigin.p_x += xmove;
w->w_stippleOrigin.p_y += ymove;
}
switch ( WindPackageType )
{
case WIND_X_WINDOWS:
break;
default:
/* Now comes the tricky part: figuring out what to redisplay.
* The simple way out is to force redisplay at both the old and
* new window positions. Naturally this code is going to be more
* ambitious. There are two steps. First, figure out what piece
* of the old window must be redisplayed, then move the window,
* then figure out what pieces of the new window must be redisplayed.
* If the window coordinates aren't moving, then any screen area
* common to the old and new positions needn't be redisplayed,
* since its contents won't change.
*/
if (!move)
{
/* Compute the intersection of the old and new areas in
* dontRedisplay. Mark old areas outside of this common area as
* needing to be redisplayed.
*/
WindOutToIn(w, &newFrameArea, &dontRedisplay);
GeoClip(&dontRedisplay, &w->w_screenArea);
(void) GeoDisjoint(&w->w_frameArea, &dontRedisplay, windReframeFunc,
(ClientData) w);
}
else
{
/* Record the entire old area as needing to be redisplayed. */
WindAreaChanged(w, &w->w_allArea);
dontRedisplay = w->w_allArea;
}
}
/* At this point, we've recorded any old area that needs to be
* redisplayed.
*/
w->w_frameArea = newFrameArea;
WindSetWindowAreas(w);
windSetWindowPosition(w);
windFixSurfaceArea(w);
windReClip();
switch (WindPackageType)
{
case WIND_X_WINDOWS:
/* Regenerate backing store, if enabled */
if ((GrCreateBackingStorePtr != NULL) &&
(!(w->w_flags & WIND_OBSCURED)))
(*GrCreateBackingStorePtr)(w);
break;
default:
/* Now that the window has been moved, record any of the new area that
* has to be redisplayed.
*/
(void) GeoDisjoint(&w->w_allArea, &dontRedisplay, windReframeFunc,
(ClientData) w);
}
/* Give the client a chance to do things like windMove().
*/
if (cr->w_reposition != NULL)
(*(cr->w_reposition))(w, &newFrameArea, TRUE);
}
bool
windReframeFunc(area, w)
Rect *area; /* Area to redisplay. */
MagWindow *w; /* Window in which to redisplay. */
{
WindAreaChanged(w, area);
return FALSE;
}
/*
* ----------------------------------------------------------------------------
*
* WindFullScreen --
*
* This procedure blows a window up so it's on top of all the others
* and is full-screen. Or, if the window was already full-screen,
* it is put back where it came from before it was made full-screen.
*
* Results:
* None.
*
* Side effects:
* The window's size and location are changed.
*
* ----------------------------------------------------------------------------
*/
void
WindFullScreen(w)
MagWindow *w; /* Window to be blown up or shrunk back. */
{
int i;
MagWindow *w2;
Rect newFrameArea;
clientRec *cr;
int newDepth;
cr = (clientRec *) w->w_client;
/* Compute default new location. */
if (w->w_flags & WIND_FULLSCREEN)
newFrameArea = w->w_oldArea;
else
newFrameArea = GrScreenRect;
/* Give the client a chance to modify newFrameArea. */
if (cr->w_reposition != NULL)
(*(cr->w_reposition))(w, &newFrameArea, FALSE);
/*
* Now, actually modify the window and its position.
*/
/* Compute new stuff. */
if (w->w_flags & WIND_FULLSCREEN)
{
newDepth = w->w_oldDepth;
w->w_flags &= ~WIND_FULLSCREEN;
}
else
{
newDepth = 0;
w->w_flags |= WIND_FULLSCREEN;
/* Record old depth and area. */
w->w_oldArea = w->w_frameArea;
w->w_oldDepth = 0;
for (w2 = windTopWindow; w2 != w; w2 = w2->w_nextWindow)
{
ASSERT(w2 != (MagWindow *) NULL, "WindFullScreen");
w->w_oldDepth += 1;
}
}
/* Change the view and screen location. */
w->w_frameArea = newFrameArea;
WindSetWindowAreas(w);
windSetWindowPosition(w);
WindMove(w, &w->w_surfaceArea);
/* Move the window to the proper depth. */
if (windTopWindow != (MagWindow *) NULL)
{
if (newDepth == 0)
{
switch ( WindPackageType )
{
case WIND_X_WINDOWS:
break;
default:
WindOver(w);
}
}
else
{
windUnlink(w);
w2 = windTopWindow;
for (i=1; i<newDepth; i++)
if (w2->w_nextWindow != NULL) w2 = w2->w_nextWindow;
w->w_nextWindow = w2->w_nextWindow;
w->w_prevWindow = w2;
w2->w_nextWindow = w;
if (w->w_nextWindow == NULL)
windBottomWindow = w;
else
w->w_nextWindow->w_prevWindow = w;
windReClip();
}
}
/* Notify the client. */
if (cr->w_reposition != NULL)
(*(cr->w_reposition))(w, &newFrameArea, TRUE);
/* Record new display areas. */
switch (WindPackageType)
{
case WIND_X_WINDOWS:
if (GrConfigureWindowPtr != NULL)
(*GrConfigureWindowPtr)(w);
if (GrCreateBackingStorePtr != NULL &&
(!(w->w_flags & WIND_OBSCURED)))
(*GrCreateBackingStorePtr)(w);
break;
default:
WindAreaChanged((MagWindow *) NULL, (Rect *) NULL);
}
}