1 /*
2  * DBWbuttons.c --
3  *
4  * This file provides a general facility whereby clients that are
5  * willing to provide handlers for button presses in layout windows
6  * can themselves, and the current handler can be switched
7  * between them.  This file also provides the default button handler,
8  * which is used to move the box.
9  *
10  *     *********************************************************************
11  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
12  *     * Permission to use, copy, modify, and distribute this              *
13  *     * software and its documentation for any purpose and without        *
14  *     * fee is hereby granted, provided that the above copyright          *
15  *     * notice appear in all copies.  The University of California        *
16  *     * makes no representations about the suitability of this            *
17  *     * software for any purpose.  It is provided "as is" without         *
18  *     * express or implied warranty.  Export of this software outside     *
19  *     * of the United States of America may require an export license.    *
20  *     *********************************************************************
21  */
22 
23 #ifndef lint
24 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/dbwind/DBWbuttons.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
25 #endif  /* not lint */
26 
27 #include <stdio.h>
28 #include <string.h>
29 
30 #include "utils/magic.h"
31 #include "utils/geometry.h"
32 #include "tiles/tile.h"
33 #include "utils/hash.h"
34 #include "database/database.h"
35 #include "windows/windows.h"
36 #include "graphics/graphics.h"
37 #include "dbwind/dbwind.h"
38 #include "utils/styles.h"
39 #include "textio/textio.h"
40 #include "textio/txcommands.h"
41 #include "utils/utils.h"
42 
43 /* The arrays below are used to store information about the various
44  * button handlers that have registered themselves.
45  */
46 
47 #define MAXBUTTONHANDLERS 10
48 
49 static char *dbwButtonHandlers[MAXBUTTONHANDLERS];
50 			/* Name of each button handler:  used to select
51 			 * that handler as the current one.  A NULL entry
52 			 * here means that this handler slot isn't in use.
53 			 */
54 static char *dbwButtonDoc[MAXBUTTONHANDLERS];
55 			/* A documentation string for each handler:  tells
56 			 * what the button pushes and releases mean.
57 			 */
58 static void (*dbwButtonProcs[MAXBUTTONHANDLERS])();
59 			/* A procedure for each handler that is invoked
60 			 * on button presses and releases when that handler
61 			 * is the current one.
62 			 */
63 static int dbwButtonCursors[MAXBUTTONHANDLERS];
64 			/* Cursor shape to use for each handler. */
65 
66 static int dbwButtonCurrentIndex;
67 			/* Index of current handler. */
68 void (*DBWButtonCurrentProc)();
69 			/* Current button-handling procedure. */
70 
71 static int buttonCorner = TOOL_ILG;	/* Nearest corner when button went
72 					 * down.
73 					 */
74 
75 /*
76  * ----------------------------------------------------------------------------
77  *
78  * DBWAddButtonHandler --
79  *
80  * 	This procedure is called by would-be button handlers to register
81  *	themselves.  After a client has called this procedure, it may
82  *	make itself the current button handler by calling the procedure
83  *	DBWChangeButtonHandler.
84  *
85  * Results:
86  *	None.
87  *
88  * Side effects:
89  *	The client's information is added to the registry of potential
90  *	button handlers.  When the handler is made the current one (by
91  *	a call to DBWChangeButtonHandler) each button press or release
92  *	in a layout window causes proc to be invoked as follows:
93  *
94  *	int
95  *	proc(w, cmd)
96  *	    MagWindow *w;
97  *	    TxCommand *cmd;
98  *	{
99  *	}
100  *
101  *	W is the window in which the button was pushed, and cmd describes
102  *	exactly what happened.
103  *
104  * ----------------------------------------------------------------------------
105  */
106 
107 void
DBWAddButtonHandler(name,proc,cursor,doc)108 DBWAddButtonHandler(name, proc, cursor, doc)
109     char *name;			/* Name of this button handler.  This name
110 				 * is what's passed to DBWChangeButtonHandler
111 				 * to activate the handler.
112 				 */
113     void (*proc)();		/* Procedure to call on button actions when
114 				 * this handler is active.
115 				 */
116     int cursor;			/* Cursor shape (e.g. STYLE_CURS_NORMAL) to
117 				 * use when this handler is active.
118 				 */
119     char *doc;			/* A documentation string for this handler:
120 				 * describes what the button pushes do when
121 				 * this handler is active.
122 				 */
123 {
124     int i;
125 
126     for (i = 0; i < MAXBUTTONHANDLERS; i++)
127     {
128 	if (dbwButtonHandlers[i] != NULL) continue;
129 	(void) StrDup(&dbwButtonHandlers[i], name);
130 	(void) StrDup(&dbwButtonDoc[i], doc);
131 	dbwButtonProcs[i] = proc;
132 	dbwButtonCursors[i] = cursor;
133 	return;
134     }
135 
136     TxError("Can't add tool \"%s\":  no space in button handler\n",
137 	    name);
138     TxError("    table.  Get your Magic wizard to increase the size of\n");
139     TxError("    MAXBUTTONHANDLERS in DBWbuttons.c\n");
140 }
141 
142 /*
143  * ----------------------------------------------------------------------------
144  *
145  * DBWChangeButtonHandler --
146  *
147  * 	Change the active button handler.
148  *
149  * Results:
150  *	The return value is the name of the previous button handler, in
151  *	case the caller should wish to restore it.
152  *
153  * Side effects:
154  *	If name is NULL, then the "next" button handler is activated, in a
155  *	circular fashion.  If name isn't NULL, then it is the name of a
156  *	handler, which is activated.  If the name doesn't match a handler
157  *	then a message is printed and the handler isn't changed.
158  *
159  * ----------------------------------------------------------------------------
160  */
161 
162 char *
DBWChangeButtonHandler(name)163 DBWChangeButtonHandler(name)
164     char *name;			/* Name of new handler.  Must be a unique
165 				 * abbreviation of a name passed previously
166 				 * to DBAddButtonHandler, or NULL.
167 				 */
168 {
169     char *oldName = dbwButtonHandlers[dbwButtonCurrentIndex];
170     static int firstTime = TRUE;
171 
172     if (name == NULL)
173     {
174 	/* Just rotate to the next available client. */
175 
176 	while (TRUE)
177 	{
178 	    dbwButtonCurrentIndex += 1;
179 	    if (dbwButtonCurrentIndex >= MAXBUTTONHANDLERS)
180 		dbwButtonCurrentIndex = 0;
181 	    if (dbwButtonHandlers[dbwButtonCurrentIndex] == NULL)
182 		continue;
183 	    if (firstTime)
184 	    {
185 		firstTime = FALSE;
186 		TxPrintf("Switching to \"%s\" tool.",
187 		    dbwButtonHandlers[dbwButtonCurrentIndex]);
188 		TxPrintf("  If you didn't really want to switch,\n");
189 		TxPrintf("    type \":tool box\" to");
190 		TxPrintf(" switch back to the box tool.\n");
191 	    }
192 	    else
193 	    {
194 		TxPrintf("Switching to \"%s\" tool.\n",
195 			dbwButtonHandlers[dbwButtonCurrentIndex]);
196 	    }
197 	    break;
198 	}
199     }
200     else
201     {
202 	int i, match, length;
203 
204 	match = -1;
205 	length = strlen(name);
206 	for (i = 0; i < MAXBUTTONHANDLERS; i++)
207 	{
208 	    if (dbwButtonHandlers[i] == NULL) continue;
209 	    if (strncmp(name, dbwButtonHandlers[i], length) != 0) continue;
210 	    if (match >= 0)
211 	    {
212 		TxError("\"%s\" is an ambiguous tool name.", name);
213 		match = -2;
214 		break;
215 	    }
216 	    match = i;
217 	}
218 
219 	if (match == -1)
220 	    TxError("\"%s\" isn't a tool name.", name);
221 	if (match < 0)
222 	{
223 	    TxError("  The legal names are:\n");
224 	    for (i = 0; i < MAXBUTTONHANDLERS; i++)
225 	    {
226 		if (dbwButtonHandlers[i] == NULL) continue;
227 		TxError("    %s\n", dbwButtonHandlers[i]);
228 	    }
229 	    return oldName;
230 	}
231 	dbwButtonCurrentIndex = match;
232     }
233 
234     GrSetCursor(dbwButtonCursors[dbwButtonCurrentIndex]);
235     DBWButtonCurrentProc = dbwButtonProcs[dbwButtonCurrentIndex];
236     return oldName;
237 }
238 
239 /*
240  * ----------------------------------------------------------------------------
241  *
242  * DBWPrintButtonDoc --
243  *
244  * 	This procedure prints out documentation for the current
245  *	button handler.
246  *
247  * Results:
248  *	None.
249  *
250  * Side effects:
251  *	Stuff gets printed on the tty, ostensibly describing what
252  *	the current buttons do.
253  *
254  * ----------------------------------------------------------------------------
255  */
256 
257 void
DBWPrintButtonDoc()258 DBWPrintButtonDoc()
259 {
260     TxPrintf("%s", dbwButtonDoc[dbwButtonCurrentIndex]);
261 }
262 
263 
264 /*
265  * ----------------------------------------------------------------------------
266  *	dbwButtonSetCursor --
267  *
268  * 	Used to set the programmable cursor for a particular
269  *	button state.
270  *
271  * Results:
272  *	None.
273  *
274  * Side effects:
275  *	Selects and sets a programmable cursor based on the given
276  *	button (for sizing or moving) and corner.
277  * ----------------------------------------------------------------------------
278  */
279 
280 void
dbwButtonSetCursor(button,corner)281 dbwButtonSetCursor(button, corner)
282     int button;			/* Button that is down. */
283     int corner;			/* Corner to be displayed in cursor. */
284 
285 {
286     switch (corner)
287     {
288 	case TOOL_BL:
289 	    if (button == TX_LEFT_BUTTON)
290 		GrSetCursor(STYLE_CURS_LLBOX);
291 	    else
292 		GrSetCursor(STYLE_CURS_LLCORNER);
293 	    break;
294 	case TOOL_BR:
295 	    if (button == TX_LEFT_BUTTON)
296 		GrSetCursor(STYLE_CURS_LRBOX);
297 	    else
298 		GrSetCursor(STYLE_CURS_LRCORNER);
299 	    break;
300 	case TOOL_TL:
301 	    if (button == TX_LEFT_BUTTON)
302 		GrSetCursor(STYLE_CURS_ULBOX);
303 	    else
304 		GrSetCursor(STYLE_CURS_ULCORNER);
305 	    break;
306 	case TOOL_TR:
307 	    if (button == TX_LEFT_BUTTON)
308 		GrSetCursor(STYLE_CURS_URBOX);
309 	    else
310 		GrSetCursor(STYLE_CURS_URCORNER);
311 	    break;
312     }
313 }
314 
315 
316 /*
317  * ----------------------------------------------------------------------------
318  *
319  * DBWBoxHandler --
320  *
321  * 	This procedure is called to handle button actions in layout
322  *	windows when the "box" handler is active.  It adjusts the box
323  *	position and size.
324  *
325  * Results:
326  *	None.
327  *
328  * Side effects:
329  *	Left button:  used to move the whole box by the lower-left corner.
330  *	Right button: used to re-size the box by its upper-right corner.
331  *		If one of the left or right buttons is pushed, then the
332  *		other is pushed, the corner is switched to the nearest
333  *		one to the cursor.  This corner is remembered for use
334  *		in box positioning/sizing when both buttons have gone up.
335  *	Middle button: used to paint whatever layers are underneath the
336  *		crosshair.
337  *
338  * ----------------------------------------------------------------------------
339  */
340 
341 void
DBWBoxHandler(w,cmd)342 DBWBoxHandler(w, cmd)
343     MagWindow *w;			/* Window containing cursor. */
344     TxCommand *cmd;		/* Describes what happened. */
345 {
346     int button = cmd->tx_button;
347 
348     if (button == TX_MIDDLE_BUTTON)
349     {
350 	if (cmd->tx_buttonAction == TX_BUTTON_DOWN)
351 	    CmdPaintEraseButton(w, &cmd->tx_p, TRUE);
352 	return;
353     }
354 
355     if (cmd->tx_buttonAction == TX_BUTTON_DOWN)
356     {
357 	if ((WindNewButtons & (TX_LEFT_BUTTON|TX_RIGHT_BUTTON))
358 		== (TX_LEFT_BUTTON|TX_RIGHT_BUTTON))
359 	{
360 	    /* Both buttons are now down.  In this case, the FIRST
361 	     * button pressed determines whether we move or size,
362 	     * and the second button is just used as a signal to pick
363 	     * the closest corner.
364 	     */
365 
366 	    buttonCorner = ToolGetCorner(&cmd->tx_p);
367 	    if (button == TX_LEFT_BUTTON) button = TX_RIGHT_BUTTON;
368 	    else button = TX_LEFT_BUTTON;
369 	}
370 	else if (button == TX_LEFT_BUTTON) buttonCorner = TOOL_BL;
371 	else buttonCorner = TOOL_TR;
372 	dbwButtonSetCursor(button, buttonCorner);
373     }
374     else
375     {
376 	/* A button has just come up.  If both buttons are down and one
377 	 * is released, we just change the cursor to reflect the current
378 	 * corner and the remaining button (i.e. move or size box).
379 	 */
380 
381 	if (WindNewButtons != 0)
382 	{
383 	    if (button == TX_LEFT_BUTTON)
384 		dbwButtonSetCursor(TX_RIGHT_BUTTON, buttonCorner);
385 	    else dbwButtonSetCursor(TX_LEFT_BUTTON, buttonCorner);
386 	    return;
387 	}
388 
389 	/* The last button has been released.  Reset the cursor to normal
390 	 * form and then move or size the box.
391 	 */
392 
393 	GrSetCursor(STYLE_CURS_NORMAL);
394 	switch (button)
395 	{
396 	    case TX_LEFT_BUTTON:
397 		ToolMoveBox(buttonCorner, &cmd->tx_p, TRUE, (CellDef *) NULL);
398 		break;
399 	    case TX_RIGHT_BUTTON:
400 		ToolMoveCorner(buttonCorner, &cmd->tx_p, TRUE,
401 			(CellDef *) NULL);
402 	}
403     }
404 }
405