/***********************************************************************
 *
 * TITLE:
 *	shellmgmt.c
 *
 * AUTHOR:
 *	Kevin J. Miller
 *
 * DESCRIPTION:
 *	Sets correct response for modal Window Manager shells Close
 *	button.  Each time you create an Application Shell or a
 *	Modal Dialog, you should call ShellAddClose() to set the
 *	MWM Close behavior correctly.  When the Dialog is managed,
 *	use ShellModalManage() instead of XtManageChild(), and
 *	before unmanaging call ShellValidateClose() to see if
 *	the MWM Close button was legally selected, and finally
 *	unmanage with ShellModalUnmanage().
 *
 *				CHANGE HISTORY
 *
 * $Log: shellmgmt.c,v $
 * Revision 1.7  1995/01/20  04:01:57  kevin
 * removed unused variables
 *
 * Revision 1.6  1994/11/22  00:36:30  kevin
 * added support for iconified main window
 *
 * Revision 1.5  1994/01/14  19:51:05  kevin
 * added <Xm/AtomMgr.h> per SGI warning
 *
 * Revision 1.4  1994/01/06  21:42:08  kevin
 * updates after SGI test
 *
 * Revision 1.3  1994/01/06  05:12:06  kevin
 * port for V20
 *
 * Revision 1.2  1993/03/03  01:14:43  kevin
 * updated warning messages
 *
 * Revision 1.1  1993/03/02  02:40:55  kevin
 * Initial revision
 *
 ***********************************************************************
 *
 * WARNINGS:
 *	none
 *
 * EXTERNAL CALLABLE COMPONENTS (PUBLIC):
 *	ShellAddClose
 *	ShellModalManage
 *	ShellValidateClose
 *	ShellModalUnmanage
 *	ShellIsIconified
 *
 * GLOBALS:
 *	none
 *
 * WAIVERS:
 *	none
 *
 * NOTES:
 *	none
 *
 * MANPAGE:
 *	none
 *
 ***********************************************************************/

#ifndef lint
static char rcsid[] = 
    "$Id: shellmgmt.c,v 1.7 1995/01/20 04:01:57 kevin OEL $";
#endif

#include <Xm/Xm.h>
#include <Xm/Protocols.h>
#include <Xm/AtomMgr.h>
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>

#include "shellmgmt.h"
#include "message.h"

#define BATCH_SIZE 16

/*
 * local function prototypes
 */
static void structCB(Widget, XtPointer, XEvent *, Boolean *);

/*
 * local variables
 */
static struct stk {		/* stack of managed widgets */
    Widget (*w)[];
    int count;
    int avail;
} stack = { NULL, 0, 0 };
static int iconic;		/* state of program */

/***********************************************************************
 *
 * FUNCTION:
 *	ShellAddClose
 *
 * INPUTS:
 *	w			- (Widget) dialog or top level widget
 *	callback		- (void (*)()) Close callback function
 *	client_data		- (Widget) client data for callback
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	stack			- (static struct stk) add App. Shell
 *
 * DESCRIPTION:
 *	The Motif dialog convenience functions do not return the
 *	Dialog Shell which is needed in order to properly set the
 *	Close response.  This function will take either a Window
 *	Manager Shell (usually an Applictaion Shell) or the child
 *	of such a shell (usually the child of a Dialog Shell created
 *	with a convenience function.
 */

void 
ShellAddClose(Widget w, XtCallbackProc callback, XtPointer client_data)
{
    Atom deleteWindowAtom;	/* window manager interaction */
    Widget s;			/* shell widget */
    Arg args[1];		/* standard Xt argument list */
    Cardinal n;			/* standard Xt argument count */

    s = w;
    if (!XtIsSubclass(s, (WidgetClass)wmShellWidgetClass)) {
	s = XtParent(s);
    }
    if (!XtIsSubclass(s, (WidgetClass)wmShellWidgetClass)) {
	Warning("INTERNAL: Widget or parent of %s is not a WMShell.",
	    XrmQuarkToString(w->core.xrm_name));
    }

    deleteWindowAtom = XmInternAtom(XtDisplay(w), "WM_DELETE_WINDOW", False);

    /*
     * if this is the Application Shell -> add it to the stack
     * also intern the WM_DELETE_WINDOW atom
     */
    if (XtClass(s) == (WidgetClass)applicationShellWidgetClass) {
	if ((stack.avail == 0) && (stack.count == 0)) {
	    stack.avail = BATCH_SIZE;
	    stack.w = (Widget (*)[])XtMalloc((Cardinal)(sizeof(Widget)
		* stack.avail));
	    (*(stack.w))[(stack.count)++] = s;
	    XtAddEventHandler(s, StructureNotifyMask, False, structCB, NULL);
	} else {
	    Warning("INTERNAL: Multiple Application Shells have been defined.");
	}
    }

    n = 0;
    XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
    XtSetValues(s, args, n);

    XmAddWMProtocolCallback(s, deleteWindowAtom, callback, client_data);
}

/***********************************************************************
 *
 * FUNCTION:
 *	ShellModalManage
 *
 * INPUTS:
 *	w		- (Widget) the dialog widget to be managed
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	stack		- (static struct stk) add Dialog Shell
 *
 * DESCRIPTION:
 *	The widget w should be the child of a Dialog Shell.  The
 *	widget is managed and the parent shell is then placed on
 *	the stack.
 */

void 
ShellModalManage(Widget w)
{
    Widget s;

    /*
     * add parent (shell) to stack
     */
    s = XtParent(w);
    if (!XtIsSubclass(s, (WidgetClass)wmShellWidgetClass)) {
	Warning("INTERNAL: Parent of managed widget (%s) is not a WMShell.",
	    XrmQuarkToString(w->core.xrm_name));
    }

    /*
     * add the current dialog to the stack
     */
    if (stack.count >= stack.avail) {
	stack.avail += BATCH_SIZE;
	stack.w = (Widget (*)[])XtRealloc((char *)(stack.w),
	    (Cardinal)(sizeof(Widget) * stack.avail));
    }
    (*(stack.w))[(stack.count)++] = s;

    /*
     * manage the child widget
     */
    XtManageChild(w);
}

/***********************************************************************
 *
 * FUNCTION:
 *	ShellValidateClose
 *
 * INPUTS:
 *	w		- (Widget) the dialog widget to be managed
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	True		- (int) if this is a legal Close or not a Close
 *	False		- (int) if this is an illegal Close
 *
 * EXTERNALLY READ:
 *	stack		- (static struct stk) check WM Shell
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 *	A Boolean function that returns True if this dialog callback
 *	had been called normally or by an active Close mechanism.
 */

int 
ShellValidateClose(Widget w)
{
    int valid;

    valid = False;

    if (!XtIsSubclass(w, (WidgetClass)wmShellWidgetClass)) {
	/*
	 * the callback was not called be the window manager
	 */
	valid = True;
    } else {
	/*
	 * if this is a shell, then it should match the top
	 * of the stack
	 */
	if ((stack.count > 0) && (w == (*(stack.w))[stack.count - 1])) {
	    valid = True;
	    if ((stack.count == 1) && ShellIsIconified()) {
		XMapRaised(XtDisplay(w), XtWindow(w));
	    }
	}
    }

    return valid;
}

/***********************************************************************
 *
 * FUNCTION:
 *	ShellModalUnmanage
 *
 * INPUTS:
 *	w		- (Widget) the dialog widget to be unmanaged
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	stack		- (static struct stk) remove Dialog Shell
 *
 * DESCRIPTION:
 *	Unmanage the dialog widget (child of a shell) and check and
 *	remove the shell from the stack.
 */

void 
ShellModalUnmanage(Widget w)
{
    Widget s;

    /*
     * get Dialog Shell
     */
    s = XtParent(w);
    if (!XtIsSubclass(s, (WidgetClass)wmShellWidgetClass)) {
	Warning("INTERNAL: Parent of managed widget (%s) is not a WMShell.",
	    XrmQuarkToString(w->core.xrm_name));
    }

    /*
     * remove Dialog Shell from stack
     */
    if ((stack.count > 0) && (s == (*(stack.w))[stack.count - 1])) {
	--(stack.count);
    } else {
	Warning("INTERNAL: Cannot remove widget (%s) from stack.",
	    XrmQuarkToString(s->core.xrm_name));
    }

    /*
     * unmanage the child widget
     */
    XtUnmanageChild(w);
}

/***********************************************************************
 *
 * FUNCTION:
 *	ShellIsIconified
 *
 * INPUTS:
 *	none
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	iconic		- (static int) current state
 *
 * EXTERNALLY READ:
 *	iconic		- (static int) current state
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 *	Check if the application is iconified.
 */

int
ShellIsIconified()
{
    return iconic;
}

/***********************************************************************
 *
 * FUNCTION:
 *	structCB
 *
 * INPUTS:
 *	w		- (Widget) not used
 *	client_data	- (XtPointer) not used
 *	event		- (XEvent *) theis event
 *	dispatch	- (Boolean *) not used
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	iconic		- (int) iconic state variable
 *
 * DESCRIPTION:
 *	Set state of iconic variable when UnmapNotify and MapNotify
 *	events come in.
 */

static void
structCB(Widget w, XtPointer client_data, XEvent *event, Boolean *dispatch)
{
    switch (event->type) {
    case UnmapNotify:
	iconic = TRUE;
	break;
    case MapNotify:
	iconic = FALSE;
	break;
    }
}
