magic/windows/windClient.c

829 lines
22 KiB
C

/* windClient.c -
*
* Send button pushes and commands to the window's command
* interpreters.
*
* *********************************************************************
* * 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: /usr/cvsroot/magic-8.0/windows/windClient.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include "utils/magic.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "windows/windows.h"
#include "graphics/glyphs.h"
#include "windows/windInt.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/main.h"
#include "utils/macros.h"
#include "utils/utils.h"
#include "utils/malloc.h"
#include "graphics/graphics.h"
#include "utils/styles.h"
#include "textio/txcommands.h"
#include "utils/undo.h"
/* The following defines are used to indicate corner positions
* of the box:
*/
#define WIND_BL 0
#define WIND_BR 1
#define WIND_TR 2
#define WIND_TL 3
#define WIND_ILG -1
/* our window client ID */
global WindClient windClientID = (WindClient) NULL;
extern int windBorderCmd();
extern int windCaptionCmd(), windCrashCmd(), windCursorCmd();
extern int windFilesCmd(), windCloseCmd(), windOpenCmd();
extern int windQuitCmd(), windRedrawCmd();
extern int windResetCmd(), windSpecialOpenCmd();
extern int windOverCmd(), windUnderCmd(), windDebugCmd();
extern int windDumpCmd(), windHelpCmd();
extern int windMacroCmd(), windIntMacroCmd();
extern int windLogCommandsCmd(), windUpdateCmd(), windSleepCmd();
extern int windSetpointCmd();
extern int windPushbuttonCmd();
extern int windPauseCmd(), windGrstatsCmd();
extern int windGrowCmd();
extern int windUndoCmd(), windRedoCmd();
extern int windCenterCmd(), windScrollCmd();
extern int windVersionCmd(), windViewCmd(), windXviewCmd(), windZoomCmd();
extern int windScrollBarsCmd(), windPositionsCmd();
extern int windNamesCmd();
#ifdef MAGIC_WRAPPER
extern int windBypassCmd();
#else
extern int windEchoCmd(), windSourceCmd(), windSendCmd();
#endif
static Rect windFrameRect;
static MagWindow *windFrameWindow;
static int windButton = TX_LEFT_BUTTON;
static int windCorner = WIND_ILG; /* Nearest corner when button went
* down.
*/
/*
* ----------------------------------------------------------------------------
* windButtonSetCursor --
*
* Used to set the programmable cursor for a particular
* button state.
*
* Results:
* None.
*
* Side effects:
* Selects and sets a programmable cursor based on the given
* button (for sizing or moving) and corner.
* ----------------------------------------------------------------------------
*/
void
windButtonSetCursor(button, corner)
int button; /* Button that is down. */
int corner; /* Corner to be displayed in cursor. */
{
switch (corner)
{
case WIND_BL:
if (button == TX_LEFT_BUTTON)
GrSetCursor(STYLE_CURS_LLWIND);
else
GrSetCursor(STYLE_CURS_LLWINDCORN);
break;
case WIND_BR:
if (button == TX_LEFT_BUTTON)
GrSetCursor(STYLE_CURS_LRWIND);
else
GrSetCursor(STYLE_CURS_LRWINDCORN);
break;
case WIND_TL:
if (button == TX_LEFT_BUTTON)
GrSetCursor(STYLE_CURS_ULWIND);
else
GrSetCursor(STYLE_CURS_ULWINDCORN);
break;
case WIND_TR:
if (button == TX_LEFT_BUTTON)
GrSetCursor(STYLE_CURS_URWIND);
else
GrSetCursor(STYLE_CURS_URWINDCORN);
break;
}
}
/*
* ----------------------------------------------------------------------------
* windGetCorner --
*
* Returns the corner of the window closest to a given screen location.
*
* Results:
* An integer value is returned, indicating the corner closest to
* the given screen location.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
int
windGetCorner(screenPoint, screenRect)
Point *screenPoint;
Rect *screenRect;
{
Rect r;
/* Find out which corner is closest. Consider only the
* intersection of the box with the window (otherwise it
* may not be possible to select off-screen corners.
*/
r = *screenRect;
GeoClip(&r, &GrScreenRect);
if (screenPoint->p_x < ((r.r_xbot + r.r_xtop)/2))
{
if (screenPoint->p_y < ((r.r_ybot + r.r_ytop)/2))
return WIND_BL;
else
return WIND_TL;
}
else
{
if (screenPoint->p_y < ((r.r_ybot + r.r_ytop)/2))
return WIND_BR;
else
return WIND_TR;
}
}
/*
* ----------------------------------------------------------------------------
* windMoveRect --
*
* Repositions a rectangle by one of its corners.
*
* Results:
* None.
*
* Side effects:
* The rectangle is changed so that the given corner is at the
* given position.
* ----------------------------------------------------------------------------
*/
void
windMoveRect(wholeRect, corner, p, rect)
bool wholeRect; /* move the whole thing? or just a corner? */
int corner; /* Specifies a corner in the format
* returned by ToolGetCorner.
*/
Point *p; /* New position of corner, in screen
* coordinates.
*/
Rect *rect;
{
int x, y, tmp;
/* Move the rect. If an illegal corner is specified, then
* move by the bottom-left corner.
*/
if (wholeRect)
{
switch (corner)
{
case WIND_BL:
x = p->p_x - rect->r_xbot;
y = p->p_y - rect->r_ybot;
break;
case WIND_BR:
x = p->p_x - rect->r_xtop;
y = p->p_y - rect->r_ybot;
break;
case WIND_TR:
x = p->p_x - rect->r_xtop;
y = p->p_y - rect->r_ytop;
break;
case WIND_TL:
x = p->p_x - rect->r_xbot;
y = p->p_y - rect->r_ytop;
break;
default:
x = p->p_x - rect->r_xbot;
y = p->p_y - rect->r_ybot;
break;
}
rect->r_xbot += x;
rect->r_ybot += y;
rect->r_xtop += x;
rect->r_ytop += y;
}
else
{
switch (corner)
{
case WIND_BL:
rect->r_xbot = p->p_x;
rect->r_ybot = p->p_y;
break;
case WIND_BR:
rect->r_xtop = p->p_x;
rect->r_ybot = p->p_y;
break;
case WIND_TR:
rect->r_xtop = p->p_x;
rect->r_ytop = p->p_y;
break;
case WIND_TL:
rect->r_xbot = p->p_x;
rect->r_ytop = p->p_y;
break;
}
/* If the movement turned the box inside out, turn it right
* side out again.
*/
if (rect->r_xbot > rect->r_xtop)
{
tmp = rect->r_xtop;
rect->r_xtop = rect->r_xbot;
rect->r_xbot = tmp;
}
if (rect->r_ybot > rect->r_ytop)
{
tmp = rect->r_ytop;
rect->r_ytop = rect->r_ybot;
rect->r_ybot = tmp;
}
}
}
/*
* ----------------------------------------------------------------------------
* Button Routines --
*
* This page contains a set of routines to handle the puck
* buttons within the window border.
*
* Results:
* None.
*
* Side effects:
* Left button: used to move the whole window by the lower-left corner.
* Right button: used to re-size the window by its upper-right corner.
* If one of the left or right buttons is pushed, then the
* other is pushed, the corner is switched to the nearest
* one to the cursor. This corner is remembered for use
* in box positioning/sizing when both buttons have gone up.
* Bottom button: Center the view on the point where the crosshair is
* at when the button is released.
* ----------------------------------------------------------------------------
*/
void
windFrameDown(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if (WindOldButtons == 0)
{
windFrameRect = w->w_frameArea;
windFrameWindow = w;
windButton = cmd->tx_button;
}
#define BOTHBUTTONS (TX_LEFT_BUTTON | TX_RIGHT_BUTTON)
if ((WindNewButtons & BOTHBUTTONS) == BOTHBUTTONS)
{
windCorner = windGetCorner(&(cmd->tx_p), &(windFrameWindow->w_frameArea));
}
else if (cmd->tx_button == TX_LEFT_BUTTON)
{
windCorner = WIND_BL;
windButtonSetCursor(windButton, windCorner);
}
else if (cmd->tx_button == TX_RIGHT_BUTTON)
{
windCorner = WIND_TR;
windButtonSetCursor(windButton, windCorner);
}
}
/*ARGSUSED*/
void
windFrameUp(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if (WindNewButtons == 0)
{
GrSetCursor(STYLE_CURS_NORMAL);
switch (cmd->tx_button)
{
case TX_LEFT_BUTTON:
case TX_RIGHT_BUTTON:
windMoveRect( (windButton == TX_LEFT_BUTTON),
windCorner, &(cmd->tx_p), &windFrameRect);
WindReframe(windFrameWindow, &windFrameRect, FALSE,
(windButton == TX_LEFT_BUTTON) );
break;
}
}
else
{
/* If both buttons are down and one is released, we just change
* the cursor to reflect the current corner and the remaining
* button (i.e. move or size window).
*/
windCorner = windGetCorner(&(cmd->tx_p), &(windFrameWindow->w_frameArea));
windButtonSetCursor(windButton, windCorner);
}
}
/*
* ----------------------------------------------------------------------------
* windFrameButtons --
*
* Handle button pushes to the window frame area (zoom and scroll)
* Always handle scroll bars ourselves, even if there is an external
* window package. BUT---in the Tcl/Tk version of Magic, the
* window does not set WIND_SCROLLBARS, causing this routine to be
* bypassed.
*
* Results:
* TRUE if the button was pushed in the frame and handled by one
* of the button handler routines, FALSE if not.
*
* Side effects:
* Depends upon where the button was pushed.
* ----------------------------------------------------------------------------
*/
bool
windFrameButtons(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
extern void windBarLocations();
Rect leftBar, botBar, up, down, right, left, zoom;
Point p;
if (w == NULL) return FALSE;
p.p_x = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
p.p_y = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
if ((w->w_flags & WIND_SCROLLBARS) != 0)
{
windBarLocations(w, &leftBar, &botBar, &up, &down, &right, &left, &zoom);
if (cmd->tx_button == TX_MIDDLE_BUTTON) {
if (GEO_ENCLOSE(&cmd->tx_p, &leftBar))
{
/* move elevator */
p.p_x = 0;
p.p_y = w->w_bbox->r_ybot +
((w->w_bbox->r_ytop - w->w_bbox->r_ybot) *
(cmd->tx_p.p_y - leftBar.r_ybot))
/ (leftBar.r_ytop - leftBar.r_ybot) -
(w->w_surfaceArea.r_ytop + w->w_surfaceArea.r_ybot)/2;
WindScroll(w, &p, (Point *) NULL);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &botBar))
{
/* move elevator */
p.p_y = 0;
p.p_x = w->w_bbox->r_xbot +
((w->w_bbox->r_xtop - w->w_bbox->r_xbot) *
(cmd->tx_p.p_x - botBar.r_xbot))
/ (botBar.r_xtop - botBar.r_xbot) -
(w->w_surfaceArea.r_xtop + w->w_surfaceArea.r_xbot)/2;
WindScroll(w, &p, (Point *) NULL);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &up))
{
/* scroll up */
p.p_y = -p.p_y;
p.p_x = 0;
WindScroll(w, (Point *) NULL, &p);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &down))
{
/* scroll down */
p.p_x = 0;
WindScroll(w, (Point *) NULL, &p);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &right))
{
/* scroll right */
p.p_x = -p.p_x;
p.p_y = 0;
WindScroll(w, (Point *) NULL, &p);
return TRUE;
}
else if (GEO_ENCLOSE(&cmd->tx_p, &left))
{
/* scroll left */
p.p_y = 0;
WindScroll(w, (Point *) NULL, &p);
return TRUE;
}
}
if (GEO_ENCLOSE(&cmd->tx_p, &zoom)) {
/* zoom in, out, or view */
switch (cmd->tx_button)
{
case TX_LEFT_BUTTON:
WindZoom(w, 2.0);
break;
case TX_MIDDLE_BUTTON:
WindView(w);
break;
case TX_RIGHT_BUTTON:
WindZoom(w, 0.5);
break;
}
return TRUE;
}
}
return FALSE;
}
/*
* ----------------------------------------------------------------------------
* windClientButtons --
*
* Handle button pushes to the window border.
*
* Results:
* None.
*
* Side effects:
* depends upon where the button was pushed.
* ----------------------------------------------------------------------------
*/
void
windClientButtons(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
/*
* Is this an initial 'down push' in a non-iconic window? If so, we
* will initiate some user-interaction sequence, such as moving the corner
* of the window or growing it to full-screen size.
*
* (An 'iconic' window is one that is closed down to an icon -- this
* currently only happens when we are using the Sun window package, but
* in the future it might happen in other cases too.)
*
*/
if ((WindOldButtons == 0) && ((w->w_flags & WIND_ISICONIC) == 0))
{
/* single button down */
Point p;
Rect caption;
windFrameWindow = NULL;
if (w == NULL) return;
p.p_x = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
p.p_y = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
caption = w->w_allArea;
if ((w->w_flags & WIND_CAPTION) != 0)
caption.r_ybot = caption.r_ytop - TOP_BORDER(w) + 1;
else
caption.r_ybot = caption.r_ytop;
/* Handle 'grow' for our window package. */
if (WindPackageType == WIND_MAGIC_WINDOWS)
{
if ((cmd->tx_button == TX_MIDDLE_BUTTON) &&
GEO_ENCLOSE(&cmd->tx_p, &caption))
{
WindFullScreen(w);
return;
}
}
if (windFrameButtons(w, cmd)) return;
/* Otherwise, continue onward */
}
/*
* At this point, we have decided that the button was not an initial
* button push for Magic's window package. Maybe an external window
* package wants it, or maybe it is a continuation of a previous Magic
* sequence (such as moving a corner of a window).
*/
switch ( WindPackageType )
{
case WIND_X_WINDOWS:
break;
default:
/* Magic Windows */
if (cmd->tx_button == TX_MIDDLE_BUTTON)
return;
if ((cmd->tx_buttonAction == TX_BUTTON_UP) &&
(windFrameWindow == NULL))
return;
/* no special area or else an up push -- reframe window */
switch (cmd->tx_buttonAction)
{
case TX_BUTTON_DOWN:
windFrameDown(w, cmd);
break;
case TX_BUTTON_UP:
windFrameUp(w, cmd);
break;
default:
TxError("windClientButtons() failed!\n");
break;
}
}
}
/*
* ----------------------------------------------------------------------------
* WindButtonInFrame --
*
* To be called from the graphics packages. Handle button pushes to
* the window frame area (zoom and scroll) by calling windFrameButtons()
* if it appears to be appropriate for this MagWindow structure.
* This bypasses the key macro handler, thus hard-coding the button
* actions for the frame.
*
* Results:
* TRUE if the button was pushed in the frame and handled by one
* of the button handler routines, FALSE if not.
*
* Side effects:
* Depends upon where the button was pushed.
* ----------------------------------------------------------------------------
*/
bool
WindButtonInFrame(w, x, y, b)
MagWindow *w;
int x;
int y;
int b;
{
TxCommand cmd;
cmd.tx_p.p_x = x;
cmd.tx_p.p_y = y;
cmd.tx_button = b;
if (windFrameButtons(w, &cmd))
{
WindUpdate();
return TRUE;
}
return FALSE;
}
/*
* ----------------------------------------------------------------------------
*
* windClientInterp
*
* Window's command interpreter.
* Dispatches to long commands, providing them with the window and command
* we are passed.
*
* Results:
* none.
*
* Side effects:
* Whatever occur as a result of executing the long
* command supplied.
*
* ----------------------------------------------------------------------------
*/
void
windCmdInterp(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int cmdNum;
switch (cmd->tx_button)
{
case TX_LEFT_BUTTON:
case TX_RIGHT_BUTTON:
case TX_MIDDLE_BUTTON:
windClientButtons(w, cmd);
break;
case TX_NO_BUTTON:
if (WindExecute(w, windClientID, cmd) >= 0)
UndoNext();
break;
default:
ASSERT(FALSE, "windCmdInterp");
break;
}
}
/*
* ----------------------------------------------------------------------------
* windCmdInit --
*
* Initialize the window client.
*
* Results:
* None.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
void
windClientInit()
{
windClientID = WindAddClient(WINDOW_CLIENT, ( bool (*)() ) NULL,
( bool (*)() ) NULL, ( void (*)() ) NULL, windCmdInterp,
( void (*)() ) NULL, ( bool (*)() ) NULL, ( void (*)() ) NULL,
(GrGlyph *) NULL);
/* Set up all of the window commands */
#ifdef MAGIC_WRAPPER
WindAddCommand(windClientID,
"*bypass command run command independently of the command line",
windBypassCmd, FALSE);
#endif
WindAddCommand(windClientID,
"*crash cause a core dump",
windCrashCmd, FALSE);
WindAddCommand(windClientID,
"*files print out currently open files",
windFilesCmd, FALSE);
WindAddCommand(windClientID,
"*grstats print out stats on graphics",
windGrstatsCmd, FALSE);
WindAddCommand(windClientID,
"*pause [args] print args and wait for <cr>",
windPauseCmd, FALSE);
WindAddCommand(windClientID,
"*winddebug set debugging mode",
windDebugCmd, FALSE);
WindAddCommand(windClientID,
"*winddump print out debugging info",
windDumpCmd, FALSE);
WindAddCommand(windClientID,
"center [x y] center window on the cursor or indicated coordinate",
windCenterCmd, FALSE);
WindAddCommand(windClientID,
"closewindow [name] close a window",
windCloseCmd, FALSE);
WindAddCommand(windClientID,
"cursor return magic coordinates of the cursor",
windCursorCmd, FALSE);
WindAddCommand(windClientID,
"grow blow a window up to full-screen size or back again",
windGrowCmd, FALSE);
WindAddCommand(windClientID,
"help [pattern] print out synopses for all commands valid\n\
in the current window (or just those\n\
containing pattern)",
windHelpCmd, FALSE);
WindAddCommand(windClientID,
"imacro [char [string]] define or print an interactive macro called char",
windIntMacroCmd, FALSE);
WindAddCommand(windClientID,
"logcommands [file [update]]\n\
log all commands into a file",
windLogCommandsCmd, FALSE);
WindAddCommand(windClientID,
"macro [char [string]] define or print a macro called char",
windMacroCmd, FALSE);
WindAddCommand(windClientID,
"openwindow [cell][name]\n\
open a new window with indicated name, bound to indicated cell",
windOpenCmd, FALSE);
WindAddCommand(windClientID,
"over move a window over (on top of) the rest",
windOverCmd, FALSE);
WindAddCommand(windClientID,
"pushbutton button act push a mouse button",
windPushbuttonCmd, FALSE);
WindAddCommand(windClientID,
"redo [count] redo commands",
windRedoCmd, FALSE);
WindAddCommand(windClientID,
"redraw redraw the display",
windRedrawCmd, FALSE);
WindAddCommand(windClientID,
"reset reset the display",
windResetCmd, FALSE);
WindAddCommand(windClientID,
"scroll dir [amount] scroll the window",
windScrollCmd, FALSE);
WindAddCommand(windClientID,
"setpoint [x y [WID]] force to cursor (point) to x,y in window WID",
windSetpointCmd, FALSE);
WindAddCommand(windClientID,
"sleep seconds sleep for a number of seconds",
windSleepCmd, FALSE);
WindAddCommand(windClientID,
"specialopen [coords] type [args]\n\
open a special window",
windSpecialOpenCmd, FALSE);
WindAddCommand(windClientID,
"quit exit magic",
windQuitCmd, FALSE);
WindAddCommand(windClientID,
"underneath move a window underneath the rest",
windUnderCmd, FALSE);
WindAddCommand(windClientID,
"undo [count] undo commands",
windUndoCmd, FALSE);
WindAddCommand(windClientID,
#ifdef MAGIC_WRAPPER
"updatedisplay [suspend|resume]\n\
force display update, or suspend/resume updates",
#else
"updatedisplay force the display to be updated",
#endif
windUpdateCmd, FALSE);
WindAddCommand(windClientID,
"version print out version info",
windVersionCmd, FALSE);
WindAddCommand(windClientID,
"view [get] zoom window out so everything is visible",
windViewCmd, FALSE);
WindAddCommand(windClientID,
"windowborder [on|off] toggle border drawing for new windows",
windBorderCmd, FALSE);
WindAddCommand(windClientID,
"windowcaption [on|off] toggle title caption for new windows",
windCaptionCmd, FALSE);
WindAddCommand(windClientID,
"windowscrollbars [on|off]\n\
toggle scroll bars for new windows",
windScrollBarsCmd, FALSE);
WindAddCommand(windClientID,
"windowpositions [file] print out window positions",
windPositionsCmd, FALSE);
WindAddCommand(windClientID,
"xview zoom window out so everything is unexpanded",
windXviewCmd, FALSE);
WindAddCommand(windClientID,
"zoom amount zoom window by amount",
windZoomCmd, FALSE);
WindAddCommand(windClientID,
"windownames [all|type] get name of current or all windows",
windNamesCmd, FALSE);
#ifndef MAGIC_WRAPPER
WindAddCommand(windClientID,
"echo [-n] [strings] print text on the terminal",
windEchoCmd, FALSE);
WindAddCommand(windClientID,
"send type command send a command to a certain window type",
windSendCmd, FALSE);
WindAddCommand(windClientID,
"source filename read in commands from file",
windSourceCmd, FALSE);
#endif
}