/* 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 #include #include #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)) { const 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); } }