/* 
 *  @OPENGROUP_COPYRIGHT@
 *  COPYRIGHT NOTICE
 *  Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc.
 *  Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group
 *  ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for
 *  the full copyright text.
 *  
 *  This software is subject to an open license. It may only be
 *  used on, with or for operating systems which are themselves open
 *  source systems. You must contact The Open Group for a license
 *  allowing distribution and sublicensing of this software on, with,
 *  or for operating systems which are not Open Source programs.
 *  
 *  See http://www.opengroup.org/openmotif/license for full
 *  details of the license agreement. Any use, reproduction, or
 *  distribution of the program constitutes recipient's acceptance of
 *  this agreement.
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
 *  PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 *  WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
 *  OR FITNESS FOR A PARTICULAR PURPOSE
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
 *  NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED
 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
 *  EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGES.
*/ 
/* 
 * HISTORY
*/ 
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$TOG: Form.c /main/19 1998/03/25 12:24:56 csn $"
#endif
#endif
/* (c) Copyright 1989, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */
/* (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 HEWLETT-PACKARD COMPANY */

#ifndef X_NOT_STDC_ENV
#include <stdlib.h>		/* for abs, float operation... */
#endif
#include <Xm/DrawP.h>
#include <Xm/FormP.h>
#include <Xm/DialogS.h>
#include "XmI.h"
#include "MessagesI.h"
#include "RepTypeI.h"
#include "GeoUtilsI.h"
#include "GMUtilsI.h"

#define MESSAGE1	_XmMMsgForm_0000
#define MESSAGE5	_XmMMsgForm_0002
#define MESSAGE7 	_XmMMsgForm_0003
	

/*  Useful macros  */

#define Value(a) (really ? (a)->value : (a)->tempValue)

#define AssignValue(a, v)  (really ? (a->value = (int) (v))\
			           : (a->tempValue = (int) (v)))

#define GetFormConstraint(w) \
	(&((XmFormConstraintPtr) (w)->core.constraints)->form)

#define NE(x) (oldc->x != newc->x)

#define ANY(x) (NE(att[LEFT].x) || NE(att[RIGHT].x) || \
		NE(att[TOP].x)  || NE(att[BOTTOM].x))

#define SIBLINGS(w,s)	(((w != NULL) && (s != NULL)) &&\
			 (XtParent(w) == XtParent(s)))

/* convenient magic numbers */

#define MAX_LOOP 10000

#define LEFT	_XmFORM_LEFT
#define RIGHT	_XmFORM_RIGHT
#define TOP	_XmFORM_TOP
#define BOTTOM	_XmFORM_BOTTOM
#define FIRST_ATTACHMENT  LEFT
#define LAST_ATTACHMENT   BOTTOM	

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

static void FromTopOffset( 
                        Widget w,
                        int offset,
                        XtArgVal *value) ;
static void FromBottomOffset( 
                        Widget w,
                        int offset,
                        XtArgVal *value) ;
static void FromLeftOffset( 
                        Widget w,
                        int offset,
                        XtArgVal *value) ;
static void FromRightOffset( 
                        Widget w,
                        int offset,
                        XtArgVal *value) ;
static void MarginWidthOut( 
                        Widget wid,
                        int offset,
                        XtArgVal *value) ;
static void MarginHeightOut( 
                        Widget wid,
                        int offset,
                        XtArgVal *value) ;
static void ClassPartInitialize( 
                        WidgetClass wc) ;
static Boolean SyncEdges( 
                        XmFormWidget fw,
                        Widget last_child,
                        Dimension *form_width,
                        Dimension *form_height,
                        Widget instigator,
                        XtWidgetGeometry *geometry) ;
static Boolean CalcFormSizeWithChange( 
                        XmFormWidget fw,
                        Dimension *w,
                        Dimension *h,
                        Widget c,
                        XtWidgetGeometry *g) ;
static void CalcFormSize( 
                        XmFormWidget fw,
                        Dimension *w,
                        Dimension *h) ;
static XtGeometryResult GeometryManager( 
                        Widget w,
                        XtWidgetGeometry *desired,
                        XtWidgetGeometry *allowed) ;
static XtGeometryResult QueryGeometry( 
                        Widget wid,
                        XtWidgetGeometry *req,
                        XtWidgetGeometry *ret) ;
static void Resize( 
                        Widget wid) ;
static void Redisplay( 
                        Widget fw,
                        XEvent *event,
                        Region region) ;
static void PlaceChildren( 
                        XmFormWidget fw,
                        Widget instigator,
                        XtWidgetGeometry *inst_geometry) ;
static void ChangeManaged( 
                        Widget wid) ;
static void GetSize( 
                        XmFormWidget fw,
		        XtWidgetGeometry *g,
                        Widget w,
                        XtWidgetGeometry *desired) ;
static void ChangeIfNeeded( 
                        XmFormWidget fw,
                        Widget w,
                        XtWidgetGeometry *desired) ;
static void DeleteChild( 
                        Widget child) ;
static Boolean SetValues( 
                        Widget cw,
                        Widget rw,
                        Widget nw,
                        ArgList args,
                        Cardinal *num_args) ;
static void SetValuesAlmost( 
                        Widget cw,
                        Widget nw,
                        XtWidgetGeometry *req,
                        XtWidgetGeometry *rep) ;
static Boolean ConstraintSetValues( 
                        register Widget old,
                        register Widget ref,
                        register Widget new_w,
                        ArgList args,
                        Cardinal *num_args) ;
static void Initialize( 
                        Widget rw,
                        Widget nw,
                        ArgList args,
                        Cardinal *num_args) ;
static void ConstraintInitialize( 
                        Widget req,
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args) ;
static void CheckConstraints( 
                        Widget w) ;
static void SortChildren( 
                        register XmFormWidget fw) ;
static void CalcEdgeValues( 
                        Widget w,
#if NeedWidePrototypes
                        int really,
#else
                        Boolean really,
#endif /* NeedWidePrototypes */
                        Widget instigator,
                        XtWidgetGeometry *inst_geometry,
                        Dimension *form_width,
                        Dimension *form_height) ;
static float CheckBottomBase( 
                        Widget sibling,
#if NeedWidePrototypes
                        int opposite) ;
#else
                        Boolean opposite) ;
#endif /* NeedWidePrototypes */
static float CheckRightBase( 
                        Widget sibling,
#if NeedWidePrototypes
                        int opposite) ;
#else
                        Boolean opposite) ;
#endif /* NeedWidePrototypes */
static float CheckLeftBase( 
                        Widget sibling,
#if NeedWidePrototypes
                        int opposite) ;
#else
                        Boolean opposite) ;
#endif /* NeedWidePrototypes */
static void CalcEdgeValue( 
                        XmFormWidget fw,
                        Widget w,
#if NeedWidePrototypes
                        int size,
                        int border_width,
#else
                        Dimension size,
                        Dimension border_width,
#endif /* NeedWidePrototypes */
                        int which,
#if NeedWidePrototypes
                        int really,
#else
                        Boolean really,
#endif /* NeedWidePrototypes */
                        Dimension *fwidth,
                        Dimension *fheight) ;
static void ComputeAttachment( 
			XmFormWidget fw,
                        Widget w,
#if NeedWidePrototypes
                        int size,
                        int border_width,
#else
                        Dimension size,
                        Dimension border_width,
#endif /* NeedWidePrototypes */
                        int which,
#if NeedWidePrototypes
                        int really,
#else
                        Boolean really,
#endif /* NeedWidePrototypes */
                        Dimension *fwidth,
                        Dimension *fheight) ;
static int GetFormOffset( 
                        XmFormWidget fw,
                        int which,
                        XmFormAttachment a) ;

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


static XtResource resources[] = 
{
   {
       XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
       sizeof(Dimension),
       XtOffsetOf( struct _XmFormRec, bulletin_board.margin_width),
       XmRImmediate, (XtPointer) XmINVALID_DIMENSION
   },
   {
       XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
       sizeof(Dimension),
       XtOffsetOf( struct _XmFormRec, bulletin_board.margin_height),
       XmRImmediate, (XtPointer) XmINVALID_DIMENSION
   },
   {
       XmNhorizontalSpacing, XmCSpacing, XmRHorizontalDimension,
       sizeof(Dimension),
       XtOffsetOf( struct _XmFormRec, form.horizontal_spacing), 
       XmRImmediate, (XtPointer) 0
   },
   {
       XmNverticalSpacing, XmCSpacing, XmRVerticalDimension,
       sizeof(Dimension),
       XtOffsetOf( struct _XmFormRec, form.vertical_spacing), 
       XmRImmediate, (XtPointer) 0
   },

   {
       XmNfractionBase, XmCMaxValue, XmRInt, sizeof(int),
       XtOffsetOf( struct _XmFormRec, form.fraction_base), 
       XmRImmediate, (XtPointer) 100
   },

   {
       XmNrubberPositioning, XmCRubberPositioning, XmRBoolean, 
       sizeof(Boolean),
       XtOffsetOf( struct _XmFormRec, form.rubber_positioning), 
       XmRImmediate, (XtPointer) False
   },

};


/*  Definition for resources that need special processing in get values  */

static XmSyntheticResource syn_resources[] =
{
   { XmNmarginWidth,
     sizeof (Dimension),
     XtOffsetOf( struct _XmFormRec, bulletin_board.margin_width), 
	 MarginWidthOut,
	 XmeToHorizontalPixels },

   { XmNmarginHeight,
     sizeof (Dimension),
     XtOffsetOf( struct _XmFormRec, bulletin_board.margin_height), 
	 MarginHeightOut,
	 XmeToVerticalPixels },

   { XmNhorizontalSpacing,
     sizeof (Dimension),
     XtOffsetOf( struct _XmFormRec, form.horizontal_spacing), 
     XmeFromHorizontalPixels,
	 XmeToHorizontalPixels },

   { XmNverticalSpacing,
     sizeof (Dimension),
     XtOffsetOf( struct _XmFormRec, form.vertical_spacing), 
     XmeFromVerticalPixels,
	 XmeToVerticalPixels },
};


/*  The constraint resource list  */

static XtResource constraints[] =
{
   {
      XmNtopAttachment, XmCAttachment, XmRAttachment, sizeof(unsigned char),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[TOP].type),
      XmRImmediate, (XtPointer) XmATTACH_NONE
   },

   {
      XmNbottomAttachment, XmCAttachment, XmRAttachment,
      sizeof(unsigned char),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[BOTTOM].type),
      XmRImmediate, (XtPointer) XmATTACH_NONE
   },

   {
      XmNleftAttachment, XmCAttachment, XmRAttachment,
      sizeof(unsigned char),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[LEFT].type),
      XmRImmediate, (XtPointer) XmATTACH_NONE
   },

   {
      XmNrightAttachment, XmCAttachment, XmRAttachment,
      sizeof(unsigned char),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[RIGHT].type),
      XmRImmediate, (XtPointer) XmATTACH_NONE
   },

   {
      XmNtopWidget, XmCWidget, XmRWidget, sizeof(Widget),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[TOP].w),
      XmRImmediate, (XtPointer) NULL
   },

   {
      XmNbottomWidget, XmCWidget, XmRWidget, sizeof(Widget),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[BOTTOM].w),
      XmRImmediate, (XtPointer) NULL
   },

   {
      XmNleftWidget, XmCWidget, XmRWidget, sizeof(Widget),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[LEFT].w),
      XmRImmediate, (XtPointer) NULL
   },

   {
      XmNrightWidget, XmCWidget, XmRWidget, sizeof(Widget),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[RIGHT].w),
      XmRImmediate, (XtPointer) NULL
   },

   {
      XmNtopPosition, XmCPosition, XmRInt, sizeof(int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[TOP].percent),
      XmRImmediate, (XtPointer) 0
   },

   {
      XmNbottomPosition, XmCPosition, XmRInt, sizeof(int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[BOTTOM].percent),
      XmRImmediate, (XtPointer) 0
   },

   {
      XmNleftPosition, XmCPosition, XmRInt, sizeof(int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[LEFT].percent),
      XmRImmediate, (XtPointer) 0
   },

   {
      XmNrightPosition, XmCPosition, XmRInt, sizeof(int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[RIGHT].percent),
      XmRImmediate, (XtPointer) 0
   },

   {
      XmNtopOffset, XmCOffset, XmRVerticalInt, sizeof(int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[TOP].offset),
      XmRImmediate, (XtPointer) XmINVALID_DIMENSION
   },

   {
      XmNbottomOffset, XmCOffset, XmRVerticalInt, sizeof(int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[BOTTOM].offset),
      XmRImmediate, (XtPointer) XmINVALID_DIMENSION
   },

   {
      XmNleftOffset, XmCOffset, XmRHorizontalInt, sizeof(int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[LEFT].offset),
      XmRImmediate, (XtPointer) XmINVALID_DIMENSION
   },

   {
      XmNrightOffset, XmCOffset, XmRHorizontalInt, sizeof(int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[RIGHT].offset),
      XmRImmediate, (XtPointer) XmINVALID_DIMENSION
   },

   {
      XmNresizable, XmCBoolean, XmRBoolean, sizeof(Boolean),
      XtOffsetOf( struct _XmFormConstraintRec, form.resizable),
      XmRImmediate, (XtPointer) True
   }
};


/*  Definition for constraint resources that need special  */
/*  processing in get values                               */

static XmSyntheticResource syn_constraint_resources[] =
{

   { XmNtopOffset,
     sizeof (int),
     XtOffsetOf( struct _XmFormConstraintRec, form.att[TOP].offset),
     FromTopOffset,
	 XmeToVerticalPixels },

   {  XmNbottomOffset,
      sizeof (int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[BOTTOM].offset),
     FromBottomOffset,
	 XmeToVerticalPixels },

   {  XmNleftOffset,
      sizeof (int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[LEFT].offset),
     FromLeftOffset,
	 XmeToHorizontalPixels },

   {  XmNrightOffset,
      sizeof (int),
      XtOffsetOf( struct _XmFormConstraintRec, form.att[RIGHT].offset),
     FromRightOffset,
	 XmeToHorizontalPixels },
};



/*  Static initialization of the attached dialog box widget class record.  */

externaldef(xmformclassrec) XmFormClassRec xmFormClassRec =
{
   {                    /* core_class fields    */
      (WidgetClass) &xmBulletinBoardClassRec,   /* superclass   */
      "XmForm",                 /* class_name           */
      sizeof(XmFormRec),        /* widget_size          */
      (XtProc)NULL,             /* class_initialize */
      ClassPartInitialize,      /* class init part proc */
      False,                    /* class_inited         */
      Initialize,               /* initialize           */
      (XtArgsProc)NULL,         /* initialize_notify    */
      XtInheritRealize,         /* realize              */
      NULL,                     /* actions              */
      0,                        /* num_actions          */
      resources,                /* resources            */
      XtNumber(resources),      /* num_resources        */
      NULLQUARK,                /* xrm_class            */
      True,                     /* compress_motion  */
      XtExposeCompressMaximal,  /* compress_exposure    */
      False,                    /* compress_enterleave  */
      False,                    /* visible_interest     */
      (XtWidgetProc)NULL,       /* destroy              */
      Resize,                   /* resize               */
      Redisplay,                /* expose               */
      SetValues,                /* set_values           */
      (XtArgsFunc)NULL,         /* set_values_hook      */
      SetValuesAlmost,          /* set_values_almost    */
      (XtArgsProc)NULL,         /* get_values_hook      */
      (XtAcceptFocusProc)NULL,  /* accept_focus         */
      XtVersion,                /* version      */
      NULL,                     /* callback_private */
      XtInheritTranslations,    /* tm_table             */
      QueryGeometry,            /* Query Geometry proc  */
      (XtStringProc)NULL,       /* disp accelerator     */
      NULL,                     /* extension            */    
   },

   {                    /* composite_class fields */
      GeometryManager,      /* geometry_manager       */
      ChangeManaged,        /* change_managed         */
      XtInheritInsertChild, /* insert_child           */
      DeleteChild,          /* delete_child           */
      NULL,                 /* extension              */
   },

   {                    /* constraint_class fields */
      constraints,                  /* constraint resource     */
      XtNumber(constraints),        /* number of constraints   */
      sizeof(XmFormConstraintRec),  /* size of constraint      */
      ConstraintInitialize,         /* initialization          */
      (XtWidgetProc)NULL,           /* destroy proc            */
      ConstraintSetValues,          /* set_values proc         */
      NULL,                         /* extension               */
   },

   {                        /* manager_class fields   */
      XtInheritTranslations,                /* translations           */
      syn_resources,                        /* syn_resources          */
      XtNumber(syn_resources),              /* num_syn_resources      */
      syn_constraint_resources,             /* syn_cont_resources     */
      XtNumber(syn_constraint_resources),   /* num_syn_cont_resources */
      XmInheritParentProcess,               /* parent_process         */
      NULL,                                 /* extension              */
   },

   {                        /* bulletin_board_class fields */
      FALSE,                                /* always_install_accelerators */
      (XmGeoCreateProc)NULL,                /* geo_matrix_create  */
      XmInheritFocusMovedProc,              /* focus_moved_proc   */
      NULL,                                 /* extension          */
   },

   {                        /* form_class fields  */
      (XtPointer) NULL,                     /* extension          */
   }
};


externaldef(xmformwidgetclass) WidgetClass 
	xmFormWidgetClass = (WidgetClass) &xmFormClassRec;

static void
FromTopOffset(
	Widget w,
	int offset,
	XtArgVal *value)
{
    XmFormWidget fw = (XmFormWidget) w->core.parent;
    XmFormConstraint fc = GetFormConstraint(w);

    *value = (XtArgVal) GetFormOffset(fw, TOP, fc->att);
    XmeFromVerticalPixels(w, offset, value);
}

static void
FromBottomOffset(
	Widget w,
	int offset,
	XtArgVal *value)
{
    XmFormWidget fw = (XmFormWidget) w->core.parent;
    XmFormConstraint fc = GetFormConstraint(w);

    *value = (XtArgVal) GetFormOffset(fw, BOTTOM, fc->att);
    XmeFromVerticalPixels(w, offset, value);
}

static void
FromLeftOffset(
	Widget w,
	int offset,
	XtArgVal *value)
{
    XmFormWidget fw = (XmFormWidget) w->core.parent;
    XmFormConstraint fc = GetFormConstraint(w);

    *value = (XtArgVal) GetFormOffset(fw, LEFT, fc->att);
    XmeFromHorizontalPixels(w, offset, value);
}

static void
FromRightOffset(
	Widget w,
	int offset,
	XtArgVal *value)
{
    XmFormWidget fw = (XmFormWidget) w->core.parent;
    XmFormConstraint fc = GetFormConstraint(w);

    *value = (XtArgVal) GetFormOffset(fw, RIGHT, fc->att);
    XmeFromHorizontalPixels(w, offset, value);
}



static void 
MarginWidthOut(
        Widget wid,
        int offset,
        XtArgVal *value )
{
        XmFormWidget fw = (XmFormWidget) wid ;

	if (fw->bulletin_board.margin_width == XmINVALID_DIMENSION)
		*value = 0;
	else
		XmeFromHorizontalPixels((Widget) fw, offset, value);
}

static void 
MarginHeightOut(
        Widget wid,
        int offset,
        XtArgVal *value )
{
        XmFormWidget fw = (XmFormWidget) wid ;

	if (fw->bulletin_board.margin_height == XmINVALID_DIMENSION)
		*value = 0;
	else
		XmeFromVerticalPixels((Widget) fw, offset, value);
}

/************************************************************************
 *
 *  ClassPartInitialize
 *	Set up the fast subclassing.
 *
 ************************************************************************/
static void 
ClassPartInitialize(
        WidgetClass wc )
{
   _XmFastSubclassInit (wc, XmFORM_BIT);
}




/************************************************************************
 *
 *  CalcFormSizeWithChange
 *	Find size of a bounding box which will include all of the 
 *	children, including the child which may change
 *
 ************************************************************************/
static Boolean 
SyncEdges(
        XmFormWidget fw,
        Widget last_child,
        Dimension *form_width,
        Dimension *form_height,
        Widget instigator,
        XtWidgetGeometry *geometry )
{
	register Widget child;
	register XmFormConstraint c;
	long int loop_count;
	Dimension tmp_w = *form_width, tmp_h = *form_height;
	Dimension sav_w, sav_h;
	Boolean settled = FALSE;
	Boolean finished = TRUE;

	sav_w = tmp_w;
	sav_h = tmp_h;

	loop_count = 0;
	while (!settled)
	{
		/*
		 * Contradictory constraints can cause the constraint
		 * processing to go into endless oscillation.  This means that
		 * proper exit condition for this loop is never satisfied.
		 * But, infinite loops are a bad thing, even if is the result
		 * of a careless user.  We therefore have added a loop counter
		 * to ensure that this loop will terminate.
		 *
		 * There are problems with this however.  In the worst case
		 * this procedure could need to loop fw->composite.num_children!
		 * times.  Unfortunately, numbers like 100! don't fit integer
		 * space well; neither will current architectures complete that
		 * many loops before the sun burns out.
		 *
		 * Soooo, we will wait for an arbitrarily large number of
		 * iterations to go by before we give up.  This allows us to
		 * claim that the procedure will always complete, and the number
		 * is large enough to accomodate all but the very large and
		 * very pathological Form widget configurations.
		 *
		 * This is gross, but it's either do this or risk truly
		 * infinite loops.
		 */

		if (loop_count++ > MAX_LOOP)
			break;

		for (child = fw->form.first_child;
			child != NULL;
			child = c->next_sibling) 
		{
			if (!XtIsManaged (child))
				break;

			c = GetFormConstraint(child);

			CalcEdgeValues(child, FALSE, instigator, geometry, 
				&tmp_w, &tmp_h);

			if (child == last_child)
				break;
		}

		if ((sav_w == tmp_w) && (sav_h == tmp_h))
			settled = TRUE;
		else
		{
			sav_w = tmp_w;
			sav_h = tmp_h;
		}
	}


	if (loop_count > MAX_LOOP)
	{
		XmeWarning( (Widget) fw, MESSAGE7);
		finished = FALSE;
	}

	*form_width = sav_w;
	*form_height = sav_h;

	return(finished);
}

static Boolean 
CalcFormSizeWithChange(
        XmFormWidget fw,
        Dimension *w,
        Dimension *h,
        Widget c,
        XtWidgetGeometry *g )
{
	Dimension junkh = fw->core.height;
	Dimension junkw = fw->core.width;
	Widget child;
	XmFormConstraint fc;
	int tmp;

	if (h == NULL) h = &junkh;
	if (w == NULL) w = &junkw;

	/* Place children, but don't do it for real--just get new size */

	for(child = fw->form.first_child; 
		child != NULL;
		child = fc->next_sibling)
	{
		if (!XtIsManaged (child))
			break;

		fc = GetFormConstraint(child);

		CalcEdgeValues(child, False, c, g, w, h);
		if (!SyncEdges(fw, child, w, h, c, g))
			return(False);
	}

	for(child = fw->form.first_child; 
		child != NULL;
		child = fc->next_sibling)
	{
		if (!XtIsManaged (child)) 
			break;

		fc = GetFormConstraint(child);

		tmp = fc->att[RIGHT].tempValue;
		if (fc->att[RIGHT].type == XmATTACH_FORM)
			tmp += GetFormOffset(fw, RIGHT, fc->att);
		if (tmp > 0) 
			ASSIGN_MAX(*w, tmp);
		
		tmp = fc->att[BOTTOM].tempValue ;
		if (fc->att[BOTTOM].type == XmATTACH_FORM)
			tmp += GetFormOffset(fw, BOTTOM, fc->att);
		if (tmp > 0) 
			ASSIGN_MAX (*h, tmp);
	}

	if (!(*w))
		*w = 1;
	if (!(*h))
		*h = 1;

	if (*w != XtWidth(fw) || *h != XtHeight(fw)) 
		return True;
	else
		return False;
}




/************************************************************************
 *
 *  CalcFormSize
 *	Find size of a bounding box which will include all of the children.
 *
 ************************************************************************/
static void 
CalcFormSize(
        XmFormWidget fw,
        Dimension *w,
        Dimension *h )
{
	Widget child;
	Dimension junkh = fw->core.height;
	Dimension junkw = fw->core.width;
	XmFormConstraint fc;
	int tmp;

	if (h == NULL) h = &junkh;
	if (w == NULL) w = &junkw;

	for (child = fw->form.first_child;
		child != NULL;
		child = fc->next_sibling)
	{
		if (!XtIsManaged (child))
			break;

		fc = GetFormConstraint(child);

		CalcEdgeValues(child, False, NULL, NULL, w, h);
		if (!SyncEdges(fw, child, w, h, NULL, NULL))
			break;
	}

	for(child = fw->form.first_child; 
		child != NULL;
		child = fc->next_sibling)
	{
		if (!XtIsManaged (child)) 
			break;

		fc = GetFormConstraint(child);

		tmp = fc->att[RIGHT].tempValue;
		if (fc->att[RIGHT].type == XmATTACH_FORM)
			tmp += GetFormOffset(fw, RIGHT, fc->att);
		if (tmp > 0) 
			ASSIGN_MAX(*w, tmp);
		
		tmp = fc->att[BOTTOM].tempValue ;
		if (fc->att[BOTTOM].type == XmATTACH_FORM)
			tmp += GetFormOffset(fw, BOTTOM, fc->att);
		if (tmp > 0) 
			ASSIGN_MAX(*h, tmp);
	}

	if (!(*w))
		*w = 1;
	if (!(*h))
		*h = 1;
}



/************************************************************************
 *
 *  GeometryManager
 *
 ************************************************************************/
static XtGeometryResult 
GeometryManager(
        Widget w,
        XtWidgetGeometry *desired,
        XtWidgetGeometry *allowed )
{
    int size_req ;
    XtWidgetGeometry original;
    XtGeometryResult reply = XtGeometryNo;
    XmFormWidget fw = (XmFormWidget) XtParent(w);
    XmFormConstraint c = GetFormConstraint(w);
    
    /* sooo confusing... */
    if (fw->form.processing_constraints) {
	fw->form.processing_constraints = FALSE;
	PlaceChildren (fw, NULL, NULL);
	return(XtGeometryNo);
    }
    
    /*
     * Fix for 4854 - If the widget is not resizable, do not set the
     *                preferred_width or the preferred_height.
     */
    if ((desired->request_mode & CWWidth) &&
 	!(desired->request_mode & XtCWQueryOnly) && c->resizable)
  	c->preferred_width = desired->width;
    if ((desired->request_mode & CWHeight) &&
 	!(desired->request_mode & XtCWQueryOnly) && c->resizable)
  	c->preferred_height = desired->height;

    if (desired->request_mode == (CWX | CWY)) return(XtGeometryNo);

    original.x = w->core.x;
    original.y = w->core.y;
    original.width = w->core.width;
    original.height = w->core.height;
    original.border_width = w->core.border_width;
    original.request_mode = (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);

    size_req = desired->request_mode & 
	              (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
    if (size_req && c->resizable) {
	XtWidgetGeometry g, r;
	XtGeometryResult res ;
	
	/* get the size the Form wants to be */
	GetSize(fw, &g, w, desired);
	
	/* GetSize takes care of the resizePolicy resource as well
	   and returns the desired change in g if any.
	   g.request_mode might be 0 at this point, which means the 
	   Form doesn't want to change, but we still want to
	   see if the child request can be accomodated into
	   the current Form size, so we move forward with the
	   0 in request mode.  */
	
	/* if the child requesed query only, propagate */
	if (desired->request_mode & XtCWQueryOnly) 
	    g.request_mode |= XtCWQueryOnly;
	
	/* Let's make a request to the parent.
	   At this point, we do not want to accept any compromise
	   because we're not sure our kid will accept it in turn. 
	   So use the Xt API, no _XmMakeGeometryRequest.
	   If g.request_mode is 0, nothing will happen, yes
	   will be returned */
	res = XtMakeGeometryRequest((Widget)fw, &g, &r);
	
	/* check that a real request has failed or that if request_mode was 0,
	   consider that a No */
	if (!g.request_mode || (res != XtGeometryYes)) {
	    Dimension orig_form_width, orig_form_height  ;
	    
	    /* let's save the original form size first */
	    orig_form_width = fw->core.width ;
	    orig_form_height = fw->core.height ;
	    
	    if (res == XtGeometryAlmost) {
		/* let's try the proposal. Stuff the Form with it so that 
		   PlaceChildren does the layout base on it */
		if (r.request_mode | CWWidth) fw->core.width = r.width ;
		if (r.request_mode | CWHeight) fw->core.height = r.height ;
	    }
	    /* else it's No and we keep the same size */
	    
	    /* let's see if there is a chance that the child request 
	       be honored: the needed form overall size is smaller than
	       the current or proposed size (now in the core field) */
	    
	    if ((g.width <= fw->core.width) &&
		(g.height <= fw->core.height)) {
		/* ok, now we'are going to try to place the kid 
		   using the new Form size for the layout */
		PlaceChildren (fw, w, desired);
		
		/* now, we check if the requestor has gotten
		   what it asked for.  The logic is that:
		   width = req_width iff req_width 
		   height = req_height iff req_height */
		if (((desired->request_mode & CWWidth && 
		      desired->width == w->core.width) ||
		     ! (desired->request_mode & CWWidth)) &&
		    ((desired->request_mode & CWHeight && 
		      desired->height == w->core.height) ||
		     ! (desired->request_mode & CWHeight))) {
		    /* ok, the kid request has been honored, although the
		       Form couldn't change its own size, let's
		       return Yes to the kid and ask the Form's parent
		       the needed size if Almost was returned */
		    if (res == XtGeometryAlmost) {
			/* let's backup the original Form size first */
			fw->core.width = orig_form_width  ;
			fw->core.height = orig_form_height;
			
			/* success guaranteed */
			XtMakeGeometryRequest((Widget)fw, &r, NULL);
			
			/* no need to do the layout PlaceCHildren, it 
			   has already been done using the correct size */
		    }
		    
		    /* simply return Yes, since PlaceChildren already
		       change the kid core fields */
		    reply = XtGeometryYes ;

		} else {
		    /* the kid hasn't gotten what it asked for in the
		       current Form geometry, we have to backup
		       everything and return either No or Almost
		       to the kid */
		    
		    if ((w->core.width != original.width) ||
			(w->core.height != original.height)) {
			
			allowed->request_mode = desired->request_mode;
			allowed->sibling = desired->sibling;
			allowed->stack_mode = desired->stack_mode;
			allowed->x = w->core.x;
			allowed->y = w->core.y;
			allowed->width = w->core.width;
			allowed->height = w->core.height;
			allowed->border_width = w->core.border_width;
			reply = XtGeometryAlmost;
			
		    } else reply = XtGeometryNo;
		    
		    /* backup the kid geometry */
		    w->core.x = original.x;
		    w->core.y = original.y;
		    w->core.width = original.width;
		    w->core.height = original.height;
		    w->core.border_width = original.border_width;
		    
		    /* backup the Form and the layout too */
		    fw->core.width = orig_form_width  ;
		    fw->core.height = orig_form_height;
		    
		    PlaceChildren (fw, w, &original); 
		}
	    } else {
		/* the size the Form wants for the kid request is bigger than 
		   its current or proposed size, return No to the kid */
		
		/* backup the original Form size first */
		fw->core.width = orig_form_width  ;
		fw->core.height = orig_form_height;
		
		/* we haden't changed anything else, just return No */
		reply = XtGeometryNo;
	    }
	} else {
	    /* ok, we got a Yes form the Form's parent, let's relayout
	       using the new size, except if query only was specified */
	    
	    if (!(desired->request_mode & XtCWQueryOnly)) {
		/* Reposition the widget only if not QueryOnly */
		PlaceChildren (fw, w, desired);
	    }
	    reply = XtGeometryYes;
	}
    } 

    /* let's deal with stacking order */

    if (desired->request_mode & (CWSibling | CWStackMode)) {
	/* always honor stand alone stack requests */
	if (!size_req) reply = XtGeometryYes;
	else 
	    /* the request concerned size as well, see if it was denied.
	       if so, propose the stack request alone */
	    if (reply != XtGeometryYes) {
		allowed->request_mode = desired->request_mode;
		allowed->sibling = desired->sibling;
		allowed->stack_mode = desired->stack_mode;
		allowed->x = w->core.x;
		allowed->y = w->core.y;
		allowed->width = w->core.width;
		allowed->height = w->core.height;
		allowed->border_width = w->core.border_width;
		reply = XtGeometryAlmost;
	    }
    }

    /* deal with the shadow resize without Expose */
    if ( fw->bulletin_board.old_shadow_thickness &&
	(fw->bulletin_board.old_width != fw->core.width ||
	 fw->bulletin_board.old_height != fw->core.height) )
	{
	    _XmClearShadowType ((Widget) fw, fw->bulletin_board.old_width,
				fw->bulletin_board.old_height,
				fw->bulletin_board.old_shadow_thickness, 0);
	}
    
    fw->bulletin_board.old_width = fw->core.width;
    fw->bulletin_board.old_height = fw->core.height;
    fw->bulletin_board.old_shadow_thickness = fw->manager.shadow_thickness;


    return (reply);
}


/************************************************************************
 *
 *  QueryGeometry
 *
 ************************************************************************/
static XtGeometryResult 
QueryGeometry(
        Widget widget,
        XtWidgetGeometry *intended,
        XtWidgetGeometry *desired )
{
    Dimension width = 0, height = 0 ;
    XmFormWidget fw = (XmFormWidget) widget ;
    
    /* first determine what is the desired size, using the resize_policy. */
    if (fw->bulletin_board.resize_policy == XmRESIZE_NONE) {
	desired->width = XtWidth(widget) ;
	desired->height = XtHeight(widget) ;
    } else {
	SortChildren(fw);
	if (GMode( intended) & CWWidth) width = intended->width;
	if (GMode( intended) & CWHeight) height = intended->height;

	if (!XtIsRealized((Widget)fw))
	{
		int i;
		Widget child;
		XmFormConstraint c;

		for (i = 0; i < fw->composite.num_children; i++)
		{
			child = fw->composite.children[i];
			c = GetFormConstraint(child);
			c->preferred_width = XtWidth(child);
			c->preferred_height = XtHeight(child);
		}
	}

	CalcFormSize(fw, &width, &height);
	if ((fw->bulletin_board.resize_policy == XmRESIZE_GROW) &&
	    ((width < XtWidth(widget)) ||
	     (height < XtHeight(widget)))) {
	    desired->width = XtWidth(widget) ;
	    desired->height = XtHeight(widget) ;
	} else {
	    desired->width = width ;
	    desired->height = height ;
	}
    }

    /* deal with user initial size setting */
    if (!XtIsRealized(widget))  {
	if (XtWidth(widget) != 0) desired->width = XtWidth(widget) ;
	if (XtHeight(widget) != 0) desired->height = XtHeight(widget) ;
    }	    

    return XmeReplyToQueryGeometry(widget, intended, desired) ;
}




/************************************************************************
 *
 *  Resize
 *	This routine is called by the parent's geometry manager after it
 *	has ok'd the resize request AND resized the form window.  This 
 *	routine is responsible for implementing the size given.
 *
 ************************************************************************/
static void 
Resize(
        Widget wid )
{
    XmFormWidget fw = (XmFormWidget) wid ;
    Boolean      draw_shadow = False;

    /* clear the shadow if its needed (will check if its now larger) */
    _XmClearShadowType ((Widget )fw, fw->bulletin_board.old_width,
			fw->bulletin_board.old_height,
			fw->bulletin_board.old_shadow_thickness, 0);

	/*
	 * if it is now smaller, redraw the shadow since there may not be a
	 * redisplay
	 */
    if ((fw->bulletin_board.old_height > fw->core.height) ||
	(fw->bulletin_board.old_width > fw->core.width))
	draw_shadow = True;

    
    fw->bulletin_board.old_width = fw->core.width;
    fw->bulletin_board.old_height = fw->core.height;
    fw->bulletin_board.old_shadow_thickness = 
	fw->manager.shadow_thickness;


    PlaceChildren (fw, NULL, NULL) ;

    if ((draw_shadow) && (XtIsRealized((Widget)fw)))
	XmeDrawShadows  (XtDisplay (fw), XtWindow (fw),
			 fw->manager.top_shadow_GC,
			 fw->manager.bottom_shadow_GC,
			 0, 0, fw->core.width, fw->core.height,
			 fw->manager.shadow_thickness,
			 fw->bulletin_board.shadow_type);
}


static void 
Redisplay(
        Widget fw,
        XEvent *event,
        Region region )
{
    XmeRedisplayGadgets( fw, event, region);

    XmeDrawShadows (XtDisplay((Widget)fw), 
		    XtWindow((Widget)fw), 
		    ((XmFormWidget) fw)->manager.top_shadow_GC,
		    ((XmFormWidget) fw)->manager.bottom_shadow_GC,
		    0, 0,
		    XtWidth(fw), XtHeight(fw),
		    ((XmFormWidget) fw)->manager.shadow_thickness, 
		    ((XmFormWidget) fw)->bulletin_board.shadow_type);
}



/************************************************************************
 *
 *  PlaceChildren
 *	Position all children according to their constraints.  
 *      Return desired width and height.
 *
 ************************************************************************/
static void 
PlaceChildren(
        XmFormWidget fw,
        Widget instigator,
        XtWidgetGeometry *inst_geometry )
{
    Widget child;
    register XmFormConstraint c;
    int height, width;
    Dimension border_width;
    int near_edge;

    for (child = fw->form.first_child;
	 child != NULL;
	 child = c->next_sibling) {
	if (!XtIsManaged(child))
	    break;

	c = GetFormConstraint(child);
	
	CalcEdgeValues(child, TRUE, instigator, inst_geometry, 
		       NULL, NULL);

	if ((child == instigator) &&
	    (inst_geometry->request_mode & CWBorderWidth))
	    border_width = inst_geometry->border_width;
	else
	    border_width = ((RectObj) child)->rectangle.border_width;

        if (LayoutIsRtoLM(fw)) {
	  /* switch the meanings of left and right attachements */
	  width = c->att[LEFT].value - c->att[RIGHT].value - (2 * border_width);
	  near_edge = RIGHT;
	} else {
	  width = c->att[RIGHT].value - c->att[LEFT].value - (2 * border_width);
	  near_edge = LEFT;
	}
	height = c->att[BOTTOM].value - c->att[TOP].value
	    - (2 * border_width);

	if (width <= 0) width = 1;
	if (height <= 0) height = 1;

	if ((c->att[near_edge].value != ((RectObj) child)->rectangle.x) ||
	    (c->att[TOP].value != ((RectObj) child)->rectangle.y)  ||
	    (width != ((RectObj) child)->rectangle.width)          || 
	    (height != ((RectObj) child)->rectangle.height)        ||
	    (border_width != ((RectObj) child)->rectangle.border_width)) {
	  
	  /* Yes policy everywhere, so don't resize the instigator */
	  if (child != instigator) {
	    XmeConfigureObject(child,
			       c->att[near_edge].value, 
			       c->att[TOP].value,
			       width, height, border_width);
	  } else {
	    XmeConfigureObject(child,
			       c->att[near_edge].value, 
			       c->att[TOP].value,
			       child->core.width, child->core.height, 
			       child->core.border_width);
	    child->core.width = width ;
	    child->core.height = height ;
	    child->core.border_width = border_width ;
	  }
	}
      }
}


/************************************************************************
 *
 *  ChangeManaged
 *	Something changed in the set of managed children, so place 
 *	the children and change the form widget size to reflect new size, 
 *	if possible.
 *
 ************************************************************************/
static void 
ChangeManaged(
        Widget wid )
{
    XmFormWidget fw = (XmFormWidget) wid ;
    XtWidgetGeometry g;
    int i, j, k;
    register XmFormConstraint c;
    register Widget w, child;
    
    /*
     * The following code works around a bug in the intrinsics
     * destroy processing.  The child is unmanaged before anything
     * else (destroy callbacks) so we have to handle the destroy
     * inside of changemanaged instead of in a destroy callback
     */
    for (k = 0; k < fw->composite.num_children; k++) {
	child = fw->composite.children[k];
	
	if (child->core.being_destroyed) {
	    /*  If anyone depends on this child,
		make into a dependency on form  */
	    
	    
	    for (i = 0; i < fw->composite.num_children; i++) {
		w = fw->composite.children[i];
		c = GetFormConstraint(w);
		
		for (j = FIRST_ATTACHMENT; j < (LAST_ATTACHMENT + 1); j++)  {
		    if (((c->att[j].type == XmATTACH_WIDGET) &&
			 (c->att[j].w == child))
			||
			((c->att[j].type == XmATTACH_OPPOSITE_WIDGET) &&
			 (c->att[j].w == child))) {
			switch (j)
			    {
			    case LEFT:
				c->att[j].type = XmATTACH_FORM;
				c->att[j].offset = w->core.x;
				break;
			    case TOP:
				c->att[j].type = XmATTACH_FORM;
				c->att[j].offset = w->core.y;
				break;
			    default:
				c->att[j].type = XmATTACH_NONE;
				break;
			    }
			c->att[j].w = NULL;
		    }
		}
	    }
	}
    }
    
    SortChildren (fw);
    
    /* Don't use XtRealizedWidget(form) as a test to initialize the
       preferred geometry, since when you realize the form before its
       kid, everything goes to the ground.
       Here we initialize a field if it hasn't been done already,
       the XmINVALID_DIMENSION has been set in ConstraintInitialize */
    
    for (i = 0; i < fw->composite.num_children; i++) {
	child = fw->composite.children[i];
	c = GetFormConstraint(child);
	if (c->preferred_width == XmINVALID_DIMENSION)  
	    c->preferred_width = XtWidth(child);
	if (c->preferred_height == XmINVALID_DIMENSION) 
	    c->preferred_height = XtHeight(child);
    }
    
    if (!XtIsRealized((Widget)fw))
	{
	    /* First time through */
	    Dimension w = 0, h = 0;
	    
	    g.request_mode = 0;
	    g.width = (fw->core.width ? fw->core.width : 1);
	    g.height = (fw->core.height ? fw->core.height : 1);
	    
	    if (!XtWidth(fw) && XtHeight(fw))
		{
		    CalcFormSize(fw, &w, NULL);
		    g.width = w;
		    g.request_mode |= CWWidth;
		}
	    else if (XtWidth(fw) && !XtHeight(fw))
		{
		    CalcFormSize(fw, NULL, &h);
		    g.height = h;
		    g.request_mode |= CWHeight;
		}
	    else if (!XtWidth(fw) && !XtHeight(fw))
		{
		    CalcFormSize(fw, &w, &h);
		    g.width = w;
		    g.height = h;
		    g.request_mode |= (CWWidth | CWHeight);
		}

	    if (g.request_mode != 0) 
		_XmMakeGeometryRequest((Widget) fw, &g);
	    
	    PlaceChildren (fw, NULL, NULL);
	}
    else
	{
	    ChangeIfNeeded(fw, NULL, NULL);
	    PlaceChildren (fw, NULL, NULL);
	}
    
    fw->bulletin_board.old_width = fw->core.width;
    fw->bulletin_board.old_height = fw->core.height;
    fw->bulletin_board.old_shadow_thickness =
	fw->manager.shadow_thickness;
    
    XmeNavigChangeManaged((Widget) fw);
}                       




/************************************************************************
 *
 *  GetSize
 *	
 *
 ************************************************************************/
static void 
GetSize(
        XmFormWidget fw,
	XtWidgetGeometry * g,
	Widget w,
        XtWidgetGeometry *desired )
{
    Boolean grow_ok = fw->bulletin_board.resize_policy != XmRESIZE_NONE,
            shrink_ok = fw->bulletin_board.resize_policy == XmRESIZE_ANY;
    
    g->request_mode = 0;
    g->width = 0;
    g->height = 0;
    
    /* Compute the desired size of the form */
    if (CalcFormSizeWithChange(fw, &g->width, &g->height, w, desired)) {

	/* there is a change - check resize policy first */
	if ((g->width > fw->core.width && !grow_ok) ||
	    (g->width < fw->core.width && !shrink_ok) ||
	    (g->height > fw->core.height && !grow_ok) ||
	    (g->height < fw->core.height && !shrink_ok))
	    return ; /* exit with request_mode = 0 */
	
	if (g->width != fw->core.width) g->request_mode |= CWWidth;
	if (g->height != fw->core.height) g->request_mode |= CWHeight; 
    }

}

/************************************************************************
 *
 *  ChangeIfNeeded
 *	Returns whether to honor widget w's resize request; only returns 
 *      False if resize would require form to change size but it cannot.
 *	Form changes size as a side effect.
 *
 ************************************************************************/
static void
ChangeIfNeeded(
        XmFormWidget fw,
        Widget w,
        XtWidgetGeometry *desired )
{
    XtWidgetGeometry g;

    /* find out the desired size of the form, using the 
       children attachment and the resizePolicy */
    GetSize(fw, &g, w, desired);
    
    _XmMakeGeometryRequest((Widget)fw, &g) ;
    
    if ( fw->bulletin_board.old_shadow_thickness &&
	(fw->bulletin_board.old_width != fw->core.width ||
	 fw->bulletin_board.old_height != fw->core.height) )
	{
	    _XmClearShadowType ((Widget) fw, fw->bulletin_board.old_width,
				fw->bulletin_board.old_height,
				fw->bulletin_board.old_shadow_thickness, 0);
	}
    
    fw->bulletin_board.old_width = fw->core.width;
    fw->bulletin_board.old_height = fw->core.height;
    fw->bulletin_board.old_shadow_thickness = fw->manager.shadow_thickness;
}



/************************************************************************
 *
 *  DeleteChild
 *	Delete a single widget from a parent widget
 *
 ************************************************************************/
static void 
DeleteChild(
        Widget child )
{
    XtWidgetProc delete_child;

    if (!XtIsRectObj(child)) return;

    _XmProcessLock();
    delete_child = ((CompositeWidgetClass) xmFormClassRec.core_class.superclass)->
			    composite_class.delete_child;
    _XmProcessUnlock();

    (*delete_child)(child);

    SortChildren((XmFormWidget) XtParent(child));
}




/************************************************************************
 *
 *  SetValues
 *
 ************************************************************************/
/*ARGSUSED*/
static Boolean 
SetValues(
        Widget cw,
        Widget rw,
        Widget nw,
        ArgList args,		/* unused */
        Cardinal * num_args )	/* unused */
{
        XmFormWidget old = (XmFormWidget) cw ;
        XmFormWidget new_w = (XmFormWidget) nw ;

	Boolean returnFlag = FALSE;
	Dimension w = 0;
	Dimension h = 0;


	/*  Check for invalid fraction base  */

	if (new_w->form.fraction_base == 0)
	{
		XmeWarning( (Widget) new_w, MESSAGE1);
		new_w->form.fraction_base = old->form.fraction_base;
	}

	if (XtIsRealized((Widget)new_w))
	{
	    /* SetValues of width and/or height = 0, signals the form to  */
	    /* recompute its bounding box and grow if it needs.           */

		if ((XtWidth(new_w) != XtWidth(old)) ||
			(XtHeight(new_w) != XtHeight(old))) 
		{
			if ((XtWidth(new_w) == 0) || (XtHeight(new_w) == 0))
			{
				CalcFormSize (new_w, &w, &h);
				if (XtWidth(new_w) == 0) XtWidth(new_w) = w;
				if (XtHeight(new_w) == 0) XtHeight(new_w) = h;

			} 
			else
			{
				w = XtWidth(new_w);
				h = XtHeight(new_w);
			}
		}


		/*  If default distance has changed, or the
			fraction base has changed, recalculate size.  */

		if ((new_w->form.horizontal_spacing !=
				old->form.horizontal_spacing)   ||
			(new_w->bulletin_board.margin_width != 
				old->bulletin_board.margin_width)     ||
			(new_w->form.vertical_spacing != 
				old->form.vertical_spacing)     ||
			(new_w->bulletin_board.margin_height != 
				old->bulletin_board.margin_height)     ||
			(new_w->form.fraction_base != 
				old->form.fraction_base))
		{
			CalcFormSize(new_w, &w, &h);
			XtWidth(new_w) = w;
			XtHeight(new_w) = h;
		}
	}
	
	return(returnFlag);
}

/*ARGSUSED*/
static void 
SetValuesAlmost(
        Widget cw,		/* unused */
        Widget nw,
        XtWidgetGeometry *req,
        XtWidgetGeometry *rep )
{
	XmFormWidget new_w = (XmFormWidget) nw ;

	if (!rep->request_mode)
		PlaceChildren(new_w, NULL, NULL);

	*req = *rep;
}




/************************************************************************
 *
 *  ConstraintSetValues
 *	If any values change, what we do is place everything again.
 *
 ************************************************************************/
/*ARGSUSED*/
static Boolean 
ConstraintSetValues(
        register Widget old,
        register Widget ref,	/* unused */
        register Widget new_w,
        ArgList args,		/* unused */
        Cardinal * num_args )	/* unused */
{
    XmFormWidget fw = (XmFormWidget) XtParent(new_w);
    register XmFormConstraint oldc, newc;
    register int i;
    
    if (!XtIsRectObj(new_w))
	return(FALSE);
    
    oldc = GetFormConstraint(old),
    newc = GetFormConstraint(new_w);
    
    if (XtWidth(new_w) != XtWidth(old))
	newc->preferred_width = XtWidth(new_w);
    if (XtHeight(new_w) != XtHeight(old))
	newc->preferred_height = XtHeight(new_w);

    /*  Validate the attachement type.  */
    
    for (i = FIRST_ATTACHMENT; i < (LAST_ATTACHMENT + 1); i++) {
	if (newc->att[i].type != oldc->att[i].type) {
	    if(    !XmRepTypeValidValue( XmRID_ATTACHMENT,
					newc->att[i].type, new_w)    )
		{
		    newc->att[i].type = oldc->att[i].type;
		}
	}
	if ((newc->att[i].type == XmATTACH_WIDGET) ||
	    (newc->att[i].type == XmATTACH_OPPOSITE_WIDGET)) {

	    while ((newc->att[i].w) && !SIBLINGS(newc->att[i].w, new_w)) {
		newc->att[i].w = XtParent(newc->att[i].w);
	    }
	}
    }
    
    /* Re do the layout only if we have to */
    if ((XtIsRealized((Widget)fw)) && (XtIsManaged(new_w)) &&
	(ANY(type) || ANY(w) || ANY(percent) || ANY(offset)))
	{
	    XtWidgetGeometry g;
	    
	    g.request_mode = 0;
	    
	    if (XtWidth(new_w) != XtWidth(old))
		{
		    g.request_mode |= CWWidth;
		    g.width = XtWidth(new_w);
		}
	    
	    if (XtHeight(new_w) != XtHeight(old))
		{
		    g.request_mode |= CWHeight;
		    g.height = XtHeight(new_w);
		}
	    
	    if (XtBorderWidth(new_w) != XtBorderWidth(old))
		{
		    g.request_mode |= CWBorderWidth;
		    g.border_width = XtBorderWidth(new_w);
		}
	    
	    fw->form.processing_constraints = TRUE;
	    SortChildren(fw);
	    ChangeIfNeeded(fw, new_w, &g);
	    PlaceChildren(fw, new_w, &g);
	    new_w->core.x ++ ; /* Force a call to the GeometryManager.
				  There are cases where a change in
				  constraints does not result in a change
				  in the geometry of new_w. As a result, 
				  processing_constraint stays True and
				  everything is screwed up... 
				  Note that this change in x is only
				  temporary since Xt will reset it to
				  its old value before calling the GM */
	}
    
    return (False);
}




/************************************************************************
 *
 *  Initialize
 *	The form widget specific initialization.
 *
 ************************************************************************/
/*ARGSUSED*/
static void 
Initialize(
        Widget rw,		/* unused */
        Widget nw,
        ArgList args,		/* unused */
        Cardinal * num_args )	/* unused */
{
        XmFormWidget new_w = (XmFormWidget) nw ;

	new_w->form.first_child = NULL;


	if (new_w->form.fraction_base == 0)
	{
		new_w->form.fraction_base = 100;
		XmeWarning( (Widget) new_w, MESSAGE1);
	}

	new_w->form.processing_constraints = FALSE;

	/* Set up for shadow drawing */

	new_w->bulletin_board.old_width = XtWidth(new_w);
	new_w->bulletin_board.old_height = XtHeight(new_w);
	new_w->bulletin_board.old_shadow_thickness =
		new_w->manager.shadow_thickness;
}

/************************************************************************
 *
 *  ConstraintInitialize
 *
 ************************************************************************/
/*ARGSUSED*/
static void 
ConstraintInitialize(
        Widget req,		/* unused */
        Widget new_w,
        ArgList args,		/* unused */
        Cardinal * num_args )	/* unused */
{
    XmFormConstraint nc;
    register int i;
    
    if (!XtIsRectObj(new_w)) return;
    
    nc = GetFormConstraint(new_w);
    
    /*  Validate the attachement type.  */
    
    for (i = FIRST_ATTACHMENT; i < (LAST_ATTACHMENT + 1); i++){
	if(!XmRepTypeValidValue( XmRID_ATTACHMENT, nc->att[i].type, new_w))
	    {
		nc->att[i].type = XmATTACH_NONE;
	    }
	if ((nc->att[i].type == XmATTACH_WIDGET) ||
	    (nc->att[i].type == XmATTACH_OPPOSITE_WIDGET)) {
	    
	    while ((nc->att[i].w) && !SIBLINGS(nc->att[i].w, new_w)) {
		nc->att[i].w = XtParent(nc->att[i].w);
	    }
	}
	nc->att[i].value = nc->att[i].tempValue = 0 ;
    }
    
    /* set the preferred geometry to some magic value that will help
       us find in ChangeManaged that it's the first time this kid is
       going thru layout */
    /* this code used to set the current geometry as preferred, 
       I don't see why it was needed, since the preferred field are 
       always used after the changemanaged is called */
    nc->preferred_width = XmINVALID_DIMENSION;
    nc->preferred_height = XmINVALID_DIMENSION;
}




/************************************************************************
 *
 *  CheckConstraints
 *
 ************************************************************************/
static void 
CheckConstraints(
        Widget w )
{
  XmFormConstraint c = GetFormConstraint(w);
  XmFormWidget fw = (XmFormWidget) XtParent(w);
  XmFormAttachment left = &c->att[LEFT], right = &c->att[RIGHT],
  top = &c->att[TOP], bottom = &c->att[BOTTOM] ;
  XmFormAttachment a;
  int which;
  int wid, ht;
  
  if (left->type == XmATTACH_NONE && right->type == XmATTACH_NONE) 
    {
      if (fw->form.rubber_positioning) 
	left->type = right->type = XmATTACH_SELF;
      else
	{
	  left->type = XmATTACH_FORM;
	  left->offset = w->core.x;
	}
    }
  
  if (top->type == XmATTACH_NONE && bottom->type == XmATTACH_NONE)
    {
      if (fw->form.rubber_positioning) 
	top->type = bottom->type = XmATTACH_SELF;
      else 
	{
	  top->type = XmATTACH_FORM;
	  top->offset = w->core.y;
	}
    }
  
  for (which = FIRST_ATTACHMENT; which < (LAST_ATTACHMENT + 1); which++) 
    {
      a = &c->att[which];
      
      switch (a->type) 
	{
	case XmATTACH_NONE:
	case XmATTACH_FORM:
	case XmATTACH_OPPOSITE_FORM:
	  a->w = NULL;
	  a->percent = 0;
	  break;
	  
	case XmATTACH_SELF:
	  a->offset = 0;
	  a->w = NULL;
	  a->type = XmATTACH_POSITION;
	  a->percent = 0;	/* default in case wid or ht are 0, although 
				   behavior in that case is poorly defined */
  	  
	  wid = w->core.x + w->core.width
	    + (2 * w->core.border_width);
	  ht = w->core.y + w->core.height
	    + (2 * w->core.border_width);
	  
	  if (wid < fw->core.width)
	    wid = fw->core.width;
	  if (ht  < fw->core.height)
	    ht  = fw->core.height;
	  
	  switch (which)
	    {
	    case LEFT:
		if (wid != 0)
		    a->percent = (w->core.x
				  * fw->form.fraction_base) / wid;
		break;
	      
	    case TOP:
		if (ht != 0)
		    a->percent = (w->core.y
				  * fw->form.fraction_base) / ht;
		break;
	      
	    case RIGHT:
		if (wid != 0)
		    a->percent = ((w->core.x + w->core.width +
				   2 * w->core.border_width) *
				  fw->form.fraction_base) / wid;
		break;
	      
	    case BOTTOM:
		if (ht != 0)
		    a->percent = ((w->core.y + w->core.height +
				   2 * w->core.border_width) *
				  fw->form.fraction_base) / ht;
		break;
	    }
	  break;
	  
	case XmATTACH_POSITION:
	  a->w = NULL;
	  break;
	  
	case XmATTACH_WIDGET:
	case XmATTACH_OPPOSITE_WIDGET:
	  a->percent = 0;
	  break;
	}
    }
}




/************************************************************************
 *
 *  SortChildren
 *
 ************************************************************************/
static void 
SortChildren(
        register XmFormWidget fw )
{
	int i, j;
	Widget child = NULL;
	register XmFormConstraint c = NULL, c1 = NULL;
	int sortedCount = 0;
	Widget last_child, att_widget;
	Boolean sortable;


	fw->form.first_child = NULL;

	for (i = 0; i < fw->composite.num_children; i++)
	{
		child = fw->composite.children[i];
		if (!XtIsRectObj(child))
			continue;
		c = GetFormConstraint(child);

		if (XtIsManaged(child))
		{
			c->sorted = False;
			c->next_sibling = NULL;
		}
		else
		{	
			c->next_sibling = fw->form.first_child;
			fw->form.first_child = child;
			c->sorted = True;
			sortedCount++;
		}

		CheckConstraints(child);
	}


	/* THIS IS PROBABLY WRONG AND SHOULD BE FIXED SOMEDAY             */
	/* WHY SHOULD UNMANAGED CHILDREN BE ALLOWED AS ATTACHMENT POINTS  */
	/* FOR MANAGED CHILDREN???                                        */

	/* While there are unsorted children, find one with only sorted  */
	/* predecessors and put it in the list.  This algorithm works    */
	/* particularly well if the order is already correct             */

	last_child = NULL;

	for ( ; sortedCount != fw->composite.num_children; sortedCount++) 
	{
		sortable = False;

		for (i = 0; !sortable && i < fw->composite.num_children; i++) 
		{
			child = fw->composite.children[i];
			if (!XtIsRectObj(child))
				continue;
			c = GetFormConstraint(child);

			if (c->sorted)
				continue;

			sortable = True;

			for (j = FIRST_ATTACHMENT; j < (LAST_ATTACHMENT + 1); j++) 
			{
				if ((c->att[j].type == XmATTACH_WIDGET) ||
				(c->att[j].type == XmATTACH_OPPOSITE_WIDGET))
				{	
					att_widget = c->att[j].w;

					if ((SIBLINGS(att_widget, child)) &&
						(XtIsRectObj(att_widget)))
					{
						c1 = GetFormConstraint (att_widget);
						if (!c1->sorted)
							sortable = False;
					}
				}
			}
		}

		if (sortable)
		{
			/*  We have found a sortable child...add to sorted list.  */

			if (last_child == NULL) 
			{
				c->next_sibling = fw->form.first_child;
				fw->form.first_child = child;
			}
			else
			{
				c1 = GetFormConstraint(last_child);
				c->next_sibling = c1->next_sibling;
				c1->next_sibling = child;
			}

			last_child = child;
			c->sorted = True;
		}

		else
		{
			/*  We failed to find a sortable child, there must be  */
			/*  a circular dependency.                             */ 

			XmeWarning( (Widget) fw, MESSAGE5);
			return;
		}
	}
}




/************************************************************************
 *
 *  CalcEdgeValues
 *
 ************************************************************************/
static void 
CalcEdgeValues(
        Widget w,
#if NeedWidePrototypes
        int really,
#else
        Boolean really,
#endif /* NeedWidePrototypes */
        Widget instigator,
        XtWidgetGeometry *inst_geometry,
        Dimension *form_width,
        Dimension *form_height )
{
	XmFormConstraint c = GetFormConstraint (w);
	XmFormWidget fw = (XmFormWidget) XtParent (w);
	XmFormAttachment left = &c->att[LEFT], right = &c->att[RIGHT],
		top = &c->att[TOP], bottom = &c->att[BOTTOM] ;
	Dimension width, height, border_width;

	if (w == instigator) 
	{ 
		if (inst_geometry->request_mode & CWWidth)
			width = inst_geometry->width; 
		else
			width = w->core.width;
		if (inst_geometry->request_mode & CWHeight)
			height = inst_geometry->height; 
		else
			height = w->core.height;
		if (inst_geometry->request_mode & CWBorderWidth)
			border_width = inst_geometry->border_width;
		else
			border_width = w->core.border_width;
	}
	else if (!fw->form.processing_constraints)
	{
		/*
		* If we just use the widget's current geometry we will
		* effectively be grow only.  That would not be correct.
		*
		* Instead we will use our idea of the child's preferred
		* size.
		*/
		width = c->preferred_width;
		height = c->preferred_height;
		border_width = w->core.border_width;
	}
	else
	{
		width = w->core.width;
		height = w->core.height;
		border_width = w->core.border_width;
	}

	width += border_width * 2;
	height += border_width * 2;


	if (width <= 0) width = 1;
	if (height <= 0) height = 1;

	if (left->type != XmATTACH_NONE)
	{
		if (right->type != XmATTACH_NONE)	    /* LEFT and right are attached */
		{
			CalcEdgeValue(fw, w, width, border_width, 
				LEFT, really, form_width, form_height);
			CalcEdgeValue(fw, w, width, border_width, 
				RIGHT, really, form_width, form_height);
		}

		else 	/*  LEFT attached, compute right  */
		{
			CalcEdgeValue(fw, w, width, border_width, 
				LEFT, really, form_width, form_height);
			ComputeAttachment(fw, w, width, border_width, RIGHT, really,
			form_width, form_height);
		}
	} 
	else
	{
		if (right->type != XmATTACH_NONE)    /* RIGHT attached, compute left */
		{
		CalcEdgeValue(fw, w, width, border_width, 
			RIGHT, really, form_width, form_height);
		ComputeAttachment(fw, w, width, border_width, 
			LEFT, really, form_width, form_height);
		}
	}

	if (top->type != XmATTACH_NONE) 
	{
		if (bottom->type != XmATTACH_NONE)   /* TOP and bottom are attached */
		{
			CalcEdgeValue(fw, w, height, border_width, 
				TOP, really, form_width, form_height);
			CalcEdgeValue(fw, w, height, border_width, 
				BOTTOM, really, form_width, form_height);
		}
		else	/* TOP attached, compute bottom */
		{
			CalcEdgeValue(fw, w, height, border_width, 
				TOP, really, form_width, form_height);
			ComputeAttachment(fw, w, height, border_width, 
				BOTTOM, really, form_width, form_height);
		}
	}
	else
	{
		if (bottom->type != XmATTACH_NONE)   /* BOTTOM attached, compute top */
		{
			CalcEdgeValue(fw, w, height, border_width, 
				BOTTOM, really, form_width, form_height);
			ComputeAttachment(fw, w, height, border_width, 
				TOP, really, form_width, form_height);
		} 
	}
}



/*********************************************************************
 *
 * CheckBottomBase
 *
 *********************************************************************/
static float 
CheckBottomBase(
        Widget sibling,
#if NeedWidePrototypes
        int opposite )
#else
        Boolean opposite )
#endif /* NeedWidePrototypes */
{
	XmFormWidget fw = (XmFormWidget) sibling->core.parent;
	XmFormConstraint c = GetFormConstraint(sibling);
	Boolean flag = FALSE;
	float return_val;

	if (!opposite)
	{
		switch (c->att[TOP].type)
		{
			case XmATTACH_POSITION:
				return_val = (float) c->att[TOP].percent /
				    (float) fw->form.fraction_base ;
			break;
			case XmATTACH_NONE:
				switch (c->att[BOTTOM].type)
				{
					case XmATTACH_FORM:
						return_val = 1.0;
					break;
					case XmATTACH_POSITION:
						return_val = (float) c->att[BOTTOM].percent /
							(float) fw->form.fraction_base ;
					break;
					case XmATTACH_OPPOSITE_WIDGET:
						flag = TRUE;
					case XmATTACH_WIDGET:
						if (SIBLINGS(c->att[BOTTOM].w, sibling))
							return_val = 
								CheckBottomBase(c->att[BOTTOM].w, flag);
						else
						{
							if (flag)
								return_val = 0.0;
							else
								return_val = 1.0;
						}
					break;
					default:
						return_val = 0.0;
					break;
				}
			break;
			case XmATTACH_OPPOSITE_FORM:
				return_val = 1.0;
			break;
			default:
				return_val = 0.0;
			break;
		}
	}
	else
	{
		switch(c->att[BOTTOM].type)
		{
			case XmATTACH_NONE:
				if (c->att[TOP].type == XmATTACH_POSITION)
					return_val = (float) c->att[TOP].percent /
						(float) fw->form.fraction_base;
				else
					return_val = 0.0;
			break;
			case XmATTACH_POSITION:
				return_val = (float) c->att[BOTTOM].percent /
				    (float) fw->form.fraction_base;
			break;
			case XmATTACH_OPPOSITE_WIDGET:
				flag = TRUE;
			case XmATTACH_WIDGET:
				if (SIBLINGS(c->att[BOTTOM].w, sibling))
					return_val = 
						CheckBottomBase(c->att[BOTTOM].w, flag);
				else
				{
					if (flag)
						return_val = 0.0;
					else
						return_val = 1.0;
				}
			break;
			case XmATTACH_FORM:
				return_val = 1.0;
			break;
			default:
				return_val = 0.0;
			break;
		}
	}

	return(return_val);
}


/*********************************************************************
 *
 * CheckRightBase
 *
 *********************************************************************/
static float 
CheckRightBase(
        Widget sibling,
#if NeedWidePrototypes
        int opposite )
#else
        Boolean opposite )
#endif /* NeedWidePrototypes */
{
	XmFormWidget fw = (XmFormWidget) sibling->core.parent;
	XmFormConstraint c = GetFormConstraint(sibling);
	Boolean flag = FALSE;
	float return_val;

	if (!opposite)
	{
		switch (c->att[LEFT].type)
		{
			case XmATTACH_POSITION:
				return_val =  (float) c->att[LEFT].percent /
				    (float) fw->form.fraction_base;
			break;
			case XmATTACH_NONE:
				switch (c->att[RIGHT].type)
				{
					case XmATTACH_FORM:
						return_val = 1.0;
					break;
					case XmATTACH_POSITION:
						return_val = (float) c->att[RIGHT].percent /
							(float) fw->form.fraction_base ;
					break;
					case XmATTACH_OPPOSITE_WIDGET:
						flag = TRUE;
					case XmATTACH_WIDGET:
						if (SIBLINGS(c->att[RIGHT].w, sibling))
							return_val = 
								CheckRightBase(c->att[RIGHT].w, flag);
						else
						{
							if (flag)
								return_val =  0.0;
							else
								return_val = 1.0;
						}
					break;
					default:
						return_val =  0.0;
					break;
				}
			break;
			case XmATTACH_OPPOSITE_FORM:
				return_val = 1.0;
			break;
			default:
				return_val = 0.0;
			break;
		}
	}
	else
	{
		switch(c->att[RIGHT].type)
		{
			case XmATTACH_NONE:
				if (c->att[LEFT].type == XmATTACH_POSITION)
					return_val = (float) c->att[LEFT].percent /
						(float) fw->form.fraction_base ;
				else
					return_val = 0.0;
			break;
			case XmATTACH_POSITION:
				return_val = (float) c->att[RIGHT].percent /
				    (float) fw->form.fraction_base;
			break;
			case XmATTACH_OPPOSITE_WIDGET:
				flag = TRUE;
			case XmATTACH_WIDGET:
				if (SIBLINGS(c->att[RIGHT].w, sibling))
					return_val = 
						CheckRightBase(c->att[RIGHT].w, flag);
				else
				{
					if (flag)
						return_val =  0.0;
					else
						return_val = 1.0;
				}
			break;
			case XmATTACH_FORM:
				return_val = 1.0;
			break;
			default:
				return_val = 0.0;
			break;
		}
	}

	return(return_val);
}

/*********************************************************************
 *
 * CheckLeftBase
 *
 *********************************************************************/
static float 
CheckLeftBase(
        Widget sibling,
#if NeedWidePrototypes
        int opposite )
#else
        Boolean opposite )
#endif /* NeedWidePrototypes */
{
	XmFormWidget fw = (XmFormWidget) sibling->core.parent;
	XmFormConstraint c = GetFormConstraint(sibling);
	Boolean flag = FALSE;
	float return_val;

	if (!opposite)
	{
		switch (c->att[RIGHT].type)
		{
			case XmATTACH_POSITION:
				return_val =  (float) c->att[RIGHT].percent /
				    (float) fw->form.fraction_base;
			break;
			case XmATTACH_NONE:
				switch (c->att[LEFT].type)
				{
					case XmATTACH_FORM:
						return_val = 1.0;
					break;
					case XmATTACH_POSITION:
						return_val = (float) c->att[LEFT].percent /
							(float) fw->form.fraction_base ;
					break;
					case XmATTACH_OPPOSITE_WIDGET:
						flag = TRUE;
					case XmATTACH_WIDGET:
						if (SIBLINGS(c->att[LEFT].w, sibling))
							return_val = 
								CheckLeftBase(c->att[LEFT].w, flag);
						else
						{
							if (flag)
								return_val =  0.0;
							else
								return_val = 1.0;
						}
					break;
					default:
						return_val =  0.0;
					break;
				}
			break;
			case XmATTACH_OPPOSITE_FORM:
				return_val = 1.0;
			break;
			default:
				return_val = 0.0;
			break;
		}
	}
	else
	{
		switch(c->att[LEFT].type)
		{
			case XmATTACH_NONE:
				if (c->att[RIGHT].type == XmATTACH_POSITION)
					return_val = (float) c->att[RIGHT].percent /
						(float) fw->form.fraction_base ;
				else
					return_val = 0.0;
			break;
			case XmATTACH_POSITION:
				return_val = (float) c->att[LEFT].percent /
				    (float) fw->form.fraction_base;
			break;
			case XmATTACH_OPPOSITE_WIDGET:
				flag = TRUE;
			case XmATTACH_WIDGET:
				if (SIBLINGS(c->att[LEFT].w, sibling))
					return_val = 
						CheckLeftBase(c->att[LEFT].w, flag);
				else
				{
					if (flag)
						return_val =  0.0;
					else
						return_val = 1.0;
				}
			break;
			case XmATTACH_FORM:
				return_val = 1.0;
			break;
			default:
				return_val = 0.0;
			break;
		}
	}

	return(return_val);
}

/*********************************************************************
 *
 *  CalcEdgeValue
 *     Note that Left attachment apply to the right side in a 
 *     Right-to-Left environment. Slightly confusing, but it is more a
 *     matter of switching right and left in the computing.
 *
 *********************************************************************/
/*ARGSUSED*/
static void 
CalcEdgeValue(
        XmFormWidget fw,
        Widget w,
#if NeedWidePrototypes
        int size,
        int border_width,	/* unused */
#else
        Dimension size,
        Dimension border_width,	/* unused */
#endif /* NeedWidePrototypes */
        int which,
#if NeedWidePrototypes
        int really,
#else
        Boolean really,
#endif /* NeedWidePrototypes */
        Dimension *fwidth,
        Dimension *fheight )
{
  float scale;
  XmFormAttachment att = GetFormConstraint(w)->att;
  XmFormAttachment a = att + which;
  XmFormConstraint c;
  XmFormAttachment ref;
  float factor;
  int temp1, temp2, temp3;
  int ctype;
  
  
  ctype = a->type;
  
  if ((ctype == XmATTACH_WIDGET) && !(SIBLINGS(a->w, w)))
    ctype = XmATTACH_FORM;
  
  if ((ctype == XmATTACH_OPPOSITE_WIDGET) && !(SIBLINGS(a->w, w)))
    ctype = XmATTACH_OPPOSITE_FORM;
  
  if (LayoutIsRtoLM(fw)) 
    switch (ctype) 
      {
      case XmATTACH_FORM:
	a->w = NULL;
	a->percent = 0;
	
	switch (which) 
	  {
	  case RIGHT:
	  case TOP:
	    AssignValue(a, GetFormOffset(fw, which, att));
	    break;
	    
	  case LEFT:
	    if (fwidth != NULL) {
	      if ((att + RIGHT)->type == XmATTACH_NONE) {
		temp1 = *fwidth - (GetFormOffset(fw, which, att));
		temp2 = temp1 - size;
		if (temp2 < 0) {
		  *fwidth += abs(temp2);
		  AssignValue(a, (temp1 + abs(temp2)));
		} else
		  AssignValue(a, temp1);
	      } else {
		temp1 = *fwidth - (GetFormOffset(fw, which, att));
		temp2 = Value(att + RIGHT);
		temp3 = temp1 - temp2 - size;
		if (temp3 < 0) {
		  *fwidth += abs(temp3);
		  AssignValue(a, (*fwidth - size - GetFormOffset(fw, which, att)));
		} else
		  AssignValue(a, temp1);
	      }
	    } else
	      AssignValue(a, fw->core.width - GetFormOffset(fw,which,att));
	    break;
	  case BOTTOM:
	    if (fheight != NULL) {
	      if ((att + TOP)->type == XmATTACH_NONE) {
		temp1 = *fheight - GetFormOffset(fw, which, att);
		temp2 = temp1 - size;
		if (temp2 < 0) {
		  *fheight += abs(temp2);
		  AssignValue(a, (temp1 + abs(temp2)));
		} else
		  AssignValue(a, temp1);
	      } else {
		temp1 = *fheight - GetFormOffset(fw, which, att);
		temp2 = Value(att + TOP);
		temp3 = temp1 - temp2 - size;
		if (temp3 < 0) {
		  *fheight += abs(temp3);
		  AssignValue(a, (*fheight - size - GetFormOffset(fw, which, att)));
		} else
		  AssignValue(a, temp1);
	      }
	    } else
	      AssignValue(a, fw->core.height - GetFormOffset(fw,which,att));
	    break;
	  }
	return;
	
      case XmATTACH_OPPOSITE_FORM:
	a->w = NULL;
	a->percent = 0;
	
	switch (which) 
	  {
	  case RIGHT:
	    if (fwidth) {
	      temp1 = *fwidth + GetFormOffset(fw, which, att);
	      if (temp1 < 0) {
		*fwidth += abs(temp1);
		temp1 = 0;
	      }
	      AssignValue(a, temp1);
	    } else
	      AssignValue(a, fw->core.width + GetFormOffset(fw, which, att));
	    break;
	    
	  case TOP:
	    if (fheight) {
	      temp1 = *fheight + GetFormOffset(fw, which, att);
	      if (temp1 < 0) {
		*fheight += abs(temp1);
		temp1 = 0;
	      }
	      AssignValue(a, temp1);
	    } else
	      AssignValue(a, fw->core.height + GetFormOffset(fw, which, att));
	    break;
	    
	  case LEFT:
	  case BOTTOM:
	    AssignValue(a, -(GetFormOffset(fw, which, att)));
	    break;
	  }
	return;
	
      case XmATTACH_POSITION:
	scale = ((float) a->percent) / fw->form.fraction_base;
	a->w = 0;
	
	switch (which) 
	  {
	  case RIGHT:
	    scale = 1.0 - scale;
	    if (fwidth != NULL)
	      AssignValue(a, (*fwidth * scale + 0.5) + 
			  GetFormOffset(fw, which, att));
	    else
	      AssignValue(a, (fw->core.width * scale + 0.5) +
			  GetFormOffset(fw, which, att));
	    break;
	  case TOP:
	    if (fheight != NULL)
	      AssignValue(a, (*fheight * scale + 0.5) + 
			  GetFormOffset(fw, which, att));
	    else
	      AssignValue(a, (fw->core.height * scale + 0.5) + 
			  GetFormOffset(fw, which, att));
	    break;
	  case LEFT:
	    scale = 1.0 - scale;
	    if (fwidth != NULL)
	      if ((att + RIGHT)->type != XmATTACH_NONE) {
		temp1 = (int) (((*fwidth  * scale) + 0.5) - 
			       GetFormOffset(fw, which, att));
		temp2 = Value(att + RIGHT);
		temp3 = temp1 - temp2 - size;
		if (temp3 < 0) {
		  if (!scale)
		    scale = 1.0;

		  *fwidth += (Dimension) (((1.0 / scale) * abs(temp3)) + 0.5);
		}
		AssignValue(a, (temp2 + size));
	      } else
		AssignValue(a, (*fwidth * scale + 0.5) - 
			    GetFormOffset(fw, which, att));
	    else
	      AssignValue(a, (fw->core.width * scale + 0.5) - 
			  GetFormOffset(fw, which, att));
	    break;
	  case BOTTOM:
	    if (fheight != NULL)
	      if ((att + TOP)->type != XmATTACH_NONE) {
		temp1 = (int) (((*fheight  * scale) + 0.5) 
			       - GetFormOffset(fw, which, att));
		temp2 = Value(att + TOP);
		temp3 = temp1 - temp2 - size;
		if (temp3 < 0) {
		  if (!scale)
		    scale = 1.0;
		  
		  *fheight += (Dimension) (((1.0 / scale) * abs(temp3)) + 0.5);
		}
		AssignValue(a, (temp2 + size));
	      } else
		AssignValue(a, (*fheight * scale + 0.5) - 
			    GetFormOffset(fw, which, att));
	    else
	      AssignValue(a, (fw->core.height * scale + 0.5) - 
			  GetFormOffset(fw, which, att));
	  }
	return;
	
      case XmATTACH_WIDGET:
	a->percent = 0;
	c = GetFormConstraint(a->w);
	
	switch (which)
	  {
	  case RIGHT:
	    ref = &c->att[LEFT];
	    AssignValue(a, Value(ref) + GetFormOffset(fw, which, att));
	    break;
	    
	  case TOP:
	    ref = &c->att[BOTTOM];
	    AssignValue(a, Value(ref) + GetFormOffset(fw, which, att));
	    break;
	    
	  case LEFT:
	    ref = &c->att[RIGHT];
	    if (att[RIGHT].type != XmATTACH_NONE) {
	      temp1 = Value(ref) - GetFormOffset(fw, which, att);
	      temp2 = temp1 - Value(&(att[RIGHT]));
	      temp3 = temp2 - size;
	      if ((fwidth) && (temp3 < 0)) {
		factor = CheckLeftBase(a->w, FALSE);
		*fwidth += (Dimension) ((factor * abs(temp3)) + 0.5);
		temp1 = Value(&(att[RIGHT])) + size;
	      }
	      AssignValue(a, temp1);
	    } else {
	      temp1 = Value(ref) - GetFormOffset(fw, which, att);
	      AssignValue(a, temp1);
	    }
	    break;
	    
	  case BOTTOM:
	    ref = &c->att[TOP];
	    if (att[TOP].type != XmATTACH_NONE) {
	      temp1 = Value(ref) - GetFormOffset(fw, which, att);
	      temp2 = temp1 - Value((&att[TOP]));
	      temp3 = temp2 - size;
	      if ((fheight) && (temp3 < 0)) {
		factor = CheckBottomBase(a->w, FALSE);
		*fheight += (Dimension) ((factor * abs(temp3)) + 0.5);
		temp1 = Value((&att[TOP])) + size;
	      }
	      AssignValue(a, temp1);
	    } else {
	      temp1 = Value(ref) - GetFormOffset(fw, which, att);
	      AssignValue(a, temp1);
	    }
	    break;
	  }
	return;
	
      case XmATTACH_OPPOSITE_WIDGET:
	a->percent = 0;
	c = GetFormConstraint(a->w);
	
	switch (which) 
	  {
	  case RIGHT:
	    ref = &c->att[RIGHT];
	    AssignValue(a, Value(ref) + GetFormOffset(fw, which, att));
	    break;
	    
	  case TOP:
	    ref = &c->att[TOP];
	    AssignValue(a, Value(ref) + GetFormOffset(fw, which, att));
	    break;
	    
	  case LEFT:
	    ref = &c->att[LEFT];
	    AssignValue(a, Value(ref) - GetFormOffset(fw, which, att));
	    break;
	    
	  case BOTTOM:
	    ref = &c->att[BOTTOM];
	    AssignValue(a, Value(ref) - GetFormOffset(fw, which, att));
	    break;
	  }
	return;
      }      
  else /* Left to Right environment */
    switch (ctype) 
      {
      case XmATTACH_FORM:
	a->w = NULL;
	a->percent = 0;
	
	switch (which) 
	  {
	  case LEFT:
	  case TOP:
	    AssignValue(a, GetFormOffset(fw, which, att));
	    break;
	    
	  case RIGHT:
	    if (fwidth != NULL) {
	      if ((att + LEFT)->type == XmATTACH_NONE) {
		temp1 = *fwidth - (GetFormOffset(fw, which, att));
		temp2 = temp1 - size;
		if (temp2 < 0) {
		  *fwidth += abs(temp2);
		  AssignValue(a, (temp1 + abs(temp2)));
		} else
		  AssignValue(a, temp1);
	      } else {
		temp1 = *fwidth - (GetFormOffset(fw, which, att));
		temp2 = Value(att + LEFT);
		temp3 = temp1 - temp2 - size;
		if (temp3 < 0) {
		  *fwidth += abs(temp3);
		  AssignValue(a, (*fwidth - size - GetFormOffset(fw, which, att)));
		} else
		  AssignValue(a, temp1);
	      }
	    } else
	      AssignValue(a, fw->core.width - GetFormOffset(fw,which,att));
	    break;
	  case BOTTOM:
	    if (fheight != NULL) {
	      if ((att + TOP)->type == XmATTACH_NONE) {
		temp1 = *fheight - GetFormOffset(fw, which, att);
		temp2 = temp1 - size;
		if (temp2 < 0) {
		  *fheight += abs(temp2);
		  AssignValue(a, (temp1 + abs(temp2)));
		} else
		  AssignValue(a, temp1);
	      } else {
		temp1 = *fheight - GetFormOffset(fw, which, att);
		temp2 = Value(att + TOP);
		temp3 = temp1 - temp2 - size;
		if (temp3 < 0) {
		  *fheight += abs(temp3);
		  AssignValue(a, (*fheight - size - GetFormOffset(fw, which, att)));
		} else
		  AssignValue(a, temp1);
	      }
	    } else
	      AssignValue(a, fw->core.height - GetFormOffset(fw,which,att));
	    break;
	  }
	return;
	
      case XmATTACH_OPPOSITE_FORM:
	a->w = NULL;
	a->percent = 0;
	
	switch (which) 
	  {
	  case LEFT:
	    if (fwidth) {
	      temp1 = *fwidth + GetFormOffset(fw, which, att);
	      if (temp1 < 0) {
		*fwidth += abs(temp1);
		temp1 = 0;
	      }
	      AssignValue(a, temp1);
	    } else
	      AssignValue(a, fw->core.width + GetFormOffset(fw, which, att));
	    break;
	    
	  case TOP:
	    if (fheight) {
	      temp1 = *fheight + GetFormOffset(fw, which, att);
	      if (temp1 < 0) {
		*fheight += abs(temp1);
		temp1 = 0;
	      }
	      AssignValue(a, temp1);
	    } else
	      AssignValue(a, fw->core.height + GetFormOffset(fw, which, att));
	    break;
	    
	  case RIGHT:
	  case BOTTOM:
	    AssignValue(a, -(GetFormOffset(fw, which, att)));
	    break;
	  }
	return;
	
      case XmATTACH_POSITION:
	scale = ((float) a->percent) / fw->form.fraction_base;
	a->w = 0;
	
	switch (which) 
	  {
	  case LEFT:
	    if (fwidth != NULL)
	      AssignValue(a, (*fwidth * scale + 0.5) + GetFormOffset(fw, which, att));
	    else
	      AssignValue(a, (fw->core.width * scale + 0.5) +
			  GetFormOffset(fw, which, att));
	    break;
	  case TOP:
	    if (fheight != NULL)
	      AssignValue(a, (*fheight * scale + 0.5) + 
			  GetFormOffset(fw, which, att));
	    else
	      AssignValue(a, (fw->core.height * scale + 0.5) + 
			  GetFormOffset(fw, which, att));
	    break;
	  case RIGHT:
	    if (fwidth != NULL)
	      if ((att + LEFT)->type != XmATTACH_NONE) {
		temp1 = (int) (((*fwidth  * scale) + 0.5) - 
			       GetFormOffset(fw, which, att));
		temp2 = Value(att + LEFT);
		temp3 = temp1 - temp2 - size;
		if (temp3 < 0) {
		  if (!scale)
		    scale = 1.0;

		  *fwidth += (Dimension) (((1.0 / scale) * abs(temp3)) + 0.5);
		}
		AssignValue(a, (temp2 + size));
	      } else
		AssignValue(a, (*fwidth * scale + 0.5) - 
			    GetFormOffset(fw, which, att));
	    else
	      AssignValue(a, (fw->core.width * scale + 0.5) - 
			  GetFormOffset(fw, which, att));
	    break;
	  case BOTTOM:
	    if (fheight != NULL)
	      if ((att + TOP)->type != XmATTACH_NONE) {
		temp1 = (int) (((*fheight  * scale) + 0.5) 
			       - GetFormOffset(fw, which, att));
		temp2 = Value(att + TOP);
		temp3 = temp1 - temp2 - size;
		if (temp3 < 0) {
		  if (!scale)
		    scale = 1.0;
		  
		  *fheight += (Dimension) (((1.0 / scale) * abs(temp3)) + 0.5);
		}
		AssignValue(a, (temp2 + size));
	      } else
		AssignValue(a, (*fheight * scale + 0.5) - 
			    GetFormOffset(fw, which, att));
	    else
	      AssignValue(a, (fw->core.height * scale + 0.5) - 
			  GetFormOffset(fw, which, att));
	  }
	return;
	
      case XmATTACH_WIDGET:
	a->percent = 0;
	c = GetFormConstraint(a->w);
	
	switch (which)
	  {
	  case LEFT:
	    ref = &c->att[RIGHT];
	    AssignValue(a, Value(ref) + GetFormOffset(fw, which, att));
	    break;
	    
	  case TOP:
	    ref = &c->att[BOTTOM];
	    AssignValue(a, Value(ref) + GetFormOffset(fw, which, att));
	    break;
	    
	  case RIGHT:
	    ref = &c->att[LEFT];
	    if (att[LEFT].type != XmATTACH_NONE) {
	      temp1 = Value(ref) - GetFormOffset(fw, which, att);
	      temp2 = temp1 - Value(&(att[LEFT]));
	      temp3 = temp2 - size;
	      if ((fwidth) && (temp3 < 0)) {
		factor = CheckRightBase(a->w, FALSE);
		*fwidth += (Dimension) ((factor * abs(temp3)) + 0.5);
		temp1 = Value(&(att[LEFT])) + size;
	      }
	      AssignValue(a, temp1);
	    } else {
	      temp1 = Value(ref) - GetFormOffset(fw, which, att);
	      AssignValue(a, temp1);
	    }
	    break;
	    
	  case BOTTOM:
	    ref = &c->att[TOP];
	    if (att[TOP].type != XmATTACH_NONE) {
	      temp1 = Value(ref) - GetFormOffset(fw, which, att);
	      temp2 = temp1 - Value((&att[TOP]));
	      temp3 = temp2 - size;
	      if ((fheight) && (temp3 < 0)) {
		factor = CheckBottomBase(a->w, FALSE);
		*fheight += (Dimension) ((factor * abs(temp3)) + 0.5);
		temp1 = Value((&att[TOP])) + size;
	      }
	      AssignValue(a, temp1);
	    } else {
	      temp1 = Value(ref) - GetFormOffset(fw, which, att);
	      AssignValue(a, temp1);
	    }
	    break;
	  }
	return;
	
      case XmATTACH_OPPOSITE_WIDGET:
	a->percent = 0;
	c = GetFormConstraint(a->w);
	
	switch (which) 
	  {
	  case LEFT:
	    ref = &c->att[LEFT];
	    AssignValue(a, Value(ref) + GetFormOffset(fw, which, att));
	    break;
	    
	  case TOP:
	    ref = &c->att[TOP];
	    AssignValue(a, Value(ref) + GetFormOffset(fw, which, att));
	    break;
	    
	  case RIGHT:
	    ref = &c->att[RIGHT];
	    AssignValue(a, Value(ref) - GetFormOffset(fw, which, att));
	    break;
	    
	  case BOTTOM:
	    ref = &c->att[BOTTOM];
	    AssignValue(a, Value(ref) - GetFormOffset(fw, which, att));
	    break;
	  }
	return;
      }      
  
}




/************************************************************************
 *
 *  ComputeAttachment
 *     Note that Left attachment apply to the right side in a 
 *     Right-to-Left environment. Slightly confusing, but it is more a
 *     matter of switching right and left in the computing.
 *
 ************************************************************************/
/*ARGSUSED*/
static void 
ComputeAttachment(
        XmFormWidget fw,
        Widget w,
#if NeedWidePrototypes
        int size,
        int border_width,	/* unused */
#else
        Dimension size,
        Dimension border_width,	/* unused */
#endif /* NeedWidePrototypes */
        int which,
#if NeedWidePrototypes
        int really,
#else
        Boolean really,
#endif /* NeedWidePrototypes */
        Dimension *fwidth,
        Dimension *fheight )
{
   XmFormConstraint c = GetFormConstraint(w);
   XmFormAttachment a = &c->att[which];
   int temp;

   if (LayoutIsRtoLM(fw))
     switch (which) 
       {
       case RIGHT:
	 temp =  Value(&c->att[LEFT]) - size;
	 if ((fwidth != NULL) && (temp < 0))
	   {
	     *fwidth += abs(temp);
	     temp = 0;
	   }
         AssignValue(a,temp);
	 break;
	 
       case LEFT:
	 temp = Value(&c->att[RIGHT]) + size;
	 if ((fwidth != NULL) && (temp > 0) && (temp > *fwidth))
	   *fwidth += (temp - *fwidth);
         AssignValue(a,temp);
	 break;
	 
       case TOP:
	 temp = Value(&c->att[BOTTOM]) - size;
	 if ((fheight != NULL) && (temp < 0))
	   {
	     *fheight += abs(temp);
	     temp = 0;
	   }
         AssignValue(a, temp);
	 break;
	 
       case BOTTOM:
	 temp = Value(&c->att[TOP]) + size;
	 if ((fheight != NULL) && (temp > 0) && (temp > *fheight))
	   *fheight += (temp - *fheight);
         AssignValue(a, temp);
	 break;
       }
   else /* Left to right */
     switch (which) 
       {
       case LEFT:
	 temp =  Value(&c->att[RIGHT]) - size;
	 if ((fwidth != NULL) && (temp < 0))
	   {
	     *fwidth += abs(temp);
	     temp = 0;
	   }
	 AssignValue(a,temp);
	 break;
	 
       case RIGHT:
	 temp = Value(&c->att[LEFT]) + size;
	 if ((fwidth != NULL) && (temp > 0) && (temp > *fwidth))
	   *fwidth += (temp - *fwidth);
	 AssignValue(a,temp);
	 break;
	 
       case TOP:
	 temp = Value(&c->att[BOTTOM]) - size;
	 if ((fheight != NULL) && (temp < 0))
	   {
	     *fheight += abs(temp);
	     temp = 0;
	   }
	 AssignValue(a, temp);
	 break;
	 
       case BOTTOM:
	 temp = Value(&c->att[TOP]) + size;
	 if ((fheight != NULL) && (temp > 0) && (temp > *fheight))
	   *fheight += (temp - *fheight);
	 AssignValue(a, temp);
	 break;
       }
}




/************************************************************************
 *
 *  GetFormOffset
 *
 ************************************************************************/
static int 
GetFormOffset(
        XmFormWidget fw,
        int which,
        XmFormAttachment att )
{
    int o;
    
    o = att[which].offset;
    
    if (o == XmINVALID_DIMENSION) {
	switch (att[which].type) {
	case XmATTACH_NONE:
	case XmATTACH_SELF:
	case XmATTACH_POSITION:
	    o = 0;
	    break;
	    
	case XmATTACH_FORM:
	case XmATTACH_OPPOSITE_FORM:
	    if ((which == LEFT) || (which == RIGHT))
		{
		    if (fw->bulletin_board.margin_width
			== XmINVALID_DIMENSION)
			o = fw->form.horizontal_spacing;
		    else
			o = fw->bulletin_board.margin_width;
		}
	    else
		{
		    if (fw->bulletin_board.margin_height 
			== XmINVALID_DIMENSION)
			o = fw->form.vertical_spacing;
		    else
			o = fw->bulletin_board.margin_height;
		}
	    break;
	    
	case XmATTACH_WIDGET:
	case XmATTACH_OPPOSITE_WIDGET:
	    if ((which == LEFT) || (which == RIGHT))
		o = fw->form.horizontal_spacing;
	    else
		o = fw->form.vertical_spacing;
	    break;
	}
    }
    
    return o;
}




/************************************************************************
 *
 *		Application Accessible External Functions
 *
 ************************************************************************/


/************************************************************************
 *
 *  XmCreateForm
 *	Create an instance of a form and return the widget id.
 *
 ************************************************************************/
Widget 
XmCreateForm(
        Widget parent,
        char *name,
        ArgList arglist,
        Cardinal argcount )
{
   return(XtCreateWidget(name, xmFormWidgetClass, parent, arglist, argcount));
}




/************************************************************************
 *
 *  XmCreateFormDialog
 *	Create an instance of a form dialog and return the widget id.
 *
 ************************************************************************/
Widget 
XmCreateFormDialog(
        Widget parent,
        char *name,
        ArgList arglist,
        Cardinal argcount )
{
   return XmeCreateClassDialog (xmFormWidgetClass,
				parent, name, arglist, argcount) ;
}
