/**
 *
 * $Id: Protocols.c,v 1.3 1998/02/22 01:00:47 rwscott Exp $
 *
 * Copyright (C) 1995 Free Software Foundation, Inc.
 *
 * This file is part of the GNU LessTif Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/

static char rcsid[] = "$Id: Protocols.c,v 1.3 1998/02/22 01:00:47 rwscott Exp $";

#include <LTconfig.h>
#include <XmI/XmI.h>

#include <Xm/XmP.h>
#include <Xm/ProtocolsP.h>
#include <Xm/VendorSEP.h>

#include <XmI/DebugUtil.h>

static void class_initialize();

static void class_part_initialize(WidgetClass w_class);

static void initialize(Widget request, Widget new_w,
		       ArgList args, Cardinal *num_args);

static void destroy(Widget w);

#if 0
static Boolean set_values(Widget current, Widget request, Widget new_w,
			  ArgList args, Cardinal *num_args);
#endif

static void _XmProtocolHandler(Widget w, XEvent *evp, String *params, Cardinal *nparams);

/*
 * resources 
 */
#define Offset(field) XtOffsetOf(XmProtocolRec, protocol.field)
static XtResource resources[] =
{
    {
	XmNextensionType, XmCExtensionType, XmRExtensionType,
	sizeof(unsigned char), XtOffsetOf(XmProtocolRec, ext.extensionType),
	XmRImmediate, (XtPointer)XmPROTOCOL_EXTENSION
    },
    {
	XmNprotocolCallback, XmCProtocolCallback, XmRCallback,
	sizeof(XtCallbackList), Offset(callbacks),
	XmRImmediate, (XtPointer)NULL
    }
};

/* *INDENT-OFF* */
XmProtocolClassRec xmProtocolClassRec = {
    /* Object class part */
    {
	/* superclass            */ (WidgetClass) &xmExtClassRec,
        /* class_name            */ "XmProtocolClass",
	/* widget_size           */ sizeof(XmProtocolRec),
	/* class_initialize      */ class_initialize,
	/* class_part_initialize */ class_part_initialize,
	/* class_inited          */ False,
	/* initialize            */ initialize,
	/* initialize_hook       */ NULL,
	/* realize               */ NULL,
	/* actions               */ NULL,
	/* num_actions           */ 0,
	/* resources             */ resources,
	/* num_resources         */ XtNumber(resources),
	/* xrm_class             */ NULLQUARK,
	/* compress_motion       */ 0,
	/* compress_exposure     */ 0,
	/* compress_enterleave   */ 0,
	/* visible_interest      */ 0,
	/* destroy               */ destroy,
	/* resize                */ NULL,
	/* expose                */ NULL,
	/* set_values            */ NULL /*set_values*/,
	/* set_values_hook       */ NULL,
	/* set_values_almost     */ NULL,
	/* get_values_hook       */ NULL /*_XmExtGetValuesHook*/,
	/* accept_focus          */ NULL,
	/* version               */ XtVersion,
	/* callback offsets      */ NULL,
	/* tm_table              */ NULL,
	/* query_geometry        */ NULL,
        /* display_accelerator   */ NULL,
	/* extension             */ NULL
    },
    /* XmExtObject part */
    {
        /* syn_resources      */ NULL,
        /* num_syn_resources  */ 0,
        /* extension          */ NULL
    },
    /* Protocol Class part */
    {
        /* extension             */ NULL
    }
};
/* *INDENT-ON* */

WidgetClass xmProtocolClass = (WidgetClass)&xmProtocolClassRec;

static void
class_initialize()
{
    DEBUGOUT(XdbDebug(__FILE__, NULL, "Protocol ClassInitialize\n"));
}

static void
class_part_initialize(WidgetClass widget_class)
{
    DEBUGOUT(XdbDebug(__FILE__, NULL, "Protocol ClassPartInitialize\n"));
}

static void
initialize(Widget request, Widget new_w,
	   ArgList args, Cardinal *num_args)
{
    DEBUGOUT(XdbDebug(__FILE__, new_w, "Protocol Initialize\n"));

    Protocol_PreHook(new_w).callback = NULL;
    Protocol_PostHook(new_w).callback = NULL;
}

static void
destroy(Widget w)
{
    DEBUGOUT(XdbDebug(__FILE__, w, "Protocol Destroy\n"));
}

#if 0
static Boolean
set_values(Widget current, Widget request, Widget new_w,
	   ArgList args, Cardinal *num_args)
{
    DEBUGOUT(XdbDebug(__FILE__, new_w, "Protocol SetValues\n"));

    return True;		/* FIX ME */
}
#endif

static XmAllProtocolsMgr
__XmGetAllMgr(Widget shell)
{
    XmAllProtocolsMgr newman;

    if (shell == NULL || !XmIsVendorShell(shell))
    {
	return NULL;
    }

    newman = (XmAllProtocolsMgr)_XmGetWidgetExtData(shell,
						    XmPROTOCOL_EXTENSION);

    DEBUGOUT(XdbDebug(__FILE__, shell,
		      "Get AllMgr: %08x %08x\n", shell, newman));

    return newman;
}

static XmProtocolMgr
__XmAddProperty(XmAllProtocolsMgr mgrs, Atom property)
{
    if (mgrs->max_protocol_mgrs == 0)
    {
	mgrs->max_protocol_mgrs = 8;

	mgrs->protocol_mgrs =
	    (XmProtocolMgr *)XtMalloc(mgrs->max_protocol_mgrs *
				      sizeof(XmProtocolMgr));
    }

    if ((mgrs->num_protocol_mgrs + 1) == mgrs->max_protocol_mgrs)
    {
	mgrs->max_protocol_mgrs *= 2;

	mgrs->protocol_mgrs =
	    (XmProtocolMgr *)XtRealloc((char *)mgrs->protocol_mgrs,
				       mgrs->max_protocol_mgrs *
				       sizeof(XmProtocolMgr));
    }

    mgrs->protocol_mgrs[mgrs->num_protocol_mgrs] =
	(XmProtocolMgr)XtCalloc(1, sizeof(XmProtocolMgrRec));
    mgrs->protocol_mgrs[mgrs->num_protocol_mgrs]->property = property;
    mgrs->num_protocol_mgrs++;

    return mgrs->protocol_mgrs[mgrs->num_protocol_mgrs - 1];
}

static XmProtocolMgr
__XmFindProperty(XmAllProtocolsMgr mgrs, Atom property)
{
    int i;

    for (i = 0; i < mgrs->num_protocol_mgrs; i++)
    {
	if (mgrs->protocol_mgrs[i]->property == property)
	    return mgrs->protocol_mgrs[i];
    }

    return NULL;
}

static XmProtocol
__XmAddProtocol(XmAllProtocolsMgr mgrs, Atom property, Atom protocol)
{
    XmProtocolMgr mgr;
    XmProtocol w;
    Widget ve;

    mgr = __XmFindProperty(mgrs, property);

    if (!mgr)
    {
	mgr = __XmAddProperty(mgrs, property);
    }

    if (mgr->max_protocols == 0)
    {
	mgr->max_protocols = 8;
	mgr->protocols =
	    (XmProtocolList)XtMalloc(mgrs->max_protocol_mgrs *
				     sizeof(XmProtocolMgr));
    }

    if ((mgr->num_protocols + 1) == mgr->max_protocols)
    {
	mgr->max_protocols *= 2;
	mgr->protocols =
	    (XmProtocolList)XtRealloc((char *)mgr->protocols,
				      mgrs->max_protocol_mgrs *
				      sizeof(XmProtocolMgr));
    }

    ve = _LtFindVendorExt(mgrs->shell);
    if (!ve)
    {
	_XmError(NULL, "Shell has no extension!\n");
    }

    w = (XmProtocol)XtCreateWidget("protocol_ext", xmProtocolClass,
				   ve, NULL, 0);

    Protocol_Atom(w) = protocol;
    mgr->protocols[mgr->num_protocols] = w;
    mgr->num_protocols++;

    return w;
}

static XmProtocol
__XmFindProtocol(XmAllProtocolsMgr mgrs, Atom property, Atom protocol)
{
    XmProtocolMgr mgr;
    int i;

    mgr = __XmFindProperty(mgrs, property);
    if (!mgr)
    {
	return NULL;
    }

    for (i = 0; i < mgr->num_protocols; i++)
    {
	if (Protocol_Atom(mgr->protocols[i]) == protocol)
	{
	    return mgr->protocols[i];
	}
    }

    return NULL;
}

static XtActionsRec actions[] =
{
    {"XmProtocolHandler", _XmProtocolHandler},
};

void
_XmInitProtocols(Widget w)
{
    XmAllProtocolsMgr newman;

    newman = (XmAllProtocolsMgr)XtCalloc(1, sizeof(XmAllProtocolsMgrRec));
    newman->shell = w;

    _XmPushWidgetExtData(w, (XmWidgetExtData)newman, XmPROTOCOL_EXTENSION);

    DEBUGOUT(XdbDebug(__FILE__, w, "Init Protocols: %08x %08x\n", w, newman));

    XtAppAddActions(XtWidgetToApplicationContext(w),
		    actions, XtNumber(actions));
}

/*
 * this ONLY gets called from VendorShellExt's realize callback
 */
void
_XmInstallProtocols(Widget w)
{
    XmAllProtocolsMgr mgrs;
    XmProtocolMgr mgr;
    Atom *protocols;
    int i, j;

    DEBUGOUT(XdbDebug(__FILE__, w, "_XmInstallProtocols\n"));

    if ((mgrs = __XmGetAllMgr(w)) == NULL)
    {
	_XmWarning(w, "No XmProtocolManager for shell %s\n", XtName(w));
	return;
    }

    for (i = 0; i < mgrs->num_protocol_mgrs; i++)
    {
	mgr = mgrs->protocol_mgrs[i];

	protocols = (Atom *)XtMalloc(sizeof(Atom) * mgr->num_protocols);

	for (j = 0; j < mgr->num_protocols; j++)
	{
	    protocols[j] = Protocol_Atom(mgr->protocols[j]);
	}

	XChangeProperty(XtDisplay(w), XtWindow(w),
			mgr->property, XA_ATOM, 32, PropModeReplace,
			(unsigned char *)protocols, mgr->num_protocols);

	XtFree((char *)protocols);
    }
}

static void
_XmProtocolHandler(Widget w, XEvent *evp, String *params, Cardinal *nparams)
{
    Atom property, protocol;
    XmAllProtocolsMgr mgrs;
    XmProtocol p;
    XmAnyCallbackStruct cd;

    DEBUGOUT(XdbDebug(__FILE__, w, "_XmProtocolHandler\n"));

    cd.reason = XmCR_WM_PROTOCOLS;
    cd.event = evp;

    if (evp->type != ClientMessage)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "XmProtocolHandler(not a client message)\n"));
	return;
    }

    property = evp->xclient.message_type;
    protocol = evp->xclient.data.l[0];

    if ((mgrs = __XmGetAllMgr(w)) == NULL)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "XmProtocolHandler(couldn't find manager struct)\n"));

	return;
    }

    if ((p = __XmFindProtocol(mgrs, property, protocol)) == NULL)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "XmProtocolHandler(couldn't find protocol)\n"));

	return;
    }

    if (Protocol_Active(p))
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "XmProtocolHandler(calling callbacks)\n"));

	if (Protocol_PreHook(p).callback)
	{
	    (Protocol_PreHook(p).callback) (mgrs->shell,
					    Protocol_PreHook(p).closure,
					    (XtPointer)&cd);
	}

	XtCallCallbackList(mgrs->shell,
			   Protocol_Callbacks(p),
			   (XtPointer)&cd);

	if (Protocol_PostHook(p).callback)
	{
	    (Protocol_PostHook(p).callback) (mgrs->shell,
					     Protocol_PostHook(p).closure,
					     (XtPointer)&cd);
	}
    }
    else
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "XmProtocolHandler(protocol not active)\n"));
    }
}

void
XmActivateProtocol(Widget shell,
		   Atom property,
		   Atom protocol)
{
    XmAllProtocolsMgr mgrs;
    XmProtocol p;

    DEBUGOUT(XdbDebug(__FILE__, shell, "XmActivateProtocol\n"));

    if ((mgrs = __XmGetAllMgr(shell)) == NULL)
    {
	return;
    }

    if ((p = __XmFindProtocol(mgrs, property, protocol)) == NULL)
    {
	return;
    }

    Protocol_Active(p) = True;
}

void
XmDeactivateProtocol(Widget shell,
		     Atom property,
		     Atom protocol)
{
    XmAllProtocolsMgr mgrs;
    XmProtocol p;

    DEBUGOUT(XdbDebug(__FILE__, shell, "XmDeactivateProtocol\n"));

    if ((mgrs = __XmGetAllMgr(shell)) == NULL)
    {
	return;
    }

    if ((p = __XmFindProtocol(mgrs, property, protocol)) == NULL)
    {
	return;
    }

    Protocol_Active(p) = False;
}

#ifdef	XmActivateWMProtocol
#undef	XmActivateWMProtocol
#endif
void
XmActivateWMProtocol(Widget shell,
		     Atom protocol)
{
    XmActivateProtocol(shell,
		       XM_WM_PROTOCOL_ATOM(shell),
		       protocol);
}

void
XmAddProtocols(Widget shell,
	       Atom property,
	       Atom *protocol,
	       Cardinal num_protocols)
{
    char *tbl, *a;
    XtTranslations ctbl;
    int i;
    XmAllProtocolsMgr mgrs;
    XmProtocol p;

    DEBUGOUT(XdbDebug(__FILE__, shell, "XmAddProtocols\n"));

    if ((mgrs = __XmGetAllMgr(shell)) == NULL)
    {
	DEBUGOUT(XdbDebug(__FILE__, shell,
			  "XmAddProtocols: Can't find Management structure:"
			  " not a shell?\n"));
	return;
    }

    a = XGetAtomName(XtDisplay(shell), property);
    tbl = (char *)XtMalloc(32 + strlen(a));
    strcpy(tbl, "<Message>");	/* len 9 */
    strcat(tbl, a);
    strcat(tbl, ": XmProtocolHandler()\n");	/* len 21 */

    ctbl = XtParseTranslationTable(tbl);
    XtAugmentTranslations(shell, ctbl);

    XtFree(tbl);
    XFree(a);

    for (i = 0; i < num_protocols; i++)
    {
	if ((p = __XmFindProtocol(mgrs, property, protocol[i])) == NULL)
	{
	    __XmAddProtocol(mgrs, property, protocol[i]);
	}
    }

    if (XtIsRealized(shell))
    {
	_XmInstallProtocols(shell);
    }

    /*
     * Automatically Activate
     */
    for (i = 0; i < num_protocols; i++)
    {
	XmActivateProtocol(shell, property, protocol[i]);
    }
}

#ifdef	XmAddWMProtocols
#undef	XmAddWMProtocols
#endif

void
XmAddWMProtocols(Widget shell,
		 Atom *protocols,
		 Cardinal num_protocols)
{
    XmAddProtocols(shell,
		   XM_WM_PROTOCOL_ATOM(shell),
		   protocols,
		   num_protocols);
}

#ifdef	XmAddWMProtocolCallback
#undef	XmAddWMProtocolCallback
#endif

void
XmAddProtocolCallback(Widget shell,
		      Atom property,
		      Atom protocol,
		      XtCallbackProc callback,
		      XtPointer closure)
{
    XmAllProtocolsMgr mgrs;
    XmProtocol p;

    DEBUGOUT(XdbDebug(__FILE__, shell, "XmAddProtocolCallback\n"));

    if ((mgrs = __XmGetAllMgr(shell)) == NULL)
    {
	DEBUGOUT(XdbDebug(__FILE__, shell,
		      "XmAddProtocolCallback: Can't find Management structure:"
			  " not a shell?\n"));

	return;
    }

    if ((p = __XmFindProtocol(mgrs, property, protocol)) == NULL)
    {
	XmAddProtocols(shell, property, &protocol, 1);

	p = __XmFindProtocol(mgrs, property, protocol);
    }

    /*
     * Add Callback
     */
    XtAddCallback((Widget)p, XmNprotocolCallback, callback, closure);
}

void
XmAddWMProtocolCallback(Widget shell,
			Atom protocol,
			XtCallbackProc callback,
			XtPointer closure)
{
    XmAddProtocolCallback(shell,
			  XM_WM_PROTOCOL_ATOM(shell),
			  protocol,
			  callback,
			  closure);
}

void
XmRemoveProtocolCallback(Widget shell,
			 Atom property,
			 Atom protocol,
			 XtCallbackProc callback,
			 XtPointer closure)
{
    XmAllProtocolsMgr mgrs;
    XmProtocol p;

    DEBUGOUT(XdbDebug(__FILE__, shell, "XmRemoveProtocolCallback\n"));

    if ((mgrs = __XmGetAllMgr(shell)) == NULL)
    {
	DEBUGOUT(XdbDebug(__FILE__, shell,
		   "XmRemoveProtocolCallback: Can't find Management structure:"
			  " not a shell?\n"));

	return;
    }

    if ((p = __XmFindProtocol(mgrs, property, protocol)) == NULL)
    {
	XmAddProtocols(shell, property, &protocol, 1);

	p = __XmFindProtocol(mgrs, property, protocol);
    }

    /*
     * Remove Callback
     */
    XtRemoveCallback((Widget)p, XmNprotocolCallback, callback, closure);
}

#ifdef	XmRemoveWMProtocolCallback
#undef	XmRemoveWMProtocolCallback
#endif

void
XmRemoveWMProtocolCallback(Widget shell,
			   Atom protocol,
			   XtCallbackProc callback,
			   XtPointer closure)
{
    XmRemoveProtocolCallback(shell,
			     XM_WM_PROTOCOL_ATOM(shell),
			     protocol,
			     callback,
			     closure);
}

void
XmRemoveProtocols(Widget shell,
		  Atom property,
		  Atom *protocols,
		  Cardinal num_protocols)
{
    XmAllProtocolsMgr mgrs;
    XmProtocol p;
    int i;

    DEBUGOUT(XdbDebug(__FILE__, shell, "XmRemoveProtocols\n"));

    if ((mgrs = __XmGetAllMgr(shell)) == NULL)
    {
	return;
    }

    for (i = 0; i < num_protocols; i++)
    {
	if ((p = __XmFindProtocol(mgrs, property, protocols[i])) == NULL)
	{
	    return;
	}

	Protocol_Active(p) = False;
    }
}

#ifdef	XmRemoveWMProtocols
#undef	XmRemoveWMProtocols
#endif

void
XmRemoveWMProtocols(Widget shell,
		    Atom *protocols,
		    Cardinal num_protocols)
{
    XmRemoveProtocols(shell,
		      XM_WM_PROTOCOL_ATOM(shell),
		      protocols,
		      num_protocols);
}

void
XmSetProtocolHooks(Widget shell,
		   Atom property,
		   Atom protocol,
		   XtCallbackProc prehook,
		   XtPointer pre_closure,
		   XtCallbackProc posthook,
		   XtPointer post_closure)
{
    XmAllProtocolsMgr mgrs;
    XmProtocol p;

    DEBUGOUT(XdbDebug(__FILE__, shell, "XmSetProtocolHooks\n"));

    if ((mgrs = __XmGetAllMgr(shell)) == NULL)
    {
	DEBUGOUT(XdbDebug(__FILE__, shell,
			  "XmSetProtocolHooks: Can't find Management structure:"
			  " not a shell?\n"));

	return;
    }

    if ((p = __XmFindProtocol(mgrs, property, protocol)) == NULL)
    {
	XmAddProtocols(shell, property, &protocol, 1);

	p = __XmFindProtocol(mgrs, property, protocol);
    }

    Protocol_PreHook(p).callback = prehook;
    Protocol_PreHook(p).closure = pre_closure;
    Protocol_PostHook(p).callback = posthook;
    Protocol_PostHook(p).closure = post_closure;
}

#ifdef	XmSetWMProtocolHooks
#undef	XmSetWMProtocolHooks
#endif
void
XmSetWMProtocolHooks(Widget shell,
		     Atom protocol,
		     XtCallbackProc prehook,
		     XtPointer pre_closure,
		     XtCallbackProc posthook,
		     XtPointer post_closure)
{
    XmSetProtocolHooks(shell,
		       XM_WM_PROTOCOL_ATOM(shell),
		       protocol,
		       prehook,
		       pre_closure,
		       posthook,
		       post_closure);
}
