/* 
 * 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
 */ 
/* 
 * HISTORY
 */ 
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$XConsortium: Mrmwrefs.c /main/14 1996/11/13 14:07:43 drk $"
#endif
#endif

/* (c) Copyright 1989, 1990, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */


/*
 *++
 *  FACILITY:
 *
 *      UIL Resource Manager (URM):
 *
 *  ABSTRACT:
 *
 *	This module contains all routines which manage references to
 *	widgets which must be handled during or immediately after
 *	widget creation.
 *
 *--
 */


/*
 *
 *  INCLUDE FILES
 *
 */

#include <stdio.h>
#include <Mrm/MrmAppl.h>
#include <Mrm/Mrm.h>
#include "MrmMsgI.h"


/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	routine acquires a resource context, and initializes
 *	it for use a a widget reference structure.
 *
 *  FORMAL PARAMETERS:
 *
 *	wref_id		to return pointer to initialized context
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *      See UrmGetResourceContext
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Urm__CW_InitWRef (URMResourceContextPtr		*wref_id)
{
  Cardinal		result;		/* function results */
  URMWRefStructPtr	refdsc;		/* buffer as reference structure */


  /*
   * Acquire and initialize resource context
   */
  result = UrmGetResourceContext ((char *(*)())NULL,(void(*)())NULL, 
				  500, wref_id);
  if ( result != MrmSUCCESS ) return result;

  refdsc = (URMWRefStructPtr) UrmRCBuffer (*wref_id);
  refdsc->num_refs = 0;
  refdsc->heap_size = 0;

  return MrmSUCCESS;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine adds a widget definition to the widget reference structure
 *
 *  FORMAL PARAMETERS:
 *
 *	wref_id		widget reference structure
 *	w_name		name of the widget to enter
 *	w_id		widget id
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *      See UrmResizeResourceContext
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Urm__CW_AddWRef (URMResourceContextPtr	wref_id,
		 String			w_name,
		 Widget			w_id)
{
  /*
   *  Local variables
   */
  Cardinal		result;		/* function results */
  URMWRefStructPtr	refdsc;		/* buffer as reference structure */
  MrmCount		name_bytes;	/* # bytes for name & NULL */
  MrmCount		bytes_needed;	/* # bytes for whole ref */
  Cardinal		ndx;		/* entry index in structure */
  MrmOffset		new_offs;	/* new offset in heap */
  Cardinal		old_size;	/* old buffer size */
  Cardinal		new_size;	/* new buffer size */
  Cardinal		delta;		/* difference in size */
  char			*old_heap;	/* old heap address */
  char			*new_heap;	/* new heap address */


  /*
   * Set up pointers. Compute size needed and available, and resize structure
   * if required. Name need not be entered if it is compiler-generated
   * (contains non-legal character '-')
   */
  refdsc = (URMWRefStructPtr) UrmRCBuffer (wref_id);
  name_bytes = strlen(w_name);
  for ( ndx=0 ; ndx<name_bytes ; ndx++ )
    if ( w_name[ndx] == '-' ) return MrmFAILURE;
  name_bytes += 1;

  bytes_needed = sizeof(URMWRef) + name_bytes;
  bytes_needed = _FULLWORD(bytes_needed);
  if ( bytes_needed > (UrmRCBufSize(wref_id)-UrmWRefBytesUsed(refdsc)) )
    {
      old_size = UrmRCBufSize (wref_id);
      new_size = 2 * old_size;
      delta = new_size - old_size;
      result = UrmResizeResourceContext (wref_id, new_size);
      if ( result != MrmSUCCESS ) return result;
      refdsc = (URMWRefStructPtr) UrmRCBuffer (wref_id);
      old_heap = (char *) refdsc+old_size-refdsc->heap_size;
      new_heap = (char *) (old_heap+delta);
      UrmBCopy (old_heap, new_heap, refdsc->heap_size);
      for ( ndx=0 ; ndx<refdsc->num_refs ; ndx++ )
	refdsc->refs[ndx].w_name_offs += delta;
    }

  /*
   * There is enough space. Copy in the name, and set up the descriptor.
   */
  new_offs = UrmRCBufSize(wref_id) - refdsc->heap_size - name_bytes;
  ndx = refdsc->num_refs;
  refdsc->refs[ndx].w_id = w_id;
  refdsc->refs[ndx].w_name_offs = new_offs;
  refdsc->num_refs += 1;
  refdsc->heap_size += name_bytes;
  strcpy ((String)refdsc+new_offs, w_name);

  return MrmSUCCESS;
}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine searches the widget reference structure for a widget,
 *	and returns its id if found.
 *
 *  FORMAL PARAMETERS:
 *
 *	wref_id		widget reference structure
 *	w_name		name of widget to be found
 *	w_id_return	to return widget id
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *      MrmSUCCESS	- found, id is returned
 *	MrmNOT_FOUND	- not found
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Urm__CW_FindWRef (URMResourceContextPtr	wref_id,
		  String		w_name,
		  Widget		*w_id_return)
{
  /*
   *  Local variables
   */
  URMWRefStructPtr	refdsc;		/* buffer as reference structure */
  int			ndx;		/* loop index */


  /*
   * Search the vector for the name, returning the id if found. Search
   * backwards, so the most recent definitions are searched first.
   */
  refdsc = (URMWRefStructPtr) UrmRCBuffer (wref_id);
  for ( ndx=refdsc->num_refs-1 ; ndx>=0 ; ndx-- )
    if ( strcmp(w_name,UrmWRefNameN(refdsc,ndx)) == 0 )
      {
	*w_id_return = UrmWRefIdN (refdsc, ndx);
	return MrmSUCCESS;
      }
  return MrmNOT_FOUND;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine creates a SetValues descriptor, initializes it from
 *	the resource descriptor, and appends it to the list. The id of the
 *	widget to be modified is set NULL to identify this descriptor as
 *	one to be set.
 *
 *  FORMAL PARAMETERS:
 *
 *	file_id		file from which to uncompress argtag
 *	svlist		list of SetValues of descriptor
 *	w_name		widget name
 *	argtag		Argument tag code
 *	argname		Argument tag name if code is URMcUnknown
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *  SIDE EFFECTS:
 *
 *--
 */

void 
Urm__CW_AppendSVWidgetRef (IDBFile		file_id,
			   URMPointerListPtr	*svlist,
			   String		w_name,
			   MrmCode		argtag,
			   String		argname)
{
  /*
   *  Local variables
   */
  URMSetValuesDescPtr	svdesc ;	/* new descriptor */
  Cardinal		uncmp_res;	/* function result */
  char			errmsg[300];


  /*
   * Initialize the pointer list if required
   */
  if ( *svlist == NULL )
    UrmPlistInit (10, svlist) ;

  /*
   * Create and set the new descriptor. The argument tag must be uncompressed
   * into its string now, when the compression code for it is available from the
   * file. If the tag code is unknown, then the string must be copied into
   * allocated memory.
   */
  svdesc = (URMSetValuesDescPtr) XtMalloc (sizeof(URMSetValuesDesc)) ;
  svdesc->setw = NULL ;
  svdesc->type = URMsvWidgetRef ;
  svdesc->tagcode = argtag ;
  if ( argtag == UilMrmUnknownCode )
    {
      svdesc->tagname = (String) XtMalloc (strlen(argname)+1);
      strcpy (svdesc->tagname, argname);
    }
  else
    {
      uncmp_res = Urm__UncompressCode (file_id, argtag, &svdesc->tagname);
      if ( uncmp_res != MrmSUCCESS )
	{
	  sprintf (errmsg, _MrmMMsg_0108, argtag);
	  XtFree ((char *)svdesc);
	  Urm__UT_Error ("Urm__CW_AppendSVWidgetRef", errmsg, 
			 NULL, NULL, MrmNOT_FOUND);
	  return;
	}
    }
  svdesc->done = FALSE ;
  svdesc->sv.wname = Urm__UT_AllocString (w_name) ;
  UrmPlistAppendPointer (( URMPointerListPtr)*svlist, (XtPointer) svdesc) ;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine creates a SetValues descriptor, initializes it from
 *	the resource descriptor, adds the callback list and appends it to the
 *	list. The id of the
 *	widget to be modified is set NULL to identify this descriptor as
 *	one to be set.
 *
 *  FORMAL PARAMETERS:
 *
 *	file_id		file from which to uncompress argtag
 *	svlist		list of SetValues of descriptor
 *	w_name		widget name
 *	argtag		Argument tag code
 *	argname		Argument tag name if code is URMcUnknown
 *	cbptr		Callback description pointer
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *  SIDE EFFECTS:
 *
 *--
 */

void 
Urm__CW_AppendCBSVWidgetRef (IDBFile			file_id,
			     URMPointerListPtr		*svlist,
			     RGMCallbackDescPtr		cbptr,
			     MrmCode			argtag,
			     String			argname)
{
  URMSetValuesDescPtr	svdesc ;	/* new descriptor */
  RGMCallbackDescPtr	cbdesc;		/* Copy of descriptor */
  MrmSize		descsize;	/* Size of descriptor to be copied */
  Cardinal		uncmp_res;	/* function result */
  char			errmsg[300];


  /*
   * Initialize the pointer list if required
   */
  if ( *svlist == NULL )
    UrmPlistInit (10, svlist) ;

  /*
   * Create and set the new descriptor. The argument tag must be uncompressed
   * into its string now, when the compression code for it is available from the
   * file. If the tag code is unknown, then the string must be copied into
   * allocated memory.
   */
  svdesc = (URMSetValuesDescPtr) XtMalloc (sizeof(URMSetValuesDesc)) ;
  svdesc->setw = NULL ;
  svdesc->type = URMsvCallBackList ;
  svdesc->tagcode = argtag ;
  if ( argtag == UilMrmUnknownCode )
    {
      svdesc->tagname = (String) XtMalloc (strlen(argname)+1);
      strcpy (svdesc->tagname, argname);
    }
  else
    {
      uncmp_res = Urm__UncompressCode (file_id, argtag, &svdesc->tagname);
      if ( uncmp_res != MrmSUCCESS )
	{
	  sprintf (errmsg, _MrmMMsg_0108, argtag);
	  XtFree ((char *)svdesc);
	  Urm__UT_Error ("Urm__CW_AppendCBSVWidgetRef", errmsg, 
			 NULL, NULL, MrmNOT_FOUND);
	  return;
	}
    }
  svdesc->done = FALSE ;
  /* Copy callback descriptor */
  descsize = sizeof(RGMCallbackDesc) + (cbptr->count)*sizeof(RGMCallbackItem);
  cbdesc = (RGMCallbackDescPtr)XtMalloc(descsize);
  memcpy(cbdesc, cbptr, descsize);
  svdesc->sv.callbacks = cbdesc ;
  UrmPlistAppendPointer (( URMPointerListPtr)*svlist, (XtPointer) svdesc) ;
}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine updates a SetValues list from a just-created widget.
 *	Any entry whose modified widget field is NULL is has the modified
 *	widget set to the given widget. 
 *
 *  FORMAL PARAMETERS:
 *
 *	svlist		List of SetValues descriptors
 *	cur_id		current widget to be entered in descriptors
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *  SIDE EFFECTS:
 *
 *--
 */

void 
Urm__CW_UpdateSVWidgetRef (URMPointerListPtr		*svlist,
			   Widget			cur_id)
{
  /*
   *  Local variables
   */
  int			ndx ;		/* loop index */
  URMSetValuesDescPtr	svdesc ;	/* current SetValues descriptor */

  /*
   * Loop over all descriptors. Enter the current widget as appropriate.
   * Since each descriptor is used only once, immediately free any
   * allocated tag string.
   */
  for ( ndx=0 ; ndx<UrmPlistNum(*svlist) ; ndx++ )
    {
      svdesc = (URMSetValuesDescPtr) UrmPlistPtrN(*svlist,ndx) ;
      if ( svdesc->done ) continue ;
      if ( svdesc->setw == NULL )
	svdesc->setw = cur_id ;
    }
}

/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	This routine updates a SetValues list from a just-created widget.
 *	Any entry whose modified widget field is NULL is has the modified
 *	widget set to the given widget. It also uses this widget to
 *	immediately update any previously saved widget which can be
 *	resolved with this new widget and to update widget references
 *	in callback items.
 *
 *  FORMAL PARAMETERS:
 *
 *	svlist		List of SetValues descriptors
 *	cur_name	current widget's name
 *	cur_id		current widget to be entered in descriptors
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *  SIDE EFFECTS:
 *
 *--
 */

void 
Urm__CW_ResolveSVWidgetRef (URMPointerListPtr		*svlist,
			    String			cur_name,
			    Widget			cur_id)
{
  /*
   *  Local variables
   */
  int			ndx ;		/* loop index */
  int			cbndx;		/* callback list index */
  URMSetValuesDescPtr	svdesc ;	/* current SetValues descriptor */
  Arg			args[1] ;	/* SetValues argument list */
  RGMCallbackDescPtr	cbdesc;		/* Current callback descriptor */
  RGMCallbackItemPtr	items;		/* Array of callback items */

  /*
   * Loop over all descriptors. Enter the current widget as appropriate.
   * Since each descriptor is used only once, immediately free any
   * allocated tag string.
   */
  for ( ndx=0 ; ndx<UrmPlistNum(*svlist) ; ndx++ )
    {
      svdesc = (URMSetValuesDescPtr) UrmPlistPtrN(*svlist,ndx) ;
      if ( svdesc->done ) continue ;
      if ( svdesc->setw == NULL )
	{
	  svdesc->setw = cur_id ;
	  continue ;
	}
      switch (svdesc->type)
	{
	case URMsvWidgetRef:
	  if ( strcmp(cur_name,svdesc->sv.wname) == 0 )
	    {
	      args[0].name = svdesc->tagname ;
	      args[0].value = (XtArgVal) cur_id ;
	      XtSetValues (svdesc->setw, args, 1) ;
	      svdesc->done = TRUE ;
	      if ( svdesc->tagcode == UilMrmUnknownCode )
		{
		  XtFree ((char *)svdesc->tagname);
		  svdesc->tagname = NULL;
		}
	      /* Does this really need to be freed here? */
	      XtFree(svdesc->sv.wname);
	      svdesc->sv.wname = NULL;
	    }
	  break;
	
	case URMsvCallBackList:
	  cbdesc = (RGMCallbackDescPtr)svdesc->sv.callbacks;
	  items = cbdesc->item;
	
	  /* Loop through callback items, resolving where possible */
	  for (cbndx = 0; cbndx < cbdesc->count; cbndx++)
	    {
	      if (items[cbndx].runtime.resolved) continue; 

	      if (strcmp(cur_name, items[cbndx].runtime.wname) == 0) 
		/* Finish resolving the callback item. */
		{
		  items[cbndx].runtime.callback.closure = (XtPointer)cur_id;
		  items[cbndx].runtime.resolved = TRUE;
		  cbdesc->unres_ref_count--;
		  /* Free the widget name. */
		  XtFree(items[cbndx].runtime.wname);
		  items[cbndx].runtime.wname = NULL;
		}
	    }
	
	  if (cbdesc->unres_ref_count == 0)
	    {
	      /* Move individual items so array functions as callback list */
	      XtCallbackRec *callbacks = (XtCallbackRec *)items;
	    
	      for (cbndx = 0; cbndx <= cbdesc->count; cbndx++)
		/* <= so that null item is copied. */
		{
		  callbacks[cbndx].callback = (XtCallbackProc)
		    items[cbndx].runtime.callback.callback;
		  callbacks[cbndx].closure = (XtPointer)
		    items[cbndx].runtime.callback.closure;
		}
		
		
	      args[0].name = svdesc->tagname;
	      args[0].value = (XtArgVal)items;
	      XtSetValues(svdesc->setw, args, 1);
	      svdesc->done = TRUE;

	      /* Free memory. */
	      if ( svdesc->tagcode == UilMrmUnknownCode )
		{
		  XtFree ((char *)svdesc->tagname);
		  svdesc->tagname = NULL;
		}

	      XtFree((char *)svdesc->sv.callbacks);
	      svdesc->sv.callbacks = NULL;
	    }
	  break;
	    
	default:
	  continue;
	}
    }
}

/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *      This routine frees a SetValues descriptor.
 *
 *  FORMAL PARAMETERS:
 *
 *      svdesc          SetValues descriptor
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *  SIDE EFFECTS:
 *
 *--
 */

void 
Urm__CW_FreeSetValuesDesc (URMSetValuesDescPtr         svdesc)
{
  if ( (svdesc->type == URMsvWidgetRef) && (svdesc->sv.wname != NULL) )
    XtFree ((char *)svdesc->sv.wname);

  XtFree ((char *)svdesc);
}
