/* 
 * 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 REV_INFO
#ifndef lint
static char rcsid[] = "$XConsortium: ExtObject.c /main/13 1995/10/25 20:03:50 cde-sun $"
#endif
#endif
/* (c) Copyright 1989, 1990  DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */
/* (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 HEWLETT-PACKARD COMPANY */
/* (c) Copyright 1988 MASSACHUSETTS INSTITUTE OF TECHNOLOGY  */
/* (c) Copyright 1988 MICROSOFT CORPORATION */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


#include <string.h>
#include "BaseClassI.h"
#include "ExtObjectI.h"
#include "SyntheticI.h"
#include "XmI.h"


/********    Static Function Declarations    ********/

static void ClassInitialize(void);
static void ClassPartInitPrehook(WidgetClass c);
static void ClassPartInitPosthook(WidgetClass c);
static void ClassPartInitialize(WidgetClass c);
static void InitializePrehook(Widget req, Widget new_w,
			      ArgList args, Cardinal *num_args);
static void Initialize(Widget req, Widget new_w, 
		       ArgList args, Cardinal *num_args);
static Boolean SetValuesPrehook(Widget req, Widget curr, Widget new_w,
				ArgList args, Cardinal *num_args);
static void GetValuesPrehook(Widget new_w, ArgList args, Cardinal *num_args);
static Boolean SetValues(Widget old, Widget ref, Widget new_w,
			 ArgList args, Cardinal *num_args);
static void GetValuesHook(Widget new_w, ArgList args, Cardinal *num_args);
static void Destroy(Widget wid);
static void UseParent(Widget w, int offset, XrmValue *value);

/********    End Static Function Declarations    ********/

/***************************************************************************
 *
 * ExtObject Resources
 *
 ***************************************************************************/


#define Offset(field)	XtOffsetOf(struct _XmExtRec, ext.field)

static XtResource extResources[] =
{
  {
    XmNlogicalParent, XmCLogicalParent, XmRWidget, 
    sizeof (Widget), Offset (logicalParent),
    XmRCallProc, (XtPointer)UseParent
  },
  {
    XmNextensionType, XmCExtensionType, XmRExtensionType, 
    sizeof (unsigned char), Offset (extensionType),
    XmRImmediate, (XtPointer)XmDEFAULT_EXTENSION
  }
};
#undef Offset

#define XmNUM_ELEMENTS	4
#define XmNUM_BYTES	255

typedef struct _XmExtCache {
  char    data[XmNUM_BYTES];
  Boolean inuse;
} XmExtCache;

typedef union {
  XmExtCache	cache;
  double	force_alignment;
} Aligned_XmExtCache;

static Aligned_XmExtCache extarray[XmNUM_ELEMENTS];


static XmBaseClassExtRec myBaseClassExtRec = {
  NULL,				/* Next extension         */
  NULLQUARK,			/* record type XmQmotif   */
  XmBaseClassExtVersion,	/* version                */
  sizeof(XmBaseClassExtRec),	/* size                   */
  InitializePrehook,		/* initialize prehook     */
  SetValuesPrehook,		/* set_values prehook     */
  NULL,				/* initialize posthook    */
  NULL,				/* set_values posthook    */
  NULL,				/* secondary class        */
  NULL,				/* creation proc          */
  NULL,				/* getSecRes data         */
  { 0 },			/* fast subclass          */
  GetValuesPrehook,		/* get_values prehook     */
  NULL,				/* get_values posthook    */
  ClassPartInitPrehook,		/* class_part_prehook     */
  ClassPartInitPosthook,	/* class_part_posthook    */
  NULL,				/* compiled_ext_resources */   
  NULL,				/* ext_resources       	  */   
  0,				/* resource_count     	  */   
  FALSE				/* use_sub_resources	  */
};

externaldef(xmextclassrec)
XmExtClassRec xmExtClassRec = {
  {	
    (WidgetClass) &objectClassRec,/* superclass 	 */   
    "dynamic",			  /* class_name 	 */   
    sizeof(XmExtRec),	 	  /* size 		 */   
    ClassInitialize, 		  /* Class Initializer 	 */   
    ClassPartInitialize,	  /* class_part_init 	 */ 
    FALSE,			  /* Class init'ed ? 	 */   
    Initialize,			  /* initialize          */   
    NULL,			  /* initialize_notify   */ 
    NULL,			  /* realize             */   
    NULL,			  /* actions             */   
    0,				  /* num_actions         */   
    extResources,		  /* resources           */   
    XtNumber(extResources),	  /* resource_count      */   
    NULLQUARK, 			  /* xrm_class           */   
    FALSE,			  /* compress_motion     */   
    FALSE,			  /* compress_exposure   */   
    FALSE,			  /* compress_enterleave */   
    FALSE,			  /* visible_interest    */   
    Destroy,			  /* destroy             */   
    NULL,			  /* resize              */   
    NULL,			  /* expose              */   
    SetValues,	 		  /* set_values          */   
    NULL,			  /* set_values_hook     */ 
    NULL,			  /* set_values_almost   */ 
    GetValuesHook,		  /* get_values_hook     */ 
    NULL,			  /* accept_focus        */   
    XtVersion, 			  /* intrinsics version  */   
    NULL,			  /* callback offsets    */   
    NULL,			  /* tm_table            */   
    NULL,			  /* query_geometry      */ 
    NULL,			  /* display_accelerator */ 
    (XtPointer)&myBaseClassExtRec /* extension           */ 
  },	
  {
    NULL,			  /* synthetic resources */
    0				  /* num syn resources	 */
  }
};

externaldef(xmextobjectclass) 
WidgetClass xmExtObjectClass = (WidgetClass) (&xmExtClassRec);

/*ARGSUSED*/
static void 
UseParent(Widget w,
	  int offset,		/* unused */
	  XrmValue *value)
{
  value->addr = (XPointer) &(w->core.parent);
}

/************************************************************************
 *
 *  ClassInitialize
 *
 ************************************************************************/

static void 
ClassInitialize(void)
{
  myBaseClassExtRec.record_type = XmQmotif;
}

/************************************************************************
 *
 *  ClassPartInitPrehook
 *
 ************************************************************************/

static void 
ClassPartInitPrehook(WidgetClass c)
{
  XmExtObjectClass wc = (XmExtObjectClass) c;
  
  if ((WidgetClass)wc != xmExtObjectClass)
    {
      XmExtObjectClass sc = (XmExtObjectClass) c->core_class.superclass;
      XmBaseClassExt *scePtr = _XmGetBaseClassExtPtr(sc, XmQmotif);

      /*
       * If our superclass uses subresources, then we need to
       * temporarily fill it's core resource fields so that objectClass
       * classPartInit will be able to find them for merging.  We
       * assume that we only need to set things up for the
       * superclass and not any deeper ancestors.
       */
      if ((*scePtr)->use_sub_resources)
	{
	  sc->object_class.resources = (*scePtr)->compiled_ext_resources;
	  sc->object_class.num_resources = (*scePtr)->num_ext_resources;
	}
    }
}

/************************************************************************
 *
 *  ClassPartInitPosthook
 *
 ************************************************************************/

static void 
ClassPartInitPosthook(WidgetClass c)
{
  XmExtObjectClass wc = (XmExtObjectClass) c;
  XmBaseClassExt  *wcePtr = _XmGetBaseClassExtPtr(wc, XmQmotif);
  
  if ((*wcePtr) && (*wcePtr)->use_sub_resources)
    {
      /*
       * Put our compiled resources back and zero out oject class so
       * it's invisible to object class create processing.
       */
      (*wcePtr)->compiled_ext_resources = wc->object_class.resources;
      (*wcePtr)->num_ext_resources = wc->object_class.num_resources;
    }
}

/************************************************************************
 *
 *  ClassPartInitialize
 *    Set up the inheritance mechanism for the routines exported by
 *    vendorShells class part.
 *
 ************************************************************************/

static void 
ClassPartInitialize(WidgetClass c)
{
  XmExtObjectClass wc = (XmExtObjectClass) c;
  
  if (wc == (XmExtObjectClass)xmExtObjectClass)
    return;

  _XmBuildExtResources(c);
}

/*ARGSUSED*/
static void 
InitializePrehook(Widget req,	/* unused */
		  Widget new_w,
		  ArgList args,
		  Cardinal *num_args)
{
  XmExtObjectClass ec = (XmExtObjectClass) XtClass(new_w);
  XmBaseClassExt  *wcePtr = _XmGetBaseClassExtPtr(ec, XmQmotif);
  
  if ((*wcePtr)->use_sub_resources)
    {
      /*
       * Get a uncompiled resource list to use with XtGetSubresources.
       * We can't do this in ClassPartInitPosthook because Xt doesn't
       * set class_inited at the right place and thereby mishandles
       * the XtGetResourceList call.
       */
      _XmProcessLock();
      if ((*wcePtr)->ext_resources == NULL)
	{
	  ec->object_class.resources = (*wcePtr)->compiled_ext_resources;
	  ec->object_class.num_resources = (*wcePtr)->num_ext_resources;
	  
	  XtGetResourceList((WidgetClass) ec,
			    &((*wcePtr)->ext_resources),
			    &((*wcePtr)->num_ext_resources));
	}

      XtGetSubresources(XtParent(new_w),
			(XtPointer)new_w,
			NULL, NULL,
			(*wcePtr)->ext_resources,
			(*wcePtr)->num_ext_resources,
			args, *num_args);
      _XmProcessUnlock();
    }
}

static void 
Initialize(Widget req,
	   Widget new_w,
	   ArgList args,
	   Cardinal *num_args)
{
  XmExtObject      ne = (XmExtObject) new_w;
  XmExtObjectClass ec = (XmExtObjectClass) XtClass(new_w);
  Widget           resParent = ne->ext.logicalParent;
  XmWidgetExtData  extData;
  XmBaseClassExt  *wcePtr = _XmGetBaseClassExtPtr(ec, XmQmotif);
  
  if (!(*wcePtr)->use_sub_resources)
    {
      if (resParent)
	{
	  extData = (XmWidgetExtData) XtCalloc(1, sizeof(XmWidgetExtDataRec));
	  _XmPushWidgetExtData(resParent, extData, ne->ext.extensionType);
	  
	  extData->widget = new_w;
	  _XmProcessLock();
	  extData->reqWidget = (Widget)
	    _XmExtObjAlloc(XtClass(new_w)->core_class.widget_size);
	  memcpy((char *)extData->reqWidget, (char *)req,
		 XtClass(new_w)->core_class.widget_size);
	  _XmProcessUnlock();
	  
	  /*  Convert the fields from unit values to pixel values  */
	  _XmExtImportArgs(new_w, args, num_args);
	}
    }
}

/*ARGSUSED*/
static Boolean 
SetValuesPrehook(Widget req,	/* unused */
		 Widget curr,	/* unused */
		 Widget new_w,
		 ArgList args,
		 Cardinal *num_args)
{
  XmExtObjectClass ec = (XmExtObjectClass) XtClass(new_w);
  XmBaseClassExt *wcePtr = _XmGetBaseClassExtPtr(ec, XmQmotif);
  
  if ((*wcePtr)->use_sub_resources)
    {
      _XmProcessLock();
      XtSetSubvalues((XtPointer)new_w,
		     (*wcePtr)->ext_resources,
		     (*wcePtr)->num_ext_resources,
		     args, *num_args);
      _XmProcessUnlock();
    }

  return False;
}

static void 
GetValuesPrehook(Widget new_w,
		 ArgList args,
		 Cardinal *num_args)
{
  XmExtObjectClass ec = (XmExtObjectClass) XtClass(new_w);
  XmBaseClassExt *wcePtr = _XmGetBaseClassExtPtr(ec, XmQmotif);
  
  if ((*wcePtr)->use_sub_resources)
    {
      _XmProcessLock();
      XtGetSubvalues((XtPointer)new_w,
		     (*wcePtr)->ext_resources,
		     (*wcePtr)->num_ext_resources,
		     args, *num_args);
      _XmProcessUnlock();
    }
}

/************************************************************************
 *
 *  SetValues
 *
 ************************************************************************/

/*ARGSUSED*/
static Boolean 
SetValues(Widget old,
	  Widget ref,
	  Widget new_w,
	  ArgList args,
	  Cardinal *num_args)
{
  XmExtObject	  ne = (XmExtObject) new_w;
  Widget	  resParent = ne->ext.logicalParent;
  XmWidgetExtData ext = _XmGetWidgetExtData(resParent, ne->ext.extensionType);
  Cardinal	  extSize;

    if(ext == NULL)
    {
#ifdef DEBUG
        XmeWarning(NULL, "_XmGetWidgetExtData() returned NULL pointer.");
#endif    
        return FALSE;
    }
  
  if (resParent)
    {
      _XmProcessLock();
      extSize = XtClass(new_w)->core_class.widget_size;
      
      ext->widget = new_w;
      
      ext->oldWidget = (Widget) _XmExtObjAlloc(extSize);
      memcpy((char *)ext->oldWidget, (char *)old, extSize); 
      
      ext->reqWidget = (Widget) _XmExtObjAlloc(extSize);
      memcpy((char *)ext->reqWidget, (char *)ref, extSize); 
      _XmProcessUnlock();

      /* Convert the necessary fields from unit values to pixel values. */
      _XmExtImportArgs(new_w, args, num_args);
    }

  return FALSE;
}

/************************************************************************
 *
 *  GetValuesHook
 *
 ************************************************************************/

static void 
GetValuesHook(Widget new_w,
	      ArgList args,
	      Cardinal *num_args)
{
  XmExtObject     ne = (XmExtObject) new_w;
  Widget          resParent = ne->ext.logicalParent;
  XmWidgetExtData ext;
  
  if (resParent)
    {
      ext = _XmGetWidgetExtData(resParent, ne->ext.extensionType);
    if(ext == NULL)
    {
#ifdef DEBUG
        XmeWarning(NULL, "_XmGetWidgetExtData() returned NULL pointer.");
#endif    
        return;
    }
      
      ext->widget = new_w;
      
      _XmExtGetValuesHook(new_w, args, num_args);
    }
}

/************************************************************************
 *
 *  Destroy
 *
 ************************************************************************/

static void 
Destroy(Widget wid)
{
  XmExtObject extObj = (XmExtObject) wid;
  Widget      resParent = extObj->ext.logicalParent;
  
  if (resParent)
    {
      XmWidgetExtData extData;
      
      _XmPopWidgetExtData(resParent, &extData, extObj->ext.extensionType);
      
      XtFree((char *) extData);
    }
}

char * 
_XmExtObjAlloc(int size)
{
  register int i;
  
  if (size <= XmNUM_BYTES)
    {
      for (i = 0; i < XmNUM_ELEMENTS; i++)
	if (! extarray[i].cache.inuse)
	  {
	    extarray[i].cache.inuse = TRUE;
	    return extarray[i].cache.data;
	  }
    }
  
  return XtMalloc(size);
}

void 
_XmExtObjFree(XtPointer element)
{
  register int i;
  
  for (i = 0; i < XmNUM_ELEMENTS; i++)
    if (extarray[i].cache.data == (char*)element)
      {
	extarray[i].cache.inuse = FALSE;
	return;
      }
  
  XtFree((char *) element);
}

/**********************************************************************
 *
 *  _XmBuildExtResources
 *	Build up the ext's synthetic 
 *	resource processing list by combining the super classes with 
 *	this class.
 *
 **********************************************************************/

void 
_XmBuildExtResources(WidgetClass c)
{
  XmExtObjectClass wc = (XmExtObjectClass) c;
  XmExtObjectClass sc;

  _XmProcessLock();
  _XmInitializeSyntheticResources(wc->ext_class.syn_resources,
				  wc->ext_class.num_syn_resources);
  
  if (wc != (XmExtObjectClass) xmExtObjectClass)
    {
      sc = (XmExtObjectClass) wc->object_class.superclass;
      
      _XmBuildResources (&(wc->ext_class.syn_resources),
			 &(wc->ext_class.num_syn_resources),
			 sc->ext_class.syn_resources,
			 sc->ext_class.num_syn_resources);
    }
  _XmProcessUnlock();
}
