Fix smtp transport response to close after all rcpt fates determined. Bug 3059
[exim.git] / src / exim_monitor / em_TextPop.c
1 /***********************************************************
2 Copyright (c) The Exim Maintainers 2022
3 Copyright 1989 by the Massachusetts Institute of Technology,
4 Cambridge, Massachusetts.
5 SPDX-License-Identifier: GPL-2.0-or-later
6
7                         All Rights Reserved
8
9 Permission to use, copy, modify, and distribute this software and its
10 documentation for any purpose and without fee is hereby granted,
11 provided that the above copyright notice appear in all copies and that
12 both that copyright notice and this permission notice appear in
13 supporting documentation, and that the names of Digital or MIT not be
14 used in advertising or publicity pertaining to distribution of the
15 software without specific, written prior permission.
16
17 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
18 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
19 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
20 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23 SOFTWARE.
24
25 ******************************************************************/
26
27
28 /****************************************************************************
29 * Modified by Philip Hazel for use with Exim. The "replace" and "insert     *
30 * file" features of the search facility have been removed.  Also took out   *
31 * the declaration of sys_errlist, as it isn't used and causes trouble on    *
32 * some systems that declare it differently. September 1996.                 *
33 * Added the arguments into the static functions declared at the head, to    *
34 * stop some compiler warnings. August 1999.                                 *
35 * Took out the separate declarations of errno and sys_nerr at the start,    *
36 * because they too aren't actually used, and the declaration causes trouble *
37 * on some systems. December 2002.                                           *
38 ****************************************************************************/
39
40
41 /************************************************************
42  *
43  * This file is broken up into three sections one dealing with
44  * each of the three popups created here:
45  *
46  * FileInsert, Search, and Replace.
47  *
48  * There is also a section at the end for utility functions
49  * used by all more than one of these dialogs.
50  *
51  * The following functions are the only non-static ones defined
52  * in this module.  They are located at the beginning of the
53  * section that contains this dialog box that uses them.
54  *
55  * void _XawTextInsertFileAction(w, event, params, num_params);
56  * void _XawTextDoSearchAction(w, event, params, num_params);
57  * void _XawTextDoReplaceAction(w, event, params, num_params);
58  * void _XawTextInsertFile(w, event, params, num_params);
59  *
60  *************************************************************/
61
62 #include <X11/IntrinsicP.h>
63 #include <X11/StringDefs.h>
64 #include <X11/Shell.h>
65
66 #include <X11/Xaw/TextP.h>
67 #include <X11/Xaw/AsciiText.h>
68 #include <X11/Xaw/Cardinals.h>
69 #include <X11/Xaw/Command.h>
70 #include <X11/Xaw/Form.h>
71 #include <X11/Xaw/Toggle.h>
72 #include <X11/Xmu/CharSet.h>
73 #include <stdio.h>
74 #include <X11/Xos.h>            /* for O_RDONLY */
75 #include <errno.h>
76
77 /* extern int errno, sys_nerr; */
78 /* extern char* sys_errlist[]; */
79
80 #define DISMISS_NAME  ("cancel")
81 #define DISMISS_NAME_LEN 6
82 #define FORM_NAME     ("form")
83 #define LABEL_NAME    ("label")
84 #define TEXT_NAME     ("text")
85
86 #define R_OFFSET      1
87
88 /* Argument types added by PH August 1999 */
89
90 static void CenterWidgetOnPoint(Widget, XEvent *);
91 static void PopdownSearch(Widget, XtPointer, XtPointer);
92 static void InitializeSearchWidget(struct SearchAndReplace *,
93   XawTextScanDirection, Boolean);
94 static void  SetResource(Widget, char *, XtArgVal);
95 static void  SetSearchLabels(struct SearchAndReplace *, String, String,
96   Boolean);
97 static Widget CreateDialog(Widget, String, String,
98   void (*)(Widget, char *, Widget));
99 static Widget  GetShell(Widget);
100 static void SetWMProtocolTranslations(Widget w);
101 static Boolean DoSearch(struct SearchAndReplace *);
102 static String GetString(Widget);
103
104 static void AddSearchChildren(Widget, char *, Widget);
105
106 static char radio_trans_string[] =
107     "<Btn1Down>,<Btn1Up>:   set() notify()";
108
109 static char search_text_trans[] =
110   "~Shift<Key>Return:      DoSearchAction(Popdown) \n\
111    Ctrl<Key>c:             PopdownSearchAction() \n\
112    ";
113
114
115
116 /************************************************************
117  *
118  * This section of the file contains all the functions that
119  * the search dialog box uses.
120  *
121  ************************************************************/
122
123 /*      Function Name: _XawTextDoSearchAction
124  *      Description: Action routine that can be bound to dialog box's
125  *                   Text Widget that will search for a string in the main
126  *                   Text Widget.
127  *      Arguments:   (Standard Action Routine args)
128  *      Returns:     none.
129  *
130  * Note:
131  *
132  * If the search was successful and the argument popdown is passed to
133  * this action routine then the widget will automatically popdown the
134  * search widget.
135  */
136
137 /* ARGSUSED */
138 void
139 _XawTextDoSearchAction(w, event, params, num_params)
140 Widget w;
141 XEvent *event;
142 String * params;
143 Cardinal * num_params;
144 {
145   TextWidget tw = (TextWidget) XtParent(XtParent(XtParent(w)));
146   Boolean popdown = FALSE;
147
148   if ( (*num_params == 1) &&
149        ((params[0][0] == 'p') || (params[0][0] == 'P')) )
150       popdown = TRUE;
151
152   if (DoSearch(tw->text.search) && popdown)
153     PopdownSearch(w, (XtPointer) tw->text.search, NULL);
154 }
155
156 /*      Function Name: _XawTextPopdownSearchAction
157  *      Description: Action routine that can be bound to dialog box's
158  *                   Text Widget that will popdown the search widget.
159  *      Arguments:   (Standard Action Routine args)
160  *      Returns:     none.
161  */
162
163 /* ARGSUSED */
164 void
165 _XawTextPopdownSearchAction(w, event, params, num_params)
166 Widget w;
167 XEvent *event;
168 String * params;
169 Cardinal * num_params;
170 {
171   TextWidget tw = (TextWidget) XtParent(XtParent(XtParent(w)));
172
173   PopdownSearch(w, (XtPointer) tw->text.search, NULL);
174 }
175
176 /*      Function Name: PopdownSearch
177  *      Description: Pops down the search widget and resets it.
178  *      Arguments: w - *** NOT USED ***.
179  *                 closure - a pointer to the search structure.
180  *                 call_data - *** NOT USED ***.
181  *      Returns: none
182  */
183
184 /* ARGSUSED */
185 static void
186 PopdownSearch(w, closure, call_data)
187 Widget w;
188 XtPointer closure;
189 XtPointer call_data;
190 {
191   struct SearchAndReplace * search = (struct SearchAndReplace *) closure;
192
193   SetSearchLabels(search, "Search", "", FALSE);
194   XtPopdown( search->search_popup );
195 }
196
197 /*      Function Name: SearchButton
198  *      Description: Performs a search when the button is clicked.
199  *      Arguments: w - *** NOT USED **.
200  *                 closure - a pointer to the search info.
201  *                 call_data - *** NOT USED ***.
202  *      Returns:
203  */
204
205 /* ARGSUSED */
206 static void
207 SearchButton(w, closure, call_data)
208 Widget w;
209 XtPointer closure;
210 XtPointer call_data;
211 {
212   (void) DoSearch( (struct SearchAndReplace *) closure );
213 }
214
215 /*      Function Name: _XawTextSearch
216  *      Description: Action routine that can be bound to the text widget
217  *                   it will popup the search dialog box.
218  *      Arguments:   w - the text widget.
219  *                   event - X Event (used to get x and y location).
220  *                   params, num_params - the parameter list.
221  *      Returns:     none.
222  *
223  * NOTE:
224  *
225  * The parameter list contains one or two entries that may be the following.
226  *
227  * First Entry:   The first entry is the direction to search by default.
228  *                This argument must be specified and may have a value of
229  *                "left" or "right".
230  *
231  * Second Entry:  This entry is optional and contains the value of the default
232  *                string to search for.
233  */
234
235 #define SEARCH_HEADER ("Text Widget - Search():")
236
237 void
238 _XawTextSearch(w, event, params, num_params)
239 Widget w;
240 XEvent *event;
241 String * params;
242 Cardinal * num_params;
243 {
244   TextWidget ctx = (TextWidget)w;
245   XawTextScanDirection dir;
246   char * ptr, buf[BUFSIZ];
247   XawTextEditType edit_mode;
248   Arg args[1];
249
250 #ifdef notdef
251   if (!ctx->text.source->Search) {
252       XBell(XtDisplay(w), 0);
253       return;
254   }
255 #endif
256
257   if ( (*num_params < 1) || (*num_params > 2) ) {
258     sprintf(buf, "%s %s\n%s", SEARCH_HEADER, "This action must have only",
259             "one or two parameters");
260     XtAppWarning(XtWidgetToApplicationContext(w), buf);
261     return;
262   }
263   else if (*num_params == 1)
264     ptr = "";
265   else
266     ptr = params[1];
267
268   switch(params[0][0]) {
269   case 'b':                     /* Left. */
270   case 'B':
271     dir = XawsdLeft;
272     break;
273   case 'f':                     /* Right. */
274   case 'F':
275     dir = XawsdRight;
276     break;
277   default:
278     sprintf(buf, "%s %s\n%s", SEARCH_HEADER, "The first parameter must be",
279             "Either 'backward' or 'forward'");
280     XtAppWarning(XtWidgetToApplicationContext(w), buf);
281     return;
282   }
283
284   if (!ctx->text.search) {
285     ctx->text.search = XtNew(struct SearchAndReplace);
286     ctx->text.search->search_popup = CreateDialog(w, ptr, "search",
287                                                   AddSearchChildren);
288     XtRealizeWidget(ctx->text.search->search_popup);
289     SetWMProtocolTranslations(ctx->text.search->search_popup);
290   }
291   else if (*num_params > 1)
292     XtVaSetValues(ctx->text.search->search_text, XtNstring, ptr, NULL);
293
294   XtSetArg(args[0], XtNeditType,&edit_mode);
295   XtGetValues(ctx->text.source, args, ONE);
296
297   InitializeSearchWidget(ctx->text.search, dir, (edit_mode == XawtextEdit));
298
299   CenterWidgetOnPoint(ctx->text.search->search_popup, event);
300   XtPopup(ctx->text.search->search_popup, XtGrabNone);
301 }
302
303 /*      Function Name: InitializeSearchWidget
304  *      Description: This function initializes the search widget and
305  *                   is called each time the search widget is poped up.
306  *      Arguments: search - the search widget structure.
307  *                 dir - direction to search.
308  *                 replace_active - state of the sensitivity for the
309  *                                  replace button.
310  *      Returns: none.
311  */
312
313 static void
314 InitializeSearchWidget(struct SearchAndReplace *search,
315   XawTextScanDirection dir, Boolean replace_active)
316 {
317 replace_active = replace_active; /* PH - shuts compilers up */
318
319   switch (dir) {
320   case XawsdLeft:
321     SetResource(search->left_toggle, XtNstate, (XtArgVal) TRUE);
322     break;
323   case XawsdRight:
324     SetResource(search->right_toggle, XtNstate, (XtArgVal) TRUE);
325     break;
326   default:
327     break;
328   }
329 }
330
331 /*      Function Name: AddSearchChildren
332  *      Description: Adds all children to the Search Dialog Widget.
333  *      Arguments: form - the form widget for the search widget.
334  *                 ptr - a pointer to the initial string for the Text Widget.
335  *                 tw - the main text widget.
336  *      Returns: none.
337  */
338
339 static void
340 AddSearchChildren(form, ptr, tw)
341 Widget form, tw;
342 char * ptr;
343 {
344   Arg args[10];
345   Cardinal num_args;
346   Widget cancel, search_button, s_label, s_text;
347   XtTranslations trans;
348   struct SearchAndReplace * search = ((TextWidget) tw)->text.search;
349
350   num_args = 0;
351   XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++;
352   XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++;
353   XtSetArg(args[num_args], XtNresizable, TRUE ); num_args++;
354   XtSetArg(args[num_args], XtNborderWidth, 0 ); num_args++;
355   search->label1 = XtCreateManagedWidget("label1", labelWidgetClass,
356                                          form, args, num_args);
357
358  /*
359  * We need to add R_OFFSET to the radio_data, because the value zero (0)
360  * has special meaning.
361  */
362
363   num_args = 0;
364   XtSetArg(args[num_args], XtNlabel, "Backward"); num_args++;
365   XtSetArg(args[num_args], XtNfromVert, search->label1); num_args++;
366   XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++;
367   XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++;
368   XtSetArg(args[num_args], XtNradioData, (caddr_t) XawsdLeft + R_OFFSET);
369   num_args++;
370   search->left_toggle = XtCreateManagedWidget("backwards", toggleWidgetClass,
371                                               form, args, num_args);
372
373   num_args = 0;
374   XtSetArg(args[num_args], XtNlabel, "Forward"); num_args++;
375   XtSetArg(args[num_args], XtNfromVert, search->label1); num_args++;
376   XtSetArg(args[num_args], XtNfromHoriz, search->left_toggle); num_args++;
377   XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++;
378   XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++;
379   XtSetArg(args[num_args], XtNradioGroup, search->left_toggle); num_args++;
380   XtSetArg(args[num_args], XtNradioData, (caddr_t) XawsdRight + R_OFFSET);
381   num_args++;
382   search->right_toggle = XtCreateManagedWidget("forwards", toggleWidgetClass,
383                                                form, args, num_args);
384
385   {
386     XtTranslations radio_translations;
387
388     radio_translations = XtParseTranslationTable(radio_trans_string);
389     XtOverrideTranslations(search->left_toggle, radio_translations);
390     XtOverrideTranslations(search->right_toggle, radio_translations);
391   }
392
393   num_args = 0;
394   XtSetArg(args[num_args], XtNfromVert, search->left_toggle); num_args++;
395   XtSetArg(args[num_args], XtNlabel, "Search for:  ");num_args++;
396   XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++;
397   XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++;
398   XtSetArg(args[num_args], XtNborderWidth, 0 ); num_args++;
399   s_label = XtCreateManagedWidget("searchLabel", labelWidgetClass,
400                                   form, args, num_args);
401
402   num_args = 0;
403   XtSetArg(args[num_args], XtNfromVert, search->left_toggle); num_args++;
404   XtSetArg(args[num_args], XtNfromHoriz, s_label); num_args++;
405   XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++;
406   XtSetArg(args[num_args], XtNright, XtChainRight); num_args++;
407   XtSetArg(args[num_args], XtNeditType, XawtextEdit); num_args++;
408   XtSetArg(args[num_args], XtNresizable, TRUE); num_args++;
409   XtSetArg(args[num_args], XtNresize, XawtextResizeWidth); num_args++;
410   XtSetArg(args[num_args], XtNstring, ptr); num_args++;
411   s_text = XtCreateManagedWidget("searchText", asciiTextWidgetClass, form,
412                                  args, num_args);
413   search->search_text = s_text;
414
415   num_args = 0;
416   XtSetArg(args[num_args], XtNlabel, "Search"); num_args++;
417   XtSetArg(args[num_args], XtNfromVert, s_text); num_args++;
418   XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++;
419   XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++;
420   search_button = XtCreateManagedWidget("search", commandWidgetClass, form,
421                                         args, num_args);
422
423   num_args = 0;
424   XtSetArg(args[num_args], XtNlabel, "Cancel"); num_args++;
425   XtSetArg(args[num_args], XtNfromVert, s_text); num_args++;
426   XtSetArg(args[num_args], XtNfromHoriz, search_button); num_args++;
427   XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++;
428   XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++;
429   cancel = XtCreateManagedWidget(DISMISS_NAME, commandWidgetClass, form,
430                                  args, num_args);
431
432   XtAddCallback(search_button, XtNcallback, SearchButton, (XtPointer) search);
433   XtAddCallback(cancel, XtNcallback, PopdownSearch, (XtPointer) search);
434
435 /*
436  * Initialize the text entry fields.
437  */
438
439   SetSearchLabels(search, "Search", "", FALSE);
440   XtSetKeyboardFocus(form, search->search_text);
441
442 /*
443  * Bind Extra translations.
444  */
445
446   trans = XtParseTranslationTable(search_text_trans);
447   XtOverrideTranslations(search->search_text, trans);
448 }
449
450 /*      Function Name: DoSearch
451  *      Description: Performs a search.
452  *      Arguments: search - the search structure.
453  *      Returns: TRUE if successful.
454  */
455
456 /* ARGSUSED */
457 static Boolean
458 DoSearch(search)
459 struct SearchAndReplace * search;
460 {
461   char msg[BUFSIZ];
462   Widget tw = XtParent(search->search_popup);
463   XawTextPosition pos;
464   XawTextScanDirection dir;
465   XawTextBlock text;
466
467   text.ptr = GetString(search->search_text);
468   text.length = strlen(text.ptr);
469   text.firstPos = 0;
470   text.format = FMT8BIT;
471
472   dir = (XawTextScanDirection) ((long)XawToggleGetCurrent(search->left_toggle) -
473                                 R_OFFSET);
474
475   pos = XawTextSearch( tw, dir, &text);
476
477   if (pos == XawTextSearchError)
478     sprintf( msg, "Could not find string '%s'.", text.ptr);
479   else {
480     if (dir == XawsdRight)
481       XawTextSetInsertionPoint( tw, pos + text.length);
482     else
483       XawTextSetInsertionPoint( tw, pos);
484
485     XawTextSetSelection( tw, pos, pos + text.length);
486     search->selection_changed = FALSE; /* selection is good. */
487     return(TRUE);
488   }
489
490   XawTextUnsetSelection(tw);
491   SetSearchLabels(search, msg, "", TRUE);
492   return(FALSE);
493 }
494
495
496 /*      Function Name: SetSearchLabels
497  *      Description: Sets both the search labels, and also rings the bell
498  *  HACKED: Only one label needed now
499  *      Arguments: search - the search structure.
500  *                 msg1, msg2 - message to put in each search label.
501  *                 bell - if TRUE then ring bell.
502  *      Returns: none.
503  */
504
505 static void
506 SetSearchLabels(struct SearchAndReplace *search, String msg1, String msg2,
507   Boolean bell)
508 {
509 msg2 = msg2; /* PH - shuts compilers up */
510   (void) SetResource( search->label1, XtNlabel, (XtArgVal) msg1);
511   /* (void) SetResource( search->label2, XtNlabel, (XtArgVal) msg2); */
512   if (bell)
513     XBell(XtDisplay(search->search_popup), 0);
514 }
515
516 /************************************************************
517  *
518  * This section of the file contains utility routines used by
519  * other functions in this file.
520  *
521  ************************************************************/
522
523
524 /*      Function Name: SetResource
525  *      Description: Sets a resource in a widget
526  *      Arguments: w - the widget.
527  *                 res_name - name of the resource.
528  *                 value - the value of the resource.
529  *      Returns: none.
530  */
531
532 static void
533 SetResource(w, res_name, value)
534 Widget w;
535 char * res_name;
536 XtArgVal value;
537 {
538   Arg args[1];
539
540   XtSetArg(args[0], res_name, value);
541   XtSetValues( w, args, ONE );
542 }
543
544 /*      Function Name: GetString
545  *      Description:   Gets the value for the string in the popup.
546  *      Arguments:     text - the text widget whose string we will get.
547  *      Returns:       the string.
548  */
549
550 static String
551 GetString(text)
552 Widget text;
553 {
554   String string;
555   Arg args[1];
556
557   XtSetArg( args[0], XtNstring, &string );
558   XtGetValues( text, args, ONE );
559   return(string);
560 }
561
562 /*      Function Name: CenterWidgetOnPoint.
563  *      Description: Centers a shell widget on a point relative to
564  *                   the root window.
565  *      Arguments: w - the shell widget.
566  *                 event - event containing the location of the point
567  *      Returns: none.
568  *
569  * NOTE: The widget is not allowed to go off the screen.
570  */
571
572 static void
573 CenterWidgetOnPoint(w, event)
574 Widget w;
575 XEvent *event;
576 {
577   Arg args[3];
578   Cardinal num_args;
579   Dimension width, height, b_width;
580   Position x=0, y=0, max_x, max_y;
581
582   if (event != NULL) {
583     switch (event->type) {
584     case ButtonPress:
585     case ButtonRelease:
586       x = event->xbutton.x_root;
587       y = event->xbutton.y_root;
588       break;
589     case KeyPress:
590     case KeyRelease:
591       x = event->xkey.x_root;
592       y = event->xkey.y_root;
593       break;
594     default:
595       return;
596     }
597   }
598
599   num_args = 0;
600   XtSetArg(args[num_args], XtNwidth, &width); num_args++;
601   XtSetArg(args[num_args], XtNheight, &height); num_args++;
602   XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
603   XtGetValues(w, args, num_args);
604
605   width += 2 * b_width;
606   height += 2 * b_width;
607
608   x -= ( (Position) width/2 );
609   if (x < 0) x = 0;
610   if ( x > (max_x = (Position) (XtScreen(w)->width - width)) ) x = max_x;
611
612   y -= ( (Position) height/2 );
613   if (y < 0) y = 0;
614   if ( y > (max_y = (Position) (XtScreen(w)->height - height)) ) y = max_y;
615
616   num_args = 0;
617   XtSetArg(args[num_args], XtNx, x); num_args++;
618   XtSetArg(args[num_args], XtNy, y); num_args++;
619   XtSetValues(w, args, num_args);
620 }
621
622 /*      Function Name: CreateDialog
623  *      Description: Actually creates a dialog.
624  *      Arguments: parent - the parent of the dialog - the main text widget.
625  *                 ptr - initial_string for the dialog.
626  *                 name - name of the dialog.
627  *                 func - function to create the children of the dialog.
628  *      Returns: the popup shell of the dialog.
629  *
630  * NOTE:
631  *
632  * The function argument is passed the following arguments.
633  *
634  * form - the from widget that is the dialog.
635  * ptr - the initial string for the dialog's text widget.
636  * parent - the parent of the dialog - the main text widget.
637  */
638
639 static Widget
640 CreateDialog(parent, ptr, name, func)
641 Widget parent;
642 String ptr, name;
643 void (*func)();
644 {
645   Widget popup, form;
646   Arg args[5];
647   Cardinal num_args;
648
649   num_args = 0;
650   XtSetArg(args[num_args], XtNiconName, name); num_args++;
651   XtSetArg(args[num_args], XtNgeometry, NULL); num_args++;
652   XtSetArg(args[num_args], XtNallowShellResize, TRUE); num_args++;
653   XtSetArg(args[num_args], XtNtransientFor, GetShell(parent)); num_args++;
654   popup = XtCreatePopupShell(name, transientShellWidgetClass,
655                              parent, args, num_args);
656
657   form = XtCreateManagedWidget(FORM_NAME, formWidgetClass, popup,
658                                NULL, ZERO);
659
660   (*func) (form, ptr, parent);
661   return(popup);
662 }
663
664  /*     Function Name: GetShell
665   *     Description: Walks up the widget hierarchy to find the
666   *             nearest shell widget.
667   *     Arguments: w - the widget whose parent shell should be returned.
668   *     Returns: The shell widget among the ancestors of w that is the
669   *             fewest levels up in the widget hierarchy.
670   */
671
672 static Widget
673 GetShell(w)
674 Widget w;
675 {
676     while ((w != NULL) && !XtIsShell(w))
677         w = XtParent(w);
678
679     return (w);
680 }
681
682 /* Add proper prototype to keep IRIX 6 compiler happy. PH */
683
684 static Boolean InParams(String, String *, Cardinal);
685
686 static Boolean InParams(str, p, n)
687     String str;
688     String *p;
689     Cardinal n;
690 {
691     int i;
692     for (i=0; i < n; p++, i++)
693         if (! XmuCompareISOLatin1(*p, str)) return True;
694     return False;
695 }
696
697 static char *WM_DELETE_WINDOW = "WM_DELETE_WINDOW";
698
699 static void WMProtocols(w, event, params, num_params)
700     Widget w;           /* popup shell */
701     XEvent *event;
702     String *params;
703     Cardinal *num_params;
704 {
705     Atom wm_delete_window;
706     Atom wm_protocols;
707
708     wm_delete_window = XInternAtom(XtDisplay(w), WM_DELETE_WINDOW, True);
709     wm_protocols = XInternAtom(XtDisplay(w), "WM_PROTOCOLS", True);
710
711     /* Respond to a recognized WM protocol request iff
712      * event type is ClientMessage and no parameters are passed, or
713      * event type is ClientMessage and event data is matched to parameters, or
714      * event type isn't ClientMessage and parameters make a request.
715      */
716 #define DO_DELETE_WINDOW InParams(WM_DELETE_WINDOW, params, *num_params)
717
718     if ((event->type == ClientMessage &&
719          event->xclient.message_type == wm_protocols &&
720          event->xclient.data.l[0] == wm_delete_window &&
721          (*num_params == 0 || DO_DELETE_WINDOW))
722         ||
723         (event->type != ClientMessage && DO_DELETE_WINDOW)) {
724
725 #undef DO_DELETE_WINDOW
726
727         Widget cancel;
728         char descendant[DISMISS_NAME_LEN + 2];
729         sprintf(descendant, "*%s", DISMISS_NAME);
730         cancel = XtNameToWidget(w, descendant);
731         if (cancel) XtCallCallbacks(cancel, XtNcallback, (XtPointer)NULL);
732     }
733 }
734
735 static void SetWMProtocolTranslations(w)
736     Widget      w;      /* realized popup shell */
737 {
738     int i;
739     XtAppContext app_context;
740     Atom wm_delete_window;
741     static XtTranslations compiled_table;       /* initially 0 */
742     static XtAppContext *app_context_list;      /* initially 0 */
743     static Cardinal list_size;                  /* initially 0 */
744
745     app_context = XtWidgetToApplicationContext(w);
746
747     /* parse translation table once */
748     if (! compiled_table) compiled_table = XtParseTranslationTable
749         ("<Message>WM_PROTOCOLS: XawWMProtocols()\n");
750
751     /* add actions once per application context */
752     for (i=0; i < list_size && app_context_list[i] != app_context; i++) ;
753     if (i == list_size) {
754         XtActionsRec actions[1];
755         actions[0].string = "XawWMProtocols";
756         actions[0].proc = WMProtocols;
757         list_size++;
758         app_context_list = (XtAppContext *) XtRealloc
759             ((char *)app_context_list, list_size * sizeof(XtAppContext));
760         XtAppAddActions(app_context, actions, 1);
761         app_context_list[i] = app_context;
762     }
763
764     /* establish communication between the window manager and each shell */
765     XtAugmentTranslations(w, compiled_table);
766     wm_delete_window = XInternAtom(XtDisplay(w), WM_DELETE_WINDOW, False);
767     (void) XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
768 }