1c9e2be55Smrg/* $XConsortium: popup.c,v 2.38 94/08/26 18:04:22 swick Exp $
2c9e2be55Smrg *
3c9e2be55Smrg *
4c9e2be55Smrg *			  COPYRIGHT 1989
5c9e2be55Smrg *		   DIGITAL EQUIPMENT CORPORATION
6c9e2be55Smrg *		       MAYNARD, MASSACHUSETTS
7c9e2be55Smrg *			ALL RIGHTS RESERVED.
8c9e2be55Smrg *
9c9e2be55Smrg * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
10c9e2be55Smrg * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
11c9e2be55Smrg * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
12c9e2be55Smrg * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
13c9e2be55Smrg *
14c9e2be55Smrg * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
15c9e2be55Smrg * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
16c9e2be55Smrg * ADDITION TO THAT SET FORTH ABOVE.
17c9e2be55Smrg *
18c9e2be55Smrg * Permission to use, copy, modify, and distribute this software and its
19c9e2be55Smrg * documentation for any purpose and without fee is hereby granted, provided
20c9e2be55Smrg * that the above copyright notice appear in all copies and that both that
21c9e2be55Smrg * copyright notice and this permission notice appear in supporting
22c9e2be55Smrg * documentation, and that the name of Digital Equipment Corporation not be
23c9e2be55Smrg * used in advertising or publicity pertaining to distribution of the software
24c9e2be55Smrg * without specific, written prior permission.
25c9e2be55Smrg */
26c9e2be55Smrg/* $XFree86$ */
27c9e2be55Smrg
28c9e2be55Smrg/* popup.c -- Handle pop-up widgets. */
29c9e2be55Smrg
30c9e2be55Smrg#include "xmh.h"
31c9e2be55Smrg#include "actions.h"
32c9e2be55Smrg
33c9e2be55Smrg#include <X11/Xaw/Cardinals.h>
34c9e2be55Smrg
35c9e2be55Smrgtypedef struct _PopupStatus {
36c9e2be55Smrg	Widget popup;		/* order of fields same as CommandStatusRec */
37c9e2be55Smrg	struct _LastInput lastInput;
38c9e2be55Smrg	char*  shell_command;	/* NULL, or contains sh -c command */
39c9e2be55Smrg} PopupStatusRec, *PopupStatus;
40c9e2be55Smrg
41c9e2be55Smrg/* these are just strings which are used more than one place in the code */
42c9e2be55Smrgstatic String XmhNconfirm = "confirm";
43c9e2be55Smrgstatic String XmhNdialog = "dialog";
44c9e2be55Smrgstatic String XmhNerror = "error";
45c9e2be55Smrgstatic String XmhNnotice = "notice";
46c9e2be55Smrgstatic String XmhNokay = "okay";
47c9e2be55Smrgstatic String XmhNprompt = "prompt";
48c9e2be55Smrgstatic String XmhNvalue = "value";
49d859ff80Smrg
50c9e2be55Smrg/* The popups were originally parented from toplevel and neglected the
51c9e2be55Smrg * transientFor resource.  In order not to break existing user resource
52c9e2be55Smrg * settings for the popups, transientFor is set independent of the parent,
53c9e2be55Smrg * which remains the toplevel widget.
54c9e2be55Smrg */
55c9e2be55Smrg
56c9e2be55Smrgstatic void DeterminePopupPosition(
57c9e2be55Smrg    Position	*x_ptr,
58c9e2be55Smrg    Position	*y_ptr,
59c9e2be55Smrg    Widget	*transFor_return) /* return a suitable top level shell */
60c9e2be55Smrg{
61c9e2be55Smrg    if (lastInput.win != (Window) -1) {
62c9e2be55Smrg	if (transFor_return) {
63c9e2be55Smrg	    Widget	source;
64c9e2be55Smrg	    source = XtWindowToWidget(XtDisplay(toplevel), lastInput.win);
65c9e2be55Smrg	    while (source && !XtIsWMShell(source))
66c9e2be55Smrg		source = XtParent(source);
67c9e2be55Smrg	    *transFor_return = source;
68c9e2be55Smrg	}
69c9e2be55Smrg	/* use the site of the last KeyPress or ButtonPress */
70c9e2be55Smrg	*x_ptr = lastInput.x;
71c9e2be55Smrg	*y_ptr = lastInput.y;
72c9e2be55Smrg    } else {
73c9e2be55Smrg	Widget	source;
74c9e2be55Smrg	int i = 0;
75c9e2be55Smrg	Dimension width, height;
76c9e2be55Smrg	Arg args[2];
77c9e2be55Smrg
78c9e2be55Smrg	/* %%% need to keep track of last screen */
79c9e2be55Smrg	/* guess which screen and use the the center of it */
80c9e2be55Smrg	while (i < numScrns && !scrnList[i]->mapped)
81c9e2be55Smrg	    i++;
82c9e2be55Smrg	source = ((i < numScrns) ? scrnList[i]->parent : toplevel);
83c9e2be55Smrg	XtSetArg(args[0], XtNwidth, &width);
84c9e2be55Smrg	XtSetArg(args[1], XtNheight, &height);
85c9e2be55Smrg	XtGetValues(source, args, TWO);
86c9e2be55Smrg	XtTranslateCoords(source, (Position) (width / 2),
87c9e2be55Smrg			  (Position) (height / 2), x_ptr, y_ptr);
88c9e2be55Smrg	if (transFor_return) *transFor_return = source;
89c9e2be55Smrg    }
90c9e2be55Smrg}
91c9e2be55Smrg
92c9e2be55Smrgstatic Boolean PositionThePopup(
93c9e2be55Smrg    Widget	popup,
94c9e2be55Smrg    Position	x,
95c9e2be55Smrg    Position	y)
96c9e2be55Smrg{
97c9e2be55Smrg    /* Hack.  Fix up the position of the popup.  The xmh app defaults file
98d859ff80Smrg     * contains an Xmh*Geometry specification; the effects of that on
99c9e2be55Smrg     * popups, and the lack of any user-supplied geometry specification for
100c9e2be55Smrg     * popups, are mitigated here, by giving the popup shell a position.
101c9e2be55Smrg     * (Xmh*Geometry is needed in case there is no user-supplied default.)
102c9e2be55Smrg     * Returns True if an explicit geometry was inferred; false if the
103c9e2be55Smrg     * widget was repositioned to (x,y).
104c9e2be55Smrg     */
105c9e2be55Smrg
106c9e2be55Smrg    Arg		args[4];
107c9e2be55Smrg    String 	top_geom, pop_geom;
108c9e2be55Smrg
109c9e2be55Smrg    XtSetArg( args[0], XtNgeometry, &top_geom );
110c9e2be55Smrg    XtGetValues( toplevel, args, ONE );
111c9e2be55Smrg    XtSetArg( args[0], XtNgeometry, &pop_geom );
112c9e2be55Smrg    XtGetValues( popup, args, ONE );
113c9e2be55Smrg
114c9e2be55Smrg    if (pop_geom == NULL || pop_geom == top_geom) {
115c9e2be55Smrg	/* if same db entry, then ... */
116c9e2be55Smrg	XtSetArg( args[0], XtNgeometry, (String) NULL);
117c9e2be55Smrg	XtSetArg( args[1], XtNx, x);
118c9e2be55Smrg	XtSetArg( args[2], XtNy, y);
119c9e2be55Smrg	XtSetArg( args[3], XtNwinGravity, SouthWestGravity);
120c9e2be55Smrg	XtSetValues( popup, args, FOUR);
121c9e2be55Smrg	return False;
122c9e2be55Smrg    }
123c9e2be55Smrg    return True;
124c9e2be55Smrg}
125c9e2be55Smrg
126c9e2be55Smrg
127c9e2be55Smrgstatic void CenterPopupPosition(
128c9e2be55Smrg    Widget	widget,
129c9e2be55Smrg    Widget	popup,
130c9e2be55Smrg    Position	px,
131c9e2be55Smrg    Position	py)
132c9e2be55Smrg{
133c9e2be55Smrg    Position	x, y;
134c9e2be55Smrg    Position	nx, ny;
135c9e2be55Smrg    Arg		args[3];
136c9e2be55Smrg
137c9e2be55Smrg    if (widget == NULL) return;
138c9e2be55Smrg    XtSetArg(args[0], XtNx, &x);
139c9e2be55Smrg    XtSetArg(args[1], XtNy, &y);
140c9e2be55Smrg    XtGetValues(popup, args, TWO);
141c9e2be55Smrg    if (x == px && y == py) {
142c9e2be55Smrg
143c9e2be55Smrg	/* Program sets geometry.  Correct our earlier calculations. */
144c9e2be55Smrg
145c9e2be55Smrg	nx = (GetWidth(widget) - GetWidth(popup)) / 2;
146c9e2be55Smrg	ny = (GetHeight(widget) - GetHeight(popup)) / 2;
147c9e2be55Smrg	if (nx < 0) nx = 0;
148c9e2be55Smrg	if (ny < 0) ny = 0;
149c9e2be55Smrg	XtTranslateCoords(widget, nx, ny, &x, &y);
150c9e2be55Smrg	XtSetArg(args[0], XtNx, x);
151c9e2be55Smrg	XtSetArg(args[1], XtNy, y);
152c9e2be55Smrg	XtSetArg(args[2], XtNwinGravity, CenterGravity);
153c9e2be55Smrg	XtSetValues(popup, args, THREE);
154c9e2be55Smrg    }
155c9e2be55Smrg}
156d859ff80Smrg
157c9e2be55Smrg
158c9e2be55Smrg/* Insure that the popup is wholly showing on the screen.
159c9e2be55Smrg   Optionally center the widget horizontally and/or vertically
160c9e2be55Smrg   on current position.
161c9e2be55Smrg */
162c9e2be55Smrg
163c9e2be55Smrgstatic void InsureVisibility(
164c9e2be55Smrg    Widget	popup,
165c9e2be55Smrg    Widget	popup_child,
166c9e2be55Smrg    Position	x,		/* assert: current position = (x,y) */
167c9e2be55Smrg    Position	y,
168c9e2be55Smrg    Boolean	centerX,
169c9e2be55Smrg    Boolean	centerY)
170c9e2be55Smrg{
171c9e2be55Smrg    Position	root_x, root_y;
172c9e2be55Smrg    Dimension	width, height, border;
173c9e2be55Smrg    Arg		args[3];
174c9e2be55Smrg
175c9e2be55Smrg
176c9e2be55Smrg    XtSetArg( args[0], XtNwidth, &width );
177c9e2be55Smrg    XtSetArg( args[1], XtNheight, &height );
178c9e2be55Smrg    XtSetArg( args[2], XtNborderWidth, &border );
179c9e2be55Smrg    XtGetValues( popup, args, THREE );
180c9e2be55Smrg
181c9e2be55Smrg    XtTranslateCoords(popup_child, (Position)0, (Position)0, &root_x, &root_y);
182c9e2be55Smrg    if (centerX) root_x -= width/2 + border;
183c9e2be55Smrg    if (centerY) root_y -= height/2 + border;
184c9e2be55Smrg    if (root_x < 0) root_x = 0;
185c9e2be55Smrg    if (root_y < 0) root_y = 0;
186c9e2be55Smrg    border <<= 1;
187c9e2be55Smrg
188c9e2be55Smrg    if ((int)(root_x + width + border) > WidthOfScreen(XtScreen(toplevel))) {
189c9e2be55Smrg	root_x = WidthOfScreen(XtScreen(toplevel)) - width - border;
190c9e2be55Smrg    }
191c9e2be55Smrg    if ((int)(root_y + height + border) > HeightOfScreen(XtScreen(toplevel))) {
192c9e2be55Smrg	root_y = HeightOfScreen(XtScreen(toplevel)) - height - border;
193c9e2be55Smrg    }
194c9e2be55Smrg
195c9e2be55Smrg    if (root_x != x || root_y != y) {
196c9e2be55Smrg	XtSetArg( args[0], XtNx, root_x );
197c9e2be55Smrg	XtSetArg( args[1], XtNy, root_y );
198c9e2be55Smrg	XtSetValues( popup, args, TWO );
199c9e2be55Smrg    }
200c9e2be55Smrg}
201c9e2be55Smrg
202c9e2be55Smrg
203c9e2be55Smrg/*ARGSUSED*/
204c9e2be55Smrgvoid DestroyPopup(
205c9e2be55Smrg    Widget		widget,		/* unused */
206c9e2be55Smrg    XtPointer		client_data,
207c9e2be55Smrg    XtPointer		call_data)	/* unused */
208c9e2be55Smrg{
209c9e2be55Smrg    Widget		popup = (Widget) client_data;
210c9e2be55Smrg    XtPopdown(popup);
211c9e2be55Smrg    XtDestroyWidget(popup);
212c9e2be55Smrg}
213c9e2be55Smrg
214c9e2be55Smrgvoid WMDeletePopup(
215c9e2be55Smrg    Widget	popup,	/* transient shell */
216c9e2be55Smrg    XEvent*	event)
217c9e2be55Smrg{
218c9e2be55Smrg    String	shellName;
219c9e2be55Smrg    String	buttonName;
220c9e2be55Smrg    Widget	button;
221c9e2be55Smrg
222c9e2be55Smrg    shellName = XtName(popup);
223c9e2be55Smrg    if (strcmp(shellName, XmhNconfirm) == 0)
224c9e2be55Smrg	buttonName = "*no";
225c9e2be55Smrg    else if (strcmp(shellName, XmhNprompt) == 0)
226c9e2be55Smrg	buttonName = "*cancel";
227c9e2be55Smrg    else if (strcmp(shellName, XmhNnotice) == 0)
228c9e2be55Smrg	buttonName = "*confirm";
229c9e2be55Smrg    else if (strcmp(shellName, XmhNerror) == 0)
230c9e2be55Smrg	buttonName = "*OK";
231c9e2be55Smrg    else
232c9e2be55Smrg	return;		/* WM may kill us */
233c9e2be55Smrg
234c9e2be55Smrg    button = XtNameToWidget(popup, buttonName);
235c9e2be55Smrg    if (! button) return;
236c9e2be55Smrg    XtCallActionProc(button, "set", event, (String*)NULL, ZERO);
237c9e2be55Smrg    XtCallActionProc(button, "notify", event, (String*)NULL, ZERO);
238c9e2be55Smrg    XtCallActionProc(button, "unset", event, (String*)NULL, ZERO);
239c9e2be55Smrg}
240c9e2be55Smrg
241c9e2be55Smrgstatic void TheUsual(
242c9e2be55Smrg    Widget	popup)	/* shell */
243c9e2be55Smrg{
244c9e2be55Smrg    XtInstallAllAccelerators(popup, popup);
245c9e2be55Smrg    XtAugmentTranslations(popup, app_resources.wm_protocols_translations);
246c9e2be55Smrg    XtRealizeWidget(popup);
247c9e2be55Smrg    XDefineCursor(XtDisplay(popup), XtWindow(popup), app_resources.cursor);
248d859ff80Smrg    (void) XSetWMProtocols(XtDisplay(popup), XtWindow(popup),
249c9e2be55Smrg			   protocolList, XtNumber(protocolList));
250c9e2be55Smrg}
251c9e2be55Smrg
252c9e2be55Smrg
253c9e2be55Smrg/*ARGSUSED*/
254c9e2be55Smrgvoid XmhPromptOkayAction(
255c9e2be55Smrg    Widget	w,		/* the "value" widget in the Dialog box */
256c9e2be55Smrg    XEvent	*event,		/* unused */
257c9e2be55Smrg    String	*params,	/* unused */
258c9e2be55Smrg    Cardinal	*num_params)	/* unused */
259c9e2be55Smrg{
260c9e2be55Smrg    XtCallCallbacks(XtNameToWidget(XtParent(w), XmhNokay), XtNcallback,
261c9e2be55Smrg		    (XtPointer)XtParent(w));
262c9e2be55Smrg}
263c9e2be55Smrg
264c9e2be55Smrg
265c9e2be55Smrgvoid PopupPrompt(
266c9e2be55Smrg    Widget		transientFor,	/* required to be a top-level shell */
267c9e2be55Smrg    String		question,		/* the prompting string */
268c9e2be55Smrg    XtCallbackProc	okayCallback)		/* CreateFolder() */
269c9e2be55Smrg{
270c9e2be55Smrg    Widget		popup;
271c9e2be55Smrg    Widget		dialog;
272c9e2be55Smrg    Widget		value;
273c9e2be55Smrg    Position		x, y;
274c9e2be55Smrg    Boolean		positioned;
275c9e2be55Smrg    Arg			args[3];
276c9e2be55Smrg    static XtTranslations PromptTextTranslations = NULL;
277c9e2be55Smrg
278c9e2be55Smrg    DeterminePopupPosition(&x, &y, (Widget*)NULL);
279c9e2be55Smrg    XtSetArg(args[0], XtNallowShellResize, True);
280c9e2be55Smrg    XtSetArg(args[1], XtNinput, True);
281c9e2be55Smrg    XtSetArg(args[2], XtNtransientFor, transientFor);
282c9e2be55Smrg    popup = XtCreatePopupShell(XmhNprompt, transientShellWidgetClass, toplevel,
283c9e2be55Smrg			       args, THREE);
284c9e2be55Smrg    positioned = PositionThePopup(popup, x, y);
285c9e2be55Smrg
286c9e2be55Smrg    XtSetArg(args[0], XtNlabel, question);
287c9e2be55Smrg    XtSetArg(args[1], XtNvalue, "");
288c9e2be55Smrg    dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, popup, args,
289c9e2be55Smrg				   TWO);
290c9e2be55Smrg    XtSetArg(args[0], XtNresizable, True);
291c9e2be55Smrg    XtSetValues( XtNameToWidget(dialog, "label"), args, ONE);
292c9e2be55Smrg    value = XtNameToWidget(dialog, XmhNvalue);
293c9e2be55Smrg    XtSetValues( value, args, ONE);
294c9e2be55Smrg    if (! PromptTextTranslations)
295c9e2be55Smrg	PromptTextTranslations = XtParseTranslationTable
296c9e2be55Smrg	    ("<Key>Return: XmhPromptOkayAction()\n\
297c9e2be55Smrg              Ctrl<Key>R:  no-op(RingBell)\n\
298c9e2be55Smrg              Ctrl<Key>S:  no-op(RingBell)\n");
299c9e2be55Smrg    XtOverrideTranslations(value, PromptTextTranslations);
300c9e2be55Smrg
301c9e2be55Smrg    XawDialogAddButton(dialog, XmhNokay, okayCallback, (XtPointer) dialog);
302c9e2be55Smrg    XawDialogAddButton(dialog, "cancel", DestroyPopup, (XtPointer) popup);
303c9e2be55Smrg    TheUsual(popup);
304c9e2be55Smrg    InsureVisibility(popup, dialog, x, y, !positioned, False);
305c9e2be55Smrg    XtPopup(popup, XtGrabNone);
306c9e2be55Smrg}
307c9e2be55Smrg
308c9e2be55Smrg
309c9e2be55Smrg/* ARGSUSED */
310c9e2be55Smrgstatic void FreePopupStatus(
311c9e2be55Smrg    Widget w,			/* unused */
312c9e2be55Smrg    XtPointer closure,
313c9e2be55Smrg    XtPointer call_data)	/* unused */
314c9e2be55Smrg{
315c9e2be55Smrg    PopupStatus popup = (PopupStatus)closure;
316c9e2be55Smrg    XtPopdown(popup->popup);
317c9e2be55Smrg    XtDestroyWidget(popup->popup);
318c9e2be55Smrg    if (popup->shell_command)
319c9e2be55Smrg	XtFree(popup->shell_command);
320c9e2be55Smrg    XtFree((char *) closure);
321c9e2be55Smrg}
322c9e2be55Smrg
323c9e2be55Smrg
324c9e2be55Smrgvoid PopupNotice(
325c9e2be55Smrg    String		message,
326c9e2be55Smrg    XtCallbackProc	callback,
327c9e2be55Smrg    XtPointer		closure)
328c9e2be55Smrg{
329c9e2be55Smrg    PopupStatus popup_status = (PopupStatus)closure;
330c9e2be55Smrg    Widget transientFor;
331c9e2be55Smrg    Widget dialog;
332c9e2be55Smrg    Widget value;
333c9e2be55Smrg    Position x, y;
334c9e2be55Smrg    Arg args[3];
335c9e2be55Smrg    char command[65], label[128];
336c9e2be55Smrg
337c9e2be55Smrg    if (popup_status == (PopupStatus)NULL) {
338c9e2be55Smrg	popup_status = XtNew(PopupStatusRec);
339c9e2be55Smrg	popup_status->lastInput = lastInput;
340c9e2be55Smrg	popup_status->shell_command = (char*)NULL;
341c9e2be55Smrg    }
342c9e2be55Smrg    if (! popup_status->shell_command) {
343c9e2be55Smrg	/* MH command */
344c9e2be55Smrg	if (sscanf( message, "%64s", command ) != 1)
345c9e2be55Smrg	    (void) strcpy( command, "system" );
346c9e2be55Smrg	else {
347c9e2be55Smrg	    int l = strlen(command);
348c9e2be55Smrg	    if (l && command[--l] == ':')
349c9e2be55Smrg		command[l] = '\0';
350c9e2be55Smrg	}
35166d665a3Smrg	snprintf(label, sizeof(label), "%.64s command returned:", command);
352c9e2be55Smrg    } else {
353c9e2be55Smrg	/* arbitrary shell command */
354c9e2be55Smrg	int len = strlen(popup_status->shell_command);
35566d665a3Smrg	snprintf(label, sizeof(label), "%.88s %s\nshell command returned:",
35666d665a3Smrg                 popup_status->shell_command,
35766d665a3Smrg                 ((len > 88) ? "[truncated]" : ""));
358c9e2be55Smrg    }
359c9e2be55Smrg
360c9e2be55Smrg    DeterminePopupPosition(&x, &y, &transientFor);
361c9e2be55Smrg    XtSetArg( args[0], XtNallowShellResize, True );
362c9e2be55Smrg    XtSetArg( args[1], XtNinput, True );
363c9e2be55Smrg    XtSetArg( args[2], XtNtransientFor, transientFor);
364c9e2be55Smrg    popup_status->popup = XtCreatePopupShell(XmhNnotice,
365c9e2be55Smrg			     transientShellWidgetClass, toplevel, args, THREE);
366c9e2be55Smrg    PositionThePopup(popup_status->popup, x, y);
367c9e2be55Smrg
368c9e2be55Smrg    XtSetArg( args[0], XtNlabel, label );
369c9e2be55Smrg    XtSetArg( args[1], XtNvalue, message );
370c9e2be55Smrg    dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass,
371c9e2be55Smrg				   popup_status->popup, args, TWO);
372c9e2be55Smrg
373c9e2be55Smrg    /* The text area of the dialog box will not be editable. */
374c9e2be55Smrg    value = XtNameToWidget(dialog, XmhNvalue);
375c9e2be55Smrg    XtSetArg( args[0], XtNeditType, XawtextRead);
376c9e2be55Smrg    XtSetArg( args[1], XtNdisplayCaret, False);
377c9e2be55Smrg    XtSetValues( value, args, TWO);
378c9e2be55Smrg    XtOverrideTranslations(value, NoTextSearchAndReplace);
379c9e2be55Smrg
380c9e2be55Smrg    XawDialogAddButton( dialog, XmhNconfirm,
381c9e2be55Smrg		       ((callback != (XtCallbackProc) NULL)
382d859ff80Smrg		          ? callback : (XtCallbackProc) FreePopupStatus),
383c9e2be55Smrg		       (XtPointer) popup_status
384c9e2be55Smrg		      );
385c9e2be55Smrg
386c9e2be55Smrg    TheUsual(popup_status->popup);
387c9e2be55Smrg    InsureVisibility(popup_status->popup, dialog, x, y, False, False);
388c9e2be55Smrg    XtPopup(popup_status->popup, XtGrabNone);
389c9e2be55Smrg}
390c9e2be55Smrg
391c9e2be55Smrg
392c9e2be55Smrgvoid PopupConfirm(
393c9e2be55Smrg    Widget		center_widget,	/* where to center; may be NULL */
394c9e2be55Smrg    String		question,
395c9e2be55Smrg    XtCallbackList	affirm_callbacks,
396c9e2be55Smrg    XtCallbackList	negate_callbacks)
397c9e2be55Smrg{
398c9e2be55Smrg    Widget	popup;
399c9e2be55Smrg    Widget	dialog;
400c9e2be55Smrg    Widget	button;
401c9e2be55Smrg    Widget	transientFor;
402c9e2be55Smrg    Position	x, y;
403c9e2be55Smrg    Arg		args[3];
404c9e2be55Smrg    static XtCallbackRec callbacks[] = {
405c9e2be55Smrg	{DestroyPopup,		(XtPointer) NULL},
406c9e2be55Smrg	{(XtCallbackProc) NULL,	(XtPointer) NULL}
407c9e2be55Smrg    };
408c9e2be55Smrg
409c9e2be55Smrg    DeterminePopupPosition(&x, &y, &transientFor);
410c9e2be55Smrg    XtSetArg(args[0], XtNinput, True);
411c9e2be55Smrg    XtSetArg(args[1], XtNallowShellResize, True);
412c9e2be55Smrg    XtSetArg(args[2], XtNtransientFor, transientFor);
413c9e2be55Smrg    popup = XtCreatePopupShell(XmhNconfirm, transientShellWidgetClass,
414c9e2be55Smrg			       toplevel, args, THREE);
415d859ff80Smrg    PositionThePopup(popup, x, y);
416c9e2be55Smrg
417c9e2be55Smrg    XtSetArg(args[0], XtNlabel, question);
418c9e2be55Smrg    dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, popup, args,
419c9e2be55Smrg				   ONE);
420d859ff80Smrg
421c9e2be55Smrg    callbacks[0].closure = (XtPointer) popup;
422c9e2be55Smrg    XtSetArg(args[0], XtNcallback, callbacks);
423d859ff80Smrg    button = XtCreateManagedWidget("yes", commandWidgetClass, dialog,
424c9e2be55Smrg				   args, ONE);
425c9e2be55Smrg    if (affirm_callbacks)
426c9e2be55Smrg	XtAddCallbacks(button, XtNcallback, affirm_callbacks);
427c9e2be55Smrg
428d859ff80Smrg    button = XtCreateManagedWidget("no", commandWidgetClass, dialog,
429c9e2be55Smrg				   args, ZERO);
430c9e2be55Smrg    XtAddCallback(button, XtNcallback, DestroyPopup, (XtPointer) popup);
431c9e2be55Smrg    if (negate_callbacks)
432c9e2be55Smrg	XtAddCallbacks(button, XtNcallback, negate_callbacks);
433c9e2be55Smrg
434c9e2be55Smrg    TheUsual(popup);
435c9e2be55Smrg    CenterPopupPosition(center_widget ? center_widget : transientFor,
436c9e2be55Smrg			popup, x, y);
437c9e2be55Smrg    InsureVisibility(popup, dialog, x, y, False, False);
438c9e2be55Smrg    XtPopup(popup, XtGrabNone);
439c9e2be55Smrg}
440c9e2be55Smrg
441c9e2be55Smrg
442c9e2be55Smrgvoid PopupError(
443c9e2be55Smrg    Widget	widget,	/* transient for this top-level shell, or NULL */
444c9e2be55Smrg    String	message)
445c9e2be55Smrg{
446c9e2be55Smrg    Widget	transFor, error_popup, dialog;
447c9e2be55Smrg    Position	x, y;
448c9e2be55Smrg    Boolean	positioned;
449c9e2be55Smrg    Arg		args[3];
450c9e2be55Smrg    static XtCallbackRec callbacks[] = {
451c9e2be55Smrg	{DestroyPopup,		(XtPointer) NULL},
452c9e2be55Smrg	{(XtCallbackProc) NULL,	(XtPointer) NULL}
453c9e2be55Smrg    };
454c9e2be55Smrg
455c9e2be55Smrg    transFor = widget;
456c9e2be55Smrg    DeterminePopupPosition(&x, &y, transFor ? (Widget*)NULL : &transFor);
457c9e2be55Smrg
458c9e2be55Smrg    XtSetArg(args[0], XtNallowShellResize, True);
459c9e2be55Smrg    XtSetArg(args[1], XtNinput, True);
460c9e2be55Smrg    XtSetArg(args[2], XtNtransientFor, transFor);
461c9e2be55Smrg    error_popup = XtCreatePopupShell(XmhNerror, transientShellWidgetClass,
462c9e2be55Smrg				     toplevel, args, THREE);
463c9e2be55Smrg    positioned = PositionThePopup(error_popup, x, y);
464c9e2be55Smrg
465c9e2be55Smrg    XtSetArg(args[0], XtNlabel, message);
466c9e2be55Smrg    dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, error_popup,
467c9e2be55Smrg				   args, ONE);
468c9e2be55Smrg    callbacks[0].closure = (XtPointer) error_popup;
469c9e2be55Smrg    XtSetArg(args[0], XtNcallback, callbacks);
470c9e2be55Smrg    XawDialogAddButton(dialog, "OK", DestroyPopup, (XtPointer) error_popup);
471c9e2be55Smrg    TheUsual(error_popup);
472c9e2be55Smrg    InsureVisibility(error_popup, dialog, x, y, !positioned, !positioned);
473c9e2be55Smrg    XtPopup(error_popup, XtGrabNone);
474c9e2be55Smrg}
475c9e2be55Smrg
476c9e2be55Smrg/*ARGSUSED*/
477c9e2be55Smrgvoid PopupWarningHandler(
478c9e2be55Smrg    String name,
479c9e2be55Smrg    String type,
480c9e2be55Smrg    String class,
481c9e2be55Smrg    String msg,
482c9e2be55Smrg    String *params,
483c9e2be55Smrg    Cardinal *num)
484c9e2be55Smrg{
485c9e2be55Smrg    char *ptr;
486c9e2be55Smrg    int i;
487c9e2be55Smrg    String par[10];
488c9e2be55Smrg    char message[500];
489c9e2be55Smrg    char buffer[500];
490c9e2be55Smrg    static Boolean allowPopup = True; /* protect against recursion */
491c9e2be55Smrg
492c9e2be55Smrg    XtGetErrorDatabaseText(name, type, class, msg, buffer, 500);
493c9e2be55Smrg
494c9e2be55Smrg    if (params && num && *num) {
495c9e2be55Smrg	i = (*num <= 10) ? *num : 10;
496c9e2be55Smrg	memmove( (char*)par, (char*)params, i * sizeof(String));
497c9e2be55Smrg	bzero( &par[i], (10-i) * sizeof(String));
498c9e2be55Smrg	if (*num > 10)
499c9e2be55Smrg	    par[9] = "(truncated)";
50066d665a3Smrg	snprintf(message, sizeof(message), buffer, par[0], par[1], par[2],
50166d665a3Smrg                 par[3], par[4], par[5], par[6], par[7], par[8], par[9]);
502c9e2be55Smrg	ptr = message;
503c9e2be55Smrg    } else {
504c9e2be55Smrg	ptr = buffer;
505c9e2be55Smrg    }
506c9e2be55Smrg    if (allowPopup) {
507c9e2be55Smrg	allowPopup = False;
508d859ff80Smrg	PopupError((Widget)NULL, ptr);
509c9e2be55Smrg	allowPopup = True;
510c9e2be55Smrg    } else {
511c9e2be55Smrg	fprintf(stderr, ptr);
512c9e2be55Smrg    }
513c9e2be55Smrg}
514