1 /* windSend.c -
2  *
3  *	Send button pushes and commands to the window's command
4  *	interpreters.
5  *
6  *     *********************************************************************
7  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
8  *     * Permission to use, copy, modify, and distribute this              *
9  *     * software and its documentation for any purpose and without        *
10  *     * fee is hereby granted, provided that the above copyright          *
11  *     * notice appear in all copies.  The University of California        *
12  *     * makes no representations about the suitability of this            *
13  *     * software for any purpose.  It is provided "as is" without         *
14  *     * express or implied warranty.  Export of this software outside     *
15  *     * of the United States of America may require an export license.    *
16  *     *********************************************************************
17  */
18 
19 #ifndef lint
20 static char rcsid[] __attribute__ ((unused)) = "$Header$";
21 #endif  /* not lint */
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
26 
27 #include "tcltk/tclmagic.h"
28 #include "utils/magic.h"
29 #include "textio/textio.h"
30 #include "utils/geometry.h"
31 #include "windows/windows.h"
32 #include "graphics/glyphs.h"
33 #include "windows/windInt.h"
34 #include "utils/stack.h"
35 #include "utils/utils.h"
36 #include "utils/signals.h"
37 #include "textio/txcommands.h"
38 
39 clientRec *windClient = NULL;
40 
41 bool windPrintCommands = FALSE;	/* debugging flag */
42 
43 global TxCommand *WindCurrentCmd; /* The current command.
44 				   */
45 global MagWindow *WindCurrentWindow; /* The window at which the current command
46 				   * was invoked.
47 				   */
48 
49 global int WindOldButtons;	/* The buttons for the last command */
50 global int WindNewButtons;	/* The buttons this time */
51 
52 static WindClient windGrabber =  (WindClient) NULL;
53 				/* If this variable is non-null then send
54 				 * all commands to it
55 				 */
56 
57 Stack *windGrabberStack = NULL;
58 
59 /* Forward declarations */
60 
61 extern void WindGrabInput();
62 extern void WindReleaseInput();
63 extern void windHelp();
64 
65 
66 /*
67  * ----------------------------------------------------------------------------
68  * WindSendCommand --
69  *
70  *	Send a command to a window to be executed.  If the window passed is
71  *	NULL then whatever window is at the point given in the command is
72  *	used.
73  *
74  * Results:
75  *	 0 if the command was able to be processed.
76  *	-1 on an ambiguous command error.
77  *	-2 on an unknown command error.
78  *	-3 on other error.
79  *
80  * Side effects:
81  *	Whatever the window wishes to do with the command.
82  * ----------------------------------------------------------------------------
83  */
84 
85 int
WindSendCommand(w,cmd,quiet)86 WindSendCommand(w, cmd, quiet)
87     MagWindow *w;
88     TxCommand *cmd;	/* A pointer to a command */
89     bool quiet;		/* Don't print error/warning messages if this is set */
90 {
91     int windCmdNum, clientCmdNum;
92     clientRec *rc;
93     bool inside;	/* tells us if we are inside of a window */
94 
95     /* This thing is a horrendous mess.  Changing WindClient to	*/
96     /* MagWindow in the arguments list is a big help, but the	*/
97     /* following code should be simplified.  Namely, w == 0	*/
98     /* only when the command comes from the command line.	*/
99     /* Tcl/Tk commands and graphics are set up to supply a	*/
100     /* valid w.  Under normal conditions, we should not need to	*/
101     /* guess what the client is.				*/
102 
103     /* The big problem here is that all windows are expected	*/
104     /* to take windClient commands---but that prevents setting	*/
105     /* up windows which don't.  This should be handled better,	*/
106     /* probably at the low level of the MagWindow structure	*/
107     /* itself.							*/
108 
109     if (windClient == (clientRec *) NULL)
110 	windClient = (clientRec *) WindGetClient(WINDOW_CLIENT, TRUE);
111 
112     /* ignore no-op commands */
113     if ( (cmd->tx_button == TX_NO_BUTTON) && (cmd->tx_argc == 0) )
114     {
115 	return 0;
116     }
117 
118     inside = FALSE;
119     ASSERT( (cmd->tx_button == TX_NO_BUTTON) || (cmd->tx_argc == 0),
120 	"WindSendCommand");
121 
122     WindOldButtons = WindNewButtons;
123     if (cmd->tx_button == TX_NO_BUTTON)
124     {
125 	if (windClient == (clientRec *)NULL) return -2;
126 
127 	/* If window commands are disallowed by the client (set by */
128 	/* the client's WIND_COMMANDS flag), report no command.	   */
129 
130 	if ((w != NULL) && !(w->w_flags & WIND_COMMANDS))
131 	    windCmdNum = -2;
132 	else
133 	    windCmdNum = Lookup(cmd->tx_argv[0], windClient->w_commandTable);
134     }
135     else
136     {
137 	if (cmd->tx_buttonAction == TX_BUTTON_DOWN)
138 	    WindNewButtons |= cmd->tx_button;
139 	else
140 	    WindNewButtons &= ~(cmd->tx_button);
141     }
142 
143     /* If we were passed a NULL MagWindow pointer, try to determine the */
144     /* window from the command's window ID number.			*/
145 
146     if (w == (MagWindow *)NULL)
147     {
148 	if (cmd->tx_wid == WIND_UNKNOWN_WINDOW)
149 	{
150 	    w = windSearchPoint( &(cmd->tx_p), &inside);
151 	    if (w != NULL)
152 		cmd->tx_wid = w->w_wid;
153 	}
154 	else if (cmd->tx_wid >= 0)
155 	    w = WindSearchWid(cmd->tx_wid);
156     }
157 
158     if (w != (MagWindow *) NULL)
159     {
160 	inside = GEO_ENCLOSE(&cmd->tx_p, &w->w_screenArea);
161 	if ((!inside) && (w->w_flags & WIND_COMMANDS))
162 	    rc = windClient;	/* Handles border regions */
163 	else
164 	    rc = (clientRec *) w->w_client;
165     }
166     else
167 	/* Can't determine a window---assume a windowless layout client */
168 	rc = (clientRec *) WindGetClient(DEFAULT_CLIENT, TRUE);
169 
170     if (windGrabber != (WindClient) NULL)
171     {
172 	/* this client wants to hog all commands */
173 	rc = (clientRec *) windGrabber;
174     }
175 
176     /* At this point, the command is all set up and ready to send to
177      * the client.
178      */
179     ASSERT(rc != (clientRec *) NULL, "WindSendCommand");
180     if (windPrintCommands)
181     {
182 	TxPrintf("Sending command:\n");
183 	windPrintCommand(cmd);
184     }
185     WindCurrentCmd = cmd;
186     WindCurrentWindow = w;
187 
188     if (cmd->tx_button == TX_NO_BUTTON)
189     {
190 	clientCmdNum = Lookup(cmd->tx_argv[0], rc->w_commandTable);
191 
192 	if ((clientCmdNum == -1) || (windCmdNum == -1))
193 	{
194 	    if (quiet == FALSE)
195 		TxError("That command abbreviation is ambiguous.\n");
196 	    return -1;
197 	}
198 	if ((windCmdNum == -2) && (clientCmdNum == -2))
199 	{
200 	    /* Not a valid command.  Help the user out by telling him
201 	     * what might be wrong. And also print out the command!
202 	     */
203 	    if (quiet == FALSE)
204 	    {
205 		TxError("Unknown command:");
206 		windPrintCommand(cmd);
207 		if (WindNewButtons != 0)
208 		{
209 		    char *bname = "unknown";
210 		    if (WindNewButtons & TX_LEFT_BUTTON) bname = "left";
211 		    else if (WindNewButtons & TX_RIGHT_BUTTON) bname = "right";
212 		    else if (WindNewButtons & TX_MIDDLE_BUTTON) bname = "middle";
213 
214 		    TxError( "'%s' window is waiting for %s button to be released.\n",
215 		    		rc->w_clientName, bname);
216 		}
217 		return -3;
218 	    }
219 	    else if (windGrabber != (WindClient) NULL)
220 	    {
221 		if (quiet == FALSE)
222 		    TxError( "'%s' window is grabbing all input.\n", rc->w_clientName);
223 		return -3;
224 	    }
225 
226 	    if (quiet == FALSE)
227 		TxError("Did you point to the correct window?\n");
228 	    return -2;
229 	}
230 
231 	/* intercept 'help' */
232 	if ((windCmdNum >= 0) &&
233 		(strncmp(windClient->w_commandTable[windCmdNum],
234 		"help", 4) == 0) )
235 	{
236 	    TxUseMore();
237 	    windHelp(cmd, "Global", windClient->w_commandTable);
238 	    if (rc != windClient)
239 		windHelp(cmd, rc->w_clientName, rc->w_commandTable);
240 	    TxStopMore();
241 	    return 0;
242 	}
243 
244 	/* If both command tables point to window commands,	*/
245 	/* only do the command once.				*/
246 
247 	if (rc == windClient) clientCmdNum = -2;
248 
249 	/* Ambiguity resolution.  If only one client reports a	*/
250 	/* valid command, then execute it.  If both clients	*/
251 	/* report a valid command, then compare them against	*/
252 	/* each other so that a full command name will take	*/
253 	/* precedence over an ambiguous command abbreviation.	*/
254 	/* Finally, if this doesn't resolve the command, the	*/
255 	/* registered client takes precedence over the general-	*/
256 	/* purpose window client, allowing clients to override	*/
257 	/* the general-purpose functions (like "zoom" and	*/
258 	/* "view", for instance).  This is the reverse of how	*/
259 	/* it was implemented prior to magic-7.3.61, but that	*/
260 	/* makes no sense.					*/
261 
262 	if ((windCmdNum < 0) && (clientCmdNum >= 0))
263 	    (*(rc->w_command))(w, cmd);
264 	else if ((windCmdNum >= 0) && (clientCmdNum < 0))
265 	    (*(windClient->w_command))(w, cmd);
266 	else if ((windCmdNum >= 0) && (clientCmdNum >= 0))
267 	{
268 	    char *(ownTable[3]);
269 	    int ownCmdNum;
270 
271 	    ownTable[0] = rc->w_commandTable[clientCmdNum];
272 	    ownTable[1] = windClient->w_commandTable[windCmdNum];
273 	    ownTable[2] = NULL;
274 	    ownCmdNum = Lookup(cmd->tx_argv[0], ownTable);
275 	    ASSERT(ownCmdNum != -2, "WindSendCommand");
276 	    if (ownCmdNum == -1)
277 	    {
278 		if (quiet == FALSE)
279 		    TxError("That command abbreviation is ambiguous\n");
280 		return -1;
281 	    }
282 	    if (ownCmdNum == 0)
283 		(*(rc->w_command))(w, cmd);
284 	    else
285 		(*(windClient->w_command))(w, cmd);
286 	}
287     }
288     else
289     {
290 	/* A button has been pushed.
291 	 * If there were no buttons pressed on the last command
292 	 * now there are, and direct all future button pushes to this
293 	 * client until all buttons are up again.
294 	 */
295 
296 	/* windClient is responsible for processing actions in the	*/
297 	/* window border, assuming that the window handles its own	*/
298 	/* borders.  Scrollbars are only available on the layout	*/
299 	/* window, which is the "default" client.  So we check for a	*/
300 	/* position in the border region, and launch the window client	*/
301 	/* if it is.							*/
302 
303         if (WindOldButtons == 0) WindGrabInput((WindClient) rc);
304 	else if (WindNewButtons == 0) WindReleaseInput((WindClient) rc);
305 	(*(rc->w_command))(w, cmd);
306     }
307 
308     /* A client may modify WindNewButtons & WindOldButtons in rare cases,
309      * so we better check again.
310      */
311     if ((WindNewButtons == 0) && (windGrabber != (WindClient) NULL))
312 	WindReleaseInput((WindClient) rc);
313 
314     return 0;
315 }
316 
317 
318 
319 
320 /*
321  * ----------------------------------------------------------------------------
322  * WindGrabInput --
323  *
324  *	Grab all input -- that is, send all further commands to the
325  *	specified client.
326  *
327  * Results:
328  *	None.
329  *
330  * Side effects:
331  *	pushes old grabber onto a stack.
332  * ----------------------------------------------------------------------------
333  */
334 
335 void
WindGrabInput(client)336 WindGrabInput(client)
337     WindClient client;
338 {
339     ASSERT( client != NULL, "WindGrabInput");
340     StackPush( (ClientData) windGrabber, windGrabberStack);
341     windGrabber = client;
342 }
343 
344 
345 
346 /*
347  * ----------------------------------------------------------------------------
348  * WindReleaseInput --
349  *
350  *	Stop grabbing the input (the inverse of WindGrabInput).
351  *
352  * Results:
353  *	None.
354  *
355  * Side effects:
356  *	The previous grabber (if any) is restored.
357  * ----------------------------------------------------------------------------
358  */
359 
360 void
WindReleaseInput(client)361 WindReleaseInput(client)
362     WindClient client;
363 {
364       ASSERT( client == windGrabber, "WindReleaseInput");
365       windGrabber = (WindClient) StackPop(windGrabberStack);
366 }
367 
368 
369 /*
370  * ----------------------------------------------------------------------------
371  * windHelp --
372  *
373  *	Print out help information for a client.
374  *
375  * Results:
376  *	None.
377  *
378  * Side effects:
379  *	None.
380  * ----------------------------------------------------------------------------
381  */
382 
383 void
windHelp(cmd,name,table)384 windHelp(cmd, name, table)
385     TxCommand *cmd;		/* Information about command options. */
386     char *name;			/* Name of client for whom help is being
387 				 * printed.
388 				 */
389     char *table[];		/* Client's command table. */
390 {
391     static char *capName = NULL;
392     static char patString[200], *pattern;
393     bool wiz;
394     char **tp;
395 #define WIZARD_CHAR	'*'
396 
397     if (cmd->tx_argc > 2)
398     {
399 	TxError("Usage:  help [pattern]\n");
400 	return;
401     }
402 
403     if (SigInterruptPending) return;
404     (void) StrDup(&capName, name);
405     if (islower(capName[0])) capName[0] += 'A' - 'a';
406 
407     TxPrintf("\n");
408     if ((cmd->tx_argc == 2) && strcmp(cmd->tx_argv[1], "wizard") == 0)
409     {
410 	pattern = "*";
411 	wiz = TRUE;
412 	TxPrintf("Wizard %s Commands\n", capName);
413 	TxPrintf("----------------------\n");
414     }
415     else
416     {
417 	if (cmd->tx_argc == 2)
418 	{
419 	    pattern = patString;
420 	    (void) sprintf(patString, "*%.195s*", cmd->tx_argv[1]);
421 	}
422 	else
423 	    pattern = "*";
424 	wiz = FALSE;
425 	TxPrintf("%s Commands\n", capName);
426 	TxPrintf("---------------\n");
427     }
428     for (tp = table; *tp != (char *) NULL; tp++)
429     {
430 	if (SigInterruptPending) return;
431 	if (Match(pattern, *tp) && (wiz ^ (**tp != WIZARD_CHAR)) )
432 	    TxPrintf("%s\n", *tp);
433     }
434 }
435