/* 
 * Motif Tools Library, Version 2.0
 * $Id: Layout.c,v 2.19 1994/07/04 03:03:50 david Exp $
 * 
 * Written by David Flanagan.
 * Copyright (c) 1992, 1993, 1994 by Dovetail Systems.
 * All Rights Reserved.  See the file COPYRIGHT for details.
 * This is not free software.  See the file SHAREWARE for details.
 * There is no warranty for this software.  See NO_WARRANTY for details.
 */

#include <Xmt/XmtP.h>
#include <Xmt/LayoutP.h>
#include <Xmt/LayoutGP.h>
#include <Xmt/ConvertersP.h>
#include <Xmt/Pixmap.h>
#include <Xm/DialogS.h>
#if XmVersion >= 1002
#include <Xm/DrawP.h>  /* declaration of _XmDrawShadows() */
#endif

	
#define offset(field) XtOffsetOf(XmtLayoutRec, field)
static XtResource resources[] = {
{XmtNlayout, XmtCLayout, XtRString, sizeof(String),
     offset(layout.layout), XtRString, NULL},
{XmtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
     offset(layout.font), XtRString, XtDefaultFont},
{XmtNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
     offset(layout.font_list), XmRFontList, (XtPointer) NULL},
{XmtNdefaultSpacing, XmtCDefaultSpacing, XtRDimension, sizeof(Dimension),
     offset(layout.default_spacing), XtRImmediate, (XtPointer) 10},
{XmtNorientation, XmCOrientation, XmROrientation,
     sizeof(unsigned char), offset(layout.orientation),
     XtRImmediate, (XtPointer) XmVERTICAL},
{XmtNdebugLayout, XmtCDebugLayout, XtRBoolean,
     sizeof(Boolean), offset(layout.debug_layout),
     XtRImmediate, (XtPointer)False},
/* override some BulletinBoard defaults */
{XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension,
     sizeof(Dimension),
     XtOffsetOf(struct _XmBulletinBoardRec, bulletin_board.margin_width),
     XmRImmediate, (XtPointer)5},
{XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension,
     sizeof(Dimension),
     XtOffsetOf(struct _XmBulletinBoardRec, bulletin_board.margin_height),
     XmRImmediate, (XtPointer)5},
};
#undef offset

#define offset(field) XtOffsetOf(XmtLayoutConstraintsRec, layout.field)
static XtResource constraint_resources[] = {
{XmtNlayoutWidth, XmtCLayoutWidth, XtRDimension,
     sizeof(Dimension), offset(width),
     XtRImmediate, (XtPointer) 0},
{XmtNlayoutHeight, XmtCLayoutHeight, XtRDimension,
     sizeof(Dimension), offset(height),
     XtRImmediate, (XtPointer) 0},
{XmtNlayoutStretchability, XmtCLayoutStretchability, XtRDimension,
     sizeof(Dimension), offset(stretchability),
     XtRImmediate, (XtPointer) -1},  /* dynamic default */
{XmtNlayoutShrinkability, XmtCLayoutShrinkability, XtRDimension,
     sizeof(Dimension), offset(shrinkability),
     XtRImmediate, (XtPointer) -1},  /* dynamic default */
{XmtNlayoutCaption, XmtCLayoutCaption, XmRXmString,
     sizeof(XmString), offset(caption),
     XtRImmediate, NULL},
{XmtNlayoutIn, XmtCLayoutIn, XtRWidget,
     sizeof(Object), offset(in),
     XtRImmediate, (XtPointer) NULL},
{XmtNlayoutPosition, XmtCLayoutPosition, XtRPosition,
     sizeof(Position), offset(position),
     XtRImmediate, (XtPointer) -1},
{XmtNlayoutAfter, XmtCLayoutAfter, XtRWidget,
     sizeof(Object), offset(after),
     XtRImmediate, (XtPointer) NULL},
{XmtNlayoutBefore, XmtCLayoutBefore, XtRWidget,
     sizeof(Object), offset(before),
     XtRImmediate, (XtPointer) NULL},
{XmtNlayoutAllowResize, XmtCLayoutAllowResize, XtRBoolean,
     sizeof(Boolean), offset(allow_resize),
     XtRImmediate, (XtPointer)True},
{XmtNlayoutSensitive, XmtCLayoutSensitive, XtRBoolean,
     sizeof(Boolean), offset(sensitive),
     XtRImmediate, (XtPointer)True},
{XmtNlayoutJustification, XmtCLayoutJustification, XmtRXmtLayoutJustification,
     sizeof(unsigned char), offset(justification),
     XtRImmediate, (XtPointer)XmtLayoutFilled},
{XmtNlayoutMarginWidth, XmtCLayoutMarginWidth, XtRUnsignedChar,
     sizeof(unsigned char), offset(margin_width),
     XtRImmediate, (XtPointer)255},  /* special unset value */
{XmtNlayoutMarginHeight, XmtCLayoutMarginHeight, XtRUnsignedChar,
     sizeof(unsigned char), offset(margin_height),
     XtRImmediate, (XtPointer)255},  /* special unset value */
{XmtNlayoutFrameType, XmtCLayoutFrameType, XmtRXmtLayoutFrameType,
     sizeof(unsigned char), offset(frame_type),
     XtRImmediate, (XtPointer)XmtLayoutFrameNone},
{XmtNlayoutFrameLineType, XmtCLayoutFrameLineType, XmtRXmtLayoutFrameLineType,
     sizeof(unsigned char), offset(frame_line_type),
     XtRImmediate, (XtPointer)XmtLayoutFrameEtchedIn},
{XmtNlayoutFramePosition, XmtCLayoutFramePosition, XmtRXmtLayoutFramePosition,
     sizeof(unsigned char), offset(frame_position),
     XtRImmediate, (XtPointer)XmtLayoutFrameInside},
{XmtNlayoutFrameMargin, XmtCLayoutFrameMargin, XtRUnsignedChar,
     sizeof(unsigned char), offset(frame_margin),
     XtRImmediate, (XtPointer)2},
{XmtNlayoutFrameThickness, XmtCLayoutFrameThickness, XtRUnsignedChar,
     sizeof(unsigned char), offset(frame_thickness),
     XtRImmediate, (XtPointer)2},
{XmtNlayoutCaptionPosition, XmtCLayoutCaptionPosition, XmtRXmtLayoutEdge,
     sizeof(unsigned char), offset(caption_position),
     XtRImmediate, (XtPointer)XmtLayoutLeft},
{XmtNlayoutCaptionJustification, XmtCLayoutCaptionJustification,
     XmtRXmtLayoutJustification,
     sizeof(unsigned char), offset(caption_justification),
     XtRImmediate, (XtPointer)XmtLayoutCentered},
{XmtNlayoutCaptionAlignment, XmtCLayoutCaptionAlignment, XmRAlignment,
     sizeof(unsigned char), offset(caption_alignment),
     XtRImmediate, (XtPointer)XmALIGNMENT_BEGINNING},
{XmtNlayoutCaptionMargin, XmtCLayoutCaptionMargin, XtRUnsignedChar,
     sizeof(unsigned char), offset(caption_margin),
     XtRImmediate, (XtPointer)2},
};
#undef offset

#if NeedFunctionPrototypes
static void ClassInitialize(void);
static void Initialize(Widget, Widget, ArgList, Cardinal *);
static void Destroy(Widget);
static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal *);
static void Resize(Widget);
static void Realize(Widget, XtValueMask *, XSetWindowAttributes *);
static void Redisplay(Widget, XEvent *, Region);
static void ChangeManaged(Widget);
static XtGeometryResult GeometryManager(Widget, XtWidgetGeometry *,
                                        XtWidgetGeometry *);
static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry *,
                                      XtWidgetGeometry *);
static void InsertChild(Widget);
static void DeleteChild(Widget);
static void ConstraintInitialize(Widget, Widget, ArgList, Cardinal *);
static void ConstraintDestroy(Widget);
static Boolean ConstraintSetValues(Widget,Widget, Widget, ArgList, Cardinal *);
#else
static void ClassInitialize();
static void Initialize();
static void Destroy();
static Boolean SetValues();
static void Resize();
static void Realize();
static void Redisplay();
static void ChangeManaged();
static XtGeometryResult GeometryManager();
static XtGeometryResult QueryGeometry();
static void InsertChild();
static void DeleteChild();
static void ConstraintInitialize();
static void ConstraintDestroy();
static Boolean ConstraintSetValues();
#endif

#define superclass (&xmBulletinBoardClassRec)

externaldef(xmtlayoutclassrec) XmtLayoutClassRec xmtLayoutClassRec = {
{   /* core_class fields */
    /* superclass         */    (WidgetClass) superclass,
    /* class_name         */    "XmtLayout",
    /* widget_size        */    sizeof(XmtLayoutRec),
    /* class_initialize   */    ClassInitialize,
    /* class_part_init    */    NULL,
    /* class_inited       */    FALSE,
    /* initialize         */    Initialize,
    /* initialize_hook    */    NULL,
    /* realize            */    Realize,
    /* 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  */    XtInheritSetValuesAlmost,
    /* get_values_hook    */    NULL,
    /* accept_focus       */    NULL,
    /* version            */    XtVersion,
    /* callback_private   */    NULL,
    /* tm_table           */    XtInheritTranslations,
    /* query_geometry     */    QueryGeometry,
    /* display_accelerator*/    XtInheritDisplayAccelerator,
    /* extension          */    NULL
  },
  { /* composite_class fields */
    /* geometry_manager   */    GeometryManager,
    /* change_managed     */    ChangeManaged,
    /* insert_child       */    InsertChild,
    /* delete_child       */    DeleteChild,
    /* extension          */    NULL
  },
  { /* constraint_class fields */
    /* resource list 	  */    constraint_resources,
    /* num resources	  */    XtNumber(constraint_resources),
    /* constraint size    */    sizeof(XmtLayoutConstraintsRec),
    /* init proc	  */    ConstraintInitialize,
    /* destroy proc       */    ConstraintDestroy,
    /* set values proc    */    ConstraintSetValues,
    /* extension 	  */    NULL
  },
  { /* manager_class	  */
    /* translations 	  */    XtInheritTranslations,
    /* syn_resources	  */	NULL,
    /* num_syn_resources  */	0,
    /* syn_cont_resources */	NULL,
    /* num_syn_cont_resources */0,
    /* parent_process     */    XmInheritParentProcess,
    /* extension	  */	NULL
  },
  { /* Bulletin Board     */
    /* always_install_accelerators */ False,
    /* geo_matrix_create  */	NULL,
    /* focus_moved_proc   */	XmInheritFocusMovedProc,
    /* extension	  */    NULL
  },
  { /* XmtLayout          */
    /* parser             */    NULL,
    /* lookup_type_proc   */    NULL,
    /* create_proc        */    NULL,
    /* extension	  */    NULL
  }
};

externaldef(xmtlayoutwidgetclass)
WidgetClass xmtLayoutWidgetClass = (WidgetClass) &xmtLayoutClassRec;

#if NeedFunctionPrototypes
static void ClassInitialize(void)
#else
static void ClassInitialize()
#endif
{
    static String justification_names[] = {
	"Bottom", "Center", "Centered", "Filled", "Left", "Right", "Top"};
    static int justification_values[] = {
	XmtLayoutFlushBottom, XmtLayoutCentered, XmtLayoutCentered,
	XmtLayoutFilled,XmtLayoutFlushLeft,
	XmtLayoutFlushRight, XmtLayoutFlushTop};
    static String justification_prefixes[] = {"Xmt", "Layout", "Flush", NULL};
    
    static String edge_names[] = {
	"Bottom", "Left", "Right", "Top"};
    static int edge_values[] = {
        XmtLayoutBottom, XmtLayoutLeft, XmtLayoutRight, XmtLayoutTop
    };
    static String edge_prefixes[] = {"Xmt", "Layout", NULL};

    static String line_type_names[] = {
        "Double", "DoubleLine", "Etched", "EtchedIn", "EtchedOut",
        "Shadow", "ShadowIn", "ShadowOut", "Single", "SingleLine",
    };
    static int line_type_values[] = {
        XmtLayoutFrameDoubleLine, XmtLayoutFrameDoubleLine,
        XmtLayoutFrameEtchedIn, XmtLayoutFrameEtchedIn,
        XmtLayoutFrameEtchedOut, XmtLayoutFrameShadowIn,
        XmtLayoutFrameShadowIn, XmtLayoutFrameShadowOut,
        XmtLayoutFrameSingleLine, XmtLayoutFrameSingleLine
    };

    static String frame_type_names[] = {
        "Bottom", "Box", "Left", "None", "Right", "Top" };
    static int frame_type_values[] = {
        XmtLayoutFrameBottom, XmtLayoutFrameBox, XmtLayoutFrameLeft,
        XmtLayoutFrameNone, XmtLayoutFrameRight, XmtLayoutFrameTop
    };
    static String frame_type_prefixes[]  = {"Xmt", "Layout", "Frame", NULL};

    static String position_names[] = { "Inside", "Outside", "Through" };
    static int position_values[] = {
        XmtLayoutFrameInside, XmtLayoutFrameOutside, XmtLayoutFrameThrough };

    static String space_type_names[] = {
        "Even", "Interval", "LCR", "LREven", "None"
    };
    static int space_type_values[] = {
        XmtLayoutSpaceEven, XmtLayoutSpaceInterval, XmtLayoutSpaceLCR,
        XmtLayoutSpaceLREven, XmtLayoutSpaceNone
    };
    static String space_type_prefixes[] = {"Xmt", "Layout", "Space", NULL};
    
/* A bitmap we use for stippling captions */
#   define _xmt_gray50_width 32
#   define _xmt_gray50_height 2
    static unsigned char _xmt_gray50_bits[] = {
	0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa};

    XmtRegisterEnumConverter(XmtRXmtLayoutJustification,
			     justification_names, justification_values,
			     XtNumber(justification_names),
			     justification_prefixes);

    XmtRegisterEnumConverter(XmtRXmtLayoutEdge,
			     edge_names, edge_values,
			     XtNumber(edge_names),
			     edge_prefixes);

    XmtRegisterEnumConverter(XmtRXmtLayoutFrameLineType,
			     line_type_names, line_type_values,
			     XtNumber(line_type_names),
			     frame_type_prefixes); /* really frame_type here */

    XmtRegisterEnumConverter(XmtRXmtLayoutFrameType,
			     frame_type_names, frame_type_values,
			     XtNumber(frame_type_names),
			     frame_type_prefixes);

    XmtRegisterEnumConverter(XmtRXmtLayoutFramePosition,
			     position_names, position_values,
			     XtNumber(position_names),
			     frame_type_prefixes); /* really */

    XmtRegisterEnumConverter(XmtRXmtLayoutSpaceType,
			     space_type_names, space_type_values,
			     XtNumber(space_type_names),
			     space_type_prefixes);

    XmtRegisterXbmData("_xmt_gray50", (char *)_xmt_gray50_bits, NULL,
		       _xmt_gray50_width, _xmt_gray50_height, 0, 0);
}


#if NeedFunctionPrototypes
static void FontFromFontList(XmtLayoutWidget lw)
#else
static void FontFromFontList(lw)
XmtLayoutWidget lw;
#endif
{
    XmFontContext context;
    XmStringCharSet charset;

    lw->layout.font_list = XmFontListCopy(lw->layout.font_list);
    (void)XmFontListInitFontContext(&context, lw->layout.font_list);
    (void)XmFontListGetNextFont(context, &charset, &lw->layout.font);
    XmFontListFreeFontContext(context);
    XtFree(charset);
}

#if NeedFunctionPrototypes
static void GetGCs(XmtLayoutWidget lw)
#else
static void GetGCs(lw)
XmtLayoutWidget lw;
#endif
{
    XGCValues gcv;
    XtGCMask mask;

    /*
     * we set a font, even though we don't use it because XmStringDraw
     * frobs the font field and will cause an X error if it is unset.
     */
    gcv.foreground = lw->manager.foreground;
    gcv.background = lw->core.background_pixel;
    gcv.font = lw->layout.font->fid;
    gcv.line_width = 0;
    mask = GCForeground | GCBackground | GCFont | GCLineWidth;
    lw->layout.gc = XtGetGC((Widget)lw, mask, &gcv);

    /*
     * We could remember this bitmap and free it when the Layout is destroyed,
     * but because of caching, this will rarely be worth the trouble.
     */
    gcv.fill_style = FillStippled;
    gcv.stipple = XmtLookupBitmap((Widget)lw, "_xmt_gray50");
    mask |= GCFillStyle | GCStipple;
    lw->layout.grey_gc = XtGetGC((Widget)lw, mask, &gcv);
}

/* ARGSUSED */
#if NeedFunctionPrototypes
static void Initialize(Widget request, Widget init,
		       ArgList args, Cardinal *num_args)
#else
static void Initialize(request, init, args, num_args)
Widget request;
Widget init;
ArgList args;
Cardinal *num_args;
#endif
{
    XmtLayoutWidget lw = (XmtLayoutWidget) init;
    Display *dpy = XtDisplay(init);
    int scr = XScreenNumberOfScreen(XtScreen(init));
    Arg arg;

    /*
     * Copy the font list resource, and get its first font.
     * Or, create a font list from the font.
     */
    if (lw->layout.font_list)
	FontFromFontList(lw);
    else
	lw->layout.font_list = XmFontListCreate(lw->layout.font,
						XmSTRING_DEFAULT_CHARSET);
    
    /* figure out resolution independence values */
    lw->layout.pixpermm =(DisplayWidth(dpy,scr)/DisplayWidthMM(dpy,scr) +
			  DisplayHeight(dpy,scr)/DisplayHeightMM(dpy,scr)) / 2;
    lw->layout.pixperem = XTextWidth(lw->layout.font, "M", 1);


    /* get a GC for use by our gadget children */
    GetGCs(lw);

    /* initialize misc. variables */
    lw->layout.needs_layout = False;
    lw->layout.layout_disabled = 0;

    lw->layout.in_parser = False;
    lw->layout.widget_info = NULL;

    /*
     * create the toplevel XmtLayoutBox gadget.
     */
    lw->layout.in_parser = True;
    XtSetArg(arg, XmtNorientation, lw->layout.orientation);
    /*
     * got to initialize it to NULL first in order to detect
     * this special case in ConstraintInitialize
     */
    lw->layout.toplevel = NULL;
    lw->layout.toplevel = XmtCreateLayoutBox(init, "topbox", &arg, 1);
    XtManageChild(lw->layout.toplevel);

    /*
     * go parse the layout string, if any.
     * We don't copy this string because we parse it once
     * and never need it again.
     */
    if (lw->layout.layout) {
	if (xmtLayoutClassRec.layout_class.parser == NULL)
	    XmtWarningMsg("XmtLayout", "noParser",
			  "%s: no parser registered for layout string.\n\tSee XmtRegisterLayoutParser().",
			  XtName((Widget)lw));
	else
	    (*xmtLayoutClassRec.layout_class.parser)(lw, lw->layout.layout);
    }
    lw->layout.in_parser = False;

    /*
     * go layout any children that were automatically created by
     * parsing the layout string, but don't issue a resize request for
     * the desired size.  If width and height are not set, we set them
     * from the preferred size.
     */
    _XmtLayoutChildren(lw, True);
    if (lw->core.width == 0)
	lw->core.width = LayoutConstraints(lw->layout.toplevel).pref_w +
	    2*lw->bulletin_board.margin_width;
    
    if (lw->core.height == 0)
	lw->core.height = LayoutConstraints(lw->layout.toplevel).pref_h +
 	    2*lw->bulletin_board.margin_height;
}

#if NeedFunctionPrototypes
static void Destroy(Widget w)
#else
static void Destroy(w)
Widget w;
#endif
{
    XmtLayoutWidget lw = (XmtLayoutWidget) w;

    XmFontListFree(lw->layout.font_list);
    XtReleaseGC(w, lw->layout.gc);
    XtReleaseGC(w, lw->layout.grey_gc);
}

#if NeedFunctionPrototypes
static void Realize(Widget w, XtValueMask *vm, XSetWindowAttributes *wa)
#else
static void Realize(w, vm, wa)
Widget w;
XtValueMask *vm;
XSetWindowAttributes *wa;
#endif
{
    XmtLayoutWidget lw = (XmtLayoutWidget)w;
    Widget *children;
    Cardinal num;
    int i;
    
    /*
     * Because Motif (1.1, at least) treats all RectObjs as XmGadgets,
     * we've had to make LayoutGadgets a subclass of Object that just
     * happen to have many of the same fields as RectObj.  There are
     * a number of hacks here to support this--see the redefinition of
     * XtIsManaged(), and XmtSetRectObj(), for example, in LayoutP.h
     * 
     * This is another of those hacks--when the Layout is realized with
     * only gadget children (as can happen with the XmtWorkingBox, e.g.)
     * the Intrinsics decide that it has no managed children, and never
     * invoke the ChangeManaged procedure.  We attempt to compensate for
     * that here.
     */
    children = lw->composite.children;
    num = lw->composite.num_children;
    for(i=0; i < num; i++)
	if (XtIsManaged(children[i]) &&
	    (XtIsWidget(children[i]) || !XmtIsLayoutGadget(children[i])))
	    break;
    if (i == num)
	ChangeManaged(w);

    /*
     * And now envelop our superclass's realize method, so we
     * actually get realized.
     */
    (*superclass->core_class.realize)(w, vm, wa);
}

/* ARGSUSED */
#if NeedFunctionPrototypes
static Boolean SetValues(Widget current, Widget request, Widget set,
			 ArgList args, Cardinal *num_args)
#else
static Boolean SetValues(current, request, set, args, num_args)
Widget current;
Widget request;
Widget set;
ArgList args;
Cardinal *num_args;
#endif
{
    XmtLayoutWidget cw = (XmtLayoutWidget) current;
    XmtLayoutWidget nw = (XmtLayoutWidget) set;
    Boolean got_new_gc = False;
    Boolean relayout = False;
    Boolean redisplay = False;

#define Changed(field) (nw->layout.field != cw->layout.field)

    /* can't change XmtNlayout */
    if (Changed(layout)) {
	XmtWarningMsg("XmtLayout", "setlayout",
		      "%s: the XmtNlayout resource may not be changed.",
		      XtName(set));
	nw->layout.layout = cw->layout.layout;
    }

    /* can't change XmtNorientation */
    if (Changed(orientation)) {
	XmtWarningMsg("XmtLayout", "setorient",
		      "%s: the XmtNorientation resource may not be changed.",
		      XtName(set));
	nw->layout.orientation = cw->layout.orientation;
    }
    
    /*
     * if fontlist or font changes, free old and figure out the other one.
     */
    if (Changed(font_list)) {
	XmFontListFree(cw->layout.font_list);
	FontFromFontList(nw);
    }
    else if (Changed(font)) {
	XmFontListFree(cw->layout.font_list);
	nw->layout.font_list = XmFontListCreate(nw->layout.font,
						XmSTRING_DEFAULT_CHARSET);
    }
    
    /*
     * if the font list changed through either of the above cases,
     * get a new GC, change resolution independence values, 
     * invoke the change_font method on all LayoutStringGadget children,
     * and re-measure all caption strings.
     */
    if (Changed(font_list)) {
	int i;
	Widget child;
	XmtLayoutConstraintsPart *c;
	
	XtReleaseGC(set, cw->layout.gc);
	XtReleaseGC(set, cw->layout.grey_gc);
	GetGCs(nw);
	got_new_gc = True;

	/*
	 * we can compute new resolution independence conversion values,
	 * but we cannot go update all the children that had their size
	 * based on the font...
	 */
	nw->layout.pixperem = XTextWidth(nw->layout.font, "M", 1);

	/*
	 * tell any LayoutGadget children that care about the change.
	 * This is a hardcoded dependency which is a little gross.
	 * It means that we don't have to keep a bunch of copies of
	 * the same fontlist around, however.
	 */
	for(i=0; i < nw->composite.num_children; i++) {
	    child = nw->composite.children[i];
	    c = LayoutConstraintsRec(child);
	    if (!XtIsWidget(child) && !XmIsGadget(child)
		&& XtIsSubclass(child, xmtLayoutGadgetClass))
		_XmtCallChangeFontMethod(child);
	    if (c->caption)
		XmStringExtent(nw->layout.font_list, c->caption,
			       &c->caption_width, &c->caption_height);
	}
	
	relayout = True;
    }

    if (Changed(debug_layout))
	relayout = True;

    /* if colors changed and we haven't already done so, get a new gc */
    if (!got_new_gc &&
	((nw->core.background_pixel != cw->core.background_pixel) ||
	 (nw->manager.foreground != cw->manager.foreground))) {
	XtReleaseGC(set, cw->layout.gc);
	XtReleaseGC(set, cw->layout.grey_gc);
	GetGCs(nw);
	redisplay = True;
    }

    /* if margin size changed, re-layout everything */
    if ((nw->bulletin_board.margin_width != cw->bulletin_board.margin_width) ||
	(nw->bulletin_board.margin_height != cw->bulletin_board.margin_height))
	relayout = True;

    /* if sensitivity changed, redisplay everything to change captions */
    if (nw->core.sensitive != cw->core.sensitive) redisplay = True;

    if (relayout)
	_XmtLayoutChildren(nw, True);
    
    return redisplay | relayout;

#undef Changed
}

#if NeedFunctionPrototypes
void _XmtLayoutChildren(XmtLayoutWidget lw, XmtWideBoolean resize)
#else
void _XmtLayoutChildren(lw, resize)
XmtLayoutWidget lw;
int resize;
#endif
{
    XtWidgetGeometry geometry;

    /* don't do anything if we've been told not to,
     * but remember to later
     */
    if (lw->layout.layout_disabled > 0) {
	lw->layout.needs_layout = True;
	return;
    }

    /*
     * figure out how big we'd like to be.
     * the gadget's query_geometry methods also do other necessary
     * computation, so this must be called before the resize method...
     */
    XtQueryGeometry(lw->layout.toplevel, NULL, &geometry);

    /* remember this preferred size */
    LayoutConstraints(lw->layout.toplevel).pref_w = geometry.width;
    LayoutConstraints(lw->layout.toplevel).pref_h = geometry.height;

    /* add a margin all the way around */
    geometry.width += 2*lw->bulletin_board.margin_width;
    geometry.height += 2*lw->bulletin_board.margin_height;

    /* ask for size plus margins unless we're being called from Resize */
    if (resize &&
	((geometry.width != lw->core.width) ||
	 (geometry.height != lw->core.height))) {
	XtGeometryResult result;
	Dimension compromisew, compromiseh;
	result = XtMakeResizeRequest((Widget)lw,
				     geometry.width, geometry.height,
				     &compromisew, &compromiseh);
	if (result == XtGeometryAlmost)
	    XtMakeResizeRequest((Widget)lw,compromisew,compromiseh,NULL, NULL);
    }

    /*
     * Whether we got a new size or not, configure all the kids
     * to the current size.
     * If the size hasn't changed, this call is a no-op.
     * This means that when we're called from changeManaged, often
     * nothing is called, and the new widget doesn't get configured
     * to appear in the right place.  To work around this, we explictly
     * set the width on the toplevel box to something different.
     */
    lw->layout.toplevel->core.width = 0;
    XtConfigureWidget(lw->layout.toplevel,
		      lw->bulletin_board.margin_width,
		      lw->bulletin_board.margin_height,
		      lw->core.width - 2*lw->bulletin_board.margin_width,
		      lw->core.height - 2*lw->bulletin_board.margin_height,
		      lw->layout.toplevel->core.border_width);

    lw->layout.needs_layout = False;
}


#if NeedFunctionPrototypes
static void DrawFrame(XmtLayoutWidget lw, XmtLayoutFrameType type,
		      XmtLayoutFrameLineType linetype,
		      int thickness, int x, int y, int w, int h)
#else
static void DrawFrame(lw, type, linetype, thickness, x, y, w, h)
XmtLayoutWidget lw;
XmtLayoutFrameType type;
XmtLayoutFrameLineType linetype;
int thickness;
int x;
int y;
int w;
int h;
#endif
{
    Display *dpy = XtDisplay((Widget)lw);
    Window win = XtWindow((Widget)lw);
    Boolean horizontal = False;
    GC topgc = lw->manager.top_shadow_GC;
    GC botgc = lw->manager.bottom_shadow_GC;
    GC tmpgc;  /* for swapping */
    int halfthick = thickness/2;

    if (type == XmtLayoutFrameNone) return;

    if (type == XmtLayoutFrameTop || type == XmtLayoutFrameBottom) 
	horizontal = True;
    
    if (thickness == 1) linetype = XmtLayoutFrameSingleLine;

    if (linetype == XmtLayoutFrameSingleLine) {
	XGCValues values;
	
	if (thickness > 1) {
	    values.line_width = thickness;
	    XChangeGC(dpy, lw->layout.gc, GCLineWidth, &values);
	}

	if (type == XmtLayoutFrameBox)
	    XDrawRectangle(dpy, win, lw->layout.gc,
			   x+halfthick, y+halfthick,
			   w-thickness, h-thickness);
	else if (horizontal)
	    XDrawLine(dpy, win, lw->layout.gc,
		      x, y+halfthick, x+w, y+halfthick);
	else
	    XDrawLine(dpy, win, lw->layout.gc,
		      x+halfthick, y, x+halfthick, y+h);
	    
	if (thickness > 1) {
	    values.line_width = 0;
	    XChangeGC(dpy, lw->layout.gc, GCLineWidth, &values);
	}
    }
    else if (linetype == XmtLayoutFrameDoubleLine)
    {
	if (type == XmtLayoutFrameBox) {
	    XDrawRectangle(dpy, win, lw->layout.gc, x, y, w-1, h-1);
	    XDrawRectangle(dpy, win, lw->layout.gc,
			   x+thickness-1, y+thickness-1,
			   w-2*(thickness-1)-1, h-2*(thickness-1)-1);
	}
	else if (horizontal) {
	    XDrawLine(dpy, win, lw->layout.gc, x, y, x+w, y);
	    XDrawLine(dpy, win, lw->layout.gc,
		      x, y+thickness-1, x+w, y+thickness-1);
	}
	else {
	    XDrawLine(dpy, win, lw->layout.gc, x, y, x, y+h);
	    XDrawLine(dpy, win, lw->layout.gc,
		      x+thickness-1, y, x+thickness-1, y+h);
	}
    }
    else {
	if (type == XmtLayoutFrameBox) {
	    
#if XmVersion >= 1002
	    {
		int xmtype;
		
		switch(linetype) {
		case XmtLayoutFrameShadowIn:
		    xmtype = XmSHADOW_IN;
		    break;
		case XmtLayoutFrameShadowOut:
		    xmtype = XmSHADOW_OUT;
		    break;
		default:
		case XmtLayoutFrameEtchedIn:
		    xmtype = XmSHADOW_ETCHED_IN;
		    break;
		case XmtLayoutFrameEtchedOut:
		    xmtype = XmSHADOW_ETCHED_OUT;
		    break;
		}
		_XmDrawShadows(dpy, win, topgc, botgc,
			       x, y, w, h, thickness, xmtype);
	    }
#else
	    {
		int roundthick = halfthick*2;

		if (linetype == XmtLayoutFrameShadowIn ||
		    linetype == XmtLayoutFrameEtchedIn) {
		    tmpgc = botgc;
		    botgc = topgc;
		    topgc = tmpgc;
		}
		
		switch(linetype) {
		case XmtLayoutFrameShadowOut:
		case XmtLayoutFrameShadowIn:
		    _XmDrawShadow(dpy, win, topgc, botgc, thickness,
				  x, y, w, h);
		    break;
		case XmtLayoutFrameEtchedOut:
		case XmtLayoutFrameEtchedIn:
		    _XmDrawShadow(dpy, win, topgc, botgc, halfthick,
				  x, y, w, h);
		    _XmDrawShadow(dpy, win, botgc, topgc, halfthick,
				  x+halfthick, y+halfthick,
				  w-roundthick, h-roundthick);
		    break;
		default:
		    break;
		}
	    }
#endif
	}
	else {  /* draw an etched line in or out */
	    if (linetype == XmtLayoutFrameShadowIn)
		linetype = XmtLayoutFrameEtchedIn;
	    if (linetype == XmtLayoutFrameShadowOut)
		linetype = XmtLayoutFrameEtchedOut;
	    if (linetype == XmtLayoutFrameEtchedIn) {
		tmpgc = botgc;
		botgc = topgc;
		topgc = tmpgc;
	    }
	    if (horizontal) {
		XFillRectangle(dpy, win, topgc, x, y, w, halfthick);
		XFillRectangle(dpy, win, botgc,
			       x, y+halfthick, w, thickness-halfthick);
	    }
	    else {
		XFillRectangle(dpy, win, topgc, x, y, halfthick, h);
		XFillRectangle(dpy, win, botgc,
			       x+halfthick, y, thickness-halfthick, h);
	    }
	}
    }
}


#if NeedFunctionPrototypes
static void DrawFrameAndCaption(XmtLayoutWidget lw, Widget c, Region region)
#else
static void DrawFrameAndCaption(lw, c, region)
XmtLayoutWidget lw;
Widget c;
Region region;
#endif
{
    XmtLayoutConstraintsPart *cc;
    int x, y, w, h;      /* coordinates of widget */
    int fx, fy, fw, fh;  /* coordinates of outside of frame */
    int cx, cy, cw, ch;  /* coordinates of caption bounding box*/
    XmtLayoutFrameType frame_type;
    XmtLayoutFrameLineType frame_line_type;
    int frame_thickness;
    int frame_margin;
    XmtLayoutFramePosition frame_position;
    int frame_total;
    int caption_total_width, caption_total_height;
    Boolean frame_redrawn;

    /*
     * For the specified child widget:
     * compute the position of the frame and caption.  If the frame intersects
     * the region, redraw it.  If the caption intersects the region, or
     * if the frame intersects the caption and the frame was redrawn, then
     * redraw the caption
     */
    
    cc = LayoutConstraintsRec(c);
    
    /* get the frame args.  If debug_layout is set, force fixed values */
    frame_type = cc->frame_type;
    frame_line_type = cc->frame_line_type;
    frame_margin = cc->frame_margin;
    frame_thickness = cc->frame_thickness;
    frame_position = cc->frame_position;
    if (lw->layout.debug_layout) {
	frame_type = XmtLayoutFrameBox;
	frame_line_type = XmtLayoutFrameSingleLine;
	frame_margin = 5;
	frame_thickness = 1;
	frame_position = XmtLayoutFrameOutside;
    }
    
    frame_total = frame_margin + frame_thickness;
    if (cc->caption) {
	caption_total_width = cc->caption_width + cc->caption_margin;
	caption_total_height = cc->caption_height + cc->caption_margin;
    }

    /* if no frame or caption, don't do anything */
    if (cc->caption == NULL && frame_type == XmtLayoutFrameNone)
	return;
    
    /* widget coordinates */
    x = c->core.x; y = c->core.y; w = c->core.width; h = c->core.height;
    
    /* compute coordinates of outside of frame */
    if (frame_type != XmtLayoutFrameNone) {
	fx = x - frame_total;
	fy = y - frame_total;
	fw = w + 2*frame_total;
	fh = h + 2*frame_total;
	if (cc->caption &&
	    (frame_position == XmtLayoutFrameOutside)) {
	    switch (cc->caption_position) {
	    case XmtLayoutTop:
		fy -= caption_total_height;
		/* note no break statement */
	    case XmtLayoutBottom:
		fh += caption_total_height;
		break;
	    case XmtLayoutLeft:
		fx -= caption_total_width;
		/* note no break statement */
	    case XmtLayoutRight:
		fw += caption_total_width;
		break;
	    }
	}
    }

    /* if the frame is just a line rather than a box, adjust coords */
    /* (x,y) will be top left of line, now, not outside of frame */
    switch(frame_type) {
    case XmtLayoutFrameBottom:
	fy += fh - frame_thickness;
	/* note: no break statement */
    case XmtLayoutFrameTop:
	fx += frame_total;
	fw -= 2*frame_total;
	break;
    case XmtLayoutFrameRight:
	fx += fw - frame_thickness;
	/* note: no break */
    case XmtLayoutFrameLeft:
	fy += frame_total;
	fh -= 2*frame_total;
	break;
    default: break;
    }
    
    /* compute coordinates of caption bounding box */
    if (cc->caption) {
	/* width and height are given */
	cw = cc->caption_width;
	ch = cc->caption_height;
	
	/* compute one dimension based on position */
	switch (cc->caption_position) {
	case XmtLayoutTop:
	    cy = y - (cc->caption_margin + ch);
	    break;
	case XmtLayoutBottom:
	    cy = y + h + cc->caption_margin;
	    break;
	case XmtLayoutLeft:
	    cx = x - (cc->caption_margin + cw);
	    break;
	case XmtLayoutRight:
	    cx = x + w + cc->caption_margin;
	    break;
	}
	
	/* compute the other dimension based on justification */
	if ((cc->caption_position == XmtLayoutTop) ||
	    (cc->caption_position == XmtLayoutBottom)) {
	    switch (cc->caption_justification) {
	    case XmtLayoutFilled:
	    case XmtLayoutFlushLeft:  cx = x; break;
	    case XmtLayoutCentered:   cx = x + (w - cw)/2; break;
	    case XmtLayoutFlushRight: cx = x + w - cw; break;
	    }
	}
	else if ((cc->caption_position == XmtLayoutLeft) ||
		 (cc->caption_position == XmtLayoutRight)) {
	    switch (cc->caption_justification) {
	    case XmtLayoutFilled:
	    case XmtLayoutFlushLeft:  cy = y; break;
	    case XmtLayoutCentered:   cy = y + (h - ch)/2; break;
	    case XmtLayoutFlushRight: cy = y + h - ch; break;
	    }
	}
	
	/* if there is a frame inside the caption, adjust position */
	if ((frame_type != XmtLayoutFrameNone) &&
	    (frame_position == XmtLayoutFrameInside)) {

	    /* adjustments for full frames */
	    if (frame_type == XmtLayoutFrameBox) {
		switch (cc->caption_position) {
		case XmtLayoutTop:     cy -= frame_total; break;
		case XmtLayoutBottom:  cy += frame_total; break;
		case XmtLayoutLeft:    cx -= frame_total; break;
		case XmtLayoutRight:   cx += frame_total; break;
		}
		
		if ((cc->caption_position == XmtLayoutTop) ||
		    (cc->caption_position == XmtLayoutBottom)) {
		    switch (cc->caption_justification) {
		    case XmtLayoutFilled:
		    case XmtLayoutFlushLeft:  cx -= frame_total; break;
		    case XmtLayoutCentered:   break;
		    case XmtLayoutFlushRight: cx += frame_total; break;
		    }
		}
		else if ((cc->caption_position == XmtLayoutLeft) ||
			 (cc->caption_position == XmtLayoutRight)) {
		    switch (cc->caption_justification) {
		    case XmtLayoutFilled:
		    case XmtLayoutFlushLeft:  cy -= frame_total; break;
		    case XmtLayoutCentered:   break;
		    case XmtLayoutFlushRight: cy += frame_total; break;
		    }
		}
	    }
	    else if (frame_type == XmtLayoutFrameLeft) {
		if (cc->caption_position == XmtLayoutLeft)
		    cx -= frame_total;
	    }
	    else if (frame_type == XmtLayoutFrameRight) {
		if (cc->caption_position == XmtLayoutRight)
		    cx += frame_total;
	    }
	    else if (frame_type == XmtLayoutFrameTop) {
		if (cc->caption_position == XmtLayoutTop)
		    cy -= frame_total;
	    }
	    else if (frame_type == XmtLayoutFrameBottom) {
		if (cc->caption_position == XmtLayoutBottom)
		    cy += frame_total;
	    }
	}
    }
    
    /* if there is a frame, and it is in the exposed region, redraw it */
    frame_redrawn = False;
    if ((frame_type != XmtLayoutFrameNone) &&
	(!region || (XRectInRegion(region, fx, fy, fw, fh) != RectangleOut))) {
	frame_redrawn = True;
	DrawFrame(lw, frame_type, frame_line_type, frame_thickness,
		  fx, fy, fw, fh);
    }
    
    /*
     * if there is a caption, and it is in the exposed region,
     * or if it intersects the frame and the frame was redrawn
     * then redraw it.
     */
    if (cc->caption &&
	(!region ||
	 (XRectInRegion(region, cx, cy, cw, ch) != RectangleOut) ||
	 (frame_redrawn && (frame_position == XmtLayoutFrameThrough)))) {
	/* X protocol ignores stippling with DrawImage functions,
	 * so we simulate XmStringDrawImage by clearing first
	 */
	XClearArea(XtDisplay((Widget)lw), XtWindow((Widget)lw),
		   cx, cy, cw, ch, False);
	XmStringDraw(XtDisplay((Widget)lw), XtWindow((Widget)lw),
		     lw->layout.font_list, cc->caption,
		     (lw->core.ancestor_sensitive &&
		      lw->core.sensitive &&
		      cc->sensitive)
		     ?lw->layout.gc
		     :lw->layout.grey_gc,
		     cx, cy, cw,
		     cc->caption_alignment,
		     XmSTRING_DIRECTION_L_TO_R, NULL);
    }
}


#if NeedFunctionPrototypes
static void DrawFramesAndCaptions(XmtLayoutWidget lw, Region region)
#else
static void DrawFramesAndCaptions(lw, region)
XmtLayoutWidget lw;
Region region;
#endif
{
    int i;
    Widget c;

    /*
     * For each child widget:
     * compute the position of the frame and caption.  If the frame intersects
     * the region, redraw it.  If the caption intersects the region, or
     * if the frame intersects the caption and the frame was redrawn, then
     * redraw the caption
     */

    ForEachChild(lw, c)
	if (XtIsManaged(c))
	    DrawFrameAndCaption(lw, c, region);
}

#if NeedFunctionPrototypes
static void Redisplay(Widget widget, XEvent *event, Region region)
#else
static void Redisplay(widget, event, region)
Widget widget;
XEvent *event;
Region region;
#endif
{
    XmtLayoutWidget lw = (XmtLayoutWidget) widget;
    
    /* invoke the Redisplay method of the topmost XmtLayoutBox */
    _XmtRedisplayGadget(lw->layout.toplevel, event, region);

    /* redisplay any Xm Gadgets */
    _XmtRedisplayGadgets(widget, event, region);

    /* go draw all the XmtLayout frames and captions */
    DrawFramesAndCaptions(lw, region);
}

#if NeedFunctionPrototypes
static void Resize(Widget w)
#else
static void Resize(w)
Widget w;
#endif
{
    if (XtIsRealized(w)) XClearWindow(XtDisplay(w), XtWindow(w));
    _XmtLayoutChildren((XmtLayoutWidget) w, False);
}

#if NeedFunctionPrototypes
static void ChangeManaged(Widget w)
#else
static void ChangeManaged(w)
Widget w;
#endif
{
    _XmtLayoutChildren((XmtLayoutWidget) w, True);
}


/* modified from Xaw Form widget.  Hope it works right */
/* ARGSUSED */
#if NeedFunctionPrototypes
static XtGeometryResult GeometryManager(Widget w, XtWidgetGeometry *request,
					XtWidgetGeometry *reply)
#else
static XtGeometryResult GeometryManager(w, request, reply)
Widget w;
XtWidgetGeometry *request;
XtWidgetGeometry *reply;
#endif
{
    XtWidgetGeometry allowed;

    if (!LayoutConstraints(w).allow_resize) return XtGeometryNo;

    if (request->request_mode & ~(XtCWQueryOnly | CWWidth | CWHeight))
	return XtGeometryNo;

    if (request->request_mode & CWWidth)
	allowed.width = request->width;
    else
	allowed.width = w->core.width;

    if (request->request_mode & CWHeight)
	allowed.height = request->height;
    else
	allowed.height = w->core.height;

    if (allowed.width == w->core.width && allowed.height == w->core.height)
	return XtGeometryNo;

    if (!(request->request_mode & XtCWQueryOnly)) {
	XtResizeWidget(w, allowed.width, allowed.height, w->core.border_width);
	_XmtLayoutChildren((XmtLayoutWidget)XtParent(w), True);
	return XtGeometryDone;
    }
    else
	return XtGeometryYes;
}


/* stolen from Xaw Form widget.  Hope it works right */
#if NeedFunctionPrototypes
static XtGeometryResult QueryGeometry(Widget widget, 
				      XtWidgetGeometry *request,
				      XtWidgetGeometry *reply)
#else
static XtGeometryResult QueryGeometry(widget, request, reply)
Widget widget;
XtWidgetGeometry *request;
XtWidgetGeometry *reply;
#endif
{
    XmtLayoutWidget w = (XmtLayoutWidget)widget;
    
    reply->width = LayoutConstraints(w->layout.toplevel).pref_w +
	              2 * w->bulletin_board.margin_width;
    reply->height = LayoutConstraints(w->layout.toplevel).pref_h +
	              2 * w->bulletin_board.margin_height;
    reply->request_mode = CWWidth | CWHeight;
    if (request->request_mode & (CWWidth | CWHeight) == (CWWidth | CWHeight)
	&& request->width == reply->width
	&& request->height == reply->height)
	return XtGeometryYes;
    else if (reply->width == w->core.width && reply->height == w->core.height)
	return XtGeometryNo;
    else
	return XtGeometryAlmost;
}

#if NeedFunctionPrototypes
static void InsertChild(Widget w)
#else
static void InsertChild(w)
Widget w;
#endif
{
    /*
     * This is another Motif workaround.
     * The BulletinBoard and Manager class InsertChild procedures ignore
     * non-rectobj children, and never insert them in the children array.
     * The workaround is to call the bulletin board method if this is a
     * RectObj, and call the composite method if this is a LayoutGadget,
     * and do nothing otherwise.  Same for DeleteChild.
     */
    if (XtIsRectObj(w))
	(*xmBulletinBoardClassRec.composite_class.insert_child)(w);
    else if (XmtIsLayoutGadget(w))
	(*compositeClassRec.composite_class.insert_child)(w);
}


#if NeedFunctionPrototypes
static void DeleteChild(Widget w)
#else
static void DeleteChild(w)
Widget w;
#endif
{
    /*
     * this is a hack.  See InsertChild()
     */
    if (XtIsRectObj(w))
	(*xmBulletinBoardClassRec.composite_class.delete_child)(w);
    else if (XmtIsLayoutGadget(w))
	(*compositeClassRec.composite_class.delete_child)(w);
}


#if NeedFunctionPrototypes
static void PlaceObjectAfter(Widget w, Widget after)
#else
static void PlaceObjectAfter(w, after)
Widget w;
Widget after;
#endif
{
    XmtLayoutConstraintsPart *c = LayoutConstraintsRec(w);
    XmtLayoutConstraintsPart *p = LayoutConstraintsRec(after);
    XmtLayoutConstraintsPart *n;

    c->in = p->in;
    c->after = after;
    c->before = p->before;
    p->before = w;
    if (c->before != NULL) {
	n = LayoutConstraintsRec(c->before);
	n->after = w;
    }
}

#if NeedFunctionPrototypes
static void PlaceObjectBefore(Widget w, Widget before)
#else
static void PlaceObjectBefore(w, before)
Widget w;
Widget before;
#endif
{
    XmtLayoutConstraintsPart *c = LayoutConstraintsRec(w);
    XmtLayoutConstraintsPart *n = LayoutConstraintsRec(before);
    XmtLayoutConstraintsPart *p;

    c->in = n->in;
    c->before = before;
    c->after = n->after;
    n->after = w;
    if (c->after == NULL)
	LayoutConstraints(c->in).first_child = w;
    else {
	p = LayoutConstraintsRec(c->after);
	p->before = w;
    }
}

#if NeedFunctionPrototypes
static void PlaceObjectIn(Widget w, Widget container, int pos)
#else
static void PlaceObjectIn(w, container, pos)
Widget w;
Widget container;
int pos;
#endif
{
    XmtLayoutConstraintsPart *c = LayoutConstraintsRec(w);
    XmtLayoutConstraintsPart *cc;  /* container */
    Widget n, p;  /* next, previous */
    int j;
    
    /* if container unspecified, put it in toplevel */
    if (container)
	c->in = container;
    else
	c->in = ((XmtLayoutWidget)XtParent(w))->layout.toplevel;

    /* now go find the position it belongs at */
    cc = LayoutConstraintsRec(c->in);
    for(p = NULL, n = cc->first_child, j = 0;
	n && (j != pos);
	p = n, n = LayoutConstraints(n).before, j++);
    c->position = j;  /* convert a -1 position to actual position */
    c->after = p;
    c->before = n;
    if (p)
	LayoutConstraints(p).before = w;
    else
	cc->first_child = w;
    if (n)
	LayoutConstraints(n).after = w;
}

#if NeedFunctionPrototypes
static void UnlinkObject(Widget w)
#else
static void UnlinkObject(w)
Widget w;
#endif
{
    XmtLayoutConstraintsPart *c = LayoutConstraintsRec(w);

    if (c->before)
	LayoutConstraints(c->before).after = c->after;
    if (c->after)
	LayoutConstraints(c->after).before = c->before;
    else if (c->in)
	LayoutConstraints(c->in).first_child = c->before;

    c->before = c->after = c->in = NULL;
}

#if NeedFunctionPrototypes
static Boolean set_parsed_info(XmtLayoutWidget lw, Widget child)
#else
static Boolean set_parsed_info(lw, child)
XmtLayoutWidget lw;
Widget child;
#endif
{
    XmtLayoutInfo *info, *last;
    XmtLayoutConstraintsPart *c = LayoutConstraintsRec(child);
    XmtLayoutConstraintsPart *mark;

    /* see if the child's name appears in the list */
    for(last = NULL, info = lw->layout.widget_info;
	info; last = info, info = info->next)
	if (info->name == child->core.xrm_name) break;
    
    /*
     * If no info was found, and if the child
     * is a scrolled window, and its name ends in "SW"
     * see if we have info for the grandchild's name without the SW.
     * This is a special case to make scrolled lists and scrolled texts
     * easier to use in a layout widget.
     */
    if (!info && XmIsScrolledWindow(child)) {
	String name = XtName(child);
	int len = strlen(name);
	XrmQuark grandchild_name;

	if ((name[len-2] == 'S') && (name[len-1] == 'W')) {
	    name = XtNewString(name);
	    name[len-2] = '\0';
	    grandchild_name = XrmStringToQuark(name);
	    XtFree(name);

	    for(last = NULL, info = lw->layout.widget_info;
		info; last = info, info = info->next)
		if (info->name == grandchild_name) break;
	}
    }

    /* if we found some info unlink it, otherwise quit */
    if (info) {
	if (last) last->next = info->next;
	else lw->layout.widget_info = info->next;
    }
    else return False;

    /* override any constraints with info from the layout string */

#define Merge(field)\
    if (info->dummy_constraints.field) c->field = info->constraints.field

    Merge(width);
    Merge(height);
    Merge(stretchability);
    Merge(shrinkability);
    Merge(allow_resize);
    Merge(sensitive);
    Merge(justification);
    Merge(margin_width);
    Merge(margin_height);
    Merge(frame_type);
    Merge(frame_line_type);
    Merge(frame_position);
    Merge(frame_margin);
    Merge(frame_thickness);
    Merge(caption_position);
    Merge(caption_justification);
    Merge(caption_alignment);
    Merge(caption_margin);
    Merge(caption);

#undef Merge
    
    /* this private flag is a special case */
    c->dont_copy_caption = info->constraints.dont_copy_caption;

    /*
     * Set after or before or in to the same value as
     * the marker widget we created in the parser.  We don't need to fully
     * attach this widget, just set a position resource so that
     * ConstraintInitialize can attach it in the right place.
     * Unlink and destroy the marker widget.
     */
    mark = LayoutConstraintsRec(info->constraints.after);
    c->in = mark->in;
    c->position = mark->position;
    UnlinkObject(info->constraints.after);
    XtDestroyWidget(info->constraints.after);

    XtFree((char *) info);
    return True;
}


/* ARGSUSED */
#if NeedFunctionPrototypes
static void ConstraintInitialize(Widget request, Widget init,
				 ArgList arglist, Cardinal *num_args)
#else
static void ConstraintInitialize(request, init, arglist, num_args)
Widget request;
Widget init;
ArgList arglist;
Cardinal *num_args;
#endif
{
    XmtLayoutConstraintsPart *c = LayoutConstraintsRec(init);
    XmtLayoutWidget lw = (XmtLayoutWidget)XtParent(init);

    /*
     * we don't use constraints for non-rectobjs.  See comment
     * in ConstraintSetValues.
     */
    if (!XtIsWidget(init) &&
	!XmIsGadget(init) &&
	!XmtIsLayoutGadget(init))
	return;

    /*
     * make a note if this is a widget or gadget.
     */
    if (XtIsWidget(init) || XmIsGadget(init))
	c->type = XmtLayoutChild;

    c->dont_copy_caption = False;

    /*
     * go check if there is constraint info about this widget saved away.
     * If so, set it, overriding any resources already set.
     * Don't bother checking if this widget was created by the parser.
     */
    if (!lw->layout.in_parser) set_parsed_info(lw, init);
    
    /*
     * if stretchability and shrinkability are unset,
     * set the default depending on type
     */
    if (c->stretchability == (Dimension) -1) {
	if ((c->type == XmtLayoutChild) ||
	    (c->type == XmtLayoutSpace) ||
	    (c->type == XmtLayoutRow) ||
	    (c->type == XmtLayoutCol))
	    c->stretchability = XmtLAYOUT_DEFAULT_STRETCHABILITY;
	else
	    c->stretchability = 0;
    }
    if (c->shrinkability == (Dimension) -1) {
	if ((c->type == XmtLayoutChild) ||
	    (c->type == XmtLayoutSpace) ||
	    (c->type == XmtLayoutRow) ||
	    (c->type == XmtLayoutCol))
	    c->shrinkability = XmtLAYOUT_DEFAULT_SHRINKABILITY;
	else
	    c->shrinkability = 0;
    }

    /*
     * if margin width or margin height are unset, set default
     * depending on type.
     */
    if (c->margin_width == 255) {
	if ((c->type == XmtLayoutRow) || (c->type == XmtLayoutCol))
	    c->margin_width = 0;
	else
	    c->margin_width = lw->layout.default_spacing/2;
    }
    if (c->margin_height == 255) {
	if ((c->type == XmtLayoutRow) || (c->type == XmtLayoutCol))
	    c->margin_height = 0;
	else
	    c->margin_height = lw->layout.default_spacing/2;
    }
	
    /* if there is a caption, copy it and figure out how big it is */
    if (c->caption) {
	if (!c->dont_copy_caption)
	    c->caption = XmStringCopy(c->caption);
	XmStringExtent(lw->layout.font_list, c->caption,
		       &c->caption_width, &c->caption_height);
    }

    /*
     * Now figure out where this object will be laid out.
     * This can be specified in a number of ways:
     *   after,
     *   before, or
     *   in, optionally combined with position.
     * The fields are examined in the order listed, and the first one
     * specified is used.  Whichever fields are specified, all
     * except position are updated to reflect the actual layout.
     * If none are specified, then the widget is inserted in the toplevel
     * row or column.  position defaults to -1, which inserts
     * at the end of the row or column.
     * Note that if the toplevel box isn't created yet, then we must
     * be creating it now, and we shouldn't place it anywhere.
     */
    if (lw->layout.toplevel != 0) {
	if (c->after)
	    PlaceObjectAfter(init, c->after);
	else if (c->before)
	    PlaceObjectBefore(init, c->before);
	else {
	    PlaceObjectIn(init, c->in, c->position);
	}
    }

    c->first_child = NULL;
}


#if NeedFunctionPrototypes
static void ConstraintDestroy(Widget w)
#else
static void ConstraintDestroy(w)
Widget w;
#endif
{
    XmtLayoutWidget lw = (XmtLayoutWidget)XtParent(w);
    XmtLayoutConstraintsPart *c = LayoutConstraintsRec(w);

    if (!XtIsWidget(w) && !XmIsGadget(w) && !XmtIsLayoutGadget(w))
	return;

    /* if there is a caption, free it */
    if (c->caption)
	XmStringFree(c->caption);

    /*
     * if the whole layout is being destroyed, don't
     * bother messing with the linked lists.  If this is a container,
     * we don't need to mess with the kids because they will be destroyed.
     */
    if (lw->core.being_destroyed)
	return;

    /* unlink the item */
    UnlinkObject(w);
}


/* ARGSUSED */
#if NeedFunctionPrototypes
static Boolean ConstraintSetValues(Widget current, Widget request, Widget set,
				   ArgList arglist, Cardinal *num_args)
#else
static Boolean ConstraintSetValues(current, request, set, arglist, num_args)
Widget current;
Widget request;
Widget set;
ArgList arglist;
Cardinal *num_args;
#endif
{
    XmtLayoutWidget lw = (XmtLayoutWidget)XtParent(set);
    XmtLayoutConstraintsPart *c = LayoutConstraintsRec(current);
    XmtLayoutConstraintsPart *s = LayoutConstraintsRec(set);
    Boolean relayout = False;
    Boolean redisplay = False;

    /*
     * ** Another gross Motif hack.
     * Motif creates screwy non-rectobj cache objects for every Gadget.
     * These objects have NULL constraint fields but are passed to
     * this function anyway.  They are subclasses of xmExtObjectClass,
     * so we could test for them and rule them out.  But since none of
     * our constraints make sense for any non-rect object, we'll just
     * test that it is a subclass of RectObj.  We'll insert the same test
     * in the other constraint methods for efficiency
     */
	
    if (!XtIsWidget(set) && !XmIsGadget(set) && !XmtIsLayoutGadget(set))
	return False;

#define Changed(field) (s->field != c->field)

    /*
     * Now that the flag values have been updated, we can check
     * them like any other resource
     */
    if (Changed(justification) || Changed(margin_width) ||
	Changed(margin_height) || Changed(frame_type) ||
	Changed(frame_position) || Changed(frame_margin) ||
	Changed(caption_position) || Changed(caption_margin) ||
	Changed(width) || Changed(height) ||
	Changed(stretchability) || Changed(shrinkability))
	relayout = True;

    if (Changed(caption_justification)) redisplay = True;

    if (Changed(sensitive) || Changed(frame_line_type) ||
	Changed(caption_alignment))
	if (XtIsRealized(lw))
	    DrawFrameAndCaption(lw, set, NULL);

    if (Changed(caption)) {
	if (c->caption) XmStringFree(c->caption);
	if (s->caption) {
	    s->caption = XmStringCopy(s->caption);
	    XmStringExtent(lw->layout.font_list, s->caption,
			   &s->caption_width, &s->caption_height);
	}
	relayout = True;
    }

    if (set != ((XmtLayoutWidget)XtParent(set))->layout.toplevel) {
	if (Changed(after)) {
	    UnlinkObject(set);
	    PlaceObjectAfter(set, s->after);
	    relayout = True;
	}
	else if (Changed(before)) {
	    UnlinkObject(set);
	    PlaceObjectBefore(set, s->before);
	    relayout = True;
	}
	else if (Changed(in) || Changed(position)) {
	    UnlinkObject(set);
	    PlaceObjectIn(set, s->in, s->position);
	    relayout = True;
	}
    }
#undef Changed

    if (relayout) _XmtLayoutChildren((XmtLayoutWidget)XtParent(set), True);

    return relayout | redisplay;
}


#if NeedFunctionPrototypes
void XmtLayoutDisableLayout(Widget w)
#else
void XmtLayoutDisableLayout(w)
Widget w;
#endif
{
    XmtLayoutWidget lw = (XmtLayoutWidget) w;

    XmtAssertWidgetClass(w, xmtLayoutWidgetClass, "XmtLayoutDisable");
    lw->layout.layout_disabled++;
}

#if NeedFunctionPrototypes
void XmtLayoutEnableLayout(Widget w)
#else
void XmtLayoutEnableLayout(w)
Widget w;
#endif
{
    XmtLayoutWidget lw = (XmtLayoutWidget) w;

    XmtAssertWidgetClass(w, xmtLayoutWidgetClass, "XmtEnableLayout");
    if (lw->layout.layout_disabled > 0) lw->layout.layout_disabled--;
    if (lw->layout.needs_layout) _XmtLayoutChildren(lw, True);
}

	
#if NeedFunctionPrototypes
int XmtLayoutConvertSizeToPixels(Widget w, double size,XmtLayoutUnitType units)
#else
int XmtLayoutConvertSizeToPixels(w, size, units)
Widget w;
double size;
XmtLayoutUnitType units;
#endif
{
    XmtLayoutWidget lw = (XmtLayoutWidget) w;

    XmtAssertWidgetClass(w, xmtLayoutWidgetClass,
			 "XmtLayoutConvertSizeToPixels");

    /* convert to pixels */
    /* note intentional missing break statements */
    switch(units) {
    case XmtLayoutPoints:
	size /= 72;                  /* 1 point = 1/72 inch */
    case XmtLayoutInches:
	size *= 25.4;                /* 1 inch = 25.4 mm */
    case XmtLayoutMillimeters:
	size *= lw->layout.pixpermm; /* pixels per mm depends on screen res */
	break;
    case XmtLayoutEns:
	size /= 2;                   /* 1 en = 1/2 em */
    case XmtLayoutEms:
	size *= lw->layout.pixperem; /* pixels per em depends on font size */
	break;
    }

    /* round to the nearest pixel */
    return (int) size;
}

#if NeedFunctionPrototypes
Widget XmtCreateLayout(Widget parent, String name,
		       ArgList args, Cardinal n)
#else
Widget XmtCreateLayout(parent, name, args, n)
Widget parent;
String name;
ArgList args;
Cardinal n;
#endif
{
    return XtCreateWidget(name, xmtLayoutWidgetClass, parent, args, n);
}

#if NeedFunctionPrototypes
Widget XmtCreateLayoutDialog(Widget parent, String name,
			     ArgList al, Cardinal ac)
#else
Widget XmtCreateLayoutDialog(parent, name, al, ac)
Widget parent;
String name;
ArgList al;
Cardinal ac;
#endif
{
    Widget shell, w;
    String shell_name;

    shell_name = XtMalloc(strlen(name) + 7);
    (void)sprintf(shell_name, "%s_shell", name);

    shell = XmCreateDialogShell(parent, shell_name, al, ac);
    XtFree(shell_name);
    XtVaSetValues(shell, XmNallowShellResize, True, NULL);

    w = XtCreateWidget(name, xmtLayoutWidgetClass, shell, al, ac);
    XtAddCallback(w, XtNdestroyCallback,
		  (XtCallbackProc)_XmDestroyParentCallback, NULL);
    return w;
}


