1 /*******************************************************************************
2 *                                                                              *
3 * DialogF -- modal dialog printf routine                                       *
4 *                                                                              *
5 * Copyright (C) 1999 Mark Edel                                                 *
6 *                                                                              *
7 * This is free software; you can redistribute it and/or modify it under the    *
8 * terms of the GNU General Public License as published by the Free Software    *
9 * Foundation; either version 2 of the License, or (at your option) any later   *
10 * version. In addition, you may distribute version of this program linked to   *
11 * Motif or Open Motif. See README for details.                                 *
12 *                                                                              *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *
15 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License        *
16 * for more details.                                                               *
17 *                                                                                *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple     *
20 * Place, Suite 330, Boston, MA  02111-1307 USA                                       *
21 *                                                                               *
22 * Nirvana Text Editor                                                                   *
23 * April 26, 1991                                                               *
24 *                                                                               *
25 * Written by Joy Kyriakopulos                                                       *
26 *                                                                               *
27 *******************************************************************************/
28 
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32 
33 #include "DialogF.h"
34 #include "misc.h"
35 #include "nedit_malloc.h"
36 
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <limits.h>
42 
43 #include <Xm/Xm.h>
44 #include <Xm/MessageB.h>
45 #include <Xm/DialogS.h>
46 #include <Xm/PushB.h>
47 #include <Xm/PushBG.h>
48 #include <Xm/SelectioB.h>
49 #include <X11/StringDefs.h>
50 #include <X11/Intrinsic.h>
51 #include <X11/keysym.h>
52 
53 #ifdef HAVE_DEBUG_H
54 #include "../debug.h"
55 #endif
56 
57 #define NUM_DIALOGS_SUPPORTED 6
58 #define NUM_BUTTONS_SUPPORTED 3		/* except prompt dialog */
59 #define NUM_BUTTONS_MAXPROMPT 4
60 #define MAX_TITLE_LEN 256
61 
62 enum dialogBtnIndecies {OK_BTN, APPLY_BTN, CANCEL_BTN, HELP_BTN};
63 
64 struct dfcallbackstruct {
65     unsigned button;		/* button pressed by user		     */
66     Boolean done_with_dialog;	/* set by callbacks; dialog can be destroyed */
67     unsigned apply_up;		/* will = 1 when apply button managed	     */
68     Boolean destroyed;		/* set when dialog is destroyed unexpectedly */
69 };
70 
71 static char **PromptHistory = NULL;
72 static int NPromptHistoryItems = -1;
73 
74 static void apply_callback (Widget w, struct dfcallbackstruct *client_data,
75 	caddr_t call_data);
76 static void help_callback (Widget w, struct dfcallbackstruct *client_data,
77 	caddr_t call_data);
78 static void cancel_callback (Widget w, struct dfcallbackstruct *client_data,
79 	caddr_t call_data);
80 static void ok_callback (Widget w, struct dfcallbackstruct *client_data,
81 	caddr_t call_data);
82 static void destroy_callback (Widget w, struct dfcallbackstruct *client_data,
83 	caddr_t call_data);
84 static void focusCB(Widget w, Widget dialog, caddr_t call_data);
85 static void addEscapeHandler(Widget dialog, struct dfcallbackstruct *df,
86     	int whichBtn);
87 static void escapeHelpCB(Widget w, XtPointer callData, XEvent *event,
88     	Boolean *cont);
89 static void escapeApplyCB(Widget w, XtPointer callData, XEvent *event,
90     	Boolean *cont);
91 static void createMnemonics(Widget w);
92 
93 /*******************************************************************************
94 * DialogF()                                                                    *
95 *                                                                              *
96 * function to put up modal versions of the XmCreate<type>Dialog functions      *
97 * (where <type> is Error, Information, Prompt, Question, Message, or Warning). *
98 * The purpose of this routine is to allow a printf-style dialog box to be      *
99 * invoked in-line without giving control back to the main loop. The message    *
100 * string can contain vsprintf specifications.                                  *
101 * DialogF displays the dialog in application-modal style, blocking the         *
102 * application and keeping the modal dialog as the top window until the user    *
103 * responds. DialogF accepts a variable number of arguments, so the calling     *
104 * routine needs to #include <stdarg.h>. The first button is automatically      *
105 * marked as the default button (activated when the user types Return,          *
106 * surrounded by a special outline), and any button named either Cancel, or     *
107 * Dismiss is marked as the cancel button (activated by the ESC key). Buttons   *
108 * marked Dismiss or Cancel are also triggered by close of dialog via the       *
109 * window close box. If there's no Cancel or Dismiss button, button 1 is        *
110 * invoked when the close box is pressed.                                       *
111 *                                                                              *
112 * Arguments:                                                                   *
113 *                                                                              *
114 * unsigned dialog_type  dialog type (e.g. DF_ERR for error dialog, refer to    *
115 *                       DialogF.h for dialog type values)                      *
116 * Widget parent         parent widget ID                                       *
117 * unsigned n            # of buttons to display; if set to 0, use defaults in  *
118 *                       XmCreate<type>Dialog; value in range 0 to              *
119 *                       NUM_BUTTONS_SUPPORTED (for prompt dialogs:             *
120 *                       NUM_BUTTONS_MAXPROMPT)                                 *
121 * char* title           dialog title                                           *
122 * char* msgstr          message string (may contain conversion specifications  *
123 *                       for vsprintf)                                          *
124 * char* input_string    if dialog type = DF_PROMPT, then: a character string   *
125 *                       array in which to put the string input by the user. Do *
126 *                       NOT include an input_string argument for other dialog  *
127 *                       types.                                                 *
128 * char* but_lbl         button label(s) for buttons requested (if n > 0, one   *
129 *                       but_lbl argument per button)                           *
130 * <anytype> <args>      arguments for vsprintf (if any)                        *
131 *                                                                              *
132 *                                                                              *
133 * Returns:                                                                     *
134 *                                                                              *
135 * button selected by user (i.e. 1, 2, or 3. or 4 for prompt)                   *
136 *                                                                              *
137 *                                                                              *
138 * Examples:                                                                    *
139 *                                                                              *
140 * but_pressed = DialogF (DF_QUES, toplevel, 3, "Direction?", "up",             *
141 *              "down", "other");                                               *
142 * but_pressed = DialogF (DF_ERR, toplevel, 1, "You can't do that!",            *
143 *              "Acknowledged");                                                *
144 * but_pressed = DialogF (DF_PROMPT, toplevel, 0, "New %s",                     *
145 *             new_sub_category, categories[i]);                                *
146 */
DialogF(int dialog_type,Widget parent,unsigned n,const char * title,const char * msgstr,...)147 unsigned DialogF(int dialog_type, Widget parent, unsigned n, const char* title,
148         const char* msgstr, ...)                    /* variable # arguments */
149 {
150     va_list var;
151 
152     Widget dialog, dialog_shell;
153     unsigned dialog_num, prompt;
154     XmString but_lbl_xms[NUM_BUTTONS_MAXPROMPT];
155     XmString msgstr_xms, input_string_xms, titstr_xms;
156     char msgstr_vsp[DF_MAX_MSG_LENGTH+1];
157     char *but_lbl, *input_string = NULL, *input_string_ptr;
158     int argcount, num_but_lbls = 0, i, but_index, cancel_index = -1;
159     Arg args[256];
160     char titleCopy[MAX_TITLE_LEN];
161 
162     struct dfcallbackstruct df;
163 
164     static int dialog_types[] = {		/* Supported dialog types */
165 		XmDIALOG_ERROR,
166 		XmDIALOG_INFORMATION,
167 		XmDIALOG_MESSAGE,
168 		XmDIALOG_QUESTION,
169 		XmDIALOG_WARNING,
170 		XmDIALOG_PROMPT
171     };
172     static char *dialog_name[] = {		/* Corresponding dialog names */
173 		"Error",
174 		"Information",
175 		"Message",
176 		"Question",
177 		"Warning",
178 		"Prompt"
179     };
180 
181     static unsigned char selectionButton_id[] =
182     {
183         XmDIALOG_OK_BUTTON,
184         XmDIALOG_APPLY_BUTTON,
185         XmDIALOG_CANCEL_BUTTON,
186         XmDIALOG_HELP_BUTTON
187     };
188     Cardinal N_SELECTION_BUTTONS = XtNumber(selectionButton_id);
189 
190     static unsigned char messageButton_id[] =
191     {
192         XmDIALOG_OK_BUTTON,
193         XmDIALOG_CANCEL_BUTTON,
194         XmDIALOG_HELP_BUTTON
195     };
196     Cardinal N_MESSAGE_BUTTONS = XtNumber(messageButton_id);
197 
198     static char *button_name[] = {		/* Motif button names */
199 		XmNokLabelString,
200 		XmNapplyLabelString,		/* button #2, if managed */
201 		XmNcancelLabelString,
202 		XmNhelpLabelString
203     };
204 
205 						/* Validate input parameters */
206     if ((dialog_type > NUM_DIALOGS_SUPPORTED) || (dialog_type <= 0)) {
207 	printf ("\nError calling DialogF - Unsupported dialog type\n");
208 	return (0);
209     }
210     dialog_num = dialog_type - 1;
211     prompt = (dialog_type == DF_PROMPT);
212     if  ((!prompt && (n > NUM_BUTTONS_SUPPORTED)) ||
213 	  (prompt && (n > NUM_BUTTONS_MAXPROMPT))) {
214 	printf ("\nError calling DialogF - Too many buttons specified\n");
215 	return (0);
216     }
217 
218     df.done_with_dialog = False;
219     df.destroyed = False;
220 
221     va_start (var, msgstr);
222     if (prompt) {		      /* Get where to put dialog input string */
223 	input_string = va_arg(var, char*);
224     }
225     if (n == NUM_BUTTONS_MAXPROMPT)
226 	df.apply_up = 1;		/* Apply button will be managed */
227     else
228     	df.apply_up = 0;		/* Apply button will not be managed */
229     for (argcount = 0; argcount < (int)n; ++argcount) {   /* Set up button labels */
230 	but_lbl = va_arg(var, char *);
231 	but_lbl_xms[num_but_lbls] = XmStringCreateLtoR (but_lbl,
232 		XmSTRING_DEFAULT_CHARSET);
233 	but_index = df.apply_up ? argcount :
234 	    	((argcount == 0) ? argcount : argcount+1);
235 	XtSetArg (args[argcount], button_name[but_index],
236 		but_lbl_xms[num_but_lbls++]);
237 	if (!strcmp(but_lbl, "Cancel") || !strcmp(but_lbl, "Dismiss"))
238 	    cancel_index = but_index;
239     }
240     if (n == 1)
241        cancel_index = 0;
242 
243     /* Get & translate msg string. NOTE: the use of vsprintf is inherently
244        dangerous because there is no way to control the length of the written
245        string. vnsprintf isn't available on all platforms, unfortunately.
246        Therefore we have to make sure that msgstr_vsp is large enough such
247        that it cannot overflow under any circumstances (within the context
248        of any DialogF call). A necessary condition is that it can at least
249        hold one file name (MAXPATHLEN). Therefore, DF_MAX_MSG_LENGTH must be
250        sufficiently larger than MAXPATHLEN.
251     */
252     vsprintf (msgstr_vsp, msgstr, var);
253     va_end(var);
254 
255     strncpy(&titleCopy[0], title, MAX_TITLE_LEN);
256     titleCopy[MAX_TITLE_LEN-1] = '\0';
257 
258     msgstr_xms = XmStringCreateLtoR(msgstr_vsp, XmSTRING_DEFAULT_CHARSET);
259     titstr_xms = XmStringCreateLtoR(titleCopy, XmSTRING_DEFAULT_CHARSET);
260 
261     if (prompt) {				/* Prompt dialog */
262 	XtSetArg (args[argcount], XmNselectionLabelString, msgstr_xms);
263 		argcount++;
264 	XtSetArg (args[argcount], XmNdialogTitle, titstr_xms);
265 		argcount++;
266 	XtSetArg (args[argcount], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL);
267 		argcount ++;
268 
269 	dialog = CreatePromptDialog(parent, dialog_name[dialog_num], args,
270 			argcount);
271 	XtAddCallback (dialog, XmNokCallback, (XtCallbackProc)ok_callback,
272 		(char *)&df);
273 	XtAddCallback (dialog, XmNcancelCallback,
274 		(XtCallbackProc)cancel_callback, (char *)&df);
275 	XtAddCallback (dialog, XmNhelpCallback, (XtCallbackProc)help_callback,
276 		(char *)&df);
277 	XtAddCallback (dialog, XmNapplyCallback, (XtCallbackProc)apply_callback,
278 		(char *)&df);
279 	RemapDeleteKey(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
280 
281 	/* Text area in prompt dialog should get focus, not ok button
282 	   since user enters text first.  To fix this, we need to turn
283 	   off the default button for the dialog, until after keyboard
284 	   focus has been established */
285 	XtVaSetValues(dialog, XmNdefaultButton, NULL, NULL);
286     	XtAddCallback(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT),
287     		XmNfocusCallback, (XtCallbackProc)focusCB, (char *)dialog);
288 
289 	/* Limit the length of the text that can be entered in text field */
290 	XtVaSetValues(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT),
291 		XmNmaxLength, DF_MAX_PROMPT_LENGTH-1, NULL);
292 
293 	/* Turn on the requested number of buttons in the dialog by
294 	   managing/unmanaging the button widgets */
295 	switch (n) {		/* number of buttons requested */
296 	case 0: case 3:
297 	    break;		/* or default of 3 buttons */
298 	case 1:
299 	    XtUnmanageChild (XmSelectionBoxGetChild (dialog,
300 		XmDIALOG_CANCEL_BUTTON) );
301 	case 2:
302 	    XtUnmanageChild (XmSelectionBoxGetChild (dialog,
303 		XmDIALOG_HELP_BUTTON) );
304 	    break;
305 	case 4:
306 	    XtManageChild (XmSelectionBoxGetChild (dialog,
307 		XmDIALOG_APPLY_BUTTON) );
308 	    df.apply_up = 1;		/* apply button managed */
309 	default:
310 	    break;
311 	}				/* end switch */
312 
313         /*  Set margin width to managed buttons.  */
314         for (i = 0; (unsigned) i < N_SELECTION_BUTTONS; i++)
315         {
316             Widget button = XmSelectionBoxGetChild(dialog, selectionButton_id[i]);
317 
318             if (XtIsManaged(button))
319             {
320                 XtVaSetValues(button, XmNmarginWidth, BUTTON_WIDTH_MARGIN, NULL);
321             }
322         }
323 
324     	/* If the button labeled cancel or dismiss is not the cancel button, or
325     	   if there is no button labeled cancel or dismiss, redirect escape key
326     	   events (this is necessary because the XmNcancelButton resource in
327     	   the bulletin board widget class is blocked from being reset) */
328     	if (cancel_index == -1)
329     	    addEscapeHandler(dialog, NULL, 0);
330     	else if (cancel_index != CANCEL_BTN)
331     	    addEscapeHandler(dialog, &df, cancel_index);
332 
333     	/* Add a callback to the window manager close callback for the dialog */
334     	AddMotifCloseCallback(XtParent(dialog),
335     	    	(XtCallbackProc)(cancel_index == APPLY_BTN ? apply_callback :
336     	    	(cancel_index == CANCEL_BTN ? cancel_callback :
337     	    	(cancel_index == HELP_BTN ? help_callback : ok_callback))), &df);
338 
339         /* Also add a callback to detect unexpected destruction (eg, because
340            the parent window is destroyed) */
341         XtAddCallback(dialog, XmNdestroyCallback,
342             (XtCallbackProc)destroy_callback, &df);
343 
344 	/* A previous call to SetDialogFPromptHistory can request that an
345 	   up-arrow history-recall mechanism be attached.  If so, do it here */
346 	if (NPromptHistoryItems != -1)
347 	    AddHistoryToTextWidget(XmSelectionBoxGetChild(dialog,XmDIALOG_TEXT),
348 		    &PromptHistory, &NPromptHistoryItems);
349 
350     	/* Pop up the dialog */
351 	ManageDialogCenteredOnPointer(dialog);
352 
353 	/* Get the focus to the input string.  There is some timing problem
354 	   within Motif that requires this to be called several times */
355 	for (i=0; i<20; i++)
356 	    XmProcessTraversal(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT),
357 		    XmTRAVERSE_CURRENT);
358 
359 	/* Wait for a response to the dialog */
360 	while (!df.done_with_dialog && !df.destroyed)
361 	    XtAppProcessEvent (XtWidgetToApplicationContext(dialog), XtIMAll);
362 
363         if (!df.destroyed) {
364 	    argcount = 0; /* Pass back string user entered */
365 	    XtSetArg (args[argcount], XmNtextString, &input_string_xms); argcount++;
366 	    XtGetValues (dialog, args, argcount);
367 	    XmStringGetLtoR (input_string_xms, XmSTRING_DEFAULT_CHARSET,
368 		&input_string_ptr);
369 	    strcpy (input_string, input_string_ptr);  /* This step is necessary */
370 	    XmStringFree(input_string_xms );
371             NEditFree(input_string_ptr);
372              /* Important! Only intercept unexpected destroy events. */
373 	    XtRemoveCallback(dialog, XmNdestroyCallback,
374             	(XtCallbackProc)destroy_callback, &df);
375 	    XtDestroyWidget(dialog);
376 	}
377 	PromptHistory = NULL;
378     	NPromptHistoryItems = -1;
379     }						  /* End prompt dialog path */
380 
381     else {				/* MessageBox dialogs */
382 	XtSetArg (args[argcount], XmNmessageString, msgstr_xms); argcount++;
383 
384 	XtSetArg (args[argcount], XmNdialogType, dialog_types[dialog_num]);
385 		argcount ++;
386 	XtSetArg (args[argcount], XmNdialogTitle, titstr_xms);
387 		argcount++;
388 	XtSetArg (args[argcount], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL);
389 		argcount ++;
390 
391 	dialog_shell = CreateDialogShell (parent, dialog_name[dialog_num],
392 			0, 0);
393 	dialog = XmCreateMessageBox (dialog_shell, "msg box", args, argcount);
394 
395 	XtAddCallback (dialog, XmNokCallback, (XtCallbackProc)ok_callback,
396 		(char *)&df);
397 	XtAddCallback (dialog, XmNcancelCallback,
398 		(XtCallbackProc)cancel_callback, (char *)&df);
399 	XtAddCallback (dialog, XmNhelpCallback, (XtCallbackProc)help_callback,
400 		(char *)&df);
401 
402 	/* Make extraneous buttons disappear */
403 	switch (n) {		/* n = number of buttons requested */
404 	case 0: case 3:
405 	    break;		/* default (0) gets you 3 buttons */
406 	case 1:
407 	    XtUnmanageChild (XmMessageBoxGetChild (dialog,
408 			XmDIALOG_CANCEL_BUTTON) );
409 	case 2:
410 	    XtUnmanageChild (XmMessageBoxGetChild (dialog,
411 			XmDIALOG_HELP_BUTTON) );
412 	    break;
413 	default:
414 	    break;
415 	}
416 
417         /*  Set margin width to managed buttons.  */
418         for (i = 0; (unsigned) i < N_MESSAGE_BUTTONS; i++)
419         {
420             Widget button = XmMessageBoxGetChild(dialog, messageButton_id[i]);
421 
422             if (XtIsManaged(button))
423             {
424                 XtVaSetValues(button, XmNmarginWidth, BUTTON_WIDTH_MARGIN, NULL);
425             }
426         }
427 
428         /* Try to create some sensible default mnemonics */
429         createMnemonics(dialog_shell);
430         AddDialogMnemonicHandler(dialog_shell, TRUE);
431 
432     	/* If the button labeled cancel or dismiss is not the cancel button, or
433     	   if there is no button labeled cancel or dismiss, redirect escape key
434     	   events (this is necessary because the XmNcancelButton resource in
435     	   the bulletin board widget class is blocked from being reset) */
436     	if (cancel_index == -1)
437     	    addEscapeHandler(dialog, NULL, 0);
438     	else if (cancel_index != CANCEL_BTN)
439     	    addEscapeHandler(dialog, &df, cancel_index);
440 
441     	/* Add a callback to the window manager close callback for the dialog */
442     	AddMotifCloseCallback(XtParent(dialog),
443     	    	(XtCallbackProc)(cancel_index == APPLY_BTN ? apply_callback :
444     	    	(cancel_index == CANCEL_BTN ? cancel_callback :
445     	    	(cancel_index == HELP_BTN ? help_callback : ok_callback))),&df);
446 
447         /* Also add a callback to detect unexpected destruction (eg, because
448            the parent window is destroyed) */
449         XtAddCallback(dialog_shell, XmNdestroyCallback,
450 	    (XtCallbackProc)destroy_callback, &df);
451 
452 	/* Pop up the dialog, wait for response*/
453 	ManageDialogCenteredOnPointer(dialog);
454 	while (!df.done_with_dialog && !df.destroyed)
455 	    XtAppProcessEvent (XtWidgetToApplicationContext(dialog), XtIMAll);
456 
457 	if (!df.destroyed) {
458              /* Important! Only intercept unexpected destroy events. */
459 	    XtRemoveCallback(dialog_shell, XmNdestroyCallback,
460             	(XtCallbackProc)destroy_callback, &df);
461 	    XtDestroyWidget(dialog_shell);
462         }
463     }
464 
465     XmStringFree(msgstr_xms);
466     XmStringFree(titstr_xms);
467     for (i = 0; i < num_but_lbls; ++i)
468 	XmStringFree(but_lbl_xms[i]);
469 
470     /* If the dialog was destroyed unexpectedly, the button was not set yet,
471        so we must set the index of the cancel button. */
472     if (df.destroyed) {
473 	df.button = cancel_index == APPLY_BTN ? 2 :
474         	(cancel_index == CANCEL_BTN ? 2 + df.apply_up :
475                 (cancel_index == HELP_BTN ? 3 + df.apply_up : 1));
476     }
477 
478     df.apply_up = 0;			/* default is apply button unmanaged */
479 
480     return (df.button);
481 }
482 
483 /*
484 ** Add up-arrow history recall to the next DialogF(DF_PROMPT... call (see
485 ** AddHistoryToTextWidget in misc.c).  This must be re-set before each call.
486 ** calling DialogF with a dialog type of DF_PROMPT automatically resets
487 ** this mode back to no-history-recall.
488 */
SetDialogFPromptHistory(char ** historyList,int nItems)489 void SetDialogFPromptHistory(char **historyList, int nItems)
490 {
491     PromptHistory = historyList;
492     NPromptHistoryItems = nItems;
493 }
494 
ok_callback(Widget w,struct dfcallbackstruct * client_data,caddr_t call_data)495 static void ok_callback (Widget w, struct dfcallbackstruct *client_data,
496 	caddr_t call_data)
497 {
498     client_data->done_with_dialog = True;
499     client_data->button = 1;		/* Return Button number pressed */
500 }
501 
cancel_callback(Widget w,struct dfcallbackstruct * client_data,caddr_t call_data)502 static void cancel_callback (Widget w, struct dfcallbackstruct *client_data,
503 	caddr_t call_data)
504 {
505     client_data->done_with_dialog = True;
506     client_data->button = 2 + client_data->apply_up; /* =3 if apply button managed */
507 }
508 
help_callback(Widget w,struct dfcallbackstruct * client_data,caddr_t call_data)509 static void help_callback (Widget w, struct dfcallbackstruct *client_data,
510 	caddr_t call_data)
511 {
512     client_data->done_with_dialog = True;
513     client_data->button = 3 + client_data->apply_up; /* =4 if apply button managed */
514 }
515 
apply_callback(Widget w,struct dfcallbackstruct * client_data,caddr_t call_data)516 static void apply_callback (Widget w, struct dfcallbackstruct *client_data,
517 	caddr_t call_data)
518 {
519     client_data->done_with_dialog = True;
520     client_data->button = 2;		/* Motif puts between OK and cancel */
521 }
522 
destroy_callback(Widget w,struct dfcallbackstruct * client_data,caddr_t call_data)523 static void destroy_callback (Widget w, struct dfcallbackstruct *client_data,
524 	caddr_t call_data)
525 {
526     client_data->destroyed = True;
527 }
528 
529 /*
530 ** callback for returning default button status to the ok button once we're
531 ** sure the text area in the prompt dialog has input focus.
532 */
focusCB(Widget w,Widget dialog,caddr_t call_data)533 static void focusCB(Widget w, Widget dialog, caddr_t call_data)
534 {
535     XtVaSetValues(dialog, XmNdefaultButton,
536     	    XmSelectionBoxGetChild(dialog, XmDIALOG_OK_BUTTON), NULL);
537 }
538 
539 /*
540 ** Message and prompt dialogs hardwire the cancel button to the XmNcancelButton
541 ** resource in the bulletin board dialog.  Since we rename the buttons, the
542 ** cancel label may not be on the dialog's idea of the Cancel button.  The only
543 ** way to make the accelerator for Cancel and Dismiss (the escape key) work
544 ** correctly in this situation is to brutally catch and redirect the event.
545 ** This routine redirects escape key events in the dialog to the callback for
546 ** the button "whichBtn", passing it argument "df".  If "df" is NULL, simply
547 ** block the event from reaching the dialog.
548 */
addEscapeHandler(Widget dialog,struct dfcallbackstruct * df,int whichBtn)549 static void addEscapeHandler(Widget dialog, struct dfcallbackstruct *df,
550     	int whichBtn)
551 {
552     XtAddEventHandler(dialog, KeyPressMask, False, whichBtn == APPLY_BTN ?
553     	    escapeApplyCB : escapeHelpCB, (XtPointer)df);
554     XtGrabKey(dialog, XKeysymToKeycode(XtDisplay(dialog), XK_Escape), 0,
555 	    True, GrabModeAsync, GrabModeAsync);
556 }
557 
558 /*
559 ** Event handler for escape key to redirect the event to the help button.
560 ** Attached when cancel label appears on Help button.
561 */
escapeHelpCB(Widget w,XtPointer callData,XEvent * event,Boolean * cont)562 static void escapeHelpCB(Widget w, XtPointer callData, XEvent *event,
563     	Boolean *cont)
564 {
565     if (event->xkey.keycode != XKeysymToKeycode(XtDisplay(w), XK_Escape))
566     	return;
567     if (callData != NULL)
568     	help_callback(w, (struct dfcallbackstruct *)callData, NULL);
569     *cont = False;
570 }
571 
572 /*
573 ** Event handler for escape key to redirect event to the apply button.
574 ** Attached when cancel label appears on Apply button.
575 */
escapeApplyCB(Widget w,XtPointer callData,XEvent * event,Boolean * cont)576 static void escapeApplyCB(Widget w, XtPointer callData, XEvent *event,
577     	Boolean *cont)
578 {
579     if (event->xkey.keycode != XKeysymToKeycode(XtDisplay(w), XK_Escape))
580     	return;
581     if (callData != NULL)
582     	apply_callback(w, (struct dfcallbackstruct *)callData, NULL);
583     *cont = False;
584 }
585 
586 /*
587 ** Only used by createMnemonics(Widget w)
588 */
recurseCreateMnemonics(Widget w,Boolean * mnemonicUsed)589 static void recurseCreateMnemonics(Widget w, Boolean *mnemonicUsed)
590 {
591     WidgetList children;
592     Cardinal   numChildren, i;
593 
594     XtVaGetValues(w,
595                   XmNchildren,    &children,
596                   XmNnumChildren, &numChildren,
597                   NULL);
598 
599     for (i = 0; i < numChildren; i++)
600     {
601         Widget child = children[i];
602 
603         if (XtIsComposite(child))
604         {
605             recurseCreateMnemonics(child, mnemonicUsed);
606         }
607         else if (XtIsSubclass(child, xmPushButtonWidgetClass) ||
608                  XtIsSubclass(child, xmPushButtonGadgetClass))
609         {
610             XmString xmslabel;
611             char *label;
612             int c;
613 
614             XtVaGetValues(child, XmNlabelString, &xmslabel, NULL);
615             if (XmStringGetLtoR(xmslabel, XmSTRING_DEFAULT_CHARSET, &label))
616             {
617                 /* Scan through the string to see if the label is already used */
618                 int labelLen = strlen(label);
619                 for (c = 0; c < labelLen; c++)
620                 {
621                     unsigned char lc = tolower((unsigned char)label[c]);
622 
623                     if (!mnemonicUsed[lc] && isalnum(lc))
624                     {
625                         mnemonicUsed[lc] = TRUE;
626                         XtVaSetValues(child, XmNmnemonic, label[c], NULL);
627                         break;
628                     }
629                 }
630 
631                 NEditFree(label);
632             }
633             XmStringFree(xmslabel);
634         }
635     }
636 }
637 
638 /*
639 ** Automatically create mnemonics for a widget.  Traverse all it's
640 ** children.  If the child is a push button, snag the first unused letter
641 ** and make that the mnemonic.  This is useful for DialogF dialogs which
642 ** can have arbitrary text in the buttons.
643 */
createMnemonics(Widget w)644 static void createMnemonics(Widget w)
645 {
646     Boolean mnemonicUsed[UCHAR_MAX + 1];
647 
648     memset(mnemonicUsed, FALSE, sizeof mnemonicUsed / sizeof *mnemonicUsed);
649     recurseCreateMnemonics(w, mnemonicUsed);
650 }
651