/* $XConsortium: CxxGrid.cxx /main/4 1995/07/17 11:45:15 drk $ */
/*
 *  @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
 */

/********************************** WARNING **********************************
 *
 * ExmGrid is a demonstration widget.  OSF provides this widget
 * solely to teach programmers how to write their own Motif widgets.
 * OSF does not support this widget in any way
 *********************************** WARNING *********************************/


/******************************************************************************
 *
 * Grid.c - ExmGrid widget.  This widget manages its children as an MxN matrix.
 *          The ExmGrid widget demonstrates how to 
 *             * create a relatively easy Motif manager widget.
 *             * install and use the XmQTspecifyRenderTable trait.
 *             * install and use the XmQTdialogShellSavvy trait. 
 *          See the "OSF/Motif Widget Writer's Guide" for details.
 *
******************************************************************************/

/* Include appropriate header files. */
#include "CxxGrid.h"
#include <Xm/GadgetP.h> /* for gadget management functions */
#include <Xm/TraitP.h>  /* for trait access functions */
#include <Xm/DialogSavvyT.h> /* for XmQTdialogSavvy trait */
#include <Xm/SpecRenderT.h> /* for XmQTspecifyRenderTable trait */

/* Define macros. */
#define Max(x, y) (((x) > (y)) ? (x) : (y))
#define WARNING_TOO_MANY_ROWS "Too many rows specified for ExmCxxGrid.\n" 
#define WARNING_TOO_MANY_COLUMNS "Too many columns specified for ExmCxxGrid.\n"

static XtResource resources[] =
{
    {
	XmNrows, 
	XmCRows, 
	XmRShort, 
	sizeof (short),
	XtOffsetOf(ExmCxxGrid, grid.rows),
	XmRImmediate, (XtPointer) 4
    },
    {
	XmNcolumns, 
	XmCColumns, 
	XmRShort, 
	sizeof (short),
	XtOffsetOf(ExmCxxGrid, grid.columns),
	XmRImmediate, (XtPointer) 4
    },
    {
	XmNmarginWidth, 
	XmCMarginWidth, 
	XmRHorizontalDimension, 
	sizeof (Dimension),
	XtOffsetOf(ExmCxxGrid, grid.margin_width),
	XmRImmediate, (XtPointer) 10
    },
    {
	XmNmarginHeight, 
	XmCMarginHeight, 
	XmRVerticalDimension,
	sizeof (Dimension),
	XtOffsetOf(ExmCxxGrid, grid.margin_height),
	XmRImmediate, (XtPointer) 10
    },
    {	
	XmNmapCallback,
	XmCCallback, 
	XmRCallback, 
	sizeof (XtCallbackList),
	XtOffsetOf(ExmCxxGrid, grid.map_callback),
	XmRImmediate, 
	(XtPointer) NULL
    },
    {	
	XmNunmapCallback,
	XmCCallback, 
	XmRCallback, 
	sizeof (XtCallbackList),
	XtOffsetOf(ExmCxxGrid, grid.unmap_callback),
	XmRImmediate, 
	(XtPointer) NULL
    },
    {	
	XmNdefaultPosition,
	XmCDefaultPosition, 
	XmRBoolean, sizeof (Boolean),
	XtOffsetOf(ExmCxxGrid, grid.default_position),
	XmRImmediate,
	(XtPointer) True
    },
    {	
	XmNbuttonRenderTable,
	XmCButtonRenderTable, 
	XmRButtonRenderTable, 
	sizeof (XmRenderTable),
	XtOffsetOf(ExmCxxGrid, grid.button_render_table),
	XmRCallProc, (XtPointer) NULL
    },
    {	
	XmNlabelRenderTable,
	XmCLabelRenderTable, 
	XmRLabelRenderTable, 
	sizeof (XmRenderTable),
	XtOffsetOf(ExmCxxGrid, grid.label_render_table),
	XmRCallProc, (XtPointer) NULL
    },
    {	
	XmNtextRenderTable,
	XmCTextRenderTable, 
	XmRTextRenderTable, 
	sizeof (XmRenderTable),
	XtOffsetOf(ExmCxxGrid, grid.text_render_table),
	XmRCallProc, (XtPointer) NULL
    },
    {	
	XmNdialogTitle,
	XmCDialogTitle, 
	XmRXmString, 
	sizeof (XmString),
	XtOffsetOf(ExmCxxGrid, grid.dialog_title),
	XmRImmediate, (XtPointer) NULL
    },

};

/* Three of the preceding resources will be handled as synthetic 
   resources. */
static XmSyntheticResource syn_resources[] = 
{
    {
	XmNmarginWidth, 
	sizeof (Dimension),
	XtOffsetOf(ExmCxxGrid, grid.margin_width),
	XmeFromHorizontalPixels,
	XmeToHorizontalPixels
    },
    {
	XmNmarginHeight, 
	sizeof (Dimension),
	XtOffsetOf(ExmCxxGrid, grid.margin_height),
	XmeFromVerticalPixels,
	XmeToVerticalPixels
    },
    {	
	XmNdialogTitle,
	sizeof (XmString),
	XtOffsetOf(ExmCxxGrid, grid.dialog_title),
	ExmCxxGrid::GetDialogTitle,
	NULL
     }
};

static XtResource constraints[] =
{
    {
	ExmNgridMarginWidthWithinCell,
	ExmCGridMarginWidthWithinCell,
	XmRHorizontalDimension, sizeof (Dimension),
	XtOffsetOf( struct ExmCxxGridConstraintRec, 
		    grid.grid_margin_width_within_cell),
	XmRImmediate, (XtPointer) 0
    },
    {
	ExmNgridMarginHeightWithinCell,
	ExmCGridMarginHeightWithinCell,
	XmRVerticalDimension,sizeof (Dimension),
	XtOffsetOf( struct ExmCxxGridConstraintRec,
		    grid.grid_margin_height_within_cell),
	XmRImmediate, (XtPointer) 0
    },
};

static XmSyntheticResource syn_constraints[] =
{
    {
        ExmNgridMarginWidthWithinCell,
	sizeof (Dimension),
	XtOffsetOf( struct ExmCxxGridConstraintRec,
		    grid.grid_margin_width_within_cell),
	XmeFromHorizontalPixels,
	XmeToHorizontalPixels
    },
    {
	ExmNgridMarginHeightWithinCell,
	sizeof (Dimension),
	XtOffsetOf( struct ExmCxxGridConstraintRec,
		    grid.grid_margin_height_within_cell),
	XmeFromVerticalPixels,
	XmeToVerticalPixels
    },
};


/*********************************
 * Class declaration for CxxGrid *
 *********************************/

ExmCxxGridClass::ExmCxxGridClass(char*		name,
				 WidgetClass	parentClass,
				 Cardinal	widgetSize,
				 XtProc		class_init,
				 XtActionList	actions,
				 Cardinal	num_actions,
				 XtResourceList	res,
				 Cardinal	num_res,
				 XtInitProc	cxx_cast)
    : XmCxxManagerClass(name, parentClass, widgetSize, class_init,
			actions, num_actions, res, num_res, cxx_cast)
{
    /*
     * Core class part
     */
    core_class.compress_motion		= TRUE;
    core_class.compress_exposure	= XtExposeCompressMaximal;
    core_class.compress_enterleave	= TRUE;
    /*
     * constraint class part
     * .resources array and .num_resources should only be set for the class
     * implementing them and should not be set in subclasses (Xt job).
     * On the other hand, .constraint_size MUST be set for subclasses, even
     * when they do not define new constraints.
     */
    if (parentClass == (WidgetClass) &xmCxxManagerClassRec) {	
	constraint_class.resources	= constraints;
	constraint_class.num_resources	= XtNumber(constraints);
    }
    constraint_class.constraint_size	= sizeof(ExmCxxGridConstraintRec);

    /*
     * manager class part
     * syn_[constraint_]resources array and num_syn_[constraint_]resources
     * should only be set for the class implementing them and should not be
     * set in subclasses (Xm job).
     */
    manager_class.translations			= XtInheritTranslations;
    if (parentClass == (WidgetClass) &xmCxxManagerClassRec) {	
	manager_class.syn_resources		= syn_resources;
	manager_class.num_syn_resources		= XtNumber(syn_resources);
	manager_class.syn_constraint_resources	= syn_constraints;
	manager_class.num_syn_constraint_resources = XtNumber(syn_constraints);
    }
    manager_class.parent_process = XmInheritParentProcess;
}

externaldef(exmcxxgridclassrec)
ExmCxxGridClass exmCxxGridClassRec("ExmCxxGrid",
				   (WidgetClass) &xmCxxManagerClassRec,
				   sizeof(ExmCxxGrid),
			           ExmCxxGridClass::ClassInitialize,
				   NULL		/*actions*/,
				   0		/*num_actions*/,
				   resources,
				   XtNumber(resources),
			           ExmCxxGrid::_MakeCxxWidget);

externaldef(exmcxxgridwidgetclass)
WidgetClass exmCxxGridWidgetClass = (WidgetClass) &exmCxxGridClassRec;

/*********************************************************************
 *
 * Trait structures
 *
 *********************************************************************/

/* Define trait record variables. */

/* Here is the trait record variable for the XmQTdialogSavvy trait. */ 
static XmConst XmDialogSavvyTraitRec gridDST = {
  0,            /* version */
  ExmCxxGrid::CallMapUnmap, /* trait method */        
};


/* Here is the trait record variable for the XmQTspecifyRenderTable trait. */ 
static XmConst XmSpecRenderTraitRec gridSRTT = {
  0,		/* version */
  ExmCxxGrid::GetTable,     /* trait method */
};

/****************************************************************************
 *
 *  GetDialogTitle
 *      Copy the XmString in XmNdialogTitle before returning it to the user.
 *
 ****************************************************************************/
void ExmCxxGrid::GetDialogTitle(Widget wid, int, XtArgVal *value)
{
   *value = (XtArgVal)XmStringCopy(((ExmCxxGrid *) wid)->grid.dialog_title);
}

			/****************************
			 *                          *
			 * Intrinsics class methods *
			 *                          *
			 ****************************/


/*********************************************************************
 *
 * ClassInitialize
 *
 ********************************************************************/
void ExmCxxGridClass::ClassInitialize() {
    /* set up base_class extension quark */
    exmCxxGridClassRec.SetBaseClassExtensionQuark();
}

/****************************************************************************
 *
 *  ClassPartInitialize:
 *      Called when this widget or a subclass of this widget is instantiated.
 *
 ****************************************************************************/

void ExmCxxGridClass::class_part_initialize()
{
  // Before anything else, do the downward chaining;
  XmCxxManagerClass::class_part_initialize();

  // Install the XmQTdialogShellSavyy trait on this class and on
  // all its future subclasses.
  XmeTraitSet((XtPointer) this, XmQTdialogShellSavvy, (XtPointer) &gridDST);

  // Install the XmQTspecifyRenderTable trait on this class and on
  // all its future subclasses.
  XmeTraitSet((XtPointer) this, XmQTspecifyRenderTable, (XtPointer) &gridSRTT);
}

/************************************************************************
 *
 *  _MakeCxxWidget
 *	Call `new' to make each widget a C++ object
 *
 ************************************************************************/
void ExmCxxGrid::_MakeCxxWidget(Widget /*req_w*/, Widget new_w,
			       ArgList, Cardinal*) {
    ExmCxxGridWidget dummy = IDLE_NEW(new_w) ExmCxxGrid;
}

/************************************************************************
 *
 *  initialize : virtual
 *     Downward chained Xt method :
 *     each class re-implementing initialize() should start by
 *     calling <superclass>::initialize()
 *
 *     Ensure that the width and height are not 0.
 *
 ************************************************************************/
void ExmCxxGrid::initialize(Widget reqw, ArgList args, Cardinal* nargs) {
  ExmCxxGridWidget rw = (ExmCxxGridWidget)reqw;

  /*
   * Before anything else, do the downward chaining;
   */
  XmCxxManager::initialize(reqw, args, nargs);

  /* Initialize one of the internal fields of the ExmGrid widget. */ 
  grid.processing_constraints = False;
    
  /* Ensure that user doesn't specify too many rows. */ 
  if (rw->grid.rows > EXM_GRID_MAX_NUMBER_OF_ROWS) {
    XmeWarning((Widget)rw, WARNING_TOO_MANY_ROWS);
    grid.rows = EXM_GRID_MAX_NUMBER_OF_ROWS;
  }
    
  /* Ensure that user doesn't specify too many columns. */ 
  if (rw->grid.columns > EXM_GRID_MAX_NUMBER_OF_COLUMNS) {
    XmeWarning((Widget)rw, WARNING_TOO_MANY_COLUMNS);
    grid.columns = EXM_GRID_MAX_NUMBER_OF_COLUMNS;
  }

  /* Copy in the dialog title XmString and update our shell */
  if (grid.dialog_title) {   
    grid.dialog_title = XmStringCopy(rw->grid.dialog_title) ;
    XmeSetWMShellTitle(grid.dialog_title, XtParent((Widget) this)) ;
  }
}

/****************************************************************************
 *
 *  Destructor
 *      Called when the widget is destroyed.
 *
 ****************************************************************************/

ExmCxxGrid::~ExmCxxGrid()
{   
  // Free dialog title memory
  XmStringFree(grid.dialog_title);
}


/************************************************************************
 *
 *  Resize 
 *
 ************************************************************************/
void ExmCxxGrid::resize()
{
  /* Configure using current size */
  layout(NULL);
}

/************************************************************************
 *
 *  Redisplay
 *
 ************************************************************************/
void ExmCxxGrid::expose(XEvent *event, Region region)
{
      /* Pass exposure down to gadget children */

  XmeRedisplayGadgets ((Widget) this, event, region);
}

/************************************************************************
 *
 *  set_values : virtual
 *     Downward chained Xt method :
 *     each class re-implementing set_values() should start by
 *     calling <superclass>::set_values()
 *
 *     Adjust the size of the manager based on shadow thickness changes.
 *
 ************************************************************************/
Boolean ExmCxxGrid::set_values(Widget old_w, Widget request_w, 
			      ArgList args, Cardinal *num_args)
{
    ExmCxxGridWidget cw = (ExmCxxGridWidget)old_w;
    ExmCxxGridWidget rw = (ExmCxxGridWidget)request_w;
    Boolean redisplay = False;
    Boolean need_relayout_flag;

    /*
     * Before anything else, do the downward chaining;
     */
    redisplay = XmCxxManager::set_values(old_w, request_w, args, num_args);


    /* Ensure that user doesn't specify too many rows. */ 
    if (rw->grid.rows > EXM_GRID_MAX_NUMBER_OF_ROWS) {
      XmeWarning((Widget)rw, WARNING_TOO_MANY_ROWS);
      grid.rows = EXM_GRID_MAX_NUMBER_OF_ROWS;
    }
    
    /* Ensure that user doesn't specify too many rows. */ 
    if (rw->grid.columns > EXM_GRID_MAX_NUMBER_OF_COLUMNS) {
      XmeWarning((Widget)rw, WARNING_TOO_MANY_COLUMNS);
      grid.columns = EXM_GRID_MAX_NUMBER_OF_COLUMNS;
    }
    
    
    /* See if any class or subclass resources have changed. */
    need_relayout_flag = need_relayout(old_w);

  /* If any geometry resources changed and a new size wasn't specified,
     recalculate a new ideal size. */ 
    if (need_relayout_flag) {
      /* Reset the widget size so that CalcSize can affect them. */
      if (core.width == cw->core.width) 
	core.width = 0;
      if (core.height == cw->core.height) 
	core.height = 0;
	
      /* Call CalcSize. */
      calc_size(NULL, &core.width, &core.height);
	
      /* If the geometry resources have changed but the size hasn't, 
	 we need to relayout manually, because Xt won't generate a 
	 Resize at this point. */
      if ((core.width == cw->core.width) &&
          (core.height == cw->core.height)) {
	layout(NULL); 
	redisplay = True ;
      }
    }

    /* ExmGrid installs the XmQTdialogShellSavvy trait.  Therefore, ExmGrid
       has to process the Xm_DIALOG_SAVVY_FORCE_ORIGIN case, which is as
       follows.  A DialogShell always mimics the child position on itself.
       That is, the "current" position of an ExmGrid within a DialogShell is
       always 0.  Therefore, if an application tries to set ExmGrid's x or
       y position to 0, the Intrinsics will not detect a position change and
       wll not trigger a geometry request.  ExmGrid has to detect this special 
       request and set core.x and core.y to the special value, 
       XmDIALOG_SAVVY_FORCE_ORIGIN.  That is, XmDIALOG_SAVVY_FORCE_ORIGIN
       tells DialogShell that ExmGrid really does want to move to an x or y
       position of 0. */
 
   if (XmIsDialogShell(XtParent((Widget) this)))
     {   /* Is parent a DialogShell? */
       Cardinal i ;

       /* We have to look in the arglist since old_w->core.x is always 0, and 
	  if new_w->core.x is also set to 0, we see no change. */
       for (i=0; i<*num_args; i++) {
         if (strcmp (args[i].name, XmNx) == 0) {
	   if ((args[i].value == 0) && (core.x == 0))
             core.x = XmDIALOG_SAVVY_FORCE_ORIGIN;
	 } 
	 if (strcmp (args[i].name, XmNy) == 0) {
           if ((args[i].value == 0) && (core.y == 0))
             core.y = XmDIALOG_SAVVY_FORCE_ORIGIN;
         }
       } /* end for */
     } /* end of if */

   /* Update wm shell title if it has changed */
   if(grid.dialog_title != cw->grid.dialog_title ) {  
     XmStringFree(cw->grid.dialog_title) ;
     grid.dialog_title = XmStringCopy(rw->grid.dialog_title) ;
     XmeSetWMShellTitle(grid.dialog_title, XtParent((Widget) this)) ;
   }

   return (redisplay);
}

/*************************************************************************
 *
 *  SetValuesAlmost:
 *       Called by the Intrinsics when an XtMakeGeometryRequest call
 *       returns either XmGeometryAlmost or XtGeometryNo.  
 *
 *************************************************************************/
void ExmCxxGrid::set_values_almost(Widget,
				   XtWidgetGeometry* request,
				   XtWidgetGeometry* reply) {
  /* ExmCxxGrid's parent said XtGeometryNo to ExmCxxGrid's geometry request. 
     Therefore, we need to relayout because this request
     was due to a change in internal geometry resource of the ExmCxxGrid */
  if (!reply->request_mode)
    layout(NULL);

  *request = *reply;
}

/************************************************************************
 *
 *  QueryGeometry
 *  	return width X height based on the children preferred sizes
 *
 ************************************************************************/
XtGeometryResult ExmCxxGrid::query_geometry(XtWidgetGeometry *request,
					   XtWidgetGeometry *reply) {
  Widget w = (Widget) this;

  /* If ExmGrid's parent calls XtQueryGeometry before ExmGrid has been 
     realized, use the current size of ExmGrid as the preferred size. */ 
  /* Deal with user initial size setting */
    if (!XtIsRealized(w))  {  /* Widget is not yet realized. */
	reply->width = XtWidth(w) ;    /* might be 0 */
	reply->height = XtHeight(w) ;  /* might be 0 */
    } else {	    /* Widget is realized. */
	/* always computes natural size afterwards */
	reply->width = 0;
	reply->height = 0; 
    }

  /* Call CalcSize to figure out what the preferred size really is. */ 
  calc_size(NULL, &reply->width, &reply->height);

  /* This function handles CWidth and CHeight */
  return XmeReplyToQueryGeometry(w, request, reply) ;
}

/************************************************************************
 *
 *  Geometry Manager
 *	Take the requested geometry, calculate the needed size for
 *	the grid and make a request to the parent.
 *      Requests to change x, y position are always denied.
 *
 ************************************************************************/
XtGeometryResult ExmCxxGrid::geometry_manager(Widget w,
					     XtWidgetGeometry *request,
					     XtWidgetGeometry *) {
  ExmCxxGrid *gw = (ExmCxxGrid *) XtParent(w);
  XtWidgetGeometry parentRequest;
  XtGeometryResult result;
  Dimension curWidth, curHeight, curBW;
  
  /* If the request was caused by ConstraintSetValues reset the flag */
  if (gw->grid.processing_constraints) {
    gw->grid.processing_constraints = False;
    /* The ConstraintSetValues added one to border_width; 
       This is the Xt trick used to fire the GM when a non core 
       geometry resource (like a constraint) changes.
       now take it away. */
    request->border_width -= 1;
  }
  
  /* Save the original child resources. */
  curWidth = w->core.width;
  curHeight = w->core.height;
  curBW = w->core.border_width;
  
  /* Deny any requests for a new position. */
  if ((request->request_mode & CWX) || (request->request_mode & CWY))
    return XtGeometryNo ;
  
  if (request->request_mode & CWWidth) 
    w->core.width = request->width;
  if (request->request_mode & CWHeight) 
    w->core.height = request->height;
  if (request->request_mode & CWBorderWidth) 
    w->core.border_width = request->border_width;

  /* Calculate a new ideal size based on these requests. */ 
  /* Setting width and height to 0 tells CalcSize to override these
     fields with the calculated width and height. */ 
  parentRequest.width = 0;
  parentRequest.height = 0;
  gw -> calc_size(w, &parentRequest.width, &parentRequest.height);
    
  /* Ask the Grid's parent if new calculated size is acceptable. */
  parentRequest.request_mode = CWWidth | CWHeight;
  if (request->request_mode & XtCWQueryOnly)
    parentRequest.request_mode |= XtCWQueryOnly;
  result = XtMakeGeometryRequest ((Widget)gw, &parentRequest, NULL);

  /*  Turn XtGeometryAlmost into XtGeometryNo. */ 
  if (result == XtGeometryAlmost) 
    result = XtGeometryNo;

  if (result == XtGeometryNo || 
      request->request_mode & XtCWQueryOnly) { 
    /* Restore original geometry. */
    w->core.width = curWidth;
    w->core.height = curHeight;
    w->core.border_width = curBW;
  } else {    
    /* result == XtGeometryYes and this wasn't just a query */
    gw -> layout(w); /* Layout with this child as the instigator,
			so that we don't resize this child. */
  }

  return (result);
}

/************************************************************************
 *
 *  ChangeManaged
 *	Process a changed in managed state of the child.  If its
 *	size is out of sync with the parent, make a resize request
 *	to change the size of the grid
 *	Note: called before ConstraintDestroy.
 *
 ************************************************************************/
void ExmCxxGrid::change_managed() {
    Dimension gridWidth, gridHeight;
    Widget w = (Widget) this;

    /* If you get an initial (C) size from the user or application, keep it.  
       Otherwise, just force width and height to 0 so that CalcSize will
       overwrite the appropriate fields. */ 
    if (!XtIsRealized(w))  {
	/* The first time, only attempts to change non specified sizes */
	gridWidth = XtWidth(w) ;   /* might be 0 */
	gridHeight = XtHeight(w) ; /* might be 0 */
    } else {
	gridWidth = 0 ;
	gridHeight = 0 ;
    }

    /* Determine the ideal size of Grid. */
    calc_size(NULL, &gridWidth, &gridHeight);

    /* Ask parent if new size is acceptable */
    while (XtMakeResizeRequest ((Widget) this, gridWidth, gridHeight,
			       &gridWidth, &gridHeight) == XtGeometryAlmost);

    /* Reconfigure children with agreed size */
    layout(NULL);

    /* Update keyboard traversal */
    XmeNavigChangeManaged ((Widget) this);
}

/************************************************************************
 *
 *  constraint_set_values : virtual
 *     Downward chained Xt method :
 *     each class re-implementing constraint_set_values() should start by
 *     calling <superclass>::constraint_set_values()
 *
 ************************************************************************/
Boolean ExmCxxGrid::constraint_set_values(Widget cw, Widget rw, Widget nw,
					 ArgList args, Cardinal* nargs) 
{
    ExmCxxGridConstraintPart *nc;
    ExmCxxGridConstraintPart *cc;

    /*
     * Before anything else, do the downward chaining;
     */
    Boolean redisplay =
      XmCxxManager::constraint_set_values(cw, rw, nw, args, nargs);

    if (!XtIsRectObj ((Widget) nw)) return (False);

    nc = GridConstraint((Widget) this);
    cc = GridConstraint(cw);


    /* Check for change in ExmNgridMarginWidth or ExmNgridMarginHeight */
    if ((nc->grid_margin_width_within_cell != 
	 cc->grid_margin_width_within_cell ||
	 nc->grid_margin_height_within_cell != 
	 cc->grid_margin_height_within_cell) && 
	XtIsManaged (nw)) {
	/* Tell the Intrinsics and the GeometryManager method that a 
	   reconfigure is needed. */
	grid.processing_constraints = True;
	/* A trick: by altering one of the core geometry fields, Xt will
	   call the parent's geometry_manager method. */ 
	nw->core.border_width += 1;
    }

    return (False);
}

/************************************************************************
 *
 *  Layout
 *
 ************************************************************************/
void ExmCxxGrid::layout(Widget instigator)
{
  Dimension mw = grid.margin_width;
  Dimension mh = grid.margin_height;
  Dimension TotalWidthOfGridWidget  = core.width;
  Dimension TotalWidthOfGridMargins, TotalHeightOfGridMargins;
  Dimension TotalHeightOfGridWidget = core.height;
  Dimension AvailWidthForChildren = 1, AvailHeightForChildren = 1; 
  Dimension WidthAllottedEachChild, HeightAllottedEachChild;
  int i, row, column;

  /* Lay out the children that ExmGrid is currently managing.  
     Each child will be placed somewhere on the rowxcolumn grid. */
  TotalWidthOfGridMargins = 2 * mw;
  if (TotalWidthOfGridWidget > TotalWidthOfGridMargins) 
    AvailWidthForChildren = TotalWidthOfGridWidget - 
      TotalWidthOfGridMargins; 
  
  WidthAllottedEachChild = AvailWidthForChildren / grid.columns;
  
  TotalHeightOfGridMargins = 2 * mh;
  if (TotalHeightOfGridWidget > TotalHeightOfGridMargins) 
    AvailHeightForChildren = TotalHeightOfGridWidget - 
      TotalHeightOfGridMargins; 
  HeightAllottedEachChild = AvailHeightForChildren / grid.rows;
  
  /* Now that we know how much space is allotted for each child, we
     can lay them all out. */
  row = 0;
  column = 0;
  for (i = 0; i < composite.num_children; i++) {
    Widget ic = composite.children[i];
    ExmCxxGridConstraintPart *glc = GridConstraint (ic);
    Dimension gmw = glc->grid_margin_width_within_cell;
    Dimension gmh = glc->grid_margin_height_within_cell;
    Position ChildsStartingX, ChildsStartingY; 
    Dimension ChildsActualWidth, ChildsActualHeight, cb;
    
    if (!XtIsManaged(ic))  
      continue;  /* ignored unmanaged children */
    
    cb = ic->core.border_width;  
    
    /* Calculate the position and the size of the child.
       During the layout, the children are all resized 
       to exactly fit the cell size minus the cell margin */ 
    
    ChildsActualWidth = WidthAllottedEachChild - (2 * (gmw + cb));
    ChildsStartingX = mw + (column * WidthAllottedEachChild) + gmw;
    ChildsStartingY = mh + (row * HeightAllottedEachChild) + gmh;
    ChildsActualHeight = HeightAllottedEachChild - 2 * (gmh + cb);
    
    /* If layout is instigated by the GeometryManager don't 
       configure the requesting child, just set its geometry and 
       let Xt configure it.   */
    if (ic != instigator) {
      XmeConfigureObject (ic, ChildsStartingX, ChildsStartingY, 
			  ChildsActualWidth, ChildsActualHeight, cb);
    }
    else {
      ic->core.x = ChildsStartingX; 
      ic->core.y = ChildsStartingY; 
      ic->core.width = ChildsActualWidth; 
      ic->core.height = ChildsActualHeight; 
      ic->core.border_width = cb;
    }
    
    /* Advance the column counter until we reach the right edge.  
       When we reach the right edge, reset the column 
       counter back to 0 (left edge) and advance the row counter. */ 
    column += 1;
    
    if (column == grid.columns) {
      column = 0;
      row += 1;
    }
  } 
}

/************************************************************************
 *
 *  CalcSize
 *
 ************************************************************************/
void ExmCxxGrid::calc_size(Widget instigator, 
			   Dimension *TotalWidthOfGridWidget,
			   Dimension *TotalHeightOfGridWidget)
{
  Dimension maxWidth = 1;
  Dimension maxHeight = 1;
  int i;
  
  /* Examine each of Grid's children.  Find the biggest child.  The 
     ideal size of the Grid will be large enough to accomodate the
     largest child. */ 
  for (i = 0; i < composite.num_children; i++) {
    Widget ic = composite.children[i];
    ExmCxxGridConstraintPart *glc = GridConstraint (ic);
    Dimension width, height;
    Dimension cw, ch, cb;
    XtWidgetGeometry reply;
    
    if (!XtIsManaged(ic)) 
      continue ;
    
    /* Get child's preferred geometry if not the instigator. */
    if (ic != instigator) {
      XtQueryGeometry (ic, NULL, &reply);
      cw = (reply.request_mode & CWWidth) ? reply.width :
      ic->core.width;
      ch = (reply.request_mode & CWHeight) ? reply.height :
      ic->core.height;
    } else {
      cw = ic->core.width;
      ch = ic->core.height;
    }
    cb = ic->core.border_width;
    
    width = cw + 2 * (cb + glc->grid_margin_width_within_cell);
    height = ch + 2 * (cb + glc->grid_margin_height_within_cell);
    
    maxWidth  = Max (width, maxWidth);
    maxHeight = Max (height, maxHeight);
  } 
  
  
  /* The total width of the grid widget should be set to the width of 
     the largest child widget times the number of columns. */
  if (!*TotalWidthOfGridWidget) {
    *TotalWidthOfGridWidget = maxWidth * grid.columns +
      (2 * (grid.margin_width));
  }
  
  /* The total height of the grid widget should be set to the height of 
     the largest child widget times the number of columns. */
  if (!*TotalHeightOfGridWidget) {
    *TotalHeightOfGridWidget = maxHeight * grid.rows +
      (2 * (grid.margin_height));
  }
}


/****************************************************************************
 *
 *  need_relayout:
 *     Called by SetValues. 
 *     Returns True if a relayout is needed.  
 *     based on this class and all superclass resources' changes. 
 *
 ***************************************************************************/
Boolean ExmCxxGrid::need_relayout(Widget old_w)
{
  ExmCxxGrid *cw = (ExmCxxGrid *) old_w;

    if (grid.margin_width != cw -> grid.margin_width ||
	grid.margin_height != cw -> grid.margin_height ||
	grid.rows != cw -> grid.rows ||
	grid.columns != cw -> grid.columns) {
	return True ;
    } else
	return False ;
}

/*-- Trait methods --*/

/****************************************************************
 *
 * Trait method for XmQTdialogShellSavvy trait. 
 *
 **************************************************************/
void ExmCxxGrid::CallMapUnmap(Widget wid, Boolean map_unmap)
{
    ExmCxxGrid *grid = (ExmCxxGrid *) wid ;
    XmAnyCallbackStruct call_data;	

    call_data.reason = (map_unmap)? XmCR_MAP : XmCR_UNMAP;
    call_data.event  = NULL;			

    if (map_unmap) {
	XtCallCallbackList (wid, grid -> grid.map_callback, 
			    &call_data);
    } else {
	XtCallCallbackList (wid, grid -> grid.unmap_callback, 
			    &call_data);
    }
}

/*****************************************************************
 *
 * Trait method for XmQTspecifyRenderTable.   
 *
 *****************************************************************/
XmRenderTable ExmCxxGrid::GetTable(Widget wid, XtEnum type)
{
    ExmCxxGrid *grid = (ExmCxxGrid *) wid ;

    switch(type) {
    case XmLABEL_RENDER_TABLE : return grid->grid.label_render_table ;
    case XmBUTTON_RENDER_TABLE : return grid->grid.button_render_table ;
    case XmTEXT_RENDER_TABLE : return grid->grid.text_render_table ;
    }
   
    return NULL ;
}


/******************************************************************************
 *
 *  ExmCreateCxxGrid:
 *      Called by an application. 
 *
 *****************************************************************************/
Widget ExmCreateCxxGrid (Widget parent, char *name, 
			 ArgList arglist, Cardinal argcount)
{
 /* This is a convenience function to instantiate an ExmGrid widget. */
   return (XtCreateWidget (name, exmCxxGridWidgetClass, parent, 
                           arglist, argcount));
}

/******************************************************************************
 *
 *  ExmCreateCxxGridDialog
 *      Called by an application to create an ExmGrid managed by a
 *      DialogShell. 
 *
 *****************************************************************************/
Widget ExmCreateCxxGridDialog (Widget parent, char *name,
			       ArgList arglist, Cardinal argcount)
{
   return XmeCreateClassDialog (exmCxxGridWidgetClass,
                                parent, name, arglist, argcount) ; 

}
