[comp.windows.x] A yes/no dialog

george@hobbes.catt.ncsu.edu (George Browning) (11/19/90)

Is the following possible?

    void mycode()
    {
        if (Confirm("Do you wish to exit the program?"))
            exit(0);
    }

    where Confirm() displays a yes/no dialog with the question and
    sends a Boolean back.

This can be done with callbacks if I set the callback for the
yes button to a certain function.  However, I have a situation
where several Confirm-like functions will be needed and multiple
callbacks would be entirely too messy.

I can run the application with -synchronous and the above will
work (I think) but I really only need synchronous for one 
or two functions.

Thanks in advance,
george
--
------------------------------------------------------------------------
| o |  George Browning                 george@hobbes.catt.ncsu.edu | o |
| o |  NC State University             Raleigh, NC                 | o |
------------------------------------------------------------------------

jordan@Morgan.COM (Jordan Hayes) (11/22/90)

George Browning <george@hobbes.catt.ncsu.edu> writes:

	Is the following possible?

	    void mycode()
	    {
	        if (Confirm("Do you wish to exit the program?"))
	            exit(0);
	    }
	
	    where Confirm() displays a yes/no dialog with the question and
	    sends a Boolean back.

Here's what I do in Motif; your mileage may vary:

-----

#include <Xm/Xm.h>
#include <Xm/MessageB.h>

void
Confirm(p, str, yesFunc, noFunc, acb)
	Widget		p;
	String		str;
	VPF		yesFunc;
	VPF		noFunc;
	XtPointer	acb;
{
	Widget		mb;
	XmString	msg;

	msg = XmStringCreateLtoR(str, XmSTRING_DEFAULT_CHARSET);

	FIRSTARG(XmNmessageString, msg)
	NEWARG(XmNdeleteResponse, XmDESTROY)
	mb = XmCreateWarningDialog(p, "confirm", sArgs, ArgIndex);
	XmStringFree(msg);

	if (yesFunc)
		XtAddCallback(mb, XmNokCallback, yesFunc, acb);

	if (noFunc)
		XtAddCallback(mb, XmNcancelCallback, noFunc, acb);

	XtManageChild(mb);
}

-----

Then I can say:

SafeExit(top)
	Widget	top;
{
	Confirm(top, "Do you really want to exit?", exit, NULL, NULL);
}

/jordan

lanzo@wgate.UUCP (Mark Lanzo) (11/30/90)

George Browning <george@hobbes.catt.ncsu.edu> writes:
    
    >	Is the following possible?
    
    >	    void mycode()
    >	    {
    >	        if (Confirm("Do you wish to exit the program?"))
    >	            exit(0);
    >	    }
    >	
    >	    where Confirm() displays a yes/no dialog with the question and
    >	    sends a Boolean back.
    

I ran into this same need a little while ago; my solution is given below.
It consists of two files:  "alert.h" and "alert.c".  There are also two
utility files "xarg.c" and "xarg.h" which are just convenience routines
for setting up argument lists; you can replace these easily enough (or
just merge them into the beginning of "alert.c").

There's some fairly heavy duty documentation in the file alert.c to
explain it all (look for the giant comment in a box of asterisks!).

Near the bottom of "alert.c" you will find an event loop which looks like:

    while(AlertResponse == -1)
	{
	XEvent event;
	XtNextEvent(&event);
	XtDispatchEvent(&event);
	}

this is what forces the synchronous operation.  However, you may have to
modify this if your application has some more complex sort of structure
and uses something besides your basic "XtMainLoop()" to dispatch events.


/****************************************************************************\
*                                                                            *
*   DISCLAIMER:                                                              *
*      Permission is hereby given for anyone to use/modify this to their     *
*      heart's content.  Use at your own risk / No warranty expressed or     *
*      implied / No support / etc. etc.  You know the spiel.                 *
*                                                                            *
\****************************************************************************/

Flames -> /dev/null
Questions, suggestions, attaboys, etc. ->   ..!uunet!wgate!lanzo
			or maybe       ->     lanzo@wgate.com

If you come up with some nice improvements or a better way of doing
this, please let me know.

--------------------- %< ---------------  xarg.h -------------------------

#define	ARGS			WidgetArgs, WidgetArgCount
#define	ARGLIST_RESET()		WidgetArgCount = 0
#define	ARGLIST_ADD(tag,value) 					\
	(XtSetArg(WidgetArgs[WidgetArgCount],tag,value),	\
	WidgetArgCount++)

#define MAX_WIDGET_ARGS	32
extern	Arg 		WidgetArgs[MAX_WIDGET_ARGS];
extern	Cardinal	WidgetArgCount;
extern	Widget		RootWidget;

--------------------- %< ---------------  xarg.c -------------------------

#include <X11/Intrinsic.h>
#include "xarg.h"

Arg 		WidgetArgs[MAX_WIDGET_ARGS];
Cardinal	WidgetArgCount;
Widget		RootWidget;

--------------------- %< ---------------  alert.h ------------------------

/*
 * Values returned depending on with button the user selects:
 */

#define CANCEL			1
#define OK			0

/*
 * These are convenient aliases for those routines which are 
 * asking a question:
 */

#define	YES			OK
#define NO			CANCEL


/*
 * Classes of alert the user can generate:
 */

#define ALERT_MESSAGE		0
#define ALERT_WORKING		1
#define ALERT_INFORMATION	2
#define ALERT_QUESTION		3
#define ALERT_WARNING		4
#define	ALERT_ERROR		5


int	alert_user();

/*
 * Convenience macros
 */

#define InformUser(tag)	    alert_user(RootWidget, tag, ALERT_INFORMATION, 0)
#define QueryUser(tag)	    alert_user(RootWidget, tag, ALERT_QUESTION,    1)
#define AskUser(tag)	    alert_user(RootWidget, tag, ALERT_QUESTION,    1)
#define WarnUser(tag)	    alert_user(RootWidget, tag, ALERT_WARNING,     0)
#define Confirm(tag)	    alert_user(RootWidget, tag, ALERT_WARNING,     1)
#define AlertUser(tag)	    alert_user(RootWidget, tag, ALERT_ERROR,       0)

--------------------- %< ---------------  alert.c ------------------------

#include <Xm/Xm.h>
#include <Xm/MessageB.h>
#include "xarg.h"
#include "alert.h"

/*
 * This routine takes a single item XmString, gets the text string
 * associated with the XmString, and creates a new XmString using
 * the XmStringCreateLtoR function.  This means that the new string
 * will be broken into segments at any "\n" characters; you will
 * end up with a multiline character string.
 */

XmString ConvertToMultilineString(xstr)
    XmString xstr;
    {
    char * text;

    if (!XmStringGetLtoR(xstr,XmSTRING_DEFAULT_CHARSET,&text)) return(NULL);
    return(XmStringCreateLtoR(text,XmSTRING_DEFAULT_CHARSET));
    }


/*
 * This routine takes a message box widget, gets the message string
 * component, and replaces it with a multiline message string.
 *
 * The Motif libraries apparently use XmStringCreate (or a close
 * relative) for creating the message strings for the widgets.
 * This has the disadvantage of only allowing single line messages.
 * I want multiline messages, so here I fetch the message string,
 * and rebuild it using XmStringCreateLtoR, which will create a 
 * multi-line compound string, by parsing "\n" characters
 * in the input text string.
 * 
 * I also remove any tabs in the string.  There are two reasons for
 * this:  (1) It makes it easy to indent continuation lines in the
 * resource file, and (2) tabs look ugly anyways on most fonts, 
 * since they do not normally represent whitespace in the font set.
 * [Instead you tend to get things like miniature 'HT' or 'TAB'
 * symbols].
 *
 * I suspect that there is a better way to do this, but I don't know
 * what it is offhand.
 */

void ConvertToMultilineMessage(msg)
    Widget msg;		    /* ID of a MessageBox widget */
    {
    XmString msg_str;
    char * text;
    register char * in, * out, c;

    ARGLIST_RESET();
    ARGLIST_ADD(XmNmessageString,&msg_str);
    XtGetValues(msg,ARGS);

    if (!XmStringGetLtoR(msg_str,XmSTRING_DEFAULT_CHARSET,&text)) return;

    /* Remove tabs */
    in = out = text;
    while(c = *in++) 
	if (c != '\t') *out++ = c;
    *out = 0;

    msg_str = XmStringCreateLtoR(text,XmSTRING_DEFAULT_CHARSET);
    if (msg_str) 
	{
	ARGLIST_RESET();
	ARGLIST_ADD(XmNmessageString,msg_str);
	XtSetValues(msg,ARGS);
	XmStringFree(msg_str);
	}
    }


/****************************************************************************\
*                                                                            *
*   FUNCTION                                                                 *
*       alert_user --                                                        *
*                                                                            *
*       Alert_user is  used to display a popup message on the                *
*       screen to which the user must respond before proceeding.             *
*       The message will appear in a little window of its own, along         *
*       with an OK button and optional CANCEL button which the user          *
*       must click on to make the requester go away.                         *
*                                                                            *
*       It returns an integer which will be OK or CANCEL depending on        *
*       which button the user selects to make the widget go away.            *
*       Some types of alerts don't have a cancel option, so OK is the        *
*       only possible response.  When both options are presented, the        *
*       CANCEL button will be the default response.                          *
*                                                                            *
*       Note that OK and CANCEL are the canonic button names, but they       *
*       can have other labels on the display, such as "Yes" and "No".        *
*                                                                            *
*       The pop-up dialogs can show one of several different "types" or      *
*       "classes" of message.  The only functional differences between       *
*       these types is that various graphical symbols are displayed          *
*       along with the message to give the user an obvious hint as to        *
*       the nature of the message being displayed.  These classes are:       *
*                                                                            *
*           * Message                                                        *
*           * Information                                                    *
*           * Question                                                       *
*           * Warning                                                        *
*           * Error                                                          *
*           * Working                                                        *
*                                                                            *
*       "Message" is the most basic type, and has no symbol.                 *
*       Other types have symbols like question-marks, stop-signs, etc.       *
*       displayed to the left of the message text.                           *
*                                                                            *
*   SYNOPSIS                                                                 *
*       #include "alert.h"                                                   *
*       response = alert_user(parent, name, type, flags);                    *
*           Widget parent;                                                   *
*           char   *name;                                                    *
*           int    type;                                                     *
*           int    flags;                                                    *
*           int    response;                                                 *
*                                                                            *
*       The file "alert.h" defines several macros for a friendlier           *
*       interface to the basic routine:                                      *
*                                                                            *
*           response = AskUser(name);                                        *
*           response = InformUser(name);                                     *
*           response = WarnUser(name);                                       *
*           response = Confirm(name);                                        *
*           response = AlertUser(name);                                      *
*                                                                            *
*       These are described later.                                           *
*                                                                            *
*   ARGUMENTS                                                                *
*       parent --   The parent widget for this widget.  Usually this         *
*                   will be the root widget for the application.             *
*                   Make sure that the parent widget is "managed"            *
*                   before you call this routine.                            *
*                                                                            *
*       name   --   The name for this widget.  This is used to look          *
*                   up resources in the resource file (such as in            *
*                   your .Xdefaults file) among other things.                *
*                                                                            *
*       type   --   The type of alert this is.  This is one of:              *
*                                                                            *
*                           ALERT_ERROR                                      *
*                           ALERT_WARNING                                    *
*                           ALERT_INFORMATION                                *
*                           ALERT_QUESTION                                   *
*                           ALERT_WORKING                                    *
*                           ALERT_MESSAGE                                    *
*                                                                            *
*                   where these values are defined in "alert.h"              *
*                   The only difference between these different              *
*                   "types" of widget is the little symbol which             *
*                   will appear on the popup gadget to the left              *
*                   of the message text.                                     *
*                                                                            *
*                   If "type" is an illegal value, the alert type            *
*                   defaults to the MESSAGE type.                            *
*                                                                            *
*       flags  --   Flags modifying how the widget behaves.                  *
*                   By default, the message box will only have an OK         *
*                   button attached to it.  If the low order bit (bit 0)     *
*                   of "flags" is set, then the widget will also have        *
*                   a CANCEL button attached to it.                          *
*                                                                            *
*                   Currently no other flags are defined, although some      *
*                   may be added later (such as to enable a HELP button).    *
*                                                                            *
*                   If the alert type is a QUESTION dialog, then the         *
*                   CANCEL button will always be enabled.                    *
*                                                                            *
*       For the various convenience macros, only the "name" is supplied.     *
*       The "parent" argument is "RootWidget", and the other arguments       *
*       depend on the macro.  The macros are:                                *
*                                                                            *
*           InformUser() -  Creates an information dialog,                   *
*                           only button is OK button.                        *
*                                                                            *
*           AskUser()    -  Creates a question dialog                        *
*                           with an OK (YES) and CANCEL (NO) button.         *
*                                                                            *
*           WarnUser()   -  Creates a warning dialog to inform user          *
*                           about a potentially dangerous problem.           *
*                           Only an OK button is present.                    *
*                                                                            *
*           Confirm()    -  Creates a warning dialog with both an            *
*                           OK and a CANCEL button, to ask the user          *
*                           to confirm doing a potentially dangerous         *
*                           action.                                          *
*                                                                            *
*           AlertUser()  -  Creates an error dialog to alert the user        *
*                           that he did something wrong or that some         *
*                           action failed.  Only an OK button appears.       *
*                                                                            *
*   RETURNS                                                                  *
*       OK     (0)  -- If they exited with the OK button.                    *
*       CANCEL (1)  -- If they exited with the CANCEL button.                *
*                                                                            *
*       The constants OK and CANCEL are defined in "alert.h".                *
*                                                                            *
*   ENVIRONMENTAL INFLUENCES                                                 *
*       Normally you will want to use the widget name so you can             *
*       so you can specify resources in the application resource-file.       *
*       For instance, if you create an alert named "save_first",             *
*       then you can specify resources in the resource file such as:         *
*                                                                            *
*            *save_first.messageString:  Save Changes?                       *
*            *save_first.okLabelString:     Yes                              *
*            *save_first.cancelLabelString: No                               *
*                                                                            *
*       The following resources are the ones that are probably of the        *
*       most intererest to the general user:                                 *
*                                                                            *
*           .messageString                                                   *
*                   The actual text of the message.                          *
*                   Make sure you specify this!  Otherwise, the              *
*                   the default text will just be something like             *
*                   "MessageLabel", which obviously isn't of much            *
*                   use to the user.                                         *
*                                                                            *
*           .okLabelString                                                   *
*                   The text on the OK button.  By default, this             *
*                   will be the string "OK".                                 *
*                                                                            *
*           .cancelLabelString                                               *
*                   The text on the CANCEL button (if displayed).            *
*                   By default, this is "CANCEL".                            *
*                                                                            *
*           .background                                                      *
*                   The background color for the pop-up window.              *
*                                                                            *
*           .width, .height, .x, .y                                          *
*                   The size and position of the widget.                     *
*                   Normally the widget is centered in the window,           *
*                   and the size expands to contain the message string.      *
*                                                                            *
*       I have augmented the semantics of the resource file for the          *
*       message string.  If you include "\n" characters in the               *
*       message string, then the message will be broken into separate        *
*       lines at the "\n" characters, so you can easily create               *
*       multiline messages.  Also, I strip all tab characters out of         *
*       the message string.  This makes it easy to indent your               *
*       message strings in the resource file for readability.                *
*       For example, a long message string might look like:                  *
*                                                                            *
*            *save_first.messageString:  \                                   *
*                   You have made changes to your file and\n\                *
*                   haven't saved your work yet.  If you exit now,\n\        *
*                   you will lose all your work.\n\n\                        *
*                   Do you want to save your changes first?                  *
*                                                                            *
*       Note that the "\n"'s in the string are used to break the message     *
*       onto multiple lines.  The "\"'s at the end of each line are          *
*       simply used to continue a long entry in the resource file, and       *
*       have absolutely nothing to do with "\n"'s in the middle of the       *
*       string.                                                              *
*                                                                            *
*       Also note that the entry is indented with TAB's and not spaces --    *
*       if you use spaces then those spaces will appear as part of your      *
*       message text.  Don't put tabs in the middle of a line either         *
*       since they will also be stripped.  This loss of use of the tab       *
*       character is not really a problem since in most X fonts the TAB      *
*       character will generate some strange symbol rather than acting       *
*       as a white-space type of character.                                  *
*                                                                            *
*   WARNINGS                                                                 *
*       The multi-line breaking and TAB removal in the message string        *
*       is an enhancement I have made specifically within the "alert"        *
*       requester dialog.  You can't use this mechanism in general           *
*       for other text strings within the resource file (which is a          *
*       shortcoming in the current implementation of Motif).                 *
*                                                                            *
*   BUGS                                                                     *
*       Probably there should be one additional alert type of even a         *
*       more serious nature than "error".  A separate "bomb" or              *
*       "bug" type of error should be available to indicate that an          *
*       internal system error occurred or something went                     *
*       catastrophically awry.  The "error" dialog should just be            *
*       used to inform the user that he's trying to do something             *
*       he shouldn't, supplied bad data, or something similar.               *
*                                                                            *
*   SEE ALSO                                                                 *
*       For more information, see the Motif documentation for the            *
*       "XmMessageBox" widget, and the various pop-up message box            *
*       creation routines like "XmCreateErrorDialog()".                      *
*                                                                            *
*       Note:  when the widgets are created, they have three buttons         *
*       attached (Help, OK, Cancel).  I always remove the Help button,       *
*       and may also remove the Cancel button.                               *
*                                                                            *
\****************************************************************************/

static int AlertResponse;

/* 
 * Function called when the user hits one of the buttons on 
 * the pop-up dialog box.
 */

static void alert_user_CB(w,user_data,call_data)
    Widget w;
    caddr_t user_data, call_data;
    {
    AlertResponse = (int) user_data;
    }


int alert_user(container,name,class,flags)
    Widget container;	/* Parent widget.		*/
    int    class;	/* Warning, Yes/No query, etc.  */
    char * name;	/* Widget root name		*/
    int    flags;
    {
    Widget alert, ok, cancel, help;
    XmString msg_str;
    int  allow_cancel = flags & 1;

    ARGLIST_RESET();
    if (allow_cancel)
	ARGLIST_ADD(XmNdefaultButtonType,XmDIALOG_CANCEL_BUTTON);

    ARGLIST_ADD(XmNautoUnmanage,True);
    ARGLIST_ADD(XmNdialogStyle,XmDIALOG_APPLICATION_MODAL);

    /*
     * Originally, I did a "ARGLIST_ADD(XmNdialogType,class)" here,
     * and just called XmCreateMessageDialog() to create the widget,
     * but that doesn't give you the little symbol as part of the
     * widget.  Contrary to documentation, setting the dialog type
     * after creating the widget (using XtSetValues) doesn't do
     * it either.  So I explicitly call the different creation
     * routines here.
     */

    switch(class)
	{
	case ALERT_ERROR:
	    alert = XmCreateErrorDialog(container,name,ARGS);
	    break;
	case ALERT_INFORMATION:
	    alert = XmCreateInformationDialog(container,name,ARGS);
	    break;
	case ALERT_QUESTION:
	    alert = XmCreateQuestionDialog(container,name,ARGS);
	    /* 
	     * Make sure "No" button appears, regardless of what
	     * user specified.  Popping up a question dialog with
             * only an OK button would be silly.
             */
	    allow_cancel = 1;
	    break;
	case ALERT_WARNING:
	    alert = XmCreateWarningDialog(container,name,ARGS);
	    break;
	case ALERT_WORKING:
	    alert = XmCreateWorkingDialog(container,name,ARGS);
	    break;
	default:
	case ALERT_MESSAGE:
	    alert = XmCreateMessageDialog(container,name,ARGS);
	    break;
	}

    ConvertToMultilineMessage(alert);
    XtAddCallback(alert,XmNokCallback,alert_user_CB,(caddr_t) OK);
    help = XmMessageBoxGetChild(alert,XmDIALOG_HELP_BUTTON);
    XtUnmanageChild(help);

    if (!allow_cancel)
	{
	cancel = XmMessageBoxGetChild(alert,XmDIALOG_CANCEL_BUTTON);
	XtUnmanageChild(cancel);
	}
    else
	{
	XtAddCallback(alert,XmNcancelCallback,alert_user_CB,(caddr_t) CANCEL);
	}

    AlertResponse = -1;
    XtManageChild(alert);
    while(AlertResponse == -1)
	{
	XEvent event;
	XtNextEvent(&event);
	XtDispatchEvent(&event);
	}
    return(AlertResponse);
    }


-- 
Mark Lanzo                      | Wandel & Goltermann Technologies, Inc.
uunet!wgate!lanzo               | 1030 Swabia Court, Research Triangle Park
lanzo@wgate.wgate.com ??        | North Carolina 27709-3585
                                | Phone: (919) 941-5730  FAX: (919) 941-5751