/**
 *
 * $Id: RCUtils.c,v 1.58 1997/09/06 03:54:02 miers Exp $
 *
 * Copyright (C) 1996 Free Software Foundation, Inc.
 *
 * This file is part of the GNU LessTif Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/

static char rcsid[] = "$Id: RCUtils.c,v 1.58 1997/09/06 03:54:02 miers Exp $";

#include <LTconfig.h>
#include <XmI/XmI.h>

#include <X11/Xfuncs.h>
#include <Xm/XmP.h>
#include <Xm/BaseClassP.h>
#include <Xm/CascadeBP.h>
#include <Xm/CascadeBGP.h>
#include <Xm/DrawnBP.h>
#include <Xm/Label.h>
#include <Xm/LabelG.h>
#include <Xm/ManagerP.h>
#include <Xm/MenuShellP.h>
#include <Xm/MenuUtilP.h>
#include <Xm/PushBP.h>
#include <Xm/PushBGP.h>
#include <Xm/RepType.h>
#include <Xm/RowColumnP.h>
#include <Xm/ScreenP.h>
#include <Xm/Separator.h>
#include <Xm/SeparatoG.h>
#include <Xm/TearOffBP.h>
#include <Xm/ToggleBP.h>
#include <Xm/ToggleBGP.h>
#include <Xm/TransltnsP.h>

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>

#include <XmI/DebugUtil.h>

/*
 * interesting prototypes
 */
static void PreferredSizeNone(Widget rc, XtWidgetGeometry *rcg,
			      int *max_width, int *max_height);

static void PreferredSizeHT(Widget rc, XtWidgetGeometry *rcg,
			    int *max_width, int *max_height);

static void PreferredSizeHC(Widget rc, XtWidgetGeometry *rcg,
			    int *max_width, int *max_height);

static void DoLayoutHT(Widget rc, Widget instig,
		       XtWidgetGeometry *instig_request,
		       int max_width, int max_height);

static void DoLayoutHC(Widget rc, Widget instig,
		       XtWidgetGeometry *instig_request,
		       int max_width, int max_height);

static void PreferredSizeHT(Widget rc, XtWidgetGeometry *rcg,
			    int *max_width, int *max_height);

static void PreferredSizeHC(Widget rc, XtWidgetGeometry *rcg,
			    int *max_width, int *max_height);

static void DoLayoutVT(Widget rc, Widget instig,
		       XtWidgetGeometry *instig_request,
		       int max_width, int max_height);

static void DoLayoutVC(Widget rc, Widget instig,
		       XtWidgetGeometry *instig_request,
		       int max_width, int max_height);

void
dump_boxes(WidgetList wl, XmRCKidGeometry boxes, int cnt)
{
    int i;

    for (i = 0; i < cnt; i++)
    {
	printf("Child: %-20s box: %-20s : ptrs %p %p\n",
	       XtName(wl[i]), XtName(boxes[i].kid),
	       wl[i], boxes[i].kid);

	printf("       x/y: %3d %3d  w/h: %3d %3d\n",
	       XtX(wl[i]), XtY(wl[i]), XtWidth(wl[i]), XtHeight(wl[i]));

	printf("   box x/y: %3d %3d  w/h: %3d %3d mt/mb: %3d %3d\n",
	       boxes[i].box.x, boxes[i].box.y,
	       boxes[i].box.width, boxes[i].box.height,
	       boxes[i].margin_top, boxes[i].margin_bottom);
    }
}

/*
 * This function computes the preferred size when the packing is PACK_NONE
 */
static void
PreferredSizeNone(Widget rc, XtWidgetGeometry *rcg,
		  int *max_width, int *max_height)
{
    int i;
    Dimension curw, curh;

    *max_width = 0;
    *max_height = 0;
    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kid_geometry = &RC_Boxes(rc)[i];

	if (!XtIsManaged(RC_Boxes(rc)[i].kid))
	{
	    continue;
	}

	curw = kid_geometry->box.x + kid_geometry->box.width +
	    2 * kid_geometry->box.border_width;
	curh = kid_geometry->box.y + kid_geometry->box.height +
	    2 * kid_geometry->box.border_width;
	if (curw > *max_width)
	{
	    *max_width = curw;
	}
	if (curh > *max_height)
	{
	    *max_height = curh;
	}
    }

    rcg->request_mode = CWWidth | CWHeight | CWBorderWidth;
    rcg->width = *max_width;
    rcg->height = *max_height;
    rcg->border_width = XtBorderWidth(rc);
}

/*
 * This function treats the three following cases :
 *      XmWORK_AREA & XmHORIZONTAL & XmPACK_TIGHT
 *      XmMENU_BAR
 * ... which kind of explains what MENU_BAR means.
 *
 * Finally, it also covers the case where PreferredSizeHC is called but
 *   no number of columns is specified.
 *
 * According to the manual page, XmNnumColumns only matters in XmPACK_COLUMN.
 * I guess this means this function shouldn't think about XmNnumColumns.
 */
static void
PreferredSizeHT(Widget rc, XtWidgetGeometry *rcg,
		int *max_width, int *max_height)
{
    int i;
    Dimension current_x = RC_MarginW(rc) + MGR_ShadowThickness(rc);
    Dimension current_y = RC_MarginH(rc) + MGR_ShadowThickness(rc);
    Dimension curh = RC_MarginH(rc) + MGR_ShadowThickness(rc);

    if (XdbInDebug(__FILE__, rc))
    {
	DEBUGOUT(XdbDebug(__FILE__, rc, "PreferredSizeHT("));
	DEBUGOUT(XdbDebug0(__FILE__, rc, "%s",
			   RC_FromResize(rc) ? "" : "ParentResize "));
	DEBUGOUT(XdbDebug0(__FILE__, rc, ")\n"));
    }

    *max_height = 0, *max_width = 0;

    /* Calculate the max_height */
    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kid_geometry = &RC_Boxes(rc)[i];
	Widget kid = kid_geometry->kid;

	if (!XtIsManaged(kid))
	{
	    continue;
	}

	DEBUGOUT(XdbDebug2(__FILE__, rc, kid,
			   "PreferredSizeHT: kid geo %d %d %dx%d\n",
			   kid_geometry->box.x,
			   kid_geometry->box.y,
			   kid_geometry->box.width,
			   kid_geometry->box.height));

	/*
	 * If we don't have enough space in this row, start another one.
	 */
	if (RC_FromResize(rc) &&
	    current_x + kid_geometry->box.width > XtWidth(rc))
	{
	    current_y += *max_height + RC_Spacing(rc);
	    current_x = RC_MarginW(rc) + MGR_ShadowThickness(rc);
	}

	current_x += kid_geometry->box.width;

	if (kid_geometry->box.height + current_y > curh)
	{
	    curh = kid_geometry->box.height + current_y;
	}

	if (kid_geometry->box.height > *max_height)
	{
	    *max_height = kid_geometry->box.height;
	}

	/* Don't add spacing after last child */
	if (i != MGR_NumChildren(rc) - 1)
	{
	    current_x += RC_Spacing(rc);
	}

	if (current_x > *max_width)
	{
	    *max_width = current_x;
	}
    }

    rcg->request_mode = CWWidth | CWHeight | CWBorderWidth;
    if (*max_width == 0)
    {
	rcg->width = 2 * (RC_MarginW(rc) + MGR_ShadowThickness(rc));
    }
    else
    {
	rcg->width = *max_width + (RC_MarginW(rc) + MGR_ShadowThickness(rc));
    }
    rcg->height = curh + RC_MarginH(rc) + MGR_ShadowThickness(rc);
    rcg->border_width = XtBorderWidth(rc);
}

/*
 * this function actually lays things out in HT, according to
 * max_width, max_height
 */
static void
DoLayoutHT(Widget rc, Widget instig, XtWidgetGeometry *instig_request,
	   int max_width, int max_height)
{
    int i, j, lmh;
    int first_in_row = 0;
    Dimension current_x = RC_MarginW(rc) + MGR_ShadowThickness(rc);
    Dimension current_y = RC_MarginH(rc) + MGR_ShadowThickness(rc);
    XmRCKidGeometry help_kid_geometry = NULL;

    /* Start layout */
    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kid_geometry = &RC_Boxes(rc)[i];
	Widget kid = kid_geometry->kid;

	if (!XtIsManaged(kid))
	{
	    continue;
	}

	/* don't lay the help button out until the end. */
	if (kid == RC_HelpPb(rc))
	{
	    help_kid_geometry = kid_geometry;

	    continue;
	}

	/* Height treatment */
	kid_geometry->box.height = max_height;

	/*
	 * check for row adjustment
	 */
	if (current_x + kid_geometry->box.width > XtWidth(rc) &&
	    current_x > RC_MarginW(rc) + MGR_ShadowThickness(rc))
	{
	    /* compute the local max_height */
	    lmh = 0;
	    for (j = first_in_row; j < i; j++)
	    {
		if (XtIsManaged(RC_Boxes(rc)[j].kid) &&
		    RC_Boxes(rc)[j].box.height > lmh)
		    lmh = RC_Boxes(rc)[j].box.height;
	    }
	    /* set the local max_height */
	    for (j = first_in_row; j < i; j++)
	    {
		RC_Boxes(rc)[j].box.height = lmh;
	    }

	    current_y += lmh;
	    current_x = RC_MarginW(rc) + MGR_ShadowThickness(rc);
	    first_in_row = i;
	}

	kid_geometry->box.x = current_x;
	kid_geometry->box.y = current_y;

	if (kid == instig && instig_request != NULL)
	{
	    *instig_request = kid_geometry->box;
	}

	current_x += RC_Spacing(rc) + kid_geometry->box.width;
    }

    /* compute the local max_height */
    lmh = 0;
    for (j = first_in_row; j < i; j++)
    {
	if (XtIsManaged(RC_Boxes(rc)[j].kid) &&
	    RC_Boxes(rc)[j].box.height > lmh)
	{
	    lmh = RC_Boxes(rc)[j].box.height;
	}
    }

    /* set the local max_height */
    for (j = first_in_row; j < i; j++)
    {
	RC_Boxes(rc)[j].box.height = lmh;
    }

    /* now we fill in the last row, so it takes up the remaining height, 
     * if XmNadjustLast is True. */
    if (RC_AdjLast(rc))
    {
	for (i = first_in_row; i < MGR_NumChildren(rc); i++)
	{
	    XmRCKidGeometry kid_geometry = &RC_Boxes(rc)[i];
	    Widget kid = kid_geometry->kid;

	    if (!XtIsManaged(kid))
	    {
		continue;
	    }

	    if ((XtHeight(rc) <= current_y + RC_MarginH(rc)) ||
		(max_height + RC_MarginH(rc) > XtHeight(rc)))
	    {
		kid_geometry->box.height = max_height;
	    }
	    else
	    {
		kid_geometry->box.height = (XtHeight(rc)
		     - (current_y + RC_MarginH(rc) + MGR_ShadowThickness(rc)));
	    }

	    if (kid == instig && instig_request != NULL)
	    {
		*instig_request = kid_geometry->box;
	    }
	}
    }

    /* In some cases we don't have a help widget here ...
     * like when it's not managed
     */
    if (RC_HelpPb(rc) && XtIsManaged(RC_HelpPb(rc)) && help_kid_geometry)
    {
	help_kid_geometry->box.x = (XtWidth(rc)
				    - MGR_ShadowThickness(rc)
				    - help_kid_geometry->box.width);
	help_kid_geometry->box.y = current_y;
    }
}

/* 
 * Horizontal Column
 *  XmNnumColumns does determine geometry here (indicating number of rows !!).
 *      All children (of certain class) get to have the same width.
 */
static void
PreferredSizeHC(Widget rc, XtWidgetGeometry *rcg,
		int *max_width, int *max_height)
{
    int i, nrows;
    int number_per_row;

    DEBUGOUT(XdbDebug(__FILE__, rc, "PreferredSizeHC()\n"));

    if (RC_NCol(rc) == 0)
    {
	/* what the hell.  punt and go to .. */
	PreferredSizeHT(rc, rcg, max_width, max_height);
	return;
    }

    number_per_row = ((_XmGeoCount_kids((CompositeWidget)rc) - 1) /
		      RC_NCol(rc)) + 1;
    *max_width = 0, *max_height = 0;

    nrows = 0;

    /* first, figure out the width of each column and the height of each row */
    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kid_geometry;
	Widget kid;

	kid_geometry = &RC_Boxes(rc)[i];
	kid = kid_geometry->kid;

	if (!XtIsManaged(kid))
	{
	    continue;
	}

	nrows++;

	if (*max_width < kid_geometry->box.width)
	{
	    *max_width = kid_geometry->box.width;
	}

	if (*max_height < kid_geometry->box.height)
	{
	    *max_height = kid_geometry->box.height;
	}
    }

    if (number_per_row > 1 || nrows > RC_NCol(rc))
    {
	nrows = RC_NCol(rc);
    }

    /* Determine the RC's size */
    rcg->request_mode = CWWidth | CWHeight | CWBorderWidth;
    rcg->width = 2 * (RC_MarginW(rc) + MGR_ShadowThickness(rc))
	+ *max_width * number_per_row
	+ RC_Spacing(rc) * (number_per_row - 1);
    rcg->height = 2 * (RC_MarginH(rc) + MGR_ShadowThickness(rc)) +
	RC_Spacing(rc) * (nrows - 1) +
	*max_height * nrows;
    rcg->border_width = XtBorderWidth(rc);
}

/*
 * actually do the HC layout
 */
static void
DoLayoutHC(Widget rc, Widget instig, XtWidgetGeometry *instig_request,
	   int max_width, int max_height)
{
    int i;
    int number_per_row;
    Dimension current_x = RC_MarginW(rc) + MGR_ShadowThickness(rc);
    Dimension current_y = RC_MarginH(rc) + MGR_ShadowThickness(rc);

    if (RC_NCol(rc) == 0)
    {
	/* what the hell.  punt and go to .. */
	DoLayoutHT(rc, instig, instig_request, max_width, max_height);
	return;
    }

    number_per_row = ((_XmGeoCount_kids((CompositeWidget)rc) - 1) /
		      RC_NCol(rc)) + 1;

    /* now we lay out the children */
    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kid_geometry;
	Widget kid;

	kid_geometry = &RC_Boxes(rc)[i];
	kid = kid_geometry->kid;

	if (!XtIsManaged(kid))
	{
	    continue;
	}

	if (i != 0 && (i % number_per_row) == 0)
	{
	    current_x = RC_MarginW(rc) + MGR_ShadowThickness(rc);
	    current_y += max_height + RC_Spacing(rc);
	}

	kid_geometry->box.x = current_x;
	kid_geometry->box.y = current_y;

	kid_geometry->box.width = max_width;
	kid_geometry->box.height = max_height;

	if (kid == instig && instig_request != NULL)
	{
	    *instig_request = kid_geometry->box;
	}

	current_x += RC_Spacing(rc) + max_width;
    }

    /* now we fill in the last row, so it takes up the remaining height, 
     * if XmNadjustLast is True. */
    if (RC_AdjLast(rc))
    {
	for (i = MGR_NumChildren(rc) - number_per_row;
	     i < MGR_NumChildren(rc);
	     i++)
	{
	    XmRCKidGeometry kid_geometry;
	    Widget kid;

	    kid_geometry = &RC_Boxes(rc)[i];
	    kid = kid_geometry->kid;

	    if (!XtIsManaged(kid))
	    {
		continue;
	    }

	    kid_geometry->box.height = (XtHeight(rc)
					- (current_y
					   + RC_MarginH(rc)
					   + MGR_ShadowThickness(rc)));

	    if (kid == instig && instig_request != NULL)
	    {
		*instig_request = kid_geometry->box;
	    }

	}
    }
}

/*
 * Called for cases :
 *      XmVERTICAL & XmPACK_TIGHT (i.e., pulldowns)
 *      XmMENU_PULLDOWN
 *
 * According to the manual pages, XmNnumColumns should not make a difference
 * in this case.
 *
 * An extra complication is added by TearOffControl. This button is always
 * child 0, and when it's there, RC_TearOffControl is non-NULL.
 */
static void
PreferredSizeVT(Widget rc, XtWidgetGeometry *rcg,
		int *max_width, int *max_height)
{
    int i;
    Dimension current_x = RC_MarginW(rc) + MGR_ShadowThickness(rc);
    Dimension current_y = RC_MarginH(rc) + MGR_ShadowThickness(rc);
    Dimension curw = RC_MarginW(rc) + MGR_ShadowThickness(rc);

    *max_width = 0, *max_height = 0;

    /* Calculate the max_width */
    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kid_geometry = &RC_Boxes(rc)[i];
	Widget kid = kid_geometry->kid;

	if (!XtIsManaged(kid))
	{
	    continue;
	}

	DEBUGOUT(XdbDebug2(__FILE__, rc, kid_geometry->kid,
			   "%p:PreferredSizeVT: kid %s\n", rc,
			   XdbWidgetGeometry2String(&kid_geometry->box)));

	if (RC_FromResize(rc) &&
	    current_y + kid_geometry->box.height > XtHeight(rc))
	{
	    current_x += *max_width + RC_Spacing(rc);
	    current_y = RC_MarginH(rc) + MGR_ShadowThickness(rc);
	}

	current_y += kid_geometry->box.height;

	if (kid_geometry->box.width + current_x > curw)
	{
	    curw = kid_geometry->box.width + current_x;
	}

	if (kid_geometry->box.width > *max_width)
	{
	    *max_width = kid_geometry->box.width;
	}

	if (i != MGR_NumChildren(rc) - 1)
	{
	    current_y += RC_Spacing(rc);
	}

	if (current_y > *max_height)
	{
	    *max_height = current_y;
	}
    }

    DEBUGOUT(XdbDebug(__FILE__, rc,
		      "PreferredSizeVT: MaxWidth %d\n", *max_width));

    rcg->request_mode = CWWidth | CWHeight | CWBorderWidth;
    rcg->width = curw + RC_MarginW(rc) + MGR_ShadowThickness(rc);
    if (*max_height == 0)
    {
	rcg->height = 2 * (RC_MarginH(rc) + MGR_ShadowThickness(rc));
    }
    else
    {
	rcg->height = *max_height + (RC_MarginH(rc) + MGR_ShadowThickness(rc));
    }
    rcg->border_width = XtBorderWidth(rc);
}

/*
 * actually do the vertical tight layout
 */
static void
DoLayoutVT(Widget rc, Widget instig, XtWidgetGeometry *instig_request,
	   int max_width, int max_height)
{
    int i, j, lmw;
    int first_in_column = 0;
    Dimension current_x = RC_MarginW(rc) + MGR_ShadowThickness(rc);
    Dimension current_y = RC_MarginH(rc) + MGR_ShadowThickness(rc);

    /* Start layout */
    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kid_geometry = &RC_Boxes(rc)[i];
	Widget kid = kid_geometry->kid;

	if (!XtIsManaged(kid))
	{
	    continue;
	}

	DEBUGOUT(XdbDebug(__FILE__, rc,
			  "Child %p:%s width changed to %d\n",
			  kid, XtName(kid), max_width));

	/*
	 * See if we need to move to the next column.
	 *
	 * This is what the first condition does; the second insures that
	 * we don't do that if this is the only child in the column.
	 */
	if (current_y + kid_geometry->box.height > XtHeight(rc) &&
	    current_y > RC_MarginH(rc) + MGR_ShadowThickness(rc))
	{
	    /* compute the local max_width */
	    lmw = 0;
	    for (j = first_in_column; j < i; j++)
	    {
		if (XtIsManaged(RC_Boxes(rc)[j].kid) &&
		    RC_Boxes(rc)[j].box.width > lmw)
		    lmw = RC_Boxes(rc)[j].box.width;
	    }

	    /* set the local max_width */
	    for (j = first_in_column; j < i; j++)
	    {
		RC_Boxes(rc)[j].box.width = lmw;
	    }

	    /* Move the child to the next column. */
	    current_x += lmw;
	    current_y = RC_MarginH(rc) + MGR_ShadowThickness(rc);
	    first_in_column = i;
	}

	kid_geometry->box.x = current_x;
	kid_geometry->box.y = current_y;

	if (kid == instig && instig_request != NULL)
	{
	    *instig_request = kid_geometry->box;
	}

	current_y += RC_Spacing(rc) + kid_geometry->box.height;
    }

    /* compute the local max_width */
    lmw = 0;
    for (j = first_in_column; j < i; j++)
    {
	if (XtIsManaged(RC_Boxes(rc)[j].kid) &&
	    RC_Boxes(rc)[j].box.width > lmw)
	{
	    lmw = RC_Boxes(rc)[j].box.width;
	}
    }

    /* set the local max_width */
    for (j = first_in_column; j < i; j++)
    {
	RC_Boxes(rc)[j].box.width = lmw;
    }

    /* now we fill in the last column, so it takes up the remaining width, 
     * if XmNadjustLast is True. */
    if (RC_AdjLast(rc))
    {
	for (i = first_in_column; i < MGR_NumChildren(rc); i++)
	{
	    XmRCKidGeometry kid_geometry = &RC_Boxes(rc)[i];
	    Widget kid = kid_geometry->kid;

	    if (!XtIsManaged(kid))
	    {
		continue;
	    }

	    if ((XtWidth(rc) <= current_x + RC_MarginW(rc)) ||
		(max_width + RC_MarginW(rc) > XtWidth(rc)))
	    {
		kid_geometry->box.width = max_width;
		DEBUGOUT(XdbDebug(__FILE__, rc,
			  "Child %p:%s AdjLast too wide width changed to %d\n",
				  kid, XtName(kid), max_width));
	    }
	    else
	    {
		kid_geometry->box.width = (XtWidth(rc) -
		       (current_x + RC_MarginW(rc) + MGR_ShadowThickness(rc)));
		DEBUGOUT(XdbDebug(__FILE__, rc,
		      "Child %p:%s AdjLast not too wide width changed to %d\n",
				  kid, XtName(kid), kid_geometry->box.width));
	    }

	    if (kid == instig && instig_request != NULL)
	    {
		*instig_request = kid_geometry->box;
	    }
	}
    }
}

/*
 * Vertical Column
 *      XmNnumColumns does determine geometry here (and it does refer
 *      to number of columns).
 *      All children (of certain class) get to have the same height.
 *      It looks like separators and tearOffs don't get treated this way
 *      in Motif ...
 *
 * Called when :
 *      XmVERTICAL & XmPACK_COLUMN
 *      XmMENU_POPUP
 */
static void
PreferredSizeVC(Widget rc, XtWidgetGeometry *rcg,
		int *max_width, int *max_height)
{
    int i, ncols;
    int number_per_column;

    DEBUGOUT(XdbDebug(__FILE__, rc,
		      "PreferredSizeVC(ncols %d)\n", RC_NCol(rc)));

    if (RC_NCol(rc) == 0)
    {
	/* what the hell.  punt and go to .. */
	PreferredSizeVT(rc, rcg, max_width, max_height);
	return;
    }

    number_per_column = ((_XmGeoCount_kids((CompositeWidget)rc) - 1) /
			 RC_NCol(rc)) + 1;
    *max_width = 0, *max_height = 0;

    ncols = 0;
    /* first, figure out the height of each row and the width of each column */
    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kid_geometry = &RC_Boxes(rc)[i];
	Widget kid = kid_geometry->kid;

	kid_geometry = &RC_Boxes(rc)[i];
	kid = kid_geometry->kid;

	if (!XtIsManaged(kid) || XmIsTearOffButton(kid))
	{
	    continue;
	}

	ncols++;
	if (*max_height < kid_geometry->box.height)
	{
	    *max_height = kid_geometry->box.height;
	}

	if (*max_width < kid_geometry->box.width)
	{
	    *max_width = kid_geometry->box.width;
	}
    }

    if (number_per_column > 1 || ncols > RC_NCol(rc))
    {
	ncols = RC_NCol(rc);
    }

    /* Figure out how big we need to be */
    rcg->request_mode = CWWidth | CWHeight | CWBorderWidth;
    rcg->width = 2 * (RC_MarginW(rc) + MGR_ShadowThickness(rc)) +
	RC_Spacing(rc) * (ncols - 1) +
	*max_width * ncols;
    rcg->height = 2 * (RC_MarginH(rc) + MGR_ShadowThickness(rc))
	+ *max_height * number_per_column
	+ RC_Spacing(rc) * (number_per_column - 1);
    rcg->border_width = XtBorderWidth(rc);
}

/*
 * actually do the vertical column layout
 */
static void
DoLayoutVC(Widget rc, Widget instig, XtWidgetGeometry *instig_request,
	   int max_width, int max_height)
{
    int i;
    int number_per_column;
    Dimension current_x = RC_MarginW(rc) + MGR_ShadowThickness(rc);
    Dimension current_y = RC_MarginH(rc) + MGR_ShadowThickness(rc);

    DEBUGOUT(XdbDebug(__FILE__, rc,
		      "LayoutVC(ncols %d)\n", RC_NCol(rc)));

    if (RC_NCol(rc) == 0)
    {
	/* what the hell.  punt and go to .. */
	DoLayoutVT(rc, instig, instig_request, max_width, max_height);
	return;
    }

    number_per_column = ((_XmGeoCount_kids((CompositeWidget)rc) - 1) /
			 RC_NCol(rc)) + 1;

    /* now we lay out the children */
    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kid_geometry;
	Widget kid;

	kid_geometry = &RC_Boxes(rc)[i];
	kid = kid_geometry->kid;

	if (!XtIsManaged(kid))
	{
	    continue;
	}

	if (i != 0 && (i % number_per_column) == 0)
	{
	    current_x += max_width + RC_Spacing(rc);
	    current_y = RC_MarginH(rc) + MGR_ShadowThickness(rc);
	}

	kid_geometry->box.x = current_x;
	kid_geometry->box.y = current_y;

	kid_geometry->box.width = max_width;
	kid_geometry->box.height = max_height;

	if (kid == instig && instig_request != NULL)
	{
	    *instig_request = kid_geometry->box;
	    DEBUGOUT(XdbDebug(__FILE__, rc,
			      "LayoutVC: instig set to %s\n",
			      XdbWidgetGeometry2String(instig_request)));
	}

	current_y += RC_Spacing(rc) + max_height;
    }

    /*
     * Now we fill in the last column 
     * so it takes up the remaining width if XmNadjustLast is True.
     *
     * (The elements in the last column were also processed above, hence no
     *      x, y, height corrections here.)
     */
    if (RC_AdjLast(rc))
    {
	for (i = MGR_NumChildren(rc) - number_per_column;
	     i < MGR_NumChildren(rc);
	     i++)
	{
	    XmRCKidGeometry kid_geometry;
	    Widget kid;

	    kid_geometry = &RC_Boxes(rc)[i];
	    kid = kid_geometry->kid;

	    if (!XtIsManaged(kid))
	    {
		continue;
	    }

	    if (current_x + RC_MarginW(rc) + MGR_ShadowThickness(rc) <
		XtWidth(rc))
	    {
		kid_geometry->box.width = (XtWidth(rc)
					   - current_x
					   - RC_MarginW(rc)
					   - MGR_ShadowThickness(rc));
	    }

	    if (kid == instig && instig_request != NULL)
	    {
		*instig_request = kid_geometry->box;
	        DEBUGOUT(XdbDebug(__FILE__, rc,
			          "LayoutVC: instig set to %s after AdjLast\n",
			          XdbWidgetGeometry2String(instig_request)));
	    }
	}
    }
}

/*
 * This function was a major inefficiency provider of the RC widget -
 * it gets called by setvalues, insert_child, and change_managed;
 * and every time it does a _XmGeoLoadValues on all its children,
 * forgetting all the info from the previous calls ...
 *
 * In the process of fixing this... Danny 23/8/1996
 *
 * Moved all insert_child and delete_child interaction with this sucker either
 *      to delete_child or to a separate function. 25/8/96.
 *
 * Much better now :-)
 *
 * Add in the borderWidth, take it out just before configuring kids rws
 *
 * MLM 4/30/97-5/1/97 rework.
 * WE RELY ON MGR_Children() and RC_Boxes() having a one to one correspondence.
 * (That's maintained by the RCInsertBox/RCDeleteBox functions).  DO NOT CHANGE
 * THIS.
 */
XmRCKidGeometry
_XmRCGetKidGeo(Widget w, Widget instig,
	       XtWidgetGeometry *instig_request,
	       int uniform_border, Dimension border,
	       int uniform_width_margins, int uniform_height_margins,
	       Widget help, Widget toc, int geoType)
{
    int i;
    XmRCKidGeometry kid_geometry;
    Dimension mmt = 0, mmb = 0;
    XmRCKidGeometry boxes = NULL;

    DEBUGOUT(XdbDebug(__FILE__, w, "_XmRCGetKidGeo: instig %s, igeo: %s\n",
		      instig ? XtName(instig) : "(null)",
		      instig_request
			? XdbWidgetGeometry2String(instig_request)
			: "(no instig request)"));

    boxes = (XmRCKidGeometry)XtCalloc(MGR_NumChildren(w) + 1,
				      sizeof(XmRCKidGeometryRec));

    for (i = 0; i < MGR_NumChildren(w); i++)
    {
	kid_geometry = &boxes[i];
	kid_geometry->kid = MGR_Children(w)[i];

	if (i != RCC_PositionIndex(kid_geometry->kid))
	{
	    _XmError(w, "RCGetKidGeo: PositionIndex doesn't match "
		     "actual position\n");
	}

	if (!XtIsManaged(kid_geometry->kid))
	{
	    if (RCC_WasManaged(kid_geometry->kid))
	    {
		RCC_WasManaged(kid_geometry->kid) = False;
	    }
	    continue;
	}

	DEBUGOUT(XdbDebug(__FILE__, w,
			  "_XmRCGetKidGeo: doing change for %p:%s\n",
			  kid_geometry->kid, XtName(kid_geometry->kid)));

	/*
	 * Don't use the instig if we're not in a menu.  This is more
	 * correct (according to the Motif comments), and it helps
	 * the new form algorithm work.
	 */
	if (RC_Type(w) == XmWORK_AREA)
	{
	    _XmGeoLoadValues(kid_geometry->kid, geoType,
			     NULL, NULL, &kid_geometry->box);
	}
	else
	{
	    _XmGeoLoadValues(kid_geometry->kid, geoType,
			     instig, instig_request, &kid_geometry->box);
	}

	DEBUGOUT(XdbDebug(__FILE__, w,
			  "_XmRCGetKidGeo: did change for %p:%s : geo %s\n",
			  kid_geometry->kid, XtName(kid_geometry->kid),
			  XdbWidgetGeometry2String(&kid_geometry->box)));

	RCC_WasManaged(kid_geometry->kid) = True;

	if (uniform_border)
	{
	    kid_geometry->box.border_width = border;
	}

	kid_geometry->box.width += (2 * XtBorderWidth(kid_geometry->kid));
	kid_geometry->box.height += (2 * XtBorderWidth(kid_geometry->kid));

	if (mmt < RCC_MarginTop(kid_geometry->kid))
	    mmt = RCC_MarginTop(kid_geometry->kid);
	if (mmb < RCC_MarginBottom(kid_geometry->kid))
	    mmb = RCC_MarginBottom(kid_geometry->kid);

	if (RC_DoMarginAdjust(w) &&
	    (XmIsLabel(kid_geometry->kid) ||
	     XmIsLabelGadget(kid_geometry->kid)))
	{
	    if (RC_Orientation(w) == XmHORIZONTAL)
	    {
		if (XmIsLabel(kid_geometry->kid))
		{
		    kid_geometry->box.height -=
			Lab_MarginTop(kid_geometry->kid) +
			Lab_MarginBottom(kid_geometry->kid);
		}
		else
		{
		    kid_geometry->box.height -=
			LabG_MarginTop(kid_geometry->kid) +
			LabG_MarginBottom(kid_geometry->kid);
		}
	    }
	    else
	    {
		if (XmIsLabel(kid_geometry->kid))
		{
		    kid_geometry->box.width -=
			Lab_MarginLeft(kid_geometry->kid) +
			Lab_MarginRight(kid_geometry->kid);
		}
		else
		{
		    kid_geometry->box.width -=
			LabG_MarginLeft(kid_geometry->kid) +
			LabG_MarginRight(kid_geometry->kid);
		}
	    }
	}
    }

    if (RC_DoMarginAdjust(w))
    {
	for (i = 0; i < MGR_NumChildren(w); i++)
	{
	    kid_geometry = &boxes[i];

	    if (!XtIsManaged(kid_geometry->kid) ||
		(!XmIsLabel(kid_geometry->kid) &&
		 !XmIsLabelGadget(kid_geometry->kid)))
	    {
		continue;
	    }

	    if (mmt > kid_geometry->margin_top)
	    {
		kid_geometry->margin_top = mmt;
	    }
	    if (mmb > kid_geometry->margin_bottom)
	    {
		kid_geometry->margin_bottom = mmb;
	    }

	    if (RC_Orientation(w) == XmHORIZONTAL)
	    {
		kid_geometry->box.height += mmt + mmb;
	    }
	    else
	    {
		kid_geometry->box.width += mmt + mmb;
	    }
	}
    }

    return boxes;
}

void
_XmRCSetKidGeo(XmRCKidGeometry kg, Widget instigator)
{
    /* Now we actually lay out our children.  */
    while (kg->kid != NULL)
    {
	DEBUGOUT(XdbDebug(__FILE__, XtParent(kg->kid),
		     "In _XmRCSetKidGeo: child %p:%s set to (%s): was %d %d\n",
			  kg->kid, XtName(kg->kid),
			  XdbWidgetGeometry2String(&kg->box),
			  XtWidth(kg->kid), XtHeight(kg->kid)));

	if (!XtIsManaged(kg->kid))
	{
	    kg++;
	    continue;
	}

	if (kg->kid == instigator)
	{
	    if (kg->box.request_mode & CWX)
	    {
		XtX(instigator) = kg->box.x - kg->box.border_width;
	    }
	    if (kg->box.request_mode & CWY)
	    {
		XtY(instigator) = kg->box.y - kg->box.border_width;
	    }
	    if (kg->box.request_mode & CWWidth)
	    {
		XtWidth(instigator) = kg->box.width - 2 * kg->box.border_width;
	    }
	    if (kg->box.request_mode & CWHeight)
	    {
		XtHeight(instigator) = kg->box.height - 2 * kg->box.border_width;
	    }
	    if (kg->box.request_mode & CWBorderWidth)
	    {
		instigator->core.border_width = kg->box.border_width;
	    }
	}
	else if (kg->box.x != XtX(kg->kid) ||
		 kg->box.y != XtY(kg->kid) ||
		 kg->box.width != XtWidth(kg->kid) ||
		 kg->box.height != XtHeight(kg->kid) ||
		 kg->box.border_width != XtBorderWidth(kg->kid))
	{
	    _XmConfigureObject(kg->kid,
			       kg->box.x - kg->box.border_width,
			       kg->box.y - kg->box.border_width,
			       kg->box.width, kg->box.height,
			       kg->box.border_width);
	}

	kg++;
    }
}

void
_XmRCSetMargins(Widget rc)
{
    int i;

    for (i = 0; i < MGR_NumChildren(rc); i++)
    {
	XmRCKidGeometry kg = &RC_Boxes(rc)[i];

	if (RC_DoMarginAdjust(rc) && XtIsManaged(kg->kid) &&
	    (XmIsLabel(kg->kid) || XmIsLabelGadget(kg->kid)))
	{
	    DEBUGOUT(XdbDebug(__FILE__, kg->kid, "Setting margins to %d %d\n",
			      kg->margin_top, kg->margin_bottom));
	    if (RC_Orientation(rc) == XmHORIZONTAL)
	    {
		if (XmIsLabelGadget(kg->kid))
		{
		    _XmAssignLabG_MarginTop((XmLabelGadget)kg->kid,
					    kg->margin_top);
		    _XmAssignLabG_MarginBottom((XmLabelGadget)kg->kid,
					       kg->margin_bottom);
		    _XmReCacheLabG(kg->kid);
		}
		else
		{
		    Lab_MarginTop(kg->kid) = kg->margin_top;
		    Lab_MarginBottom(kg->kid) = kg->margin_bottom;
		}
	    }
	    else
	    {
		if (XmIsLabelGadget(kg->kid))
		{
		    _XmAssignLabG_MarginLeft((XmLabelGadget)kg->kid,
					     kg->margin_top);
		    _XmAssignLabG_MarginRight((XmLabelGadget)kg->kid,
					      kg->margin_bottom);
		    _XmReCacheLabG(kg->kid);
		}
		else
		{
		    Lab_MarginLeft(kg->kid) = kg->margin_top;
		    Lab_MarginRight(kg->kid) = kg->margin_bottom;
		}
	    }
	}
    }
}

/*
 * If CW is non-null, make sure not to resize it.
 *
 * The last parameter returns the RC size, to be used in geometry_manager.
 *  Why ? Because geometry_manager needs to call XmRowColumnLayout in test mode,
 *  and figure out from that whether the geometry change is acceptable. If it
 *  is, then it might need to resize the RC itself as a consequence.
 *  And *that* geometry change is a whole different issue from the one we can do
 *  inside XmRowColumnLayout.
 *
 * The meaning of "mode" :
 *  - test             == 1     means this is a query; nothing should be changed
 *  - RC_FromResize(rc) == 1    means (we're being called from the resize
 *                              method) we cannot resize ourselves, but must
 *                              fit in the size given in XtWidth/XtHeight
 *  - else                      means both Test and Resize are off
 */
XtGeometryResult
_XmRCAdjustSize(Widget rc, Widget instig, XtWidgetGeometry *instig_request)
{
    int max_width, max_height;
    XtWidgetGeometry rcg;
    XtGeometryResult ret = XtGeometryNo;

    DEBUGOUT(XdbDebug(__FILE__, rc,
		      "_XmRCAdjustSize: current size %d %d instig %p\n",
		      XtWidth(rc), XtHeight(rc), instig));

    /* No need to do anything else if we don't have any children */
    if (MGR_NumChildren(rc) == 0 ||
	_XmGeoCount_kids((CompositeWidget)rc) == 0)
    {
	return XtGeometryNo;
    }

    DEBUGOUT(XdbDebug(__FILE__, rc, "XmRCAdjustSize (%s) %s\n",
		      XdbRcType2String(RC_Type(rc)),
		      !RC_FromResize(rc) ? "" : "ParentResize"));

    if (RC_Boxes(rc))
	XtFree((char *)RC_Boxes(rc));

    RC_Boxes(rc) = _XmRCGetKidGeo(rc, instig, instig_request,
				  RC_EntryBorder(rc), RC_EntryBorder(rc),
				  0, 0, NULL, NULL,
				  XmGET_PREFERRED_SIZE);

    /*
     * As you can see the rcg parameter of the PreferredSize*() functions is
     * never NULL
     */
    switch (RC_Orientation(rc))
    {
    case XmHORIZONTAL:
	if (RC_Packing(rc) == XmPACK_TIGHT)
	{
	    PreferredSizeHT(rc, &rcg, &max_width, &max_height);
	}
	else if (RC_Packing(rc) == XmPACK_COLUMN)
	{
	    PreferredSizeHC(rc, &rcg, &max_width, &max_height);
	}
	else
	{
	    PreferredSizeNone(rc, &rcg, &max_width, &max_height);
	}
	break;

    case XmVERTICAL:
	if (RC_Packing(rc) == XmPACK_TIGHT)
	{
	    PreferredSizeVT(rc, &rcg, &max_width, &max_height);
	}
	else if (RC_Packing(rc) == XmPACK_COLUMN)
	{
	    PreferredSizeVC(rc, &rcg, &max_width, &max_height);
	}
	else
	{
	    PreferredSizeNone(rc, &rcg, &max_width, &max_height);
	}
	break;

    default:
	_XmError(rc, "EEK!  What kind of RC is this?\n");
    }


    if (max_width == 0)
    {
	max_width = 1;
    }
    if (max_height == 0)
    {
	max_height = 1;
    }

    if (!RC_FromResize(rc))
    {
	DEBUGOUT(XdbDebug(__FILE__, rc,
			  "_XmRCAdjustSize: requesting %s from parent %s\n",
			XdbWidgetGeometry2String(&rcg), XtName(XtParent(rc))));

	if (!RC_ResizeWidth(rc) && XtIsRealized(rc))
	{
	    rcg.request_mode &= ~CWWidth;
	    rcg.width = XtWidth(rc);
	}
	if (!RC_ResizeHeight(rc) && XtIsRealized(rc))
	{
	    rcg.request_mode &= ~CWHeight;
	    rcg.height = XtHeight(rc);
	}

	if ((ret = _XmMakeGeometryRequest(rc, &rcg)) == XtGeometryYes)
	{
	    DEBUGOUT(XdbDebug(__FILE__, rc,
		       "_XmMakeGeometryRequest for RC said yes: size %d %d.\n",
			      XtWidth(rc), XtHeight(rc)));
	}
	else
	{
	    DEBUGOUT(XdbDebug(__FILE__, rc,
		       "_XmMakeGeometryRequest for RC no: size stays %d %d.\n",
			      XtWidth(rc), XtHeight(rc)));
	}
    }
    else
    {
	DEBUGOUT(XdbDebug(__FILE__, rc,
			  "_XmRCAdjustSize: RC resize is %d x %d %d\n",
			  XtWidth(rc), XtHeight(rc), XtBorderWidth(rc)));
    }

    switch (RC_Orientation(rc))
    {
    case XmHORIZONTAL:
	if (RC_Packing(rc) == XmPACK_TIGHT)
	{
	    DoLayoutHT(rc, instig, instig_request, max_width, max_height);
	}
	else if (RC_Packing(rc) == XmPACK_COLUMN)
	{
	    DoLayoutHC(rc, instig, instig_request, max_width, max_height);
	}
	else
	{
	    /* Nothing to be done for XmPACK_NONE */
	}
	break;

    case XmVERTICAL:
	if (RC_Packing(rc) == XmPACK_TIGHT)
	{
	    DoLayoutVT(rc, instig, instig_request, max_width, max_height);
	}
	else if (RC_Packing(rc) == XmPACK_COLUMN)
	{
	    DoLayoutVC(rc, instig, instig_request, max_width, max_height);
	}
	else
	{
	    /* Nothing to be done for XmPACK_NONE */
	}
	break;

    default:
	_XmError(rc, "EEK!  What kind of RC is this?\n");
    }

    if (instig == NULL)
    {
	_XmRCSetMargins(rc);
	_XmRCSetKidGeo(RC_Boxes(rc), instig);
    }

    return ret;
}

void
_XmRCPreferredSize(Widget rc, XtWidgetGeometry *rcg)
{
    int max_width, max_height;

    /* No need to do anything else if we don't have any children */
    if (MGR_NumChildren(rc) == 0 ||
	_XmGeoCount_kids((CompositeWidget)rc) == 0)
    {
	rcg->request_mode = 0;
	return;
    }

    if (RC_Boxes(rc))
	XtFree((char *)RC_Boxes(rc));

    RC_Boxes(rc) = _XmRCGetKidGeo(rc, NULL, NULL,
				  RC_EntryBorder(rc), RC_EntryBorder(rc),
				  0, 0, NULL, NULL,
				  XmGET_PREFERRED_SIZE);

    /*
     * As you can see the rcg parameter of the PreferredSize*() functions is
     * never NULL
     */
    switch (RC_Orientation(rc))
    {
    case XmHORIZONTAL:
	if (RC_Packing(rc) == XmPACK_TIGHT)
	{
	    PreferredSizeHT(rc, rcg, &max_width, &max_height);
	}
	else if (RC_Packing(rc) == XmPACK_COLUMN)
	{
	    PreferredSizeHC(rc, rcg, &max_width, &max_height);
	}
	else
	{
	    PreferredSizeNone(rc, rcg, &max_width, &max_height);
	}
	break;

    case XmVERTICAL:
	if (RC_Packing(rc) == XmPACK_TIGHT)
	{
	    PreferredSizeVT(rc, rcg, &max_width, &max_height);
	}
	else if (RC_Packing(rc) == XmPACK_COLUMN)
	{
	    PreferredSizeVC(rc, rcg, &max_width, &max_height);
	}
	else
	{
	    PreferredSizeNone(rc, rcg, &max_width, &max_height);
	}
	break;

    default:
	_XmError(rc, "EEK!  What kind of RC is this?\n");
    }
}

/*********************** convenience functions **************************/

Widget
XmCreateMenuBar(Widget parent, char *name,
		Arg *arglist, Cardinal argcount)
{
    /* menu bar's have the their rowColumnType set to XmMENU_BAR and
       are homogeneous (they only accept CascadeButtons, and 
       CascadeButtonGadgets */

    Widget w;
    Arg myArgList[5];
    int n = 0;

    ArgList combined;

    XtSetArg(myArgList[n], XmNrowColumnType, XmMENU_BAR); n++;
    XtSetArg(myArgList[n], XmNorientation, XmHORIZONTAL); n++;
    XtSetArg(myArgList[n], XmNpacking, XmPACK_TIGHT); n++;
    XtSetArg(myArgList[n], XmNisHomogeneous, True); n++;
    XtSetArg(myArgList[n], XmNentryClass, xmCascadeButtonWidgetClass); n++;

    combined = XtMergeArgLists(myArgList, n, arglist, argcount);

    w = XtCreateWidget(name,
		       xmRowColumnWidgetClass,
		       parent,
		       combined, n + argcount);

    XtFree((char *)combined);

    return w;
}

Widget
XmCreateOptionMenu(Widget parent, char *name,
		   Arg *arglist, Cardinal argcount)
{
    /* option menus have the their rowColumnType set to XmMENU_OPTION */
    Widget w;
    Arg myArgList[4];
    int n = 0;

    ArgList combined;

    XtSetArg(myArgList[n], XmNrowColumnType, XmMENU_OPTION); n++;
    XtSetArg(myArgList[n], XmNorientation, XmHORIZONTAL); n++;
    XtSetArg(myArgList[n], XmNentryAlignment, XmALIGNMENT_CENTER); n++;

    combined = XtMergeArgLists(myArgList, n, arglist, argcount);

    w = XtCreateWidget(name,
		       xmRowColumnWidgetClass,
		       parent,
		       combined, n + argcount);

    XtFree((char *)combined);

    return w;
}

static void
_XmPopupButtonPressHandler(Widget w, XtPointer client_data,
			   XEvent *event, Boolean *cont)
{
    Widget rc = (Widget)client_data;
    XmMenuState state = _XmGetMenuState(w);

    /* first check if the event is valid wrt our menupost resource */
    if (event->xany.type != ButtonPress ||
	event->xbutton.button != RC_PostButton(rc)
    /* should also check the modifiers, but since we don't set 
       them to a useful value... */ )
    {
	return;
    }

    /* if we're already waiting to be managed, do nothing. */
    if (state->RC_ButtonEventStatus.waiting_to_be_managed)
    {
	return;
    }

    DEBUGOUT(XdbDebug(__FILE__, rc, "  Filling in ButtonEventStatusRec\n"));

    state->RC_ButtonEventStatus.waiting_to_be_managed = True;
    state->RC_ButtonEventStatus.event = event->xbutton;
    state->RC_ButtonEventStatus.time = event->xbutton.time;
    state->RC_ButtonEventStatus.verified = True;	/* Is it really? FIX ME */
}

Widget
XmCreatePopupMenu(Widget parent, char *name,
		  Arg *arglist, Cardinal argcount)
{
    /* popup menus have the their rowColumnType set to XmMENU_POPUP */

    Widget w, ms;
    Arg myArgList[1];
    Arg shell_args[3];
    int shell_ac;
    int n = 0;
    char *popup_name = XtMalloc(strlen("popup_") + strlen(name) + 1);

    ArgList combined;

    strcpy(popup_name, "popup_");
    strcat(popup_name, name);


    XtSetArg(myArgList[n], XmNrowColumnType, XmMENU_POPUP);
    n++;

    combined = XtMergeArgLists(myArgList, n, arglist, argcount);

    shell_ac = 0;
    XtSetArg(shell_args[shell_ac], XmNwidth, 10); shell_ac++;	/* FIX ME */
    XtSetArg(shell_args[shell_ac], XmNheight, 10); shell_ac++;	/* FIX ME */
    XtSetArg(shell_args[shell_ac], XmNallowShellResize, True); shell_ac++;

    ms = XtCreatePopupShell(popup_name,
			    xmMenuShellWidgetClass,
			    parent, shell_args, shell_ac);

    XtFree(popup_name);

    w = XtCreateWidget(name,
		       xmRowColumnWidgetClass,
		       ms,
		       combined, n + argcount);


    /* since we're not managed -- we leave that up to the application
       to get us to pop up, we realize ourselves here. */
    /*XtRealizeWidget(w); */

    XtAddEventHandler(parent, ButtonPressMask, False,
		      _XmPopupButtonPressHandler, (XtPointer)w);

    XtFree((char *)combined);

    return w;
}

Widget
XmCreatePulldownMenu(Widget parent, char *name,
		     Arg *arglist, Cardinal argcount)
{
    /* pulldown menus have their rowColumnType set to XmMENU_PULLDOWN */
    Widget w, ms;
    Arg myArgList[5];
    int n = 0;
    char *popup_name = XtMalloc(strlen("popup_") + strlen(name) + 1);
    ArgList combined;
    Arg shell_args[3];
    int shell_ac;

    strcpy(popup_name, "popup_");
    strcat(popup_name, name);

    XtSetArg(myArgList[n], XmNrowColumnType, XmMENU_PULLDOWN); n++;
    XtSetArg(myArgList[n], XmNorientation, XmVERTICAL); n++;
    XtSetArg(myArgList[n], XmNpacking, XmPACK_TIGHT); n++;
    XtSetArg(myArgList[n], XmNnumColumns, 1); n++;

    combined = XtMergeArgLists(myArgList, n, arglist, argcount);

    shell_ac = 0;
    XtSetArg(shell_args[shell_ac], XmNwidth, 10); shell_ac++; /* FIX ME */
    XtSetArg(shell_args[shell_ac], XmNheight, 10); shell_ac++;/* FIX ME */
    XtSetArg(shell_args[shell_ac], XmNallowShellResize, True); shell_ac++;

    ms = XtCreatePopupShell(popup_name,
			    xmMenuShellWidgetClass,
			    parent, shell_args, shell_ac);

    XtFree(popup_name);

    w = XtCreateWidget(name,
		       xmRowColumnWidgetClass,
		       ms,
		       combined, n + argcount);

    XtFree((char *)combined);

    return w;
}

Widget
XmCreateRadioBox(Widget parent, char *name,
		 Arg *arglist, Cardinal argcount)
{
    /* radio boxes have their rowColumnType set to XmWORK_AREA, and their 
       radioBehavior set to true.  The also are homogeneous and accept 
       ToggleButtons, and ToggleButtonGadgets */

    Widget w;
    Arg myArgList[2];
    int n = 0;

    ArgList combined;

    XtSetArg(myArgList[n], XmNrowColumnType, XmWORK_AREA); n++;
    XtSetArg(myArgList[n], XmNradioBehavior, True); n++;

    combined = XtMergeArgLists(myArgList, n, arglist, argcount);

    w = XtCreateWidget(name,
		       xmRowColumnWidgetClass,
		       parent,
		       combined, n + argcount);

    XtFree((char *)combined);

    return w;
}

Widget
XmCreateRowColumn(Widget parent, char *name,
		  Arg *arglist, Cardinal argcount)
{
    return XtCreateWidget(name, xmRowColumnWidgetClass, parent,
			  arglist, argcount);
}

Widget
XmCreateWorkArea(Widget parent, char *name,
		 Arg *arglist, Cardinal argcount)
{
    Widget w;
    Arg myArgList[2];
    int n = 0;

    ArgList combined;

    XtSetArg(myArgList[n], XmNrowColumnType, XmWORK_AREA); n++;

    combined = XtMergeArgLists(myArgList, n, arglist, argcount);

    w = XtCreateWidget(name,
		       xmRowColumnWidgetClass,
		       parent,
		       combined, n + argcount);

    XtFree((char *)combined);

    return w;
}

void
XmMenuPosition(Widget menu, XButtonPressedEvent *event)
{
    int x, y;

    if (!XmIsMenuShell(XtParent(menu)) || RC_Type(menu) != XmMENU_POPUP)
    {
	_XmWarning(menu, "XmMenuPosition called with a non popup menu.");
	return;
    }

    x = event->x_root;
    y = event->y_root;

    if (x + menu->core.width >= WidthOfScreen(XtScreen(menu)))
    {
	x = WidthOfScreen(XtScreen(menu)) - menu->core.width - 1;
    }
    if (y + menu->core.height >= HeightOfScreen(XtScreen(menu)))
    {
	y = HeightOfScreen(XtScreen(menu)) - menu->core.height - 1;
    }

    if (x < 0)
    {
	x = 0;
    }
    if (y < 0)
    {
	y = 0;
    }

    /* should be all that's needed */
    _XmMoveObject(XtParent(menu), x, y);
}

Widget
XmGetTearOffControl(Widget menu)
{
    if (XmIsRowColumn(menu))
    {
	return RC_TearOffControl(menu);
    }
    return NULL;
}

void
XmAddToPostFromList(Widget menu_wid, Widget widget)
{
    /* well, we could either be adding menu_wid to widget's post
     * from list or adding widget to menu_wid's post from list.
     * I'll opt for the latter. */
    if (RC_PostFromList(menu_wid) == NULL)
    {
	RC_PostFromListSize(menu_wid) = 5;

	RC_PostFromList(menu_wid) = (Widget *)XtMalloc(sizeof(Widget) *
						RC_PostFromListSize(menu_wid));
	RC_PostFromCount(menu_wid) = 0;
    }

    RC_PostFromList(menu_wid)[RC_PostFromCount(menu_wid)++] = widget;

    if (RC_PostFromCount(menu_wid) == RC_PostFromListSize(menu_wid))
    {
	RC_PostFromListSize(menu_wid) *= 2;

	RC_PostFromList(menu_wid) =
	    (Widget *)XtRealloc((char *)RC_PostFromList(menu_wid),
				sizeof(Widget) *
				RC_PostFromListSize(menu_wid));
    }
}

void
XmRemoveFromPostFromList(Widget menu_wid, Widget widget)
{
    int i;

    if (RC_PostFromList(menu_wid) == NULL)
	return;			/* should we post an error/warning? */

    for (i = 0; i < RC_PostFromCount(menu_wid); i++)
    {
	if (RC_PostFromList(menu_wid)[i] == widget)
	{
	    int j;

	    /* found it, now slide everything down. */
	    for (j = i; j < RC_PostFromCount(menu_wid) - 1; j++)
	    {
		RC_PostFromList(menu_wid)[j] = RC_PostFromList(menu_wid)[j + 1];
	    }

	    RC_PostFromCount(menu_wid)--;

	    return;
	}
    }
    /* should getting to this point also be an error? */
}

/*
 * The result of XmGetPostedFromWidget when the callback is activated by
 * an accelerator is not defined in the manual.
 */
Widget
XmGetPostedFromWidget(Widget menu)
{
    if (XmIsRowColumn(menu))
    {
	return RC_LastSelectToplevel(menu);
    }

    return NULL;
}
