/* $XConsortium: Grid.c /main/5 1995/07/15 20:40:45 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 <Exm/GridP.h>  /* private header file for the ExmGrid widget */
#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 ExmGrid.\n" 
#define WARNING_TOO_MANY_COLUMNS "Too many columns specified for ExmGrid.\n"


/* Declare static functions. */
static void GetDialogTitle( 
                        Widget bb,
                        int resource,
                        XtArgVal *value) ;
static void ClassPartInitialize(
                        WidgetClass widgetClass);
static void Initialize(
                        Widget request_w,
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args );
static void Destroy( 
                        Widget wid) ;
static void Resize (
                        Widget w );
static void Redisplay (
                        Widget w,
                        XEvent *event,
                        Region region);
static Boolean SetValues (
                        Widget old_w,
                        Widget request_w,
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args );
static void SetValuesAlmost( 
                        Widget cw,
                        Widget nw,
                        XtWidgetGeometry *request,
                        XtWidgetGeometry *reply) ;
static XtGeometryResult QueryGeometry (
                        Widget w,
                        XtWidgetGeometry *request,
                        XtWidgetGeometry *reply);
static XtGeometryResult GeometryManager (
                        Widget w,
                        XtWidgetGeometry *request,
                        XtWidgetGeometry *reply);
static void ChangeManaged (
                        Widget w);
static Boolean ConstraintSetValues (
                        Widget cw,
                        Widget rw,
                        Widget nw,
                        ArgList args,
                        Cardinal *num_args);
static void Layout (
                        Widget wid,
                        Widget instigator);
static void CalcSize (
                        Widget wid,
                        Widget instigator,
                        Dimension *gridWidth,
                        Dimension *gridHeight);
static Boolean NeedRelayout (
                        Widget new,
                        Widget cur);
static void CallMapUnmap(Widget wid,
			 Boolean map_unmap) ;
static XmRenderTable GetTable(Widget wid,
			   XtEnum type);


/* No translations and no actions. */


/* Define the resources for the ExmGrid widget. */ 
static XtResource resources[] =
{
    {
	XmNrows, 
	XmCRows, 
	XmRShort, 
	sizeof (short),
	XtOffsetOf( ExmGridRec, grid.rows),
	XmRImmediate, 
        (XtPointer) 4
    },
    {
	XmNcolumns, 
	XmCColumns, 
	XmRShort, 
	sizeof (short),
	XtOffsetOf( ExmGridRec, grid.columns),
	XmRImmediate, 
        (XtPointer) 4
    },
    {
	XmNmarginWidth, 
	XmCMarginWidth, 
	XmRHorizontalDimension, 
	sizeof (Dimension),
	XtOffsetOf( ExmGridRec, grid.margin_width),
	XmRImmediate, 
        (XtPointer) 10
    },
    {
	XmNmarginHeight, 
	XmCMarginHeight, 
	XmRVerticalDimension,
	sizeof (Dimension),
	XtOffsetOf(ExmGridRec, grid.margin_height),
	XmRImmediate, 
        (XtPointer) 10
    },
    {	
	XmNmapCallback,
	XmCCallback, 
	XmRCallback, 
	sizeof (XtCallbackList),
	XtOffsetOf(ExmGridRec, grid.map_callback),
	XmRImmediate, 
	(XtPointer) NULL
    },
    {	
	XmNunmapCallback,
	XmCCallback, 
	XmRCallback, 
	sizeof (XtCallbackList),
	XtOffsetOf(ExmGridRec, grid.unmap_callback),
	XmRImmediate, 
	(XtPointer) NULL
    },
    {	
	XmNdefaultPosition,
	XmCDefaultPosition, 
	XmRBoolean, sizeof (Boolean),
	XtOffsetOf(ExmGridRec, grid.default_position),
	XmRImmediate,
	(XtPointer) True
    },
    {	
	XmNbuttonRenderTable,
	XmCButtonRenderTable, 
	XmRButtonRenderTable, 
	sizeof (XmRenderTable),
	XtOffsetOf(ExmGridRec, grid.button_render_table),
	XmRCallProc, (XtPointer) NULL
    },
    {	
	XmNlabelRenderTable,
	XmCLabelRenderTable, 
	XmRLabelRenderTable, 
	sizeof (XmRenderTable),
	XtOffsetOf(ExmGridRec, grid.label_render_table),
	XmRCallProc, (XtPointer) NULL
    },
    {	
	XmNtextRenderTable,
	XmCTextRenderTable, 
	XmRTextRenderTable, 
	sizeof (XmRenderTable),
	XtOffsetOf(ExmGridRec, grid.text_render_table),
	XmRCallProc, (XtPointer) NULL
    },
    {	
	XmNdialogTitle,
	XmCDialogTitle, 
	XmRXmString, 
	sizeof (XmString),
	XtOffsetOf(ExmGridRec, 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( ExmGridRec, grid.margin_width),
	XmeFromHorizontalPixels,
	XmeToHorizontalPixels
    },
    {
	XmNmarginHeight, 
	sizeof (Dimension),
	XtOffsetOf( ExmGridRec, grid.margin_height),
	XmeFromVerticalPixels,
	XmeToVerticalPixels
    },
    {	
	XmNdialogTitle,
	sizeof (XmString),
	XtOffsetOf( ExmGridRec, grid.dialog_title),
	GetDialogTitle,
	NULL
     }
};


/* Define the two constraints of ExmGrid. */
static XtResource constraints[] =
{
    {
        ExmNgridMarginWidthWithinCell,
        ExmCGridMarginWidthWithinCell,
        XmRHorizontalDimension, 
        sizeof (Dimension),
        XtOffsetOf( ExmGridConstraintRec, grid.grid_margin_width_within_cell),
        XmRImmediate, 
        (XtPointer) 0
    },
    {
        ExmNgridMarginHeightWithinCell,
        ExmCGridMarginHeightWithinCell,
        XmRVerticalDimension,
        sizeof (Dimension),
        XtOffsetOf( ExmGridConstraintRec, grid.grid_margin_height_within_cell),
        XmRImmediate, 
        (XtPointer) 0
    },
};


/* Both of the preceding constraints will be handled as synthetic 
   constraints. */
static XmSyntheticResource syn_constraints[] =
{
    {
        ExmNgridMarginWidthWithinCell,
        sizeof (Dimension),
        XtOffsetOf( ExmGridConstraintRec, grid.grid_margin_width_within_cell),
        XmeFromHorizontalPixels,
        XmeToHorizontalPixels
    },
    {
        ExmNgridMarginHeightWithinCell,
        sizeof (Dimension),
        XtOffsetOf( ExmGridConstraintRec, grid.grid_margin_height_within_cell),
        XmeFromVerticalPixels,
        XmeToVerticalPixels
    },
};



/* Define the widget class record.  See Chapter 4 of the 
   "OSF/Motif Widget Writer's Guide" for details. */ 
externaldef(exmgridclassrec) ExmGridClassRec exmGridClassRec = 
{ 
  { /* Here is the Core class record. */ 
    /* superclass */                 (WidgetClass) &xmManagerClassRec, 
    /* class_name */                 "ExmGrid",
    /* widget_size */                sizeof(ExmGridRec),
    /* class_initialize */           NULL,
    /* class_part_initialize */      ClassPartInitialize, 
    /* class_inited */               FALSE,
    /* initialize */                 Initialize,
    /* initialize_hook */            NULL,
    /* realize */                    XtInheritRealize,
    /* actions */                    NULL,
    /* num_actions */                0,
    /* resources */                  resources,
    /* num_resources */              XtNumber(resources),
    /* xrm_class */                  NULLQUARK,
    /* compress_motion */            TRUE,
    /* compress_exposure */          XtExposeCompressMaximal,
    /* compress_enterleave */        TRUE,
    /* visible_interest */           FALSE,
    /* destroy */                    Destroy,
    /* resize */                     Resize,
    /* expose */                     Redisplay,
    /* set_values */                 SetValues,
    /* set_values_hook */            NULL,
    /* set_values_almost */          SetValuesAlmost,
    /* get_values_hook */            NULL,
    /* accept_focus */               NULL,
    /* version */                    XtVersion,
    /* callback_private */           NULL,
    /* tm_table */                   XtInheritTranslations,
    /* query_geometry */             QueryGeometry,
    /* display_accelerator */        NULL,
    /* extension */                  NULL,
  },    
  { /* Here is the Composite class record. */ 
    /* geometry_manager */           GeometryManager,
    /* change_managed */             ChangeManaged,
    /* insert_child */               XtInheritInsertChild,
    /* delete_child */               XtInheritDeleteChild,
    /* extension */                  NULL,
  },    
  { /* Here is the Constaint class record. */ 
    /* constraint_resources */       constraints,
    /* constraint_num_resources */   XtNumber(constraints),
    /* constraint_size */            sizeof(ExmGridConstraintRec), 
    /* constraint_initialize */      NULL,
    /* constraint_destroy */         NULL,
    /* constraint_set_values */      ConstraintSetValues,
    /* extension */                  NULL,
  },    
  { /* Here is the XmManager class record. */ 
    /* translations */               XtInheritTranslations,
    /* syn_resources */              syn_resources,
    /* num_syn_resources */          XtNumber(syn_resources),
    /* syn_constraint_resources */   syn_constraints,
    /* num_syn_constraint_resources */ XtNumber(syn_constraints),
    /* parent_process */             XmInheritParentProcess,
    /* extension */                  NULL,
  },    
  { /* Here is the ExmGrid class record. */ 
    /* layout */                     Layout,
    /* calc_size */                  CalcSize,
    /* need_relayout */              NeedRelayout,
    /* extension */                  NULL,
  }    
};    

/* Establish the widget class name as an externally accessible symbol.
   Use the "externaldef" macro rather than the "extern" keyword. */
externaldef(exmgridwidgetclass) WidgetClass exmGridWidgetClass =
    (WidgetClass) &exmGridClassRec;


             /* Define trait record variables. */

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


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


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


/****************************************************************************
 *
 *  ClassPartInitialize:
 *      Called when this widget or a subclass of this widget is instantiated.
 *
 ****************************************************************************/
static void
ClassPartInitialize (
        WidgetClass widgetClass
                    )
{
 ExmGridWidgetClass wc = (ExmGridWidgetClass)widgetClass;
 ExmGridWidgetClass sc = (ExmGridWidgetClass)wc->core_class.superclass;

  /* The following code allows subclasses of ExmGrid to inherit three of  
     ExmGrid's methods. */
    if (wc->grid_class.layout == ExmInheritLayout)
	wc->grid_class.layout = sc->grid_class.layout;
    if (wc->grid_class.calc_size == ExmInheritCalcSize)
	wc->grid_class.calc_size = sc->grid_class.calc_size;
    if (wc->grid_class.need_relayout == ExmInheritNeedRelayout)
	wc->grid_class.need_relayout = sc->grid_class.need_relayout;
   
  /* Install the XmQTdialogShellSavyy trait on this class and on
     all its future subclasses. */
    XmeTraitSet(widgetClass, XmQTdialogShellSavvy, (XtPointer) &gridDST);

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


/**************************************************************************
 *
 *  Initialize:
 *      Called when this widget is first instantiated.
 *
 ***************************************************************************/
static void 
Initialize (
        Widget request_w,
        Widget new_w,
        ArgList args,
        Cardinal *num_args
           )
{
 ExmGridWidget rw = (ExmGridWidget)request_w;
 ExmGridWidget nw = (ExmGridWidget)new_w;
    
  /* Initialize one of the internal fields of the ExmGrid widget. */ 
    nw->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);
      nw->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);
      nw->grid.columns = EXM_GRID_MAX_NUMBER_OF_COLUMNS;
    }

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


/****************************************************************************
 *
 *  Destroy:
 *      Called when the widget is destroyed.
 *
 ****************************************************************************/
static void 
Destroy(
        Widget wid )
{   
 ExmGridWidget grid = (ExmGridWidget)wid;

  /* Deallocate the dynamic memory that holds the dialog_title. */ 
    XmStringFree(grid->grid.dialog_title) ;
}


/****************************************************************************
 *
 *  Resize:
 *
 ****************************************************************************/
static void  
Resize (
        Widget w
       )
{
 ExmGridWidgetClass gwc = (ExmGridWidgetClass) XtClass(w);
	
  /* Configure the children by calling Layout. */
    if (gwc->grid_class.layout)
      (*(gwc->grid_class.layout))(w, NULL); 
    else 
      Layout (w, NULL); 
}




/****************************************************************************
 *
 *  Redisplay:
 *      Called by the Intrinsics in response to an exposure event.
 *
 ***************************************************************************/
static void 
Redisplay (
        Widget w,
        XEvent *event,
        Region region
          )
{
  /* Pass exposure event down to gadget children. */
    XmeRedisplayGadgets (w, event, region);
}



/*****************************************************************************
 *
 *  SetValues:
 *      Called by the Intrinsics whenever any of the resource values change.
 *
 ****************************************************************************/
static Boolean 
SetValues (
        Widget old_w,
        Widget request_w,
        Widget new_w,
        ArgList args,
        Cardinal *num_args 
          )
{
 ExmGridWidget cw = (ExmGridWidget)old_w;
 ExmGridWidget rw = (ExmGridWidget)request_w;
 ExmGridWidget nw = (ExmGridWidget)new_w;
 Boolean redisplay = False; 
 Boolean need_relayout;
 ExmGridWidgetClass gwc = (ExmGridWidgetClass) XtClass(new_w);
	    
  /* 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);
      nw->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);
      nw->grid.columns = EXM_GRID_MAX_NUMBER_OF_COLUMNS;
    }
    
  /* See if any class or subclass resources have changed. */
    if (gwc->grid_class.need_relayout)
      need_relayout = (*(gwc->grid_class.need_relayout))(old_w, new_w); 
    else 
      need_relayout = NeedRelayout (old_w, new_w);

  /* If any geometry resources changed and a new size wasn't specified,
     recalculate a new ideal size. */ 
    if (need_relayout) {
      /* Reset the widget size so that CalcSize can affect them. */
	if (nw->core.width == cw->core.width) 
          nw->core.width = 0;
	if (nw->core.height == cw->core.height) 
          nw->core.height = 0;
	
      /* Call CalcSize. */
        if (gwc->grid_class.calc_size)
          (*(gwc->grid_class.calc_size))(new_w, NULL,
                                         &nw->core.width, &nw->core.height); 
        else 
          CalcSize (new_w, NULL, &nw->core.width, &nw->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 ((nw->core.width == cw->core.width) &&
          (nw->core.height == cw->core.height)) {

         /* Call Layout to configure the children. */
           if (gwc->grid_class.layout)
             (*(gwc->grid_class.layout))(new_w, NULL); 
           else 
             Layout(new_w, 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(new_w)))  {   /* 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) && (new_w->core.x == 0))
             new_w->core.x = XmDIALOG_SAVVY_FORCE_ORIGIN;
	 } 
	 if (strcmp (args[i].name, XmNy) == 0) {
           if ((args[i].value == 0) && (new_w->core.y == 0))
             new_w->core.y = XmDIALOG_SAVVY_FORCE_ORIGIN;
         }
      } /* end for */
   } /* end of if */

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

   return (redisplay);
}



/*************************************************************************
 *
 *  SetValuesAlmost:
 *       Called by the Intrinsics when an XtMakeGeometryRequest call
 *       returns either XmGeometryAlmost or XtGeometryNo.  
 *
 ***************************************************************************/
static void 
SetValuesAlmost(
        Widget cw,		/* unused */
        Widget nw,
        XtWidgetGeometry *request,
        XtWidgetGeometry *reply )
{  
 ExmGridWidgetClass gwc = (ExmGridWidgetClass) XtClass(nw);
	
  /* ExmGrid's parent said XtGeometryNo to ExmGrid's geometry request. 
     Therefore, we need to relayout because this request
     was due to a change in internal geometry resource of the ExmGrid */
    if (!reply->request_mode) {
	if (gwc->grid_class.layout)
	    (*(gwc->grid_class.layout))(nw, NULL); 
	else 
	    Layout(nw, NULL);
    }

    *request = *reply;
}


/*************************************************************************
 *
 *  QueryGeometry:
 *       Called by a parent of Grid when the parent needs to find out Grid's
 *       preferred size.  QueryGeometry calls CalcSize to do find the
 *       preferred size. 
 *
 ***************************************************************************/
static XtGeometryResult 
QueryGeometry (
        Widget w,
        XtWidgetGeometry *request,
        XtWidgetGeometry *reply 
              )
{
 ExmGridWidgetClass gwc = (ExmGridWidgetClass) XtClass(w);
	
  /* 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. */ 
    if (gwc->grid_class.calc_size)
      (*(gwc->grid_class.calc_size))(w, NULL, &reply->width, &reply->height); 
    else 
      CalcSize (w, NULL, &reply->width, &reply->height);

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


/****************************************************************************
 *
 *  GeometryManager:
 *       Called by Intrinsics in response to a geometry change request from 
 *       one of the children of ExmGrid.
 *
 ***************************************************************************/
static XtGeometryResult 
GeometryManager (
        Widget w,  /* instigator */
        XtWidgetGeometry *request,
        XtWidgetGeometry *reply
                )
{
 ExmGridWidget gw = (ExmGridWidget) XtParent(w);
 XtWidgetGeometry parentRequest;
 XtGeometryResult result;
 Dimension curWidth, curHeight, curBW;
 ExmGridWidgetClass gwc = (ExmGridWidgetClass) XtClass((Widget)gw);

  /* 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;
    if (gwc->grid_class.calc_size)
	(*(gwc->grid_class.calc_size))((Widget)gw, w,
				       &parentRequest.width, 
				       &parentRequest.height); 
    else 
	CalcSize ((Widget)gw, 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 */
       if (gwc->grid_class.layout)
	   (*(gwc->grid_class.layout))((Widget)gw, w); 
       else 
	   Layout ((Widget)gw, w); /* Layout with this child as the instigator,
			              so that we don't resize this child. */
   }

   return (result);
}



/**************************************************************************
 *
 *  ChangeManaged:
 *      Called by the Intrinsics whenever either of the following happens:
 *           * a managed child becomes unmanaged.
 *           * an unmanaged child becomes managed.
 *
 *************************************************************************/
static void 
ChangeManaged(
        Widget w
             )
{
 Dimension gridWidth, gridHeight;
 ExmGridWidgetClass gwc = (ExmGridWidgetClass) XtClass(w);
	
  /* 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. */
    if (gwc->grid_class.calc_size)
	(*(gwc->grid_class.calc_size))(w, NULL, &gridWidth, &gridHeight); 
    else 
	CalcSize (w, NULL, &gridWidth, &gridHeight);

 /* Ask parent of Grid if Grid's new size is acceptable.  Keep asking until
    parent returns either XtGeometryYes or XtGeometryNo. */
    while (XtMakeResizeRequest (w, gridWidth, gridHeight,
   		                &gridWidth, &gridHeight) == XtGeometryAlmost);

    /* Now that we have a size for the Grid, we can layout the children
       of the grid. */ 
    if (gwc->grid_class.layout)
	(*(gwc->grid_class.layout))(w, NULL); 
    else 
	Layout (w, NULL);
    
    /* Update keyboard traversal */
    XmeNavigChangeManaged (w);
}




/**************************************************************************
 *
 *  ConstraintSetValues:
 *      Called by Intrinsics if there is any change in any of the constraint
 *      resources. 
 *
 **************************************************************************/
static Boolean 
ConstraintSetValues (
    Widget cw,
    Widget rw,
    Widget nw,
    ArgList args,
    Cardinal *num_args 
                    )
{
 ExmGridConstraint nc;
 ExmGridConstraint cc;
 ExmGridWidget gw;

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

    gw = (ExmGridWidget)XtParent(nw);
    nc = ExmGridCPart(nw);
    cc = ExmGridCPart(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. */
	gw->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:
 *     Does all the placement of children.
 *     Instigator tells whether or not to resize all children.
 *
 *************************************************************************/
static void 
Layout (
        Widget wid,
        Widget instigator
       )
{
 ExmGridWidget gw = (ExmGridWidget) wid ;
 Dimension mw = gw->grid.margin_width;
 Dimension mh = gw->grid.margin_height;
 Dimension TotalWidthOfGridWidget  = gw->core.width;
 Dimension TotalWidthOfGridMargins, TotalHeightOfGridMargins;
 Dimension TotalHeightOfGridWidget = gw->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 / gw->grid.columns;

    TotalHeightOfGridMargins = 2 * mh;
    if (TotalHeightOfGridWidget > TotalHeightOfGridMargins) 
	AvailHeightForChildren = TotalHeightOfGridWidget - 
	    TotalHeightOfGridMargins; 
    HeightAllottedEachChild = AvailHeightForChildren / gw->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 < gw->composite.num_children; i++) {
	Widget ic = gw->composite.children[i];
	ExmGridConstraint glc = ExmGridCPart (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 == gw->grid.columns) {
	    column = 0;
	    row += 1;
	}
    } 
}



/******************************************************************************
 *
 *  CalcSize:
 *     Called by QueryGeometry, SetValues, GeometryManager, and ChangeManaged.
 *     Calculate the ideal size of the ExmGrid widget. 
 *     Only affects the returned size if it is 0.
 *
 ****************************************************************************/
static void 
CalcSize (
        Widget wid,
        Widget instigator,
        Dimension *TotalWidthOfGridWidget,
        Dimension *TotalHeightOfGridWidget
         )
{
 ExmGridWidget gw = (ExmGridWidget) wid ;
 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 < gw->composite.num_children; i++) {
	Widget ic = gw->composite.children[i];
	ExmGridConstraint glc = ExmGridCPart (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 * gw->grid.columns +
   			         (2 * (gw->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 * gw->grid.rows +
   			          (2 * (gw->grid.margin_height));
   }
}


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

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




/*-- Trait methods --*/


/****************************************************************
 *
 * Trait method for XmQTdialogShellSavvy trait. 
 *
 **************************************************************/
static void
CallMapUnmap(
	 Widget wid,
	 Boolean map_unmap)
{
    ExmGridWidget grid = (ExmGridWidget) 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.   
 *
*****************************************************************/

static XmRenderTable
GetTable(
	 Widget wid,
	 XtEnum type)
{
    ExmGridWidget grid = (ExmGridWidget) 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 ;
}


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


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

}
