453 lines
13 KiB
C
453 lines
13 KiB
C
/* windSend.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$";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "tcltk/tclmagic.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 "utils/stack.h"
|
|
#include "utils/utils.h"
|
|
#include "utils/signals.h"
|
|
#include "textio/txcommands.h"
|
|
|
|
clientRec *windClient = NULL;
|
|
|
|
bool windPrintCommands = FALSE; /* debugging flag */
|
|
|
|
global TxCommand *WindCurrentCmd; /* The current command.
|
|
*/
|
|
global MagWindow *WindCurrentWindow; /* The window at which the current command
|
|
* was invoked.
|
|
*/
|
|
|
|
global int WindOldButtons; /* The buttons for the last command */
|
|
global int WindNewButtons; /* The buttons this time */
|
|
|
|
static WindClient windGrabber = (WindClient) NULL;
|
|
/* If this variable is non-null then send
|
|
* all commands to it
|
|
*/
|
|
|
|
Stack *windGrabberStack = NULL;
|
|
|
|
/* Forward declarations */
|
|
|
|
extern void WindGrabInput();
|
|
extern void WindReleaseInput();
|
|
extern void windHelp();
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* WindSendCommand --
|
|
*
|
|
* Send a command to a window to be executed. If the window passed is
|
|
* NULL then whatever window is at the point given in the command is
|
|
* used.
|
|
*
|
|
* Results:
|
|
* 0 if the command was able to be processed.
|
|
* -1 on an ambiguous command error.
|
|
* -2 on an unknown command error.
|
|
* -3 on other error.
|
|
*
|
|
* Side effects:
|
|
* Whatever the window wishes to do with the command.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
WindSendCommand(w, cmd, quiet)
|
|
MagWindow *w;
|
|
TxCommand *cmd; /* A pointer to a command */
|
|
bool quiet; /* Don't print error/warning messages if this is set */
|
|
{
|
|
int windCmdNum, clientCmdNum;
|
|
clientRec *rc;
|
|
bool inside; /* tells us if we are inside of a window */
|
|
|
|
/* This thing is a horrendous mess. Changing WindClient to */
|
|
/* MagWindow in the arguments list is a big help, but the */
|
|
/* following code should be simplified. Namely, w == 0 */
|
|
/* only when the command comes from the command line. */
|
|
/* Tcl/Tk commands and graphics are set up to supply a */
|
|
/* valid w. Under normal conditions, we should not need to */
|
|
/* guess what the client is. */
|
|
|
|
/* The big problem here is that all windows are expected */
|
|
/* to take windClient commands---but that prevents setting */
|
|
/* up windows which don't. This should be handled better, */
|
|
/* probably at the low level of the MagWindow structure */
|
|
/* itself. */
|
|
|
|
if (windClient == (clientRec *) NULL)
|
|
windClient = (clientRec *) WindGetClient(WINDOW_CLIENT, TRUE);
|
|
|
|
/* ignore no-op commands */
|
|
if ( (cmd->tx_button == TX_NO_BUTTON) && (cmd->tx_argc == 0) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
inside = FALSE;
|
|
ASSERT( (cmd->tx_button == TX_NO_BUTTON) || (cmd->tx_argc == 0),
|
|
"WindSendCommand");
|
|
|
|
WindOldButtons = WindNewButtons;
|
|
if (cmd->tx_button == TX_NO_BUTTON)
|
|
{
|
|
if (windClient == (clientRec *)NULL) return -2;
|
|
|
|
/* If window commands are disallowed by the client (set by */
|
|
/* the client's WIND_COMMANDS flag), report no command. */
|
|
|
|
if ((w != NULL) && !(w->w_flags & WIND_COMMANDS))
|
|
windCmdNum = -2;
|
|
else
|
|
windCmdNum = Lookup(cmd->tx_argv[0], windClient->w_commandTable);
|
|
}
|
|
else
|
|
{
|
|
if (cmd->tx_buttonAction == TX_BUTTON_DOWN)
|
|
WindNewButtons |= cmd->tx_button;
|
|
else
|
|
WindNewButtons &= ~(cmd->tx_button);
|
|
}
|
|
|
|
/* If we were passed a NULL MagWindow pointer, try to determine the */
|
|
/* window from the command's window ID number. */
|
|
|
|
if (w == (MagWindow *)NULL)
|
|
{
|
|
if (cmd->tx_wid == WIND_UNKNOWN_WINDOW)
|
|
{
|
|
w = windSearchPoint( &(cmd->tx_p), &inside);
|
|
if (w != NULL)
|
|
cmd->tx_wid = w->w_wid;
|
|
}
|
|
else if (cmd->tx_wid >= 0)
|
|
w = WindSearchWid(cmd->tx_wid);
|
|
}
|
|
|
|
if (w != (MagWindow *) NULL)
|
|
{
|
|
inside = GEO_ENCLOSE(&cmd->tx_p, &w->w_screenArea);
|
|
if ((!inside) && (w->w_flags & WIND_COMMANDS))
|
|
rc = windClient; /* Handles border regions */
|
|
else
|
|
rc = (clientRec *) w->w_client;
|
|
}
|
|
else
|
|
/* Can't determine a window---assume a windowless layout client */
|
|
rc = (clientRec *) WindGetClient(DEFAULT_CLIENT, TRUE);
|
|
|
|
if (windGrabber != (WindClient) NULL)
|
|
{
|
|
/* this client wants to hog all commands */
|
|
rc = (clientRec *) windGrabber;
|
|
}
|
|
|
|
/* At this point, the command is all set up and ready to send to
|
|
* the client.
|
|
*/
|
|
ASSERT(rc != (clientRec *) NULL, "WindSendCommand");
|
|
if (windPrintCommands)
|
|
{
|
|
TxPrintf("Sending command:\n");
|
|
windPrintCommand(cmd);
|
|
}
|
|
WindCurrentCmd = cmd;
|
|
WindCurrentWindow = w;
|
|
|
|
if (cmd->tx_button == TX_NO_BUTTON)
|
|
{
|
|
clientCmdNum = Lookup(cmd->tx_argv[0], rc->w_commandTable);
|
|
|
|
if ((clientCmdNum == -1) || (windCmdNum == -1))
|
|
{
|
|
if (quiet == FALSE)
|
|
TxError("That command abbreviation is ambiguous.\n");
|
|
return -1;
|
|
}
|
|
if ((windCmdNum == -2) && (clientCmdNum == -2))
|
|
{
|
|
/* Not a valid command. Help the user out by telling him
|
|
* what might be wrong. And also print out the command!
|
|
*/
|
|
if (quiet == FALSE)
|
|
{
|
|
TxError("Unknown command:");
|
|
windPrintCommand(cmd);
|
|
if (WindNewButtons != 0)
|
|
{
|
|
char *bname = "unknown";
|
|
if (WindNewButtons & TX_LEFT_BUTTON) bname = "left";
|
|
else if (WindNewButtons & TX_RIGHT_BUTTON) bname = "right";
|
|
else if (WindNewButtons & TX_MIDDLE_BUTTON) bname = "middle";
|
|
|
|
TxError( "'%s' window is waiting for %s button to be released.\n",
|
|
rc->w_clientName, bname);
|
|
}
|
|
return -3;
|
|
}
|
|
else if (windGrabber != (WindClient) NULL)
|
|
{
|
|
if (quiet == FALSE)
|
|
TxError( "'%s' window is grabbing all input.\n", rc->w_clientName);
|
|
return -3;
|
|
}
|
|
|
|
if (quiet == FALSE)
|
|
TxError("Did you point to the correct window?\n");
|
|
return -2;
|
|
}
|
|
|
|
/* intercept 'help' */
|
|
if ((windCmdNum >= 0) &&
|
|
(strncmp(windClient->w_commandTable[windCmdNum],
|
|
"help", 4) == 0) )
|
|
{
|
|
TxUseMore();
|
|
windHelp(cmd, "Global", windClient->w_commandTable);
|
|
if (rc != windClient)
|
|
windHelp(cmd, rc->w_clientName, rc->w_commandTable);
|
|
TxStopMore();
|
|
return 0;
|
|
}
|
|
|
|
/* If both command tables point to window commands, */
|
|
/* only do the command once. */
|
|
|
|
if (rc == windClient) clientCmdNum = -2;
|
|
|
|
/* Ambiguity resolution. If only one client reports a */
|
|
/* valid command, then execute it. If both clients */
|
|
/* report a valid command, then compare them against */
|
|
/* each other so that a full command name will take */
|
|
/* precedence over an ambiguous command abbreviation. */
|
|
/* Finally, if this doesn't resolve the command, the */
|
|
/* registered client takes precedence over the general- */
|
|
/* purpose window client, allowing clients to override */
|
|
/* the general-purpose functions (like "zoom" and */
|
|
/* "view", for instance). This is the reverse of how */
|
|
/* it was implemented prior to magic-7.3.61, but that */
|
|
/* makes no sense. */
|
|
|
|
if ((windCmdNum < 0) && (clientCmdNum >= 0))
|
|
(*(rc->w_command))(w, cmd);
|
|
else if ((windCmdNum >= 0) && (clientCmdNum < 0))
|
|
(*(windClient->w_command))(w, cmd);
|
|
else if ((windCmdNum >= 0) && (clientCmdNum >= 0))
|
|
{
|
|
char *(ownTable[3]);
|
|
int ownCmdNum;
|
|
|
|
ownTable[0] = rc->w_commandTable[clientCmdNum];
|
|
ownTable[1] = windClient->w_commandTable[windCmdNum];
|
|
ownTable[2] = NULL;
|
|
ownCmdNum = Lookup(cmd->tx_argv[0], ownTable);
|
|
ASSERT(ownCmdNum != -2, "WindSendCommand");
|
|
if (ownCmdNum == -1)
|
|
{
|
|
if (quiet == FALSE)
|
|
TxError("That command abbreviation is ambiguous\n");
|
|
return -1;
|
|
}
|
|
if (ownCmdNum == 0)
|
|
(*(rc->w_command))(w, cmd);
|
|
else
|
|
(*(windClient->w_command))(w, cmd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* A button has been pushed.
|
|
* If there were no buttons pressed on the last command
|
|
* now there are, and direct all future button pushes to this
|
|
* client until all buttons are up again.
|
|
*/
|
|
|
|
/* windClient is responsible for processing actions in the */
|
|
/* window border, assuming that the window handles its own */
|
|
/* borders. Scrollbars are only available on the layout */
|
|
/* window, which is the "default" client. So we check for a */
|
|
/* position in the border region, and launch the window client */
|
|
/* if it is. */
|
|
|
|
if (WindOldButtons == 0) WindGrabInput((WindClient) rc);
|
|
else if (WindNewButtons == 0) WindReleaseInput((WindClient) rc);
|
|
(*(rc->w_command))(w, cmd);
|
|
}
|
|
|
|
/* A client may modify WindNewButtons & WindOldButtons in rare cases,
|
|
* so we better check again.
|
|
*/
|
|
if ((WindNewButtons == 0) && (windGrabber != (WindClient) NULL))
|
|
WindReleaseInput((WindClient) rc);
|
|
|
|
/* This is a bit of a hack, because it shouldn't be done in so many
|
|
* places, but the command logging needs to regenerate the setpoint
|
|
* positions as commands, and it needs the command's Y value to be
|
|
* correct for the command. It is okay to modify the command's
|
|
* recorded position value, because logging the command is the only
|
|
* other thing done with the structure.
|
|
*/
|
|
if (w != NULL)
|
|
{
|
|
switch (WindPackageType)
|
|
{
|
|
case WIND_X_WINDOWS:
|
|
/* Windows have origin at lower-left corner */
|
|
cmd->tx_p.p_y = w->w_allArea.r_ytop - cmd->tx_p.p_y;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* WindGrabInput --
|
|
*
|
|
* Grab all input -- that is, send all further commands to the
|
|
* specified client.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* pushes old grabber onto a stack.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
WindGrabInput(client)
|
|
WindClient client;
|
|
{
|
|
ASSERT( client != NULL, "WindGrabInput");
|
|
StackPush( (ClientData) windGrabber, windGrabberStack);
|
|
windGrabber = client;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* WindReleaseInput --
|
|
*
|
|
* Stop grabbing the input (the inverse of WindGrabInput).
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* The previous grabber (if any) is restored.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
WindReleaseInput(client)
|
|
WindClient client;
|
|
{
|
|
ASSERT( client == windGrabber, "WindReleaseInput");
|
|
windGrabber = (WindClient) StackPop(windGrabberStack);
|
|
}
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
* windHelp --
|
|
*
|
|
* Print out help information for a client.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
windHelp(cmd, name, table)
|
|
TxCommand *cmd; /* Information about command options. */
|
|
char *name; /* Name of client for whom help is being
|
|
* printed.
|
|
*/
|
|
char *table[]; /* Client's command table. */
|
|
{
|
|
static char *capName = NULL;
|
|
static char patString[200], *pattern;
|
|
bool wiz;
|
|
char **tp;
|
|
#define WIZARD_CHAR '*'
|
|
|
|
if (cmd->tx_argc > 2)
|
|
{
|
|
TxError("Usage: help [pattern]\n");
|
|
return;
|
|
}
|
|
|
|
if (SigInterruptPending) return;
|
|
(void) StrDup(&capName, name);
|
|
if (islower(capName[0])) capName[0] += 'A' - 'a';
|
|
|
|
TxPrintf("\n");
|
|
if ((cmd->tx_argc == 2) && strcmp(cmd->tx_argv[1], "wizard") == 0)
|
|
{
|
|
pattern = "*";
|
|
wiz = TRUE;
|
|
TxPrintf("Wizard %s Commands\n", capName);
|
|
TxPrintf("----------------------\n");
|
|
}
|
|
else
|
|
{
|
|
if (cmd->tx_argc == 2)
|
|
{
|
|
pattern = patString;
|
|
(void) sprintf(patString, "*%.195s*", cmd->tx_argv[1]);
|
|
}
|
|
else
|
|
pattern = "*";
|
|
wiz = FALSE;
|
|
TxPrintf("%s Commands\n", capName);
|
|
TxPrintf("---------------\n");
|
|
}
|
|
for (tp = table; *tp != (char *) NULL; tp++)
|
|
{
|
|
if (SigInterruptPending) return;
|
|
if (Match(pattern, *tp) && (wiz ^ (**tp != WIZARD_CHAR)) )
|
|
TxPrintf("%s\n", *tp);
|
|
}
|
|
}
|