[comp.windows.x] Popup Dialog Example

converse@EXPO.LCS.MIT.EDU (Donna Converse) (08/08/89)

This example of an application which uses a popup dialog box will
run with the X11R3 Athena widget set, and is intended to be used
by programmers to learn about popup dialog boxes.

It does not require an application defaults file. 

			--------------------------

/*
 * $XConsortium: XawPopup.c,v 1.1 89/08/07 18:25:50 converse Exp $
 *
 * Copyright 1989 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Donna Converse, MIT X Consortium
 */

/* An example of popup dialog boxes using X11R3 and the Athena widget set */

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Box.h>
#include <X11/Cardinals.h>
#include <X11/Command.h>
#include <X11/Dialog.h>

static char *previous_label = NULL; 	/* storage of popup label prompt */

void PopupPrompt();		/* the callback of the main demo button */
void Quit();			/* the callback of the main quit button */
void ColorTheButton();		/* the callback of the popup ok button */
void DestroyPopupPrompt();	/* the callback of the popup cancel button */
void Ok();			/* an action proc calling ColorTheButton */

XtActionsRec actionTable[] = {
    {"Ok",	Ok}
};

void main(argc, argv)
    int	argc;
    char **argv;
{
    Arg		args[5];
    Widget	toplevel, box;
    static XtCallbackRec prompt_callbacks[] = {
	{(XtCallbackProc) PopupPrompt,	(caddr_t) NULL},
	{(XtCallbackProc) NULL,		(caddr_t) NULL}
    };
    static XtCallbackRec quit_callbacks[] = {
	{(XtCallbackProc) Quit,		(caddr_t) NULL},
	{(XtCallbackProc) NULL,		(caddr_t) NULL}
    };

    toplevel = XtInitialize(NULL, "Example", NULL, ZERO, &argc, argv);

    XtAppAddActions(XtWidgetToApplicationContext(toplevel), actionTable,
		    XtNumber(actionTable));

    /* This application expects user input, so we set the input resource. */

    XtSetArg(args[0], XtNinput, True);
    XtSetValues(toplevel, args, ONE);

    /* Two buttons, with callback routines, in a box. */

    box = XtCreateManagedWidget("box", boxWidgetClass, toplevel, args, ZERO);

    XtSetArg(args[0], XtNcallback, prompt_callbacks);
    XtSetArg(args[1], XtNlabel, "Press to see Simple Popup Demonstration");
    XtCreateManagedWidget("go", commandWidgetClass, box, args, TWO);

    quit_callbacks[0].closure = (caddr_t) toplevel;
    XtSetArg(args[0], XtNcallback, quit_callbacks);
    XtCreateManagedWidget("quit", commandWidgetClass, box, args, ONE);

    XtRealizeWidget(toplevel);
    XtMainLoop();
}


/*ARGSUSED*/
static void PopupPrompt(button, client_data, call_data)
    Widget	button;		/* the command button in the main window */
    caddr_t	client_data;	/* unused */
    caddr_t	call_data;	/* unused */
{
    Arg		args[5];
    Widget	popup, dialog;
    Position	x, y;
    Cardinal	n = 0;

    /* This popup shell expects user input. */

    XtSetArg(args[n], XtNinput, True);			n++;

    /* This will position the upper left hand corner of the popup at the
     * upper left hand corner of the widget which invoked this callback,
     * inset and lowered by 15 pixels.  I don't deal with the possibility
     * that the popup will be all or partially off the edge of the screen.
     */

    XtTranslateCoords(button, (Position) 0, (Position) 0, &x, &y);
    x += 15; y += 15;
    XtSetArg(args[n], XtNx, x);				n++;
    XtSetArg(args[n], XtNy, y);				n++;
    XtSetArg(args[n], XtNallowShellResize, True);	n++;
    popup = XtCreatePopupShell("prompt", transientShellWidgetClass, button,
			       args, n);

    /* The popup will contain a dialog box, prompting the user for input. */

    XtSetArg(args[0], XtNlabel, "What color should the main button be?"); 
    XtSetArg(args[1], XtNvalue, "");
    dialog = XtCreateManagedWidget("dialog", dialogWidgetClass, popup, args,
				   TWO);

    /* The prompting message's size is dynamic; allow it to request resize. */

    XtSetArg(args[0], XtNresizable, True);
    XtSetValues( XtNameToWidget(dialog, "value"), args, ONE);

    /* A carriage return will have the same effect as clicking on the
     * ok button.
     *
     * Placing the text translations here instead of in an app defaults file
     * has the effect of enforcing a user interface policy from the source
     * code, which has been strongly argued to be inconvenient for users.
     * Here, it is placed in the code for simplicity.  If you wanted to
     * specify it in an app defaults file, the format would be:
     *
     * Example*Dialog.value.translations: #override\n\
     *					  <Key>Return: Ok()\n
     */
    {
	static String text_translations = 
	    "#override\n	<Key>Return: Ok()\n";
	XtTranslations text_table;

	text_table = XtParseTranslationTable(text_translations);
	XtOverrideTranslations(XtNameToWidget(dialog, "value"), text_table);
    }

    /* XtDialogAddButton() is not documented in R3, but it is there. */

    XtDialogAddButton(dialog, "ok", ColorTheButton, (caddr_t) button);
    XtDialogAddButton(dialog, "cancel", DestroyPopupPrompt, (caddr_t) popup);
    XtRealizeWidget(popup);

    /* The dialog popup will allow user input to the rest of the application
     * when the grab kind is GrabNone.   Since I allow input to the rest of
     * the application and I don't destroy the popup before creating a new
     * one, I am allowing multiple identical popups, one for each time the
     * user clicks on the main application button.
     *
     * Try it with XtGrabExclusive instead of XtGrabNone.  Will the user
     * be able to quit the application while the popup is up?  Does the
     * command button which causes the popup to happen remain highlighted?
     */

    XtPopup(popup, XtGrabNone);
}


/*ARGSUSED*/
static void Quit(widget, client_data, call_data)
    Widget	widget;		/* unused, the quit button */
    caddr_t	client_data;	/* the toplevel shell */
    caddr_t	call_data;	/* unused */
{
    Widget	toplevel = (Widget) client_data;
    XtUnmapWidget(toplevel);
    XtDestroyApplicationContext( XtWidgetToApplicationContext( toplevel ));
    exit(0);
}


/*ARGSUSED*/
static void ColorTheButton(ok_button, client_data, call_data)
    Widget	ok_button;	/* the ok button in the popup shell */
    caddr_t	client_data;	/* the main window button */
    caddr_t	call_data;	/* unused */
{
    Widget	dialog = XtParent(ok_button);
    Widget	popup  = XtParent(dialog);
    char        *cname = XtNewString( XtDialogGetValueString( dialog));
    Colormap	cmap   = DefaultColormapOfScreen( XtScreen( popup));
    Display	*dpy   = XtDisplay(dialog);
    Arg		args[3];
    XColor	color;
    static unsigned long initial_pixel = NULL;
    static unsigned long pixel[1];

    /* I assume CellsOfScreen will give an impossible pixel value. */
    if (initial_pixel == NULL) 
	pixel[0] = initial_pixel = CellsOfScreen( XtScreen(popup));

    if ((XParseColor(dpy, cmap, cname, &color)) &&
	(XAllocColor(dpy, cmap, &color))) {
	XtSetArg(args[0], XtNbackground, color.pixel);
	XtSetValues((Widget) client_data, args, ONE);
	DestroyPopupPrompt(ok_button, (caddr_t) popup, (caddr_t) NULL);

	
/*	 Given that resources are scarce, and that I know that no other
 * part of my application has allocated the previous color, I should
 * free it.   Independent of whether the visual type is static or
 * dynamic, and independent of the number of cells in the colormap, the
 * server should accept n requests to free pixel p, where n is the 
 * number of times that pixel was allocated by this client.  Upon the 
 * nth request the resource will be freed.  Upon the (n+1)th request a
 * Bad Access error should occur.
 *
 *	At least one server is improperly implemented, allowing any
 * number of requests without giving a Bad Access error.  At least one
 * other server does not allow allocated colors to be freed in a static 
 * visual.   In both cases, the visual type is a static one.
 *
 * 	If you just want to learn about popups, and this code
 * breaks with a protocol error of Bad Access upon freeing colors, 
 * comment out the next few lines.
 */

	if (pixel[0] != initial_pixel)
	    XFreeColors(dpy, cmap, pixel, 1, (unsigned long) NULL);
	pixel[0] = color.pixel;
    }
    else {
	char	str[300], *label;
	(void) sprintf(str, "Can't get color \"%s\".  Try again.",  cname);
	label = XtNewString(str);
	XtSetArg(args[0], XtNlabel, label);
	XtSetArg(args[1], XtNvalue, "");
	XtSetValues(dialog, args, TWO);
	if (previous_label)
	    XtFree(previous_label);
	previous_label = label;
    }
    XtFree(cname);
}


/*ARGSUSED*/
static void DestroyPopupPrompt(widget, client_data, call_data)
    Widget	widget;		/* unused, a button in the popup */
    caddr_t	client_data;	/* the popup shell */
    caddr_t	call_data;	/* unused */
{
    Widget	popup = (Widget) client_data;
    XtPopdown(popup);
    XtDestroyWidget(popup);
    if (previous_label) {
        XtFree(previous_label);
	previous_label = NULL;
    }
}


/*ARGSUSED*/
static void Ok(widget, event, params, num_params)
    Widget	widget;		/* the text widget in the dialog */
    XEvent	*event;		/* unused */
    String	*params;	/* unused */
    Cardinal	*num_params;	/* unused */
{
    Widget	dialog = XtParent(widget);
    Widget	ok_button = XtNameToWidget(dialog, "ok");
    Widget	color_button = XtParent(XtParent(dialog));

    ColorTheButton(ok_button, (caddr_t) color_button, (caddr_t) NULL);
}