/* 
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
*/ 
/* 
 * Motif Release 1.2.3
*/ 
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$TOG: WmProperty.c /main/7 1997/12/02 10:00:00 bill $"
#endif
#endif

/*
 * Included Files:
 */

#include "WmGlobal.h"
#include "WmICCC.h"
#include <stdio.h>
#ifdef WSM
#include <Dt/WsmP.h>
#include <X11/Xatom.h>
#endif /* WSM */

/*
 * include extern functions
 */

#include "WmColormap.h"
#include "WmError.h"
#include "WmResParse.h"



/*
 * Function Declarations:
 */

#include "WmProperty.h"


/*
 * Global Variables:
 */

static SizeHints sizeHints;



/*************************************<->*************************************
 *
 *  SizeHints *
 *  GetNormalHints (pCD)
 *
 *
 *  Description:
 *  -----------
 *  This function replaces the XGetNormalHints Xlib function.  This function
 *  gets the information in the WM_NORMAL_HINTS property on the client window.
 *  The property encoding can be any of the supported versions (R2, R3+).
 *
 *
 *  Inputs:
 *  ------
 *  pCD = (client)
 *
 * 
 *  Outputs:
 *  -------
 *  Return = A pointer to a filled out SizeHints structure is returned.
 *           Default values are set if the WM_NORMAL_HINTS property could
 *           not be retrieved.
 *
 *************************************<->***********************************/

SizeHints * 
GetNormalHints(
        ClientData *pCD )

{
    PropSizeHints *property = NULL;
    Atom actualType;
    int actualFormat;
    unsigned long leftover;
    unsigned long nitems;


    /*
     * Retrieve the property data.
     *
     *     ICCC_R2 version:  nitems = PROP_SIZE_HINTS_ELEMENTS - 3
     *     ICCC_CURRENT version: nitems = PROP_SIZE_HINTS_ELEMENTS
     */

#ifdef WSM
    if ((!HasProperty (pCD, XA_WM_NORMAL_HINTS)) ||
	((Success != XGetWindowProperty (DISPLAY, pCD->client,
			XA_WM_NORMAL_HINTS, 0L, (long)PROP_SIZE_HINTS_ELEMENTS,
			False, XA_WM_SIZE_HINTS, &actualType, &actualFormat,
			&nitems, &leftover, (unsigned char **)&property)) ||
	  (actualType != XA_WM_SIZE_HINTS) ||
	  (nitems < (PROP_SIZE_HINTS_ELEMENTS - 3)) ||
	  (actualFormat != 32)))
#else /* WSM */
    if ((Success != XGetWindowProperty (DISPLAY, pCD->client,
			XA_WM_NORMAL_HINTS, 0L, (long)PROP_SIZE_HINTS_ELEMENTS,
			False, XA_WM_SIZE_HINTS, &actualType, &actualFormat,
			&nitems, &leftover, (unsigned char **)&property)) ||
	 (actualType != XA_WM_SIZE_HINTS) ||
	 (nitems < (PROP_SIZE_HINTS_ELEMENTS - 3)) ||
	 (actualFormat != 32))
#endif /* WSM */
    {
	/*
	 * Indicate no property values were retrieved:
	 */

	sizeHints.icccVersion = ICCC_UNKNOWN;
	sizeHints.flags = 0;
    }
    else
    {
	/*
	 * Parse the hint values out of the property data:
	 */

	sizeHints.flags = property->flags;
	sizeHints.x = property->x;
	sizeHints.y = property->y;
	sizeHints.width = property->width;
	sizeHints.height = property->height;
	sizeHints.min_width = property->minWidth;
	sizeHints.min_height = property->minHeight;
	sizeHints.max_width = property->maxWidth;
	sizeHints.max_height = property->maxHeight;
	sizeHints.width_inc = property->widthInc;
	sizeHints.height_inc = property->heightInc;
	sizeHints.min_aspect.x = (int)property->minAspectX;
	sizeHints.min_aspect.y = (int)property->minAspectY;
	sizeHints.max_aspect.x = (int)property->maxAspectX;
	sizeHints.max_aspect.y = (int)property->maxAspectY;


	if (nitems == (PROP_SIZE_HINTS_ELEMENTS - 3))
	{
	    /*
	     *  This is ICCC_R2.
	     */

	    sizeHints.icccVersion = ICCC_R2;
	}
	else
	{
	    /*
	     *  This is ICCC_CURRENT.
	     */

	    sizeHints.icccVersion = ICCC_CURRENT;
	    sizeHints.base_width = property->baseWidth;
	    sizeHints.base_height = property->baseHeight;
	    sizeHints.win_gravity = property->winGravity;
	}
    }


    /*
     * Free the property data buffer:
     */

    if (property)
    {
	XFree ((char *)property);
    }


    /*
     * Return the hints values:
     */

    return (&sizeHints);


} /* END OF FUNCTION GetNormalHints */



/*************************************<->*************************************
 *
 *  ProcessWmProtocols (pCD)
 *
 *
 *  Description:
 *  -----------
 *  This function reads and processes the WM_PROTOCOLS property that is
 *  associated with a client window.
 *
 *  ICCC_COMPLIANT check added to allow older clients to work, for now...
 *  eventually, this code should be removed.
 *
 *  Inputs:
 *  ------
 *  pCD = pointer to client data
 *
 * 
 *  Outputs:
 *  -------
 *  pCD = (clientProtocols, clientProtocolCount, protocolFlags)
 *
 *************************************<->***********************************/

void ProcessWmProtocols (ClientData *pCD)
{
    int rValue;
    Atom *property = NULL;
#ifndef ICCC_COMPLIANT
    Atom actualType;
    int actualFormat;
    unsigned long leftover;
    unsigned long nitems;
#else
    int nitems;
#endif /* ICCC_COMPLIANT */
    int i;


    if (pCD->clientProtocols)
    {
	XtFree ((char *)pCD->clientProtocols);
	pCD->clientProtocols = NULL;
    }
    pCD->clientProtocolCount = 0;
    pCD->protocolFlags = 0;


    /*
     * Read the WM_PROTOCOLS property.
     */

#ifndef ICCC_COMPLIANT
#ifdef WSM
    if (!HasProperty (pCD, wmGD.xa_WM_PROTOCOLS))
	rValue = -1;
    else
#endif /* WSM */
    rValue = XGetWindowProperty (DISPLAY, pCD->client, wmGD.xa_WM_PROTOCOLS, 0L,
		 (long)MAX_CLIENT_PROTOCOL_COUNT, False, AnyPropertyType,
		 &actualType, &actualFormat, &nitems, &leftover,
		 (unsigned char **)&property);


    if ((rValue != Success) || (actualType == None) || (actualFormat != 32))
#else
#ifdef WSM
    if (!HasProperty (pCD, wmGD.xa_WM_PROTOCOLS))
	rValue = -1;
    else
#endif /* WSM */
    rValue = XGetWMProtocols (DISPLAY, pCD->client, 
		 (Atom **)&property, &nitems);

    if (0 == rValue) 
#endif /* ICCC_COMPLIANT */
    {
	/*
	 * WM_PROTOCOLS does not exist or it is an invalid type or size.
	 */

	pCD->clientProtocols = NULL;
    }
    else
    {
        if (!(pCD->clientProtocols = (Atom *)XtMalloc (nitems * sizeof (Atom))))
        {
	    /* unable to allocate space */
	    Warning (((char *)GETMESSAGE(54, 1, "Insufficient memory for window management data")));
        }
	else
	{
	    /*
	     * Save the protocols in the client data and look for predefined
	     * protocols.
	     */

    	    pCD->clientProtocolCount = nitems;

    	    for (i = 0; i < nitems; i++)
    	    {
		pCD->clientProtocols[i] = property[i];
		if (property[i] == wmGD.xa_WM_SAVE_YOURSELF)
		{
		    pCD->protocolFlags |= PROTOCOL_WM_SAVE_YOURSELF;
		}
		else if (property[i] == wmGD.xa_WM_TAKE_FOCUS)
		{
		    pCD->protocolFlags |= PROTOCOL_WM_TAKE_FOCUS;
		}
		else if (property[i] == wmGD.xa_WM_DELETE_WINDOW)
		{
		    pCD->protocolFlags |= PROTOCOL_WM_DELETE_WINDOW;
		}
		else if (property[i] == wmGD.xa_MWM_MESSAGES)
		{
		    pCD->protocolFlags |= PROTOCOL_MWM_MESSAGES;
		}
    	    }
	}
    }


    if (property)
    {
	XFree ((char *)property);
    }


} /* END OF FUNCTION ProcessWmProtocols */



/*************************************<->*************************************
 *
 *  ProcessMwmMessages (pCD)
 *
 *
 *  Description:
 *  -----------
 *  This function reads and processes the _MWM_MESSAGES property that is
 *  associated with a client window.
 *
 *
 *  Inputs:
 *  ------
 *  pCD = pointer to client data
 *
 * 
 *  Outputs:
 *  -------
 *  pCD = (mwmMessagesCount, mwmMessages)
 *
 *************************************<->***********************************/

void ProcessMwmMessages (ClientData *pCD)
{
    int rValue;
    long *property = NULL;
    Atom actualType;
    int actualFormat;
    unsigned long leftover;
    unsigned long nitems;
    int i;


    if (pCD->mwmMessages)
    {
	XtFree ((char *)pCD->mwmMessages);
	pCD->mwmMessages = NULL;
    }
    pCD->mwmMessagesCount = 0;


    /*
     * Read the _MWM_MESSAGES property.
     */

#ifdef WSM
    if (!HasProperty (pCD, wmGD.xa_MWM_MESSAGES))
        rValue = ~Success;
    else
#endif /* WSM */
    rValue = XGetWindowProperty (DISPLAY, pCD->client, wmGD.xa_MWM_MESSAGES, 0L,
		 (long)MAX_MWM_MESSAGES_COUNT, False, AnyPropertyType,
		 &actualType, &actualFormat, &nitems, &leftover,
		 (unsigned char **)&property);


    if ((rValue != Success) || (actualType == None) || (actualFormat != 32)
	|| (nitems == 0))
    {
	/*
	 * _MWM_MESSAGES does not exist or it is an invalid type.
	 */

	pCD->mwmMessages = NULL;
    }
    else
    {
        if (!(pCD->mwmMessages = (long *)XtMalloc (nitems * sizeof (long))))
        {
	    /* unable to allocate space */
	    Warning (((char *)GETMESSAGE(54, 2, "Insufficient memory for window management data")));
        }
	else
	{
	    /*
	     * Save the protocols in the client data and look for predefined
	     * protocols.
	     */

    	    pCD->mwmMessagesCount = nitems;

    	    for (i = 0; i < nitems; i++)
    	    {
		if ((pCD->mwmMessages[i] = property[i]) == wmGD.xa_MWM_OFFSET)
		{
		    pCD->protocolFlags |= PROTOCOL_MWM_OFFSET;
		}
    	    }
	}
    }


    if (property)
    {
	XFree ((char *)property);
    }


} /* END OF FUNCTION ProcessMwmMessages */



/*************************************<->*************************************
 *
 *  SetMwmInfo (propWindow, flags, wmWindow)
 *
 *
 *  Description:
 *  -----------
 *  This function sets up the _MOTIF_WM_INFO property on the specified (usually
 *  the root) window.
 *
 *
 *  Inputs:
 *  ------
 *  propWindow = window on which the _MOTIF_WM_INFO property is to be set
 *
 *  flags = motifWmInfo.flags value
 *
 *  wmWindow = motifWmInfo.wmWindow value
 *
 * 
 *  Outputs:
 *  -------
 *  _MWM_INFO = this property is set on the specified window
 *
 *************************************<->***********************************/

void SetMwmInfo (Window propWindow, long flags, Window wmWindow)
{
    PropMwmInfo property;


    property.flags = flags;
    property.wmWindow = wmWindow;

    XChangeProperty (DISPLAY, propWindow, wmGD.xa_MWM_INFO, wmGD.xa_MWM_INFO,
	32, PropModeReplace, (unsigned char *)&property,
	PROP_MWM_INFO_ELEMENTS);

} /* END OF FUNCTION SetMwmInfo */

#ifdef WSM

/*************************************<->*************************************
 *
 *  SetMwmSaveSessionInfo (wmWindow)
 *
 *
 *  Description:
 *  -----------
 *  This function sets up the WM_SAVE_YOURSELF property on the wm window
 *
 *
 *  Inputs:
 *  ------
 *  wmWindow = motifWmInfo.wmWindow
 *
 * 
 *  Outputs:
 *  -------
 *  WM_SAVE_YOURSELF = this property is set on the wm window
 *
 *************************************<->***********************************/

void SetMwmSaveSessionInfo (Window wmWindow)
{

    Atom  property;
    property = wmGD.xa_WM_SAVE_YOURSELF;
    
    XChangeProperty (DISPLAY, wmWindow, 
		     wmGD.xa_WM_PROTOCOLS, XA_ATOM,
		     32, PropModeReplace,
		     (unsigned char *) &property, 1);
    SetWMState(wmWindow, NORMAL_STATE, 0);
    
} /* END OF FUNCTION SetMwmSaveSessionInfo */
#endif /* WSM */


/*************************************<->*************************************
 *
 *  GetWMState (window)
 *
 *
 *  Description:
 *  -----------
 *  This function gets the WM_STATE property on a client top-level
 *  window.
 *
 *
 *  Inputs:
 *  ------
 *  window = client window from which the WM_STATE property is to be retrieved
 *
 * 
 *  Outputs:
 *  -------
 *  RETURN = a pointer to the WM_STATE property value (NULL if not defined)
 *
 *
 *  Comments:
 *  --------
 *  This function will eventually be superceded when WM_STATE support is
 *  added to the official X release.
 * 
 *************************************<->***********************************/

PropWMState *
GetWMState(
        Window window )
{
    int ret_val;
    PropWMState *property = NULL;
    Atom actual_type;
    int actual_format;
    unsigned long nitems;
    unsigned long leftover;


    ret_val = XGetWindowProperty (DISPLAY, window, wmGD.xa_WM_STATE, 
		  0L, PROP_WM_STATE_ELEMENTS,
		  False, wmGD.xa_WM_STATE, 
		  &actual_type, &actual_format, 
		  &nitems, &leftover, (unsigned char **)&property);

    if (!((ret_val == Success) && (actual_type == wmGD.xa_WM_STATE) &&
         (nitems == PROP_WM_STATE_ELEMENTS)))
    {
        /*
         * The property could not be retrieved or is not correctly set up.
         */

        if (property)
        {
	    XFree ((char *)property);
	    property = NULL;
        }
    }

    return (property);

} /* END OF FUNCTION GetWMState */



/*************************************<->*************************************
 *
 *  SetWMState (window, state, icon)
 *
 *
 *  Description:
 *  -----------
 *  This function sets up the WM_STATE property on a client top-level
 *  window.
 *
 *
 *  Inputs:
 *  ------
 *  window = client window on which the WM_STATE property is to be set
 *
 *  state = state of the client application
 *
 *  icon = window manager's icon window
 *
 * 
 *  Outputs:
 *  -------
 *  WM_STATE = this property is set on the client window
 *
 *
 *  Comments:
 *  --------
 *  This function will eventually be superceded when WM_STATE support is
 *  added to the official X release.
 * 
 *************************************<->***********************************/

void SetWMState (Window window, int state, Window icon)
{
    PropWMState property;


    property.state = state;
    property.icon = icon;

    XChangeProperty (DISPLAY, window, wmGD.xa_WM_STATE, wmGD.xa_WM_STATE, 32,
	PropModeReplace, (unsigned char *)&property, PROP_WM_STATE_ELEMENTS);

} /* END OF FUNCTION SetWMState */



/*************************************<->*************************************
 *
 *  PropMwmHints *
 *  GetMwmHints (pCD)
 *
 *
 *  Description:
 *  -----------
 *  This function reads any _MWM_HINTS property that is associated with a 
 *  client window.
 *
 *  Inputs:
 *  ------
 *  pCD = pointer to client data
 * 
 *  Outputs:
 *  -------
 *  RETURN = ptr to mwm hints property, or NULL ptr if failure
 *
 *************************************<->***********************************/

PropMwmHints *
GetMwmHints(
        ClientData *pCD )

{
    int ret_val;
    PropMwmHints *property = NULL;
    Atom actual_type;
    int actual_format;
    unsigned long nitems;
    unsigned long leftover;


#ifdef WSM
    if (!HasProperty(pCD, wmGD.xa_MWM_HINTS))
	ret_val = ~Success;
    else
#endif /* WSM */
    ret_val = XGetWindowProperty (DISPLAY, pCD->client, wmGD.xa_MWM_HINTS, 
		  0L, PROP_MWM_HINTS_ELEMENTS,
		  False, wmGD.xa_MWM_HINTS, 
		  &actual_type, &actual_format, 
		  &nitems, &leftover, (unsigned char **)&property);

    /*
     * Retrieve the property data.
     *
     *     Motif 1.1.n clients:	nitems	= PROP_MWM_HINTS_ELEMENTS
     *     Motif 1.2 clients:	nitems 	= PROP_MWM_HINTS_ELEMENTS + 2
     *
     * NOTES:  We don't need to check (nitems == PROP_MWM_HINTS_ELEMENTS)
     *         since...
     *
     * If running Motif 1.1.n client with Mwm 1.2, then ignore extra elements
     *    since property.flags won't have extra elements set.
     *
     * If running Motif 1.2 client with Mwm 1.1.n, then ignore extra elements
     *    since Mwm 1.1.n won't try to access the extra elements.
     */

    if ((ret_val == Success) && (actual_type == wmGD.xa_MWM_HINTS))
    {
	return (property);			/* indicate success */
    }


    /*
     * The property could not be retrieved or is not correctly set up.
     */

    if (property)
    {
	XFree ((char *)property);
    }

    return (NULL);			/* indicate failure */


} /* END OF FUNCTION GetMwmHints */



/*************************************<->*************************************
 *
 *  PropMwmInfo *
 *  GetMwmInfo (rootWindowOfScreen)
 *
 *
 *  Description:
 *  -----------
 *  This function reads the _MOTIF_WM_INFO property from the root window if
 *  it is setup.
 * 
 *  Inputs:
 *  ------
 *  pSD = pointer to screen data 
 *
 *  Outputs:
 *  -------
 *  RETURN = ptr to motif wm info property, or NULL ptr if no property
 *
 *************************************<->***********************************/

PropMwmInfo *GetMwmInfo (Window rootWindowOfScreen)
{
    int ret_val;
    PropMwmInfo *property = NULL;
    Atom actual_type;
    int actual_format;
    unsigned long nitems;
    unsigned long leftover;


    ret_val = XGetWindowProperty (DISPLAY, rootWindowOfScreen,
                                     wmGD.xa_MWM_INFO,
                                     0L, PROP_MWM_INFO_ELEMENTS,
                                     False, wmGD.xa_MWM_INFO,
                                     &actual_type, &actual_format,
                                     &nitems, &leftover,
                                     (unsigned char **)&property);

    if ((ret_val == Success) && (actual_type == wmGD.xa_MWM_INFO) &&
        (nitems == PROP_MWM_INFO_ELEMENTS)) 
    {
	return (property);			/* indicate success */
    }


    /*
     * The property could not be retrieved or is not correctly set up.
     */

    if (property)
    {
	XFree ((char *)property);
    }

    return (NULL);			/* indicate failure */


} /* END OF FUNCTION GetMwmInfo */



/*************************************<->*************************************
 *
 *  ProcessWmColormapWindows (pCD)
 *
 *
 *  Description:
 *  -----------
 *  This function retrieves and processes the WM_COLORMAP_WINDOWS client
 *  window property.
 *
 *
 *  Inputs:
 *  ------
 *  pCD = pointer to client data
 *
 * 
 *  Outputs:
 *  -------
 *  pCD = (cmapWindows, clientCmapList, clientCmapCount, clientCmapIndex)
 *
 *************************************<->***********************************/

void ProcessWmColormapWindows (ClientData *pCD)
{
    int rValue;
    Window *property = NULL;
    Atom actualType;
    int actualFormat;
    unsigned long leftover;
    unsigned long nitems;
    int i;
    Window *pWindows;
    Colormap *pColormaps;
    int colormapCount;
    XWindowAttributes wAttributes;
    ClientData *pcd;
    XSetWindowAttributes sAttributes;
#ifndef OLD_COLORMAP /* colormaps */
    int *pCmapFlags;
#endif


    /*
     * pCD->clientCmapCount and pCD->clientCmapIndex are initialized in
     * WmWinInfo.c.
     */

    /*
     * Read the WM_COLORMAP_WINDOWS property.
     */

    rValue = XGetWindowProperty (DISPLAY, pCD->client,
		 wmGD.xa_WM_COLORMAP_WINDOWS, 0L,
		 (long)MAX_COLORMAP_WINDOWS_COUNT, False, AnyPropertyType,
		 &actualType, &actualFormat, &nitems, &leftover,
		 (unsigned char **)&property);


    if ((rValue == Success) && (actualType != None) && (actualFormat == 32) &&
	(nitems > 0))
    {
	/*
	 * WM_COLORMAP_WINDOWS exists and is a valid type.
	 */

        if (!(pWindows = (Window *)XtMalloc ((nitems * sizeof (Window)) + 1)) ||
            !(pColormaps = (Colormap *)XtMalloc ((nitems*sizeof(Colormap)) + 1)))
        {
	    /* unable to allocate space */
	    Warning (((char *)GETMESSAGE(54, 3, "Insufficient memory for window management data")));
	    if (pWindows)
	    {
		XtFree ((char *)pWindows);
	    }
        }
#ifndef OLD_COLORMAP /* colormap */
	/* Is the above OSF code a bug -- allocates one extra byte, rather */
	/* than one extra element, for the top window if needed? */
	else if ( ! (pCmapFlags = (int *)XtCalloc(nitems+1,sizeof(int)))) {
			/* unable to allocate space */
			Warning (((char *)GETMESSAGE(54, 4, "Insufficient memory for window manager flags")));
			XtFree ((char *)pWindows); XtFree ((char *)pColormaps);
	}
#endif
	else
	{
	    /*
	     * Check to see if the top-level client window is in the list.
	     * If it is not then add it to the head of the list.
	     */

    	    for (i = 0; i < nitems; i++)
	    {
		if (property[i] == pCD->client)
		{
		    break;
		}
	    }

	    colormapCount = 0;
	    if (i == nitems)
	    {
		/* add the client window to the colormap window list */
		pWindows[0] = pCD->client;
		pColormaps[0] = FindColormap (pCD, pCD->client);
		colormapCount++;
	    }

	    sAttributes.event_mask = (ColormapChangeMask);
    	    for (i = 0; i < nitems; i++)
    	    {
		if ((pColormaps[colormapCount] = 
		     FindColormap (pCD, property[i])) != None)
		{
		    pWindows[colormapCount] = property[i];
		    colormapCount++;
		}
		else if (XFindContext (DISPLAY, property[i],
		             wmGD.windowContextType, (caddr_t *)&pcd))
		{
		    /*
		     * The window is not a top level window or a window that
		     * is already being tracked for colormap changes.
		     * Track colormap attribute changes.
		     */

		    XChangeWindowAttributes (DISPLAY, property[i], CWEventMask,
			&sAttributes);
		

		    if (XGetWindowAttributes (DISPLAY, property[i],
			    &wAttributes))
		    {
			pWindows[colormapCount] = property[i];
			pColormaps[colormapCount] = wAttributes.colormap;
		        colormapCount++;
		    }
		}
	    }

	    /*
	     * Free up the old colormap window data if it has been set.  Set
	     * new window contexts.
	     */

	    ResetColormapData (pCD, pWindows, colormapCount);
		

	    /*
	     * Set the colormap window data.
	     */

	    pCD->clientColormap = pColormaps[0];
	    if (colormapCount > 1)
	    {
		/*
		 * The top level window and at least one other window is in
		 * the colormap windows list.
		 */

		pCD->clientCmapCount = colormapCount;
		pCD->cmapWindows = pWindows;
		pCD->clientCmapList = pColormaps;
		pCD->clientCmapIndex = 0;
#ifndef OLD_COLORMAP /* colormap */
		pCD->clientCmapFlags = pCmapFlags;
#endif
	    }
	    else
	    {
		/*
		 * Only the top level window is being tracked for colormap
		 * data.
		 */

		pCD->clientCmapCount = 0;
		XtFree ((char *)pWindows);
		XtFree ((char *)pColormaps);
#ifndef OLD_COLORMAP /* colormap */
		XtFree((char *)pCmapFlags);
#endif
	    }
	}
    }


    if (property)
    {
	XFree ((char *)property);
    }


} /* END OF FUNCTION ProcessWmColormapWindows */



/*************************************<->*************************************
 *
 *  FindColormap (pCD, window)
 *
 *
 *  Description:
 *  -----------
 *  This function checks colormap information that is currently saved in
 *  the client data for the colormap of the specified window.
 *
 *
 *  Inputs:
 *  ------
 *  pCD = pointer to client data
 *
 *  window = get the colormap id for this window
 *
 * 
 *  Outputs:
 *  -------
 *  RETURN = colormap id for window (NULL if no colormap information)
 *
 *************************************<->***********************************/

Colormap FindColormap (ClientData *pCD, Window window)
{
    Colormap colormap = (Colormap)0;
    int i;


    if (pCD->clientCmapCount == 0)
    {
	/*
	 * If the colormap count is 0 there is no list of colormaps and
	 * clientColormap is the colormap of the top-level window.
	 */

	if (window == pCD->client)
	{
	    colormap = pCD->clientColormap;
	}
    }
    else
    {
	for (i = 0; i < pCD->clientCmapCount; i++)
	{
	    if (pCD->cmapWindows[i] == window)
	    {
		colormap = pCD->clientCmapList[i];
		break;
	    }
	}
    }

    return (colormap);

} /* END OF FUNCTION FindColormap */



/*************************************<->*************************************
 *
 *  GetMwmMenuItems (pCD)
 *
 *
 *  Description:
 *  -----------
 *  This function reads and processes any _MWM_MENU property that is
 *  associated with a client window and returns a list of MenuItem structures
 *  specified by the property, or NULL.
 *
 *
 *  Inputs:
 *  ------
 *  pCD = pointer to client data
 *
 * 
 *  Outputs:
 *  -------
 *  Return = MenuItem list or NULL.
 *
 *************************************<->***********************************/

MenuItem *
GetMwmMenuItems(
        ClientData *pCD )
{
    int rValue;
    XTextProperty textProperty;
    MenuItem *menuItems;

    /*
     * Read the _MWM_MENU property.
     */

    textProperty.value = (unsigned char *)NULL;
    rValue = XGetTextProperty(DISPLAY, pCD->client, &textProperty,
			      wmGD.xa_MWM_MENU);
    if ((rValue == 0) || (textProperty.value == (unsigned char *)NULL))
    /* _MWM_MENU does not exist or it is an invalid type.  */
    {
	menuItems = NULL;
    }

    else
    /* parse the property string */
    {
	char **textList;
	int nItems;

	if (XmbTextPropertyToTextList(DISPLAY, &textProperty,
				      &textList, &nItems) != Success)
	{
	    menuItems = NULL;
	}
	else
	{
	    if(textList)
	    {
		menuItems = ParseMwmMenuStr (PSD_FOR_CLIENT(pCD),
					 (unsigned char *)textList[0]);
		XFreeStringList(textList);
	    }
	    else
		menuItems = NULL;
	}

	XFree((void *)textProperty.value);
    }

    return (menuItems);

} /* END OF FUNCTION GetMwmMenuItems */

#ifdef WSM


/*************************************<->*************************************
 *
 *  GetWorkspaceHints (display, window, ppWsAtoms, pCount, pbAll)
 *
 *
 *  Description:
 *  -----------
 *  Get the contents of the WM_COMMAND property on a window
 *
 *
 *  Inputs:
 *  ------
 *  display	- X display 
 *  window	- window to get hints from
 *  ppWsAtoms	- pointer to a list of workspace atoms (to be returned)
 *  pCount	- ptr to a number of atoms (to be returned)
 *  pbAll	- ptr to a boolean (to be returned)
 *
 *
 *  Returns:
 *  --------
 *  Success if suceeded, otherwise failure code.
 * 
 *
 *  Outputs:
 *  -------
 *  *ppWsAtoms 	- list of workspace atoms
 *  *pCount	- number of atoms in *ppWsAtoms
 *  *pbAll	- True if should put in all workspaces
 *
 *
 *  Comments:
 *  --------
 *  The caller must XtFree *ppWsAtoms when done!!!
 * 
 *************************************<->***********************************/

Status GetWorkspaceHints (Display *display, Window window, 
			  Atom **ppWsAtoms, unsigned int *pCount,
			  Boolean *pbAll)
{
    int rcode;
    DtWorkspaceHints *pWsHints;
    Atom *paWs;

    rcode = _DtWsmGetWorkspaceHints(display, window, &pWsHints);
    if (rcode == Success)
    {
	if (pWsHints->flags & DT_WORKSPACE_HINTS_WORKSPACES)
	{
	    paWs = (Atom *) 
		XtMalloc (pWsHints->numWorkspaces * sizeof(Atom));
	    memcpy (paWs, 
		    pWsHints->pWorkspaces, 
		    (pWsHints->numWorkspaces * sizeof(Atom)));

	    *pCount = pWsHints->numWorkspaces;
	    *ppWsAtoms = paWs;
	}
	else
	{
	    *pCount = 0;
	    *ppWsAtoms = NULL;
	}

	if ((pWsHints->flags & DT_WORKSPACE_HINTS_WSFLAGS) &&
	    (pWsHints->wsflags & DT_WORKSPACE_FLAGS_OCCUPY_ALL))
	{
	    *pbAll = True;
	}
	else
	{
	    *pbAll = False;
	}


	_DtWsmFreeWorkspaceHints (pWsHints);
    }
	
    return(rcode);

} /* END OF FUNCTION GetWorkspaceHints */


/*************************************<->*************************************
 *
 *  SetEmbeddedClientsProperty (propWindow, pEmbeddedClients, 
 *  				cEmbeddedCLients)
 *
 *
 *  Description:
 *  -----------
 *  This function writes the _DT_WORKSPACE_EMBEDDED_CLIENTS property
 *
 *
 *  Inputs:
 *  ------
 *  propWindow = window on which the property is to be written
 *  pEmbeddedClients = pointer to data (array of window IDs)
 *  cEmbeddedClients = number of window IDs in the array
 *
 *************************************<->***********************************/

void SetEmbeddedClientsProperty (Window propWindow, 
    Window *pEmbeddedClients, unsigned long cEmbeddedClients)
{
    XChangeProperty (DISPLAY, propWindow, wmGD.xa_DT_EMBEDDED_CLIENTS, 
	wmGD.xa_DT_EMBEDDED_CLIENTS,
	32, PropModeReplace, (unsigned char *)pEmbeddedClients,
	cEmbeddedClients);

} /* END OF FUNCTION SetEmbeddedClientsProperty */

#ifdef HP_VUE

/*************************************<->*************************************
 *
 *  SetWorkspaceInfo (propWindow, pWsInfo, cInfo)
 *
 *
 *  Description:
 *  -----------
 *  This function sets up the _DT_WORKSPACE_INFO property
 *
 *
 *  Inputs:
 *  ------
 *  propWindow = window on which the _DT_WORKSPACE_INFO property is to be set
 *  pWsInfo =  pointer to workspace info data
 *  cInfo = size of workspace info data
 * 
 *
 *************************************<->***********************************/

void SetWorkspaceInfo (Window propWindow, WorkspaceInfo *pWsInfo, unsigned long cInfo)
{
    XChangeProperty (DISPLAY, propWindow, wmGD.xa_DT_WORKSPACE_INFO, 
	wmGD.xa_DT_WORKSPACE_INFO,
	32, PropModeReplace, (unsigned char *)pWsInfo,
	(cInfo * sizeof(WorkspaceInfo))/sizeof(long));

} /* END OF FUNCTION SetWorkspaceInfo */
#endif /* HP_VUE */


/*************************************<->*************************************
 *
 *  SetWorkspaceListProperty (pSD)
 *
 *
 *  Description:
 *  -----------
 *  This function sets up the _DT_WORKSPACE_LIST property
 *
 *
 *  Inputs:
 *  ------
 *  pSD = ptr to screen data
 * 
 *
 *************************************<->***********************************/

void
SetWorkspaceListProperty (WmScreenData *pSD)
{
    WmWorkspaceData *pws;
    Atom *pWsList;
    int count;

    pWsList = (Atom *) 
	XtMalloc (pSD->numWorkspaces * sizeof(Atom));

    pws = pSD->pWS;
    for (count = 0; count < pSD->numWorkspaces; count++)
    {
	pWsList[count] = pws->id;
	pws++;
    }

    XChangeProperty (DISPLAY, pSD->wmWorkspaceWin, 
        wmGD.xa_DT_WORKSPACE_LIST, 
	XA_ATOM,
	32, PropModeReplace, (unsigned char *)pWsList,
	(pSD->numWorkspaces * sizeof(Atom))/sizeof(long));

    XtFree ((char *) pWsList);

} /* END OF FUNCTION SetWorkspaceListProperty */


/*************************************<->*************************************
 *
 *  SetCurrentWorkspaceProperty (pSD)
 *
 *
 *  Description:
 *  -----------
 *  This function sets up the _DT_WORKSPACE_CURRENT property
 *
 *
 *  Inputs:
 *  ------
 *  pSD = ptr to screen data
 * 
 *
 *************************************<->***********************************/

void
SetCurrentWorkspaceProperty (WmScreenData *pSD)
{
    Atom aCurrent;

    aCurrent = pSD->pActiveWS->id;

    XChangeProperty (DISPLAY, pSD->wmWorkspaceWin, 
        wmGD.xa_DT_WORKSPACE_CURRENT, 
	XA_ATOM,
	32, PropModeReplace, (unsigned char *)&aCurrent,
	(sizeof(Atom))/sizeof(long));

    XSync (DISPLAY, False);     /* XFlush didn't work here, why? */

} /* END OF FUNCTION SetCurrentWorkspaceProperty */


/*************************************<->*************************************
 *
 *  SetWorkspaceInfoProperty (pWS)
 *
 *
 *  Description:
 *  -----------
 *  This function sets up the _DT_WORKSPACE_INFO_<name> property
 *  for a particular workspace
 *
 *
 *  Inputs:
 *  ------
 *  pWS = ptr to workspace data
 * 
 *
 *************************************<->***********************************/

void
SetWorkspaceInfoProperty (WmWorkspaceData *pWS)
{
    char *pch;
    Atom aProperty;
    String sTitle;
    char **ppchList;
    int iNumStrings;
    int count, iwin;
    int i, ix;
    Status status;
    XTextProperty tp;
#define WIP_NUMBER_SIZE		16

    /*
     * Construct our property name
     */
    pch = WorkspacePropertyName (pWS);

    aProperty = XmInternAtom (DISPLAY, pch, FALSE);

    XtFree ((char *) pch);

    /*
     * Determine the number of strings in our vector. One for each of
     *
     *     workspace title
     *     pixel set id
     *     backdrop background
     *     backdrop foreground
     *     backdrop name
     *     number of backdrop windows
     *     list of backdrop windows
     */
    iNumStrings =  6;	/* number of fields minus backdrop window(s) */
    count = 1;		/* number of backdrop windows */
    iNumStrings += count;

    /* allocate string vector */
    ppchList = (char **) XtMalloc (iNumStrings * sizeof (char *));
    pch = (char *) XtMalloc (iNumStrings * WIP_NUMBER_SIZE * sizeof(char));
    
    i = 0;

    /* Convert workspace title to ascii */
    sTitle = (String) WmXmStringToString (pWS->title);
    ppchList[i++] = (char *) sTitle;

    /*  Pixel set id */
    ix = (i * WIP_NUMBER_SIZE);
    sprintf (&pch[ix], "%d", pWS->backdrop.colorSet);
    ppchList[i++] = &pch[ix];

    /* backdrop background */
    ix = (i * WIP_NUMBER_SIZE);
    sprintf (&pch[ix], "0x%lx", pWS->backdrop.background);
    ppchList[i++] = &pch[ix];

    /* backdrop foreground */
    ix = (i * WIP_NUMBER_SIZE);
    sprintf (&pch[ix], "0x%lx", pWS->backdrop.foreground);
    ppchList[i++] = &pch[ix];

    /* backdrop name */
    ix = (i * WIP_NUMBER_SIZE);
    sprintf (&pch[ix], "0x%lx", pWS->backdrop.nameAtom);
    ppchList[i++] = &pch[ix];

    /* number of backdrop windows */
    ix = (i * WIP_NUMBER_SIZE);
    if ((pWS->backdrop.window == None))
    {
	strcpy (&pch[ix], "0");
    }
    else
    {
	sprintf (&pch[ix], "%d", count);
    }
    ppchList[i++] = &pch[ix];

    /* backdrop windows */
    /* 
     * One or zero backdrop windows 
     * (NULL written if zero)
     */
    ix = (i * WIP_NUMBER_SIZE);
    sprintf (&pch[ix], "0x%lx", pWS->backdrop.window);
    ppchList[i++] = &pch[ix];

    /*
     * Write out the property
     */
    status = XmbTextListToTextProperty (DISPLAY, ppchList, iNumStrings,
					    XStdICCTextStyle, &tp);
    if ((status == Success) || (status > 0))
    {
        /*
         * Complete or partial conversion
         */
        XSetTextProperty (DISPLAY, pWS->pSD->wmWorkspaceWin, &tp, aProperty);
        XFree (tp.value);
    }

    XtFree ((char *) ppchList);
    XtFree (pch);
    if (sTitle) XtFree ((char *)sTitle);

} /* END OF FUNCTION SetWorkspaceInfoProperty */


/*************************************<->*************************************
 *
 *  DeleteWorkspaceInfoProperty (pWS)
 *
 *
 *  Description:
 *  -----------
 *  This function deletes a _DT_WORKSPACE_INFO_<name> property
 *  for a particular workspace
 *
 *
 *  Inputs:
 *  ------
 *  pWS = ptr to workspace data
 * 
 *
 *************************************<->***********************************/

void
DeleteWorkspaceInfoProperty (WmWorkspaceData *pWS)
{
    char *pch;
    Atom aProperty;

    /*
     * Get the atom for the workspace property.
     */
    pch = WorkspacePropertyName (pWS);

    aProperty = XmInternAtom (DISPLAY, pch, FALSE);

    XtFree ((char *) pch);

    /*
     * Do the property deletion
     */
    XDeleteProperty (DISPLAY, pWS->pSD->wmWorkspaceWin, aProperty);
    XFlush (DISPLAY);


} /* END OF FUNCTION DeleteWorkspaceInfoProperty */


/*************************************<->*************************************
 *
 *  WorkspacePropertyName (pWS)
 *
 *
 *  Description:
 *  -----------
 *  This function returns a string containing the property name for a 
 *  workspace.
 *
 *
 *  Inputs:
 *  ------
 *  pWS = ptr to workspace data
 *
 *  Returns
 *  -------
 *  string containing the workspace property name (Free with XtFree)
 *
 *
 *************************************<->***********************************/

char *
WorkspacePropertyName (WmWorkspaceData *pWS)
{
    char *pch;
    char *pchName;
    int len;
    Atom aProperty;

    /*
     * Construct our property name
     */
    pchName = pWS->name;
    len = strlen(pchName) + strlen (_XA_DT_WORKSPACE_INFO) + 4;

    pch = (char *) XtMalloc (len);
    strcpy (pch, _XA_DT_WORKSPACE_INFO);
    strcat (pch, "_");
    strcat (pch, pchName);

    return (pch);

} /* END OF FUNCTION WorkspacePropertyName */


/*************************************<->*************************************
 *
 *  SetWorkspacePresence (propWindow, pWsPresence, cPresence)
 *
 *
 *  Description:
 *  -----------
 *  This function sets up the _DT_WORKSPACE_PRESENCE property
 *
 *
 *  Inputs:
 *  ------
 *  propWindow = window on which the _DT_WORKSPACE_PRESENCE property 
 *               is to be set
 *  pWsPresence =  pointer to workspace presence data
 *  cPresence = size of workspace presence data
 * 
 *
 *************************************<->***********************************/

void SetWorkspacePresence (Window propWindow, Atom *pWsPresence, unsigned long cPresence)
{
    XChangeProperty (DISPLAY, propWindow, wmGD.xa_DT_WORKSPACE_PRESENCE, 
	wmGD.xa_DT_WORKSPACE_PRESENCE, 32, PropModeReplace, 
	(unsigned char *)pWsPresence, cPresence);
    XFlush (DISPLAY);

} /* END OF FUNCTION SetWorkspacePresence */




/*************************************<->*************************************
 *
 *  GetDtSessionHints (pSD, sNum)
 *
 *
 *  Description:
 *  -----------
 *  This function reads and processes _DT_SESSION_HINTS property that is
 *  associated with the root window of each screen managed by dtwm
 *
 *
 *  Inputs:
 *  ------
 *
 *
 *  Outputs:
 *  -------
 *
 *************************************<->***********************************/

void GetDtSessionHints (WmScreenData *pSD, int sNum)

{

    int           rValue;
    char          *property = NULL;
    Atom          actualType;
    int           actualFormat;
    unsigned long leftover;
    unsigned long nitems;

    

    /*
     * Read the  property.
     */

    rValue = XGetWindowProperty (DISPLAY, pSD->rootWindow,
                                 wmGD.xa_DT_SESSION_HINTS, 0L,
                                 (long)1000000, False, AnyPropertyType,
                                 &actualType, &actualFormat, &nitems,
                                 &leftover, (unsigned char **)&property);


    if ((rValue != Success) || (actualType == None) || (actualFormat != 8))
        /* _DT_SESSION_HINTS does not exist or it is an invalid type.  */
    {
        pSD->pDtSessionItems = NULL;

    }

    else
        /* parse the property string */
    {
	ParseDtSessionHints (pSD, (unsigned char *)property);
    }


    if (property)
    {
        XFree ((char *)property);
    }
    
    /*
     * Delete the property so we don't see it if the user
     * restarts dtwm.
     */
#ifndef DEBUG_SESSION_HINTS
    XDeleteProperty (DISPLAY, pSD->rootWindow, wmGD.xa_DT_SESSION_HINTS);
#endif /* DEBUG_SESSION_HINTS */
} /* END OF FUNCTION GetDtSessionHints */




/*************************************<->*************************************
 *
 *  GetDtWmRequest (pSD, pszReq, pmore)
 *
 *
 *  Description:
 *  -----------
 *  This function returns the next request
 *
 *
 *  Inputs:
 *  ------
 *  pSD - pointer to screen data
 *  psdReq - pointer to a char pointer
 *
 *
 *  Outputs:
 *  -------
 *  *pszReq	- pointer to null terminated string containing next
 *                request
 *  *pmore	- set to true if more data is left in the property
 *
 *  Comments:
 *  ---------
 *  The data for pszReq is allocated in here. The caller must free up
 *  this space using XtFree.
 *
 *
 *************************************<->***********************************/

void 
GetDtWmRequest (
		WmScreenData *pSD, 
		char **pszReq, 
		Boolean *pmore)

{

    int           rValue;
    char	 *chRequest = NULL;
    static char  *property = NULL;
    static int    iNext = -1;
    int 	  i;
    Atom          actualType;
    int           actualFormat;
    unsigned long leftover;
    static unsigned long nitems = 0;

    
    /*
     * We need to read the property again if we have no data left
     * over from last time;
     */
    if (property == NULL)
    {
#ifdef PARANOID
	/*
	 * Lock down the server to prevent changes to this
	 * property while we "edit" it.
	 */
	XGrabServer(DISPLAY);
#endif /* PARANOID */

	/*
	 * Read the property and delete it.
	 */
	rValue = XGetWindowProperty (DISPLAY, pSD->wmWorkspaceWin,
				     wmGD.xa_DT_WM_REQUEST, 0L,
				     (long)1000000, True, AnyPropertyType,
				     &actualType, &actualFormat, &nitems,
				     &leftover, (unsigned char **)&property);

#ifdef PARANOID
	/* Give the server back */ 
	XUngrabServer(DISPLAY);
#endif /* PARANOID */

	/* 
	 * Validate the property that we've read
	 */
	if ((rValue != Success) || 
	    (actualType == None) || 
	    (actualFormat != 8))
	{
	    /* The property does not exist or it is an invalid type. */
	    property = NULL;
	    iNext = -1;
	    nitems = 0;
	}
	else
	{
	    /* the property is fine, set the index of the next char. */
	    iNext = 0;
	}
    }


    /* 
     * If we've got something, then extract and return the next
     * request.
     */
    if (property && iNext >= 0)
    {
	int len = 0;

	for (i=iNext; i<nitems; i++)
	{
	    if (property [i] == '\0')
	    {
		break;
	    }
	}
	if (i>=nitems) i=nitems;

	len = i - iNext + 1 + ((property[i] == '\0') ? 0 : 1);

        chRequest = (char *) XtMalloc (len);
	if (chRequest == NULL)
	{
	    Warning (((char *)GETMESSAGE(54, 2, "Insufficient memory for window management data")));
	}
	else
	{
	    /* dequeue the request */
	    strncpy (chRequest, &property[iNext], len);
	    if (property[i] != '\0')
	    {
		chRequest[len-1]='\0';
	    }
	    iNext = i+1;
	}
	
	if (iNext >= nitems)
	{
	    /*
	     * Extracted the last request, free up the storage
	     * and reset for next time.
	     */
	    XFree ((char *)property);
	    iNext = -1;
	    property = NULL;
	}
    }
    
    *pmore = (property != NULL);
    *pszReq = chRequest;

} /* END OF FUNCTION GetDtWmRequest */


/*************************************<->*************************************
 *
 *  GetIntialPropertyList (ClientData *)
 *
 *
 *  Description:
 *  -----------
 *  Get the list of initial properties on the window
 *
 *
 *  Inputs:
 *  ------
 *  pCD		- pointer to client data
 *
 * 
 *  Outputs:
 *  -------
 *  pCD		- paInitialProperties member is updated
 *
 *
 *  Comments:
 *  --------
 *  The caller must XFree the paIntialialProperties member!
 * 
 *************************************<->***********************************/

void
GetInitialPropertyList (ClientData *pCD)
{
    Atom *paList;
    int iProps;

    paList = XListProperties (DISPLAY, pCD->client, &iProps);

    if (paList)
    {
	pCD->paInitialProperties = paList;
	pCD->numInitialProperties = iProps;
    }
    else
    {
	pCD->paInitialProperties = NULL;
	pCD->numInitialProperties = 0;
    }

} /* END OF FUNCTION GetInitialPropertyList */


/*************************************<->*************************************
 *
 *  DiscardIntialPropertyList (ClientData *)
 *
 *
 *  Description:
 *  -----------
 *  Tosses out the intial property list for a client, frees data
 *
 *
 *  Inputs:
 *  ------
 *  pCD		- pointer to client data
 *
 * 
 *  Outputs:
 *  -------
 *  pCD		- paInitialProperties member is updated
 *
 *
 *  Comments:
 *  --------
 *  
 * 
 *************************************<->***********************************/

void
DiscardInitialPropertyList (ClientData *pCD)
{
    if (pCD->paInitialProperties)
    {
	/*
	 * Free the initial property list. 
	 * (see HasProperty() function)
	 */
	XFree ((char *) pCD->paInitialProperties);
	pCD->paInitialProperties = NULL;
	pCD->numInitialProperties = 0;
    }
} /* END OF FUNCTION DiscardInitialPropertyList */


/*************************************<->*************************************
 *
 *  HasProperty (pCD, aProperty)
 *
 *
 *  Description:
 *  -----------
 *  Returns True if this client had this property when it was mapped
 *
 *
 *  Inputs:
 *  ------
 *  pCD		- pointer to client data
 *  aProperty	- atom of property to test for
 *
 * 
 *  Outputs:
 *  -------
 *  Return	- True if this property was on the initial list for this
 *		  client; False otherwise.
 *
 *
 *  Comments:
 *  --------
 * 
 *************************************<->***********************************/

Boolean
HasProperty (
	ClientData *	pCD,
	Atom		aProperty)
{
    Boolean 	bFound = False;
    Atom  *	paList;
    int		count;

    paList = pCD->paInitialProperties;

    if (paList)
    {
	count = pCD->numInitialProperties;
	while ((!bFound) && (count > 0))
	{
	    bFound = (*paList == aProperty);
	    count--;
	    paList++;
	}
    }
    else
    {
	/*
	 * The property list doesn't exist. Return
	 * True to force a read of this property. The most likely
	 * case is that this property was updated after the 
	 * window was managed and needs to be read.
	 */
	bFound = True;
    }
    return (bFound);

} /* END OF FUNCTION HasProperty */
#endif /* WSM */



