/* 
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
*/ 

#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$XConsortium: RowCol1_1.c /main/7 1996/10/30 11:17:38 drk $"
#endif
#endif
/*
*  (c) Copyright 1989, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */
/*
*  (c) Copyright 1987, 1988, 1989, 1990, HEWLETT-PACKARD COMPANY */
#include <stdio.h>
#include <ctype.h>
#include <Xm/XmP.h>
#include <X11/Xutil.h>
#include <X11/ShellP.h>
#include <Xm/MenuShellP.h>
#include <Xm/LabelP.h>
#include <Xm/LabelGP.h>
#include <Xm/CascadeBP.h>
#include <Xm/CascadeBGP.h>
#include <Xm/ToggleBP.h>
#include <Xm/ToggleBGP.h>
#include <Xm/RowColumnP.h>

#define UNDEFINED_TYPE -1
#define POST_TIME_OUT	3 /* sec */ * 1000

#define Double(x)       ((x) << 1)
#define Half(x)         ((x) >> 1)

#define IsSensitive(r)      XtIsSensitive(r)
#define IsManaged(w)        XtIsManaged(w)
#define IsNull(p)       ((p) == NULL)

#define PackTight(m)        (RC_Packing (m) == XmPACK_TIGHT)
#define PackColumn(m)       (RC_Packing (m) == XmPACK_COLUMN)
#define PackNone(m)         (RC_Packing (m) == XmPACK_NONE)

#define Asking(i)       ((i) == 0)
#define IsVertical(m)   \
       (((XmRowColumnWidget) (m))->row_column.orientation == XmVERTICAL)
#define IsHorizontal(m) \
       (((XmRowColumnWidget) (m))->row_column.orientation == XmHORIZONTAL)
#define IsAligned(m)    \
       (((XmRowColumnWidget) (m))->row_column.do_alignment)

#define IsPopup(m)     \
    (((XmRowColumnWidget) (m))->row_column.type == XmMENU_POPUP)
#define IsPulldown(m)  \
    (((XmRowColumnWidget) (m))->row_column.type == XmMENU_PULLDOWN)
#define IsOption(m)    \
    (((XmRowColumnWidget) (m))->row_column.type == XmMENU_OPTION)
#define IsBar(m)       \
    (((XmRowColumnWidget) (m))->row_column.type == XmMENU_BAR)
#define IsWorkArea(m)  \
    (((XmRowColumnWidget) (m))->row_column.type == XmWORK_AREA)
#define IsRadio(m)     \
    ((((XmRowColumnWidget) (m))->row_column.type == XmWORK_AREA) && \
         (((XmRowColumnWidget) (m)->row_column.radio)))
#define IsHelp(m,w)     ((w) == RC_HelpPb (m))

#define WasManaged(w)  \
    (((XmRowColumnConstraintRec *) ((w)->core.constraints))-> \
     row_column.was_managed)

#define BX(b)           ((b)->x)
#define BY(b)           ((b)->y)
#define BWidth(b)       ((b)->width)
#define BHeight(b)      ((b)->height)
#define BBorder(b)      ((b)->border_width)

#define SetPosition(b,x,y)  { BX (b) = x;  BY (b) = y; }

#define ChangeMargin(margin,new,sum)    \
    if ((margin) != new)        \
    {               \
        sum += new - (margin);  \
        (margin) = new;     \
    }

#define ForAllChildren(m, i, q)     \
    for (i = 0, q = m->composite.children; \
     i < m->composite.num_children;     \
     i++, q++)

#define ForManagedChildren(m, i, q)  \
    for (i = 0, q = m->composite.children; \
     i < m->composite.num_children;     \
     i++, q++)          \
                    \
    if (XtIsManaged(*q))

     
/* Warning Messages */
#define BadWidthMsg \
    "Attempt to set width to zero: defaulting to 16"
#define BadWidthSVMsg \
    "Attempt to set width to zero ignored"
#define BadHeightMsg \
    "Attempt to set height to zero: defaulting to 16"
#define BadHeightSVMsg \
    "Attempt to set height to zero ignored"
#define BadPopupHelpMsg \
    "XmNhelpWidget not used by PopUps: forced to NULL"
#define BadPulldownHelpMsg \
    "XmNhelpWidget not used by Pulldowns: forced to NULL"
#define BadOptionHelpMsg \
    "XmNhelpWidget not used by Option menus: forced to NULL"
#define BadWorkAreaHelpMsg \
    "XmNhelpWidget not used by Work Areas: forced to NULL"
#define BadTypeMsg \
    "Unknown value of XmNrowColumnType: defaulting to WorkArea"
#define BadTypeParentMsg \
    "Widget hierarchy not appropriate for this XmNrowColumnType:\n\
defaulting to WorkArea"
#define BadTypeSVMsg \
    "Attempt to change XmNrowColumnType after initialization: ignored"
#define BadOrientationMsg \
    "Unknown value of XmNorientation: using default instead"
#define BadOrientationSVMsg \
    "Attempt to set XmNorientation to unknown value ignored"
#define BadPackingMsg \
    "Unknown value of XmNpacking: using default instead"
#define BadPackingSVMsg \
    "Attempt to set XmNpacking to unknown value ignored"
#define BadAlignmentMsg \
    "Unknown value of XmNentryAlignment: using default instead"
#define BadAlignmentSVMsg \
    "Attempt to set XmNentryAlignment to unknown value ignored"
#define BadMenuBarHomogenousSVMsg \
    "Attempt to set XmNisHomogenous to FALSE for a RowColumn widget of type \
     XmMENU_BAR ignored"
#define BadMenuBarEntryClassSVMsg \
    "Attempt to change XmNentryClass for a RowColumn widget of type \
     XmMENU_BAR ignored"
#define BadPulldownWhichButtonSVMsg \
    "Attempt to change XmNwhichButton via XtSetValues for a RowColumn widget \
     of type XmMENU_PULLDOWN ignored"
#define BadPulldownMenuPostSVMsg \
    "Attempt to change XmNmenuPost via XtSetValues for a RowColumn widget \
     of type XmMENU_PULLDOWN ignored"
#define BadMenuPostMsg \
    "Attempt to set XmNpostMenu to an illegal value ignored"

#define BadShadowThicknessSVMsg \
    "Attempt to change XmNshadowThickness for a RowColumn widget not of type \
     XmMENU_PULLDOWN or XmMENU_POPUP ignored"
#define BadOptionOrientationSVMsg \
    "Attempt to change XmNorientation for a RowColumn widget of type \
     XmMENU_OPTION ignored"

#define WrongMenuChildMsg \
    "Attempt to add wrong type child to a menu (i.e. RowColumn) widget"
#define WrongChildMsg \
    "Attempt to add wrong type child to a homogeneous RowColumn widget"


/*
 * forward declarations
 */

                        /* class support procs */
static Boolean          SetValues();
static int              AddKid();
static XtGeometryResult QueryGeometry();
static void             Redisplay();
static void             ManagedSetChanged();
static void             ClassInitialize();
static void             ClassPartInitialize();
static void             Initialize();
static void             Resize();
static XtGeometryResult GeometryManager();
static void             RemoveChild();
static void             Realize();
static void             Destroy();
static void             ConstraintDestroy();
static void             ConstraintInitialize();
static void             _XmMenuTraversalHandler();
static void             MoveLeft();
static void             MoveRight();
static void             Move_Up_Down();
static void             FindPrevMenuBarCascade();
static void             FindNextMenuBarCascade();
static void             FindPrevMenuBarItem();
static void             FindNextMenuBarItem();
static void             DoNothing();
static void 		PreparePostFromList();


                        /* action procs */
static void _XmMenuBtnUp();
static void _XmMenuBtnDown();
static void _XmGetGadget();
static void _XmRC_Unmap();
static void _XmRC_Enter();
static void _XmRC_FocusIn();
static void _XmRC_FocusOut();
static void _XmRC_GadgetEscape();
static void _XmRC_GadgetTraverseLeft();
static void _XmRC_GadgetTraverseRight();
static void _XmRC_GadgetTraverseUp();
static void _XmRC_GadgetTraverseDown();
static Boolean ShouldDispatchFocusOut();

static void _XmMenuTraverseLeft();
static void _XmMenuTraverseRight();
static void _XmMenuTraverseUp();
static void _XmMenuTraverseDown();
static void _XmMenuEscape();
static void _XmMenuUnmap();
static void _XmMenuFocusOut();
static void _XmMenuFocusIn();
static void Noop();

void _XmGetActiveTopLevelMenu();
void XmAddToPostFromList ();
void XmRemoveFromPostFromList ();
static void AddToPostFromList ();
static void RemoveFromPostFromList ();

                       /* RowColumnClass methods */
static void PositionMenu();
static void SetCascadeField();
static void ArmAndActivate();
static void MenuPopDown();
static void MenuArm();
static void SetMenuTraversal();
static void MenuDisarm();
static void MenuBarCleanup();
static void MenuProcedureEntry();

                        /* utility procs */
static void FixEventBindings();
static void FixVisual();
static void FixCallback();
static void LocatePulldown();
static void PreferredSize();

static void ChildsActivateCallback();
static void EntryFired();
static void AdaptToSize();
static void DoMarginAdjustment();
static void SetMenuHistory();
static void Layout();

static void DoProcessMenuTree();
static int  _XmMatchInKeyboardList();
static Boolean _XmAllWidgetsAccessible();

static Boolean CheckKey();
static Boolean ProcessKey();
static void GrabKeyOnAssocWidgets ();

static int OnPostFromList();
static void GetTopManager();
static void ProcessMenuTree();
static void ProcessSingleWidget();
static void AddToKeyboardList();
static void RemoveFromKeyboardList();
static void KeyboardInputHandler();
static void SetTraversal();
static void UpdateOptionMenuCBG ();
static Widget find_first_managed_child();

extern Widget _XmGetTabGroup();
extern Widget _XmFindTopMostShell();
extern void _XmGadgetArm();
extern void _XmGadgetActivate();
extern void _XmGadgetSelect();

/*
 * event translation tables for a menu widget, we use the parameters to
 * signal that this widget invoking the action proc is the menu, not a
 * child of the menu
 */

static XtTranslations menu_traversal_parsed;
#ifndef MCCABE
static char menu_traversal_table [] =
   "<Key>osfHelp:	Help()\n\
    <Key>osfLeft:	MenuGadgetTraverseLeft()\n\
    <Key>osfRight:	MenuGadgetTraverseRight()\n\
    <Key>osfUp:		MenuGadgetTraverseUp()\n\
    <Key>osfDown:	MenuGadgetTraverseDown()\n\
    <Unmap>:		MenuUnmap()\n\
    <FocusIn>:		MenuFocusIn()\n\
    <FocusOut>:		MenuFocusOut()\n\
    <EnterWindow>Normal:	MenuEnter()";
#else
static char menu_traversal_table [];
#endif

static XtTranslations option_parsed;
#ifndef MCCABE
static char option_table [] =
    "<Key>osfSelect: 	ManagerGadgetSelect()\n\
     <Key>osfActivate:	ManagerGadgetSelect()\n\
     <Key>osfHelp:	Help()\n\
     ~Shift ~Meta ~Alt<Key>Return:	ManagerGadgetSelect()\n\
     ~Shift ~Meta ~Alt<Key>space:	ManagerGadgetSelect()\n\
     <BtnDown>:		MenuBtnDown()\n\
     <BtnUp>:		MenuBtnUp()";
#else
static char option_table [];
#endif

static XtTranslations menu_parsed;
#ifndef MCCABE
static char menu_table [] =
    "<Key>osfSelect: 	ManagerGadgetSelect()\n\
     <Key>osfActivate:	ManagerGadgetSelect()\n\
     <Key>osfHelp:	Help()\n\
     <Key>osfCancel: 	MenuGadgetEscape()\n\
     ~Shift ~Meta ~Alt<Key>Return:	ManagerGadgetSelect() \n\
     ~Shift ~Meta ~Alt<Key>space:	ManagerGadgetSelect() \n\
     <BtnDown>:		MenuBtnDown()\n\
     <BtnUp>:		MenuBtnUp()";
#else
static char menu_table [];
#endif


/* this is used exclusively for the help system */
static XmGadget ActiveGadgetChild = NULL;


/*
 * action binding table for row column widget
 */

static XtActionsRec action_table [] =
{
    {"Help",                (XtActionProc) _XmManagerHelp},
    {"MenuBtnDown",	    (XtActionProc) _XmMenuBtnDown},
    {"MenuBtnUp",	    (XtActionProc) _XmMenuBtnUp},
    {"PulldownBtnDown",     (XtActionProc) _XmMenuBtnDown},
    {"PulldownBtnUp",       (XtActionProc) _XmMenuBtnUp},
    {"PopupBtnDown",        (XtActionProc) _XmMenuBtnDown},
    {"PopupBtnUp",          (XtActionProc) _XmMenuBtnUp},
    {"MenuBarBtnDown",      (XtActionProc) _XmMenuBtnDown},
    {"MenuBarBtnUp",        (XtActionProc) _XmMenuBtnUp},
    {"WorkAreaBtnDown",     (XtActionProc) _XmGadgetArm},
    {"WorkAreaBtnUp",       (XtActionProc) _XmGadgetActivate},

    /* One step from removal in 1.2 */
    {"_MenuGetGadget",       (XtActionProc) _XmGetGadget},

    {"FocusOut",            (XtActionProc) _XmMenuFocusOut},
    {"FocusIn",             (XtActionProc) _XmMenuFocusIn},
    {"Unmap",               (XtActionProc) _XmMenuUnmap},
    {"Noop",                (XtActionProc) Noop},
    {"MenuTraverseLeft",    (XtActionProc) _XmMenuTraverseLeft},
    {"MenuTraverseRight",   (XtActionProc) _XmMenuTraverseRight},
    {"MenuTraverseUp",      (XtActionProc) _XmMenuTraverseUp},
    {"MenuTraverseDown",    (XtActionProc) _XmMenuTraverseDown},
    {"MenuEscape",	    (XtActionProc) _XmMenuEscape},

    {"MenuFocusIn",         (XtActionProc) _XmRC_FocusIn},
    {"MenuFocusOut",        (XtActionProc) _XmRC_FocusOut},
    {"MenuUnmap",           (XtActionProc) _XmRC_Unmap},
    {"MenuEnter",           (XtActionProc) _XmRC_Enter},

    {"MenuGadgetReturn",         (XtActionProc) _XmGadgetSelect},
    {"MenuGadgetEscape",         (XtActionProc) _XmRC_GadgetEscape},
    {"MenuGadgetTraverseLeft",   (XtActionProc) _XmRC_GadgetTraverseLeft},
    {"MenuGadgetTraverseRight",  (XtActionProc) _XmRC_GadgetTraverseRight},
    {"MenuGadgetTraverseUp",     (XtActionProc) _XmRC_GadgetTraverseUp},
    {"MenuGadgetTraverseDown",   (XtActionProc) _XmRC_GadgetTraverseDown},

    { NULL,                     NULL}
};


/*
 * define the resourse stuff for a rowcolumn widget
 */

static int        resource_0_int      = 0;
static Widget     resource_0_widget   = 0;
static Boolean    resource_False_boolean  = 0;
static Boolean    resource_True_boolean  = 1;
static short      resource_1_short    = 1;
static Dimension  resource_min_width   = 16;  /* 'cuz it's the size of */
static Dimension  resource_min_height  = 16;  /* a hot spot... */
static unsigned char resource_type        = XmWORK_AREA;
static unsigned char resource_alignment   = XmALIGNMENT_BEGINNING;
static unsigned char resource_packing     = XmNO_PACKING;
static unsigned char resource_orient      = XmNO_ORIENTATION;

static XtResource resources[]  =  
{
    {   XmNresizeWidth,
        XmCResizeWidth,
        XmRBoolean,
        sizeof(Boolean),
        XtOffset(XmRowColumnWidget, row_column.resize_width),
        XmRImmediate,
        (caddr_t) TRUE
    },
    {   XmNresizeHeight,
        XmCResizeHeight,
        XmRBoolean,
        sizeof(Boolean),
        XtOffset(XmRowColumnWidget, row_column.resize_height),
        XmRImmediate,
        (caddr_t) TRUE
    },
    {   XmNwhichButton,
        XmCWhichButton,
        XmRWhichButton,
        sizeof(unsigned int),
        XtOffset(XmRowColumnWidget, row_column.postButton),
        XmRImmediate,
        (caddr_t) -1,
    },
    {   XmNmenuPost,
        XmCMenuPost,
        XmRString,
        sizeof(String),
        XtOffset(XmRowColumnWidget, row_column.menuPost),
        XmRString,
        NULL,
    },
    {   XmNadjustLast,
        XmCAdjustLast,
        XmRBoolean,
        sizeof(Boolean),
        XtOffset(XmRowColumnWidget, row_column.adjust_last),
        XmRImmediate,
        (caddr_t) TRUE,
    },
    {   XmNmarginWidth, 
        XmCMarginWidth, 
        XmRHorizontalDimension, 
        sizeof (Dimension),
        XtOffset (XmRowColumnWidget, row_column.margin_width), 
        XmRImmediate, 
        (caddr_t) XmINVALID_DIMENSION
    },
    {   XmNmarginHeight, 
        XmCMarginHeight, 
        XmRVerticalDimension, 
        sizeof (Dimension),
        XtOffset (XmRowColumnWidget, row_column.margin_height), 
        XmRImmediate, 
        (caddr_t) XmINVALID_DIMENSION
    },
    {   XmNentryCallback,
        XmCCallback, 
        XmRCallback, 
        sizeof (caddr_t),
        XtOffset (XmRowColumnWidget, row_column.entry_callback), 
        XmRCallback, 
        NULL
    },
    {   XmNmapCallback, 
        XmCCallback, 
        XmRCallback,
        sizeof (caddr_t),
        XtOffset (XmRowColumnWidget, row_column.map_callback), 
        XmRCallback, 
        NULL
    },
    {   XmNunmapCallback, 
        XmCCallback, 
        XmRCallback, 
        sizeof (caddr_t),
        XtOffset (XmRowColumnWidget, row_column.unmap_callback), 
        XmRCallback, 
        NULL
    },
    {   XmNorientation, 
        XmCOrientation, 
        XmROrientation, 
        sizeof(unsigned char),
        XtOffset (XmRowColumnWidget, row_column.orientation), 
        XmROrientation, 
        (caddr_t) &resource_orient
    },
    {   XmNspacing, 
        XmCSpacing, 
        XmRHorizontalDimension, 
        sizeof(Dimension),
        XtOffset (XmRowColumnWidget, row_column.spacing), 
        XmRImmediate, 
        (caddr_t) XmINVALID_DIMENSION
    },
    {   XmNentryBorder,         /* border width of all the */
        XmCEntryBorder,         /* entries, always uniform */
        XmRHorizontalDimension, 
        sizeof(Dimension),
        XtOffset (XmRowColumnWidget, row_column.entry_border), 
        XmRImmediate, 
	(caddr_t) 0
    },
    {   XmNisAligned,           /* T/F, do all entrys have */
        XmCIsAligned,           /* same alignment */
        XmRBoolean, 
        sizeof(Boolean),
        XtOffset(XmRowColumnWidget, row_column.do_alignment),
        XmRBoolean, 
        (caddr_t) &resource_True_boolean
    },
    {   XmNentryAlignment,          /* how entries are to be */
        XmCAlignment,               /* aligned */
        XmRAlignment, 
        sizeof(unsigned char),
        XtOffset(XmRowColumnWidget, row_column.entry_alignment),
        XmRAlignment, 
        (caddr_t) &resource_alignment
    },
    {   XmNadjustMargin,            /* should all entries have */
        XmCAdjustMargin,            /* the same label margins */
        XmRBoolean, 
        sizeof(Boolean),
        XtOffset(XmRowColumnWidget, row_column.adjust_margin),
        XmRBoolean, 
        (caddr_t) &resource_True_boolean
    },
    {   XmNpacking,         /* how to pack menu entries */
        XmCPacking,         /* Tight, Column, None */
        XmRPacking,
        sizeof (unsigned char),
        XtOffset(XmRowColumnWidget, row_column.packing),
        XmRPacking,
        (caddr_t) &resource_packing
    },
    {   XmNnumColumns,          /* if packing columnar then */
        XmCNumColumns,          /* this is how many */
        XmRShort,
        sizeof (short),
        XtOffset(XmRowColumnWidget, row_column.num_columns),
        XmRShort, 
        (caddr_t) &resource_1_short
    },
    {   XmNradioBehavior,           /* should the menu enforce */
        XmCRadioBehavior,           /* toggle button exclusivity, */
        XmRBoolean,             /* ie, radio buttons */
        sizeof (Boolean),
        XtOffset(XmRowColumnWidget, row_column.radio),
        XmRBoolean, 
        (caddr_t) &resource_False_boolean
    },
    {   XmNradioAlwaysOne,          /* should there always be one */
        XmCRadioAlwaysOne,          /* radio button on. */
        XmRBoolean,
        sizeof (Boolean),
        XtOffset(XmRowColumnWidget, row_column.radio_one),
        XmRBoolean, 
        (caddr_t) &resource_True_boolean
    },
    {   XmNisHomogeneous,           /* should we enforce the */
        XmCIsHomogeneous,           /* rule that only one type of */
        XmRBoolean,             /* entry is allow in the menu */
        sizeof (Boolean),
        XtOffset(XmRowColumnWidget, row_column.homogeneous),
        XmRBoolean, 
        (caddr_t) &resource_False_boolean
    },
    {   XmNentryClass,              /* if enforcing homogeneous */
        XmCEntryClass,              /* menu, this tells the class */
        XmRInt,
        sizeof (WidgetClass),
        XtOffset(XmRowColumnWidget, row_column.entry_class),
        XmRWidgetClass, 
        (caddr_t) NULL
    },
    {   XmNrowColumnType,       /* warning - non-standard resource */
        XmCRowColumnType, 
        XmRRowColumnType, 
        sizeof(unsigned char),
        XtOffset (XmRowColumnWidget, row_column.type), 
        XmRRowColumnType, 
        (caddr_t) &resource_type
    },
    {   XmNmenuHelpWidget,          /* which widget is the help */
        XmCMenuWidget,              /* widget */
        XmRMenuWidget, 
        sizeof (Widget),
        XtOffset (XmRowColumnWidget, row_column.help_pushbutton), 
        XmRMenuWidget, 
        (caddr_t) &resource_0_widget
    },
    {   XmNlabelString,               /* option menus have a label */
        XmCString, 
        XmRXmString, 
        sizeof(_XmString),
        XtOffset (XmRowColumnWidget, row_column.option_label), 
        XmRImmediate, 
        (caddr_t)NULL
    },
    {   XmNsubMenuId,               /* option menus have built-in */
        XmCMenuWidget,              /* submenu */
        XmRMenuWidget, 
        sizeof (Widget),
        XtOffset (XmRowColumnWidget, row_column.option_submenu), 
        XmRMenuWidget, 
        (caddr_t) &resource_0_widget
    },
    {   XmNmenuHistory,         /* pretend a subwidget fired */
        XmCMenuWidget,              /* off, used to pre-load the */
        XmRMenuWidget,              /* option menu and popup menu */
        sizeof (Widget),            /* mouse/muscle memory */
        XtOffset (XmRowColumnWidget, row_column.memory_subwidget), 
        XmRMenuWidget, 
        (caddr_t) &resource_0_widget
    },
    {   XmNpopupEnabled,            /* are accelerator enabled */
        XmCPopupEnabled,            /* in the popup menu? */
        XmRBoolean,
        sizeof (Boolean),
        XtOffset(XmRowColumnWidget, row_column.popup_enabled),
        XmRBoolean, 
        (caddr_t) &resource_True_boolean
    },
    {   XmNmenuAccelerator,         /* popup menu accelerator */
        XmCAccelerators,
        XmRString,
        sizeof (char *),
        XtOffset(XmRowColumnWidget, row_column.menu_accelerator),
        XmRString, 
        (caddr_t) ""
    },
    {   XmNmnemonic,                /* option menu mnemonic */
        XmCMnemonic,
        XmRKeySym,
        sizeof (KeySym),
        XtOffset(XmRowColumnWidget, row_column.mnemonic),
        XmRImmediate, 
        (caddr_t) NULL
    },
    {
	XmNmnemonicCharSet,
	XmCMnemonicCharSet,
	XmRString,
	sizeof(XmStringCharSet),
	XtOffset(XmRowColumnWidget,row_column.mnemonicCharSet),
	XmRImmediate,
	(caddr_t) XmSTRING_DEFAULT_CHARSET
    },
    {
        XmNshadowThickness,
	XmCShadowThickness,
	XmRHorizontalDimension, 
	sizeof (Dimension),
	XtOffset (XmRowColumnWidget, manager.shadow_thickness),
	XmRImmediate,
	(caddr_t) XmINVALID_DIMENSION
    },
    {
	XmNpostFromList,
	XmCPostFromList,
	XmRWidgetList,
	sizeof (Widget *),
	XtOffset (XmRowColumnWidget, row_column.postFromList),
	XmRWidgetList,
	(caddr_t) NULL,
    },
    {
        XmNpostFromCount,
	XmCPostFromCount,
	XmRInt,
	sizeof (int),
	XtOffset (XmRowColumnWidget, row_column.postFromCount),
	XmRImmediate,
	(caddr_t) -1
    },
    {
	XmNnavigationType, 
	XmCNavigationType, 
	XmRNavigationType, 
	sizeof (unsigned char),
	XtOffset(XmManagerWidget, manager.navigation_type),
	XmRImmediate, 
	(caddr_t) XmTAB_GROUP,
    },
};


static XmSyntheticResource get_resources[] = 
{
    {   XmNspacing,
        sizeof(Dimension),
        XtOffset(XmRowColumnWidget,row_column.spacing),
        _XmFromHorizontalPixels,
        _XmToHorizontalPixels,
    },
    {   XmNmarginHeight,
        sizeof(Dimension),
        XtOffset (XmRowColumnWidget, row_column.margin_height),
        _XmFromVerticalPixels,
        _XmToVerticalPixels,
    },
    {   XmNmarginWidth,
        sizeof(Dimension),
        XtOffset (XmRowColumnWidget, row_column.margin_width), 
        _XmFromHorizontalPixels,
        _XmToHorizontalPixels,
    },
    {
        XmNentryBorder,
        sizeof(Dimension),
        XtOffset (XmRowColumnWidget, row_column.entry_border), 
        _XmFromHorizontalPixels,
        _XmToHorizontalPixels,
    },
};





/*
 * static initialization of the row column widget class record, must do
 * each field
 */

externaldef(xmrowcolumnclassrec) XmRowColumnClassRec xmRowColumnClassRec = 
{
    {                   /* core class record */
        (WidgetClass)&xmManagerClassRec, /* superclass ptr */
        "XmRowColumn",                 /* class_name */
        sizeof (XmRowColumnWidgetRec), /* size of widget instance */
        ClassInitialize,               /* class init proc */
        ClassPartInitialize,           /* class part init */
        FALSE,                         /* class is not init'ed */
        Initialize,                    /* widget init proc*/
        NULL,                          /* init_hook proc */
        Realize,                       /* widget realize proc */
        action_table,                  /* class action table */
        XtNumber (action_table),
        resources,                     /* this class's resource list */
        XtNumber (resources),          /*  "     " resource_count */
        NULLQUARK,                     /* xrm_class            */
        TRUE,                          /* don't compress motion */
        XtExposeCompressMaximal,       /* do compress exposure */
        FALSE,                         /* don't compress enter-leave */
        FALSE,                         /* no VisibilityNotify */
        Destroy,                       /* class destroy proc */
        Resize,                        /* class resize proc */
        Redisplay,                        /* class expose proc */
        SetValues,                     /* class set_value proc */
        NULL,                          /* set_value_hook proc */
        XtInheritSetValuesAlmost,      /* set_value_almost proc */
        NULL,                          /* get_values_hook */
        NULL,                          /* class accept focus proc */
        XtVersion,                     /* current version */
        NULL,                          /* callback offset list */
        NULL,                          /* translation table */
        QueryGeometry,                /* query geo proc */
        NULL,                          /* display accelerator */
        NULL,                          /* extension */
    },
    {                  /* composite class record */
        GeometryManager,        /* childrens geo mgr proc */
        ManagedSetChanged,     /* set changed proc */
        (XtArgsProc)AddKid,     /* add a child */
        RemoveChild,            /* remove a child */
        NULL,                    /* extension */
    },
    {                  /* constraint class record */
        NULL,                    /* constraint resources */
        0,                       /* constraint resource_count */
        sizeof(XmRowColumnConstraintRec),  /* constraint_size */
        ConstraintInitialize,    /* initialize */
        ConstraintDestroy,       /* destroy */
        NULL,                    /* set_values */
        NULL,                    /* extension */
    },
    {                  /* manager class record */
        XtInheritTranslations,   /* translations */
        get_resources,           /* syn_resources */
        XtNumber(get_resources), /* num_syn_resources */
        NULL,                    /* syn_constraint_resources */
        0,                       /* num_syn_constraint_resources */
        XmInheritParentProcess,  /* parent_process         */
        NULL,                    /* extension */
    },
    {                  /* row column class record */
        MenuProcedureEntry,	 /* proc to interface with menu widgets */
        ArmAndActivate,          /* proc to arm&activate menu */
        _XmMenuTraversalHandler, /* traversal handler */
        NULL,                    /* extension */
    }
};






/*
 * now make a public symbol that points to this class record
 */

externaldef(xmrowcolumnwidgetclass) WidgetClass xmRowColumnWidgetClass = 
   (WidgetClass) &xmRowColumnClassRec;

static Widget lastSelectToplevel = NULL;

/* REQUIRES FIX FOR MULTI SCREEN!!! */
XmButtonEventStatusRec _XmButtonEventStatus = {-1,FALSE,TRUE, NULL};

/*ARGSUSED*/
static XtTimerCallbackProc PostTimeOut (popup, id)
XmRowColumnWidget popup;
XtIntervalId *id;
{  
  if (_XmButtonEventStatus.waiting_to_be_managed)
     MenuPopDown (popup, &_XmButtonEventStatus.event);
  _XmButtonEventStatus.waiting_to_be_managed = FALSE;
}


/*
 * ButtonEventHandler is inserted at the head of the event handlers.  We must
 * pre-verify the events that popup a menupane.  When the application manages
 * the popup menupane, MenuShell's managed_set_changed(), checks the
 * verification.
 */
/* ARGSUSED */
static void ButtonEventHandler(w, popup, event)
Widget w;
XmRowColumnWidget popup;
XEvent * event;
{
   XButtonEvent *xbutton_event = (XButtonEvent *)event;

   _XmButtonEventStatus.time = xbutton_event->time;
   _XmButtonEventStatus.verified = _XmMatchBtnEvent( event,
      RC_PostEventType(popup), RC_PostButton(popup), RC_PostModifiers(popup));

   if (xbutton_event->type == ButtonPress) 
   {
      _XmButtonEventStatus.waiting_to_be_managed = TRUE;
      XtAppAddTimeOut(XtWidgetToApplicationContext(popup), 
	  (unsigned long) POST_TIME_OUT, 
	  (XtTimerCallbackProc) PostTimeOut, (caddr_t) popup);
      _XmButtonEventStatus.event = *xbutton_event;
   }
}

static void AddHandlersToPostFromWidget (popup, widget)
Widget popup;
Widget widget;
{
   Cursor cursor;
   
   cursor = XmGetMenuCursor(XtDisplay(popup));

   XtInsertEventHandler(widget, ButtonPressMask|ButtonReleaseMask,
                     False, ButtonEventHandler, popup, XtListHead);

   XtAddEventHandler(widget, KeyPressMask|KeyReleaseMask,
		     False, KeyboardInputHandler, popup);

   /*
    * Add an event handler on the associated widget for ButtonRelease
    * events.  This is so that a quick press/release pair does not get
    * lost if the release is processed before our pointer grab is made.
    * This will guarantee that the associated widget gets the button
    * release event; it would be discarded if the widget was not selecting
    * for button release events.
    */
   XtAddEventHandler(widget, ButtonReleaseMask,
		     False, DoNothing, NULL);

   /* 
    * Must add a passive grab, so that owner_events is set to True
    * when the button grab is activated; this is so that enter/leave
    * events get dispatched by the server to the client.
    */
   XtGrabButton (widget, RC_PostButton(popup), RC_PostModifiers(popup), 
      TRUE, ButtonReleaseMask, GrabModeSync, GrabModeSync, None, cursor);
}

static void RemoveHandlersFromPostFromWidget (popup, widget)
Widget popup;
Widget widget;
{
   XtRemoveEventHandler(widget,	ButtonPressMask|ButtonReleaseMask,
			False, ButtonEventHandler, popup);

   XtRemoveEventHandler(widget,	KeyPressMask|KeyReleaseMask,
			False, KeyboardInputHandler, popup);

   XtRemoveEventHandler(widget, ButtonReleaseMask,
			False, DoNothing, NULL);

   /* Remove our passive grab */
   XtUngrabButton (widget, RC_PostButton(popup), AnyModifier); 
}


/*
 * Add the Popup Menu Event Handlers needed for posting and accelerators
 */
static void AddPopupEventHandlers (pane)
XmRowColumnWidget pane;
{
   int i;
   
   /* to myself for gadgets */
   XtAddEventHandler(pane, KeyPressMask|KeyReleaseMask,
		     False, KeyboardInputHandler, pane);

   /* Add to Our shell parent */
   XtAddEventHandler(XtParent(pane), KeyPressMask|KeyReleaseMask,
		     False, KeyboardInputHandler, pane);

   /* add to all of the widgets in the postFromList*/
   for (i=0; i < pane->row_column.postFromCount; i++)
   {
      AddHandlersToPostFromWidget (pane, pane->row_column.postFromList[i]);
   }
}

/*
 * Remove the Popup Menu Event Handlers needed for posting and accelerators
 */
static void RemovePopupEventHandlers (pane)
XmRowColumnWidget pane;
{
   int i;
   
   /* Remove it from us */
   XtRemoveEventHandler(pane, KeyPressMask|KeyReleaseMask,
			False, KeyboardInputHandler, pane);

   /* Remove it from our shell parent */
   XtRemoveEventHandler(XtParent(pane), KeyPressMask|KeyReleaseMask,
			False, KeyboardInputHandler, pane);

   /* Remove it from the postFrom widgets */
   for (i=0; i < pane->row_column.postFromCount; i++)
   {
      RemoveHandlersFromPostFromWidget (pane,
					pane->row_column.postFromList[i]);
   }
}



/*
 * Destroy the widget, and any subwidgets there are
 */

static void Destroy (m)
XmRowColumnWidget m;
{
   Widget topManager;
   int i;
   
   XtRemoveAllCallbacks (m, XmNentryCallback);
   XtRemoveAllCallbacks (m, XmNmapCallback);
   XtRemoveAllCallbacks (m, XmNunmapCallback);

   XtFree(MGR_KeyboardList(m));
   /*
    * If we had added any event handlers for processing accelerators or
    * mnemonics, then we must remove them now.
    */
   if (IsPopup(m))
   {
      if (RC_PopupEnabled(m))
	  RemovePopupEventHandlers (m);
   }
   else if (IsOption(m) || IsBar(m))
   {
      /* Remove it from the associated widget */
      GetTopManager (m, &topManager);
      XtRemoveEventHandler(topManager, KeyPressMask|KeyReleaseMask,
			   False, KeyboardInputHandler, m);

      /* Remove it from us */
      XtRemoveEventHandler(m, KeyPressMask|KeyReleaseMask,
			   False, KeyboardInputHandler, m);

   }

   /*
    * If we're still connected to a cascade button, then we need to break
    * that link, so that the cascade button doesn't attempt to reference
    * us again, and also so that accelerators and mnemonics can be cleared up.
    */
   else
   {
      Arg args[1];

      for (i=0; i < m->row_column.postFromCount; i++)
      {
	 if (! m->row_column.postFromList[i]->core.being_destroyed)
	 {
	    XtSetArg (args[0], XmNsubMenuId, NULL);
	    XtSetValues (m->row_column.postFromList[i], args, 1);
	 }
      }
   }

   /* free allocated postFromList for popups and pulldowns */
   if (IsPopup(m) || IsPulldown(m))
       XtFree (m->row_column.postFromList);

   if ((IsPopup(m) && RC_PopupEnabled(m))  ||
       (IsBar(m) && RC_MenuAccelerator(m)) ||
       (IsOption(m) && RC_Mnemonic(m)))
   {
      Cardinal num_children;

      /*
       * By the time we reach here, our children are destroyed, but
       * the children's list is bogus; so we need to temporarily zero
       * out our num_children field, so DoProcessMenuTree() will not
       * attempt to process our children.
       */
      num_children = m->composite.num_children;
      m->composite.num_children = 0;
      DoProcessMenuTree(m, XmDELETE);
      m->composite.num_children = num_children;
   }
}


/*
 * Destroy any keyboard grabs/entries for the child
 */
static void ConstraintDestroy (w)
Widget w;
{
   if (!XtIsRectObj(w)) return;

   DoProcessMenuTree(w, XmDELETE);
}


/*
 * do all the stuff needed to make a subwidget of a menu work correctly
 */
static int fix_widget (m, w)
    XmRowColumnWidget m;
    Widget w;
{
    /*
     * now patchup the event binding table for the subwidget so that
     * it acts the way we want it to
     */

    FixEventBindings (m, w);

    /*
     * and patch the visual aspects of the subwidget
     */

    FixVisual (m, w);

    /*
     * and patch the callback list so that we will be called whenever
     * he fires off
     */

    FixCallback (m, w);
}



/*
 * Add a child to this row column widget
 */
static int AddKid (w)
Widget w;
{
    XmRowColumnWidget m = (XmRowColumnWidget) XtParent(w);

    if (!IsWorkArea(m) /* it's a menu */ &&
        (XtClass(w) != xmLabelGadgetClass) && (XtIsRectObj(w)) &&
        !XmIsPushButtonGadget(w) &&
        !XmIsCascadeButtonGadget(w) &&
        !XmIsToggleButtonGadget(w) &&
        !XmIsSeparatorGadget(w) &&
        (XtClass(w) != xmLabelWidgetClass) &&
        !XmIsPushButton(w) &&
        !XmIsCascadeButton(w) &&
        !XmIsToggleButton(w) &&
        !XmIsSeparator(w))
        _XmWarning(m,WrongMenuChildMsg);

    /*
     * if the rowcolumn is homogeneous, make sure that class matches
     * the entry class.  Two exceptions are made:  1) if the entry class is
     * CascadeButton or CascadeButtonGadget, either of those classes are
     * allowed. 2) if the entry class is ToggleButton or ToggleButtonGadget,
     * either of those classes are allowed.
     */
    if (XtIsRectObj(w) && RC_IsHomogeneous(m) && 
	(RC_EntryClass(m) != XtClass(w)))
    {
       if (! ((RC_EntryClass(m) == xmCascadeButtonWidgetClass &&
	       XmIsCascadeButtonGadget(w)) ||
	      (RC_EntryClass(m) == xmCascadeButtonGadgetClass &&
	       XmIsCascadeButton(w)) ||
	      (RC_EntryClass(m) == xmToggleButtonGadgetClass &&
	       XmIsToggleButton(w)) ||
	      (RC_EntryClass(m) == xmToggleButtonWidgetClass &&
	       XmIsToggleButtonGadget(w))))
       {
	   
	  _XmWarning (m, WrongChildMsg);
       }
    }
       
    /*
     * use composite class insert proc to do all the dirty work
     */
    (*((XmManagerWidgetClass)xmManagerWidgetClass)->composite_class.
                 insert_child) (w);

    /*
     * now change the subwidget so that it acts the way we want it to
     */
    fix_widget (m, w);

    return (TRUE);
}




/*
 * delete a single widget from a parent widget
 */
static void RemoveChild (child)
Widget    child;
{
   XmRowColumnWidget m = (XmRowColumnWidget) XtParent(child);

    if (child == RC_HelpPb (m)) 
       RC_HelpPb (m) = NULL;

    else if (child == RC_MemWidget(m))
    {
       RC_MemWidget(m) = NULL;
    }

    /*
     * If this child is in a top level menupane, then we want to remove
     * the event handler we added for catching keyboard input.
     */
    if (XtIsWidget(child) &&
	((IsPopup(m) || IsBar(m) || IsPulldown(m)) && 
	  XmIsLabel(child) && (child->core.widget_class != xmLabelWidgetClass)))
    {
	   XtRemoveEventHandler(child, KeyPressMask|KeyReleaseMask, False,
				KeyboardInputHandler, m);
    }

    /*
     * use composite class insert proc to do all the dirty work
     */
    (*((CompositeWidgetClass)compositeWidgetClass)->composite_class.
                delete_child) (child);
}


/*
 * The set of our managed children changed, so maybe change the size of the
 * row column widget to fit them; there is no instigator of this change, and 
 * ignore any dimensional misfit of the row column widget and the entries, 
 * which is a result of our geometry mgr being nasty.  Get it laid out.
 */
static void ManagedSetChanged (m)
XmRowColumnWidget m;
{
   Widget  *q;
   int i;
   Dimension w = 0;
   Dimension h = 0;
   Boolean any_changed = FALSE;

   /*
    * We have to manage the "was_managed" field of the 
    * constraint record.
    */

   ForAllChildren(m, i, q)
   {
      if (WasManaged(*q) != IsManaged(*q))
      {
	 any_changed = TRUE;
      }
      
      WasManaged(*q) = IsManaged(*q);
   }

   if (!any_changed)
   {
      /* Must have been a popup child -- we don't really care */
      return;
   }

   DoMarginAdjustment (m);
   
   /*
    * find out what size we need to be with the current set of kids
    */
   PreferredSize (m, &w, &h);

   /*
    * now decide if the menu needs to change size
    */
   if ((w != XtWidth (m)) || (h != XtHeight (m)))
   {
      XtWidgetGeometry menu_desired, menu_allowed;
      menu_desired.request_mode = 0;
      
      if (w != XtWidth (m))
      {
	 menu_desired.width =  w;
	 menu_desired.request_mode |= CWWidth;
      }
      
      if (h != XtHeight (m))
      {
	 menu_desired.height = h;
	 menu_desired.request_mode |= CWHeight;
      }

      XtMakeGeometryRequest(m,&menu_desired,&menu_allowed);
   }

   /*
    * if we get to here the row column widget has been changed and his 
    * window has been resized, so effectively we need to do a Resize.
    */

   AdaptToSize (m, NULL, NULL);
   
   /*	Clear shadow if necessary. */
   
   if (m->row_column.old_shadow_thickness)
       _XmClearShadowType (m, m->row_column.old_width, 
			   m->row_column.old_height, 
			   m->row_column.old_shadow_thickness, 0);
   
   m->row_column.old_width = m->core.width;
   m->row_column.old_height = m->core.height;
   m->row_column.old_shadow_thickness = m->manager.shadow_thickness;

   _XmNavigChangeManaged(m);
}                       


/*
 * make the row column widget appear
 */
static void Realize (m, window_mask, window_attributes)
XmRowColumnWidget m;
Mask *window_mask;
XSetWindowAttributes *window_attributes;
{
   if (IsOption(m))
   {
      XmRowColumnWidget sm = (XmRowColumnWidget) RC_OptionSubMenu(m);
      int i;
      Dimension w=0, h=0;

      if (!IsNull(sm))
      {
	 /* if there is no memory widget, set it up */
	 if (!RC_MemWidget(m))
	 {
	    if (!RC_MemWidget(sm))
	    {
	       /* submenu does not have a memory widget */
	       for(i = 0;i < sm->composite.num_children; i++)
	       {
		  if (XtIsManaged(sm->composite.children[i]))
		  {
		     /* choose first managed child as memory widget */
		     RC_MemWidget(m) = sm->composite.children[i];
		     RC_MemWidget(sm) = RC_MemWidget(m);
		     break;
		  }
	       }
	    }
	    else
		/* use submenu's memory widget */
		RC_MemWidget(m) = RC_MemWidget(sm);

	    /* update option menu label */
	    if ( RC_MemWidget(m) )	/* in case it doesn't exist! */
	    {
	       for (i = 0; i < m->composite.num_children; i++)
	       {
		  if (XmIsCascadeButtonGadget(m->composite.children[i]))
		  {
		     UpdateOptionMenuCBG (m->composite.children[i],
					  RC_MemWidget(m));
		     break;
		  }
	       }
	    }
	 }
	 
	 /* find out what size we need to be */
	 PreferredSize (m, &w, &h);

	 /* now decide if the menu needs to change size */
	 if ((w != XtWidth (m)) || (h != XtHeight (m)))
	 {
	    XtWidgetGeometry menu_desired, menu_allowed;
	    
	    menu_desired.request_mode = 0;
	    
	    if (w != XtWidth (m))
	    {
	       menu_desired.width = w;
	       menu_desired.request_mode |= CWWidth;
	    }

	    if (h != XtHeight (m))
	    {
	       menu_desired.height = h;
	       menu_desired.request_mode |= CWHeight;
	    }

	    XtMakeGeometryRequest(m, &menu_desired,&menu_allowed);
	 }

	 AdaptToSize (m, NULL, NULL);
      }
   }

   /* fix menu window so that any button down is OwnerEvent true. */
   if (!IsWorkArea(m))
   {
      /*
       * Originally, we simply set the OwnerGrabButtonMask in our
       * event mask.  Unfortunately, if the application ever modifies
       * our translations or adds an event handler which caused the
       * intrinsics to regenerate our X event mask, this bit was
       * lost.  So .. we add a dummy event handler for this mask bit,
       * thus guaranteeing that it is always part of our event mask.
       */
      window_attributes->event_mask |= OwnerGrabButtonMask;
      XtAddEventHandler(m, OwnerGrabButtonMask, False, Noop, NULL);
   }

   /*
    * Don't propagate events for row column widgets
    * and set bit gravity to NW
    */
   (*window_mask) |= CWDontPropagate | CWBitGravity;
   window_attributes->bit_gravity = NorthWestGravity;

   window_attributes->do_not_propagate_mask = ButtonPressMask| 
       ButtonReleaseMask|KeyPressMask|KeyReleaseMask|PointerMotionMask;

   XtCreateWindow ( m, InputOutput, CopyFromParent, *window_mask, 
		   window_attributes);

   /*
    * Keep menus which are a child of shell widgets mapped at all times.
    * Mapping is now done by the menu shell widget.
    */
   
   if (XmIsMenuShell (XtParent(m)))
       m->core.mapped_when_managed = FALSE;
}


/*
 * utilities for setvalue procs
 */
static Boolean do_entry_stuff (old, new)
XmRowColumnWidget old, new;
{
    XtWidgetGeometry desired;

    Boolean need_expose = FALSE;

    if ((RC_EntryBorder (old) != RC_EntryBorder (new)) &&
        (RC_EntryBorder(new)))
    {
        Widget *p;
        int i;

        desired.request_mode = CWBorderWidth;
        desired.border_width = RC_EntryBorder (new);

        ForAllChildren (new, i, p)
        {
            _XmConfigureObject(*p,(*p)->core.x,(*p)->core.y,
                (*p)->core.width, (*p)->core.height,
                desired.border_width);
        }

        need_expose = TRUE;
    }

    if ((RC_EntryAlignment (old) != RC_EntryAlignment (new)) && 
        (IsAligned (new)) &&
        (!IsOption(new)))
    {
        Widget *p;
        Arg al[2];
        int i;

        XtSetArg (al[0], XmNalignment, RC_EntryAlignment(new));

        ForAllChildren (new, i, p)
        {
            XtSetValues (*p, al, 1);
        }

        need_expose  = TRUE;
    }
    
    return (need_expose);
}

static void do_size (old, new)
XmRowColumnWidget old, new;
{
    Widget *p;
    int i;
    int orient = RC_Orientation (old) != RC_Orientation (new);
    Dimension w;
    Dimension h;

    if (orient)                 /* flip all the separator */
    {                       /* widgets too */
        Arg al[2];
        int ac = 0;

        XtSetArg (al[ac], XmNorientation, 
            (IsVertical (new) ? XmHORIZONTAL : XmVERTICAL));

        ForAllChildren (new, i, p)
        {
            if (XmIsSeparator(*p) || XmIsSeparatorGadget(*p))
            XtSetValues (*p, al, 1);
        }
    }

    if ((!XtWidth(new))  || (XtWidth (new) != XtWidth(old))   ||
        (!XtHeight(new)) || (XtHeight (new) != XtHeight(old)) ||
        (orient          || 
        ((IsPopup(new) || IsPulldown(new) || IsBar(new)) && 
            (MGR_ShadowThickness(new) != MGR_ShadowThickness(old))) ||
        (RC_EntryBorder (old) != RC_EntryBorder (new)) || 
        (RC_MarginW     (old) != RC_MarginW     (new)) || 
        (RC_MarginH     (old) != RC_MarginH     (new)) || 
        (RC_Spacing     (old) != RC_Spacing     (new)) || 
        (RC_Packing     (old) != RC_Packing     (new)) || 
        (RC_NCol        (old) != RC_NCol        (new)) || 
        (RC_AdjLast     (old) != RC_AdjLast     (new)) || 
        (RC_AdjMargin   (old) != RC_AdjMargin   (new)) || 
        (RC_HelpPb      (old) != RC_HelpPb      (new))))
    {
        if (!RC_ResizeWidth(new) && RC_ResizeHeight(new))
        {
            w = new->core.width;
            h = 0;
        }
        else if (RC_ResizeWidth(new) && !RC_ResizeHeight(new))
        {
            w = 0;
            h = new->core.height;
        }
        else if (RC_ResizeWidth(new) && RC_ResizeHeight(new))
        {
            w = 0;
            h = 0;
        }
        else
        {
            AdaptToSize(new,NULL,NULL);
            return;
        }

        PreferredSize (new, &w, &h);

        AdaptToSize(new,NULL,NULL);

        XtWidth(new) = w;
        XtHeight(new) = h;
    }
}


static Boolean set_values_non_popup (old, new)
XmRowColumnWidget   old;            /* the old state widget */
XmRowColumnWidget   new;            /* the new, real widget */
{
    Widget child;
    Arg args[4];
    int n;
    Boolean need_expose = FALSE;

    if (IsBar(new))
        new->manager.traversal_on = old->manager.traversal_on;

    /* fdt : should this only be done for a menubar?? */
    need_expose |= RC_HelpPb (old) != RC_HelpPb (new);

    /*
     * If we are an option menu, then we must check to see if our mnemonic
     * has changed.  If we're a menubar, then see if our accelerator has
     * changed.
     */
    if (IsOption(new))
    {
       if (RC_OptionSubMenu(new) != RC_OptionSubMenu(old)) {
          XtSetArg(args[0], XmNsubMenuId, RC_OptionSubMenu(new));
          if (child = XmOptionButtonGadget(new))
             XtSetValues(child, args, 1);

          if (child = find_first_managed_child(RC_OptionSubMenu(new), FIRST_BUTTON)) {
             RC_MemWidget (new) = child;
          }
       }
       if (RC_MemWidget (old) != RC_MemWidget (new))
          SetMenuHistory (new, RC_MemWidget (new));

       n = 0;
       if (RC_OptionLabel(new) != RC_OptionLabel(old)) {
          XtSetArg(args[n], XmNlabelString, RC_OptionLabel(new)); n++;
          XtSetArg(args[n], XmNlabelType, XmSTRING); n++;
       }
       if ((RC_Mnemonic(new) != RC_Mnemonic(old)) ||
           (RC_MnemonicCharSet(new) != RC_MnemonicCharSet(old)))
       {
          XtSetArg(args[n], XmNmnemonic, RC_Mnemonic(new)); n++;
          XtSetArg(args[n], XmNmnemonicCharSet, RC_MnemonicCharSet(new)); n++;
       }
       if (n && (child = XmOptionLabelGadget(new)))
          XtSetValues(child, args, n);
       DoProcessMenuTree(new, XmREPLACE);
    }
    else if (IsBar(new) && (RC_MenuAccelerator(new) != RC_MenuAccelerator(old)))
    {
        if (RC_MenuAccelerator(new))
        {
            RC_MenuAccelerator(new) = (String)strcpy(XtMalloc( XmStrlen(
                RC_MenuAccelerator(new)) + 1), RC_MenuAccelerator(new));
        }
        DoProcessMenuTree(new, XmREPLACE);
        if (RC_MenuAccelerator(old))
           XtFree(RC_MenuAccelerator(old));
    }

   /*
    * Moved here in case Option Menu geometry changed
    */
    need_expose |= do_entry_stuff (old, new);
    do_size (old, new);

    return (need_expose);
}



static Boolean set_values_popup (old, new)
XmRowColumnWidget   old;            /* the old state widget */
XmRowColumnWidget   new;            /* the new, real widget */
{
    int need_expose  = FALSE;

    new->manager.traversal_on = old->manager.traversal_on;

    need_expose |= do_entry_stuff (old, new);
    do_size (old, new);

    if ((XtX (old) != XtX (new)) ||     /* signal the shell that it */
        (XtY (old) != XtY (new)))       /* had better move itself */
    {                                   /* to the menu's location */
        RC_SetWidgetMoved (new, TRUE);      /* and that it has to move */
        RC_SetWindowMoved (new, TRUE);      /* the menu's window back */
    }

    /*
     * If we are a popup menu, then we need to check the 
     * state of the popupEnabled resource; we may need to add or remove the 
     * event handler we use to catch accelerators and mnemonics.
     */
    if (IsPopup(new))
    {
       if (RC_PopupEnabled(new) != RC_PopupEnabled(old))
       {
          if (RC_PopupEnabled(new))
          {
	     AddPopupEventHandlers(new);
             DoProcessMenuTree(new, XmADD);
          }
          else
          {
	     RemovePopupEventHandlers (new);
             DoProcessMenuTree(new, XmDELETE);
          }
       }

       /* See if our accelerator has changed */
       if (RC_PopupEnabled(new) &&
           (RC_MenuAccelerator(new) != RC_MenuAccelerator(old)))
       {
          if (RC_MenuAccelerator(new))
          {
             RC_MenuAccelerator(new) = (String)strcpy(XtMalloc( XmStrlen(
                 RC_MenuAccelerator(new)) + 1), RC_MenuAccelerator(new));
          }
          DoProcessMenuTree(new, XmREPLACE);
          if (RC_MenuAccelerator(old))
             XtFree(RC_MenuAccelerator(old));
       }
    }

    return (need_expose);
}



/* 
 * Empty event handler, added to the associated widget for a popup menu.
 * Forces the widget to select for button release events.
 */
/* ARGSUSED */
static void DoNothing (w, client_data, event)
Widget w;
caddr_t client_data;
XEvent * event;
{
}

static void set_values_passive_grab (old, new)
XmRowColumnWidget   old;            /* the old state widget */
XmRowColumnWidget   new;            /* the new, real widget */
{
   int i;
   Cursor cursor;

   if (IsPopup(old))
   {
      /* Keep our passive grab up to date. */
      if (RC_PopupEnabled(old))
      {
         /* Remove it from the postFrom widgets */
         for (i=0; i < old->row_column.postFromCount; i++)
         {
            /* Remove our passive grab */
            if (XtIsRealized(old->row_column.postFromList[i]))
            {
               XtUngrabButton (old->row_column.postFromList[i], 
		  RC_PostButton(old), RC_PostModifiers(old));
            }
         }

         if (RC_PopupEnabled(new))
         {
            cursor = XmGetMenuCursor(XtDisplay(new));

            /* add to all of the widgets in the postFromList*/
            for (i=0; i < new->row_column.postFromCount; i++)
            {
               /*
                * Must add a passive grab, so that owner_events is
                * set to True when the button grab is activated
                * this is so that enter/leave
                * events get dispatched by the server to the client.
                */
	       XtGrabButton (new->row_column.postFromList[i], 
		  RC_PostButton(new), RC_PostModifiers(new), 
		  TRUE, ButtonReleaseMask, GrabModeSync, GrabModeSync, 
		  None, cursor);
            }
         }
      }
   }
}


static Boolean SetValues (old, req, new)
XmRowColumnWidget    old;           /* the real widget */
XmRowColumnWidget    req;           /* after arglist */
XmRowColumnWidget    new;           /* after superclasses */
{
   int i;
   int need_expose = FALSE;

   if (!XtWidth(req))
   {
      _XmWarning(req,BadWidthSVMsg);
      XtWidth(req) = 16;
   }

   if (!XtHeight(req))
   {
      _XmWarning(req,BadHeightSVMsg);
      XtHeight(req) = 16;
   }

   switch (RC_Orientation(req))
   {
    case XmVERTICAL:
    case XmHORIZONTAL:
      break;
    case XmNO_ORIENTATION:
    default:
      _XmWarning(req,BadOrientationSVMsg);
      RC_Orientation(new) = RC_Orientation(old);
      break;
   }

   switch (RC_Packing(req))
   {
    case XmNO_PACKING:
    case XmPACK_TIGHT:
    case XmPACK_COLUMN:
    case XmPACK_NONE:
      break;
    default:
      _XmWarning(req,BadPackingSVMsg);
      RC_Packing(new) = RC_Packing(old);
      break;
   }

   if (RC_Type(req) != RC_Type(old))
   {
      /* Type CANNOT be changed after initialization */
      _XmWarning(new,BadTypeSVMsg);
      RC_Type(new) = RC_Type(old);
   }

   switch (RC_EntryAlignment(req))
   {
    case XmALIGNMENT_BEGINNING:
    case XmALIGNMENT_CENTER:
    case XmALIGNMENT_END:
      break;
    default:
      _XmWarning(req,BadAlignmentSVMsg);
      RC_EntryAlignment(new) = RC_EntryAlignment(old);
      break;
   }

   if (IsBar(new))
   {
      if (RC_IsHomogeneous(req) != RC_IsHomogeneous(old))
      {
	 /* can't change this for menu bars */
	 _XmWarning(new,BadMenuBarHomogenousSVMsg);
	 RC_IsHomogeneous(new) = TRUE;
      }
      if (RC_EntryClass(req) != RC_EntryClass(old))
      {
	 /* can't change this for menu bars */
	 _XmWarning(new,BadMenuBarEntryClassSVMsg);
	 RC_EntryClass(new) = xmCascadeButtonWidgetClass;
      }
   }

   if (RC_MenuPost(new) != RC_MenuPost(old))
   {
      if (IsPulldown(new))
      {
         /* MenuPost cannot be changed via SetValues for Pulldowns */
         _XmWarning(new,BadPulldownMenuPostSVMsg);
         /* just in case WhichButton was set */
         RC_PostButton(new) = RC_PostButton(old);
      }
      else
      {
         if (_XmMapBtnEvent(RC_MenuPost(new), &RC_PostEventType(new),
               &RC_PostButton(new), &RC_PostModifiers(new)) == FALSE)
         {
            _XmWarning(new,BadMenuPostMsg);
            /* Do Nothing - No change to postButton/Modifiers/EventType */
         }
         else
            set_values_passive_grab(old, new);
      }
   }
   else   /* For backwards compatibility... */
      if (RC_PostButton(new) != RC_PostButton(old))
      {
         if (IsPulldown(new))
         {
            /* WhichButton cannot be changed via SetValues for Pulldowns */
            _XmWarning(new,BadPulldownWhichButtonSVMsg);
            RC_PostButton(new) = RC_PostButton(old);
         }
         else
         {
            RC_PostModifiers(new) = AnyModifier;
            RC_PostEventType(new) = ButtonPress;
            set_values_passive_grab(old, new);
         }
      }


   /*
    * Shadow thickness is forced to zero for all types except
    * pulldown, popup, and menubar
    */
   if (IsPulldown(new) || IsPopup(new) || IsBar(new))
   {
      if (MGR_ShadowThickness(req) != MGR_ShadowThickness(old))
	  need_expose |= TRUE;
   }

   else if (MGR_ShadowThickness(req) != MGR_ShadowThickness(old))
   {
      _XmWarning(new,BadShadowThicknessSVMsg);
      MGR_ShadowThickness(new) = 0;
   }

   if (IsOption(new) &&
       (RC_Orientation(req) != RC_Orientation(old)))
   {
      _XmWarning(new,BadOptionOrientationSVMsg);
      RC_Orientation(new) = XmHORIZONTAL;
   }

   /* postFromList changes, popups and pulldowns only */
   if (IsPopup(new) || IsPulldown(new))
   {
      if ((new->row_column.postFromList != old->row_column.postFromList) ||
	  (new->row_column.postFromCount != old->row_column.postFromCount))
      {
	 /* use temp - postFromCount decremented in RemoveFromPostFromList() */
	 int cnt;
	 if (old->row_column.postFromList)
	 {
	    cnt = old->row_column.postFromCount;
	    for (i=0; i < cnt; i++)
	    {
	       RemoveHandlersFromPostFromWidget(new,
		  old->row_column.postFromList[i]);
	    }
	    XtFree(old->row_column.postFromList);
	 }

         PreparePostFromList(new);
      }
   }
	 
   if (IsBar (new) || IsWorkArea (new) || IsOption (new))
       need_expose |= set_values_non_popup (old, new);
   else
       need_expose |= set_values_popup (old, new);
   
   return (need_expose);
}


static char *GetRealKey(rc, str)
XmRowColumnWidget rc;
char *str;
{
   KeySym   keysym;
   Modifiers mods;
   char tmp[128];
   char *ks;
    
   keysym = XStringToKeysym(str);
   if (keysym == NoSymbol) 
      return(NULL);
            
   _XmVirtualToActualKeysym(XtDisplay(rc), keysym, &keysym, &mods);

   if (!(ks = XKeysymToString(keysym)))
      return(NULL);

   tmp[0] = '\0';
   if (mods & ControlMask)
      strcpy(tmp, "Ctrl ");

   if (mods & ShiftMask)
      strcat(tmp, "Shift ");
            
   strcat(tmp,"<KeyUp>");
   strcat(tmp, ks);

   return(XtNewString(tmp));
}

static void MenuBarInitialize (bar)
XmRowColumnWidget bar;
{
    Widget topManager;

    RC_IsHomogeneous(bar) = TRUE;
    RC_EntryClass(bar) = xmCascadeButtonWidgetClass;
    RC_SetArmed(bar,FALSE);
    bar->manager.traversal_on = False;
    bar->row_column.lastSelectToplevel = (Widget) bar;

    if (RC_PostButton(bar) == -1)
        RC_PostButton(bar) = Button1;
    

    if (RC_Packing(bar) == XmNO_PACKING)
        RC_Packing(bar) = XmPACK_TIGHT;

    if (RC_Orientation(bar) == XmNO_ORIENTATION)
        RC_Orientation(bar) = XmHORIZONTAL;

    if (RC_Spacing(bar) == XmINVALID_DIMENSION)
	RC_Spacing(bar) = 0;

    if (bar->core.tm.translations == NULL)
        bar->core.tm.translations = menu_parsed;

    XtOverrideTranslations(bar, menu_traversal_parsed);
    
    if (RC_MenuAccelerator(bar) && (*RC_MenuAccelerator(bar) == '\0'))
        if (!(RC_MenuAccelerator(bar) = GetRealKey(bar, "osfMenuBar")))
	      RC_MenuAccelerator(bar) = "<KeyUp>F10";

    /*
     * Add an event handler to both us and the associated widget; we
     * need one in case we have gadget children.
     */
    GetTopManager (bar, &topManager);
    XtAddEventHandler(bar, KeyPressMask|KeyReleaseMask,
        False, KeyboardInputHandler, bar);
    XtAddEventHandler(topManager, KeyPressMask|KeyReleaseMask,
        False, KeyboardInputHandler, bar);
    
    if (RC_MenuAccelerator(bar))
        DoProcessMenuTree(bar, XmADD);
}

/*
 * prepare postFromList: if its at its default state, its parent should
 * be in the list.  If a list has been specified but the count has not,
 * then set the count to 0.  This is only useful for Popup and Pulldown panes.
 */
static void PreparePostFromList(rowcol)
XmRowColumnWidget rowcol;
{
   Widget * tempPtr;
   Boolean forceParent = FALSE;
   int i;
   
   if (rowcol->row_column.postFromCount < 0)
   {
      if (IsPopup(rowcol) && rowcol->row_column.postFromList == NULL)
      {
	 /* default state for popups, set to parent */
	 rowcol->row_column.postFromCount = 1;
	 forceParent = True;
      }
      else
	  /* user provided a list but no count, default count to 0 */
	  rowcol->row_column.postFromCount = 0;
   }

   /* malloc enough space for 1 more addition to the list */
   rowcol->row_column.postFromListSize = rowcol->row_column.postFromCount + 1;

   tempPtr = rowcol->row_column.postFromList;
   rowcol->row_column.postFromList = (Widget *)
       XtMalloc (rowcol->row_column.postFromListSize * sizeof(Widget));

   if (tempPtr)
   {
      /* use temp - postFromCount incremented in AddToPostFromList() */
      int cnt = rowcol->row_column.postFromCount;
      /* reset the postFromCount for correct AddToPostFromList() assignment */
      rowcol->row_column.postFromCount = 0;	

      for (i=0; i < cnt; i++)
      {
	 XmAddToPostFromList (rowcol, tempPtr[i]);
      }
   }
   else if (forceParent)
   {
      /* no postFromList, then parent of Popup is on this list */
      rowcol->row_column.postFromList[0] = XtParent(XtParent(rowcol));
   }
}

static void PopupInitialize (popup)
XmRowColumnWidget popup;
{
   popup->manager.traversal_on = False;
   popup->row_column.lastSelectToplevel = (Widget) popup;
   
   if (RC_PostButton(popup) == -1)
       RC_PostButton(popup) = Button3;
    
   if (RC_Packing(popup) == XmNO_PACKING)
       RC_Packing(popup) = XmPACK_TIGHT;

   if (RC_Orientation(popup) == (char) XmNO_ORIENTATION)
       RC_Orientation(popup) = XmVERTICAL;

   if (RC_HelpPb(popup) != NULL)
   {
      _XmWarning(popup, BadPopupHelpMsg);
      RC_HelpPb(popup) = NULL;
   }
   
   if (RC_Spacing(popup) == XmINVALID_DIMENSION)
       RC_Spacing(popup) = 0;

   if (popup->core.tm.translations == NULL)
       popup->core.tm.translations = menu_parsed;

   XtOverrideTranslations(popup, menu_traversal_parsed);

   /* If no accelerator specified, use the default */
   if (RC_MenuAccelerator(popup) && (*RC_MenuAccelerator(popup) == '\0'))
      if (!(RC_MenuAccelerator(popup) = GetRealKey(popup, "osfMenu")))
         RC_MenuAccelerator(popup) = "<KeyUp>F4";

   /* Save a copy of the accelerator string */
   if (RC_MenuAccelerator(popup))
      RC_MenuAccelerator(popup) = 
         (String) strcpy (XtMalloc(XmStrlen(RC_MenuAccelerator(popup)) + 1),
         RC_MenuAccelerator(popup));

   PreparePostFromList(popup);
    
   /* Add event handlers to all appropriate widgets */
   if (RC_PopupEnabled(popup))
   {
      AddPopupEventHandlers (popup);

      /* Register all accelerators */
      DoProcessMenuTree(popup, XmADD);
   }
}

static void PulldownInitialize (pulldown)
XmRowColumnWidget pulldown;
{
    pulldown->manager.traversal_on = False;
    pulldown->row_column.lastSelectToplevel = (Widget) NULL;

    if (RC_Packing(pulldown) == XmNO_PACKING)
        RC_Packing(pulldown) = XmPACK_TIGHT;

    if (RC_Orientation(pulldown) == (char) XmNO_ORIENTATION)
        RC_Orientation(pulldown) = XmVERTICAL;

    if (RC_HelpPb(pulldown) != NULL)
    {
        _XmWarning(pulldown, BadPulldownHelpMsg);
        RC_HelpPb(pulldown) = NULL;
    }

    if (RC_Spacing(pulldown) == XmINVALID_DIMENSION)
        RC_Spacing(pulldown) = 0;
    
    if (pulldown->core.tm.translations == NULL)
        pulldown->core.tm.translations = menu_parsed;

    XtOverrideTranslations(pulldown, menu_traversal_parsed);

    RC_MenuAccelerator(pulldown) = NULL;
    PreparePostFromList(pulldown);

    /* add event handler to myself for gadgets */
    XtAddEventHandler(pulldown, KeyPressMask|KeyReleaseMask,
		      False, KeyboardInputHandler, pulldown);
}
       

static void OptionInitialize (option)
XmRowColumnWidget option;
{
    char b[200];
    int n;
    Arg args[15];
    Widget topManager;
    Widget child;

    if (RC_HelpPb(option) != NULL)
    {
        _XmWarning(option, BadOptionHelpMsg);
        RC_HelpPb(option) = NULL;
    }

    RC_Packing(option) = XmPACK_TIGHT;
    RC_NCol(option) = 2;
    RC_Orientation(option) = XmHORIZONTAL;
    option->row_column.lastSelectToplevel = (Widget) option;

    if (RC_PostButton(option) == -1)
        RC_PostButton(option) = Button1;
    
    if (RC_Spacing(option) == XmINVALID_DIMENSION)
        RC_Spacing(option) = 3;

    if (option->core.tm.translations == NULL)
        option->core.tm.translations = option_parsed;

    XtOverrideTranslations(option,
         ((XmManagerClassRec *)XtClass(option))->manager_class.translations);

    /* Create the label widget portion of the option menu */
    n = 0;
    XtSetArg(args[n], XmNlabelString, RC_OptionLabel(option)); n++;
    child = XmCreateLabelGadget(option,"",args,n);
    XtManageChild (child);

    /* Create the cascade button widget portion of the option menu */
    sprintf (b, "%s_cascadeBtn", option->core.name);
    n = 0;
    XtSetArg(args[n], XmNsubMenuId, RC_OptionSubMenu(option)); n++;
    XtSetArg(args[n], XmNshadowThickness, MGR_ShadowThickness(option)); n++;
    XtSetArg(args[n], XmNmarginWidth, 0); n++;
    XtSetArg(args[n], XmNmarginLeft, 0); n++;
    XtSetArg(args[n], XmNmarginHeight, 0); n++;
    XtSetArg(args[n], XmNmarginTop, 0); n++;
    XtSetArg(args[n], XmNmarginBottom, 0); n++;
    XtSetArg(args[n], XmNalignment, XmALIGNMENT_CENTER); n++;
    child = XmCreateCascadeButtonGadget(option,b,args,n);
    XtManageChild (child);

    RC_MenuAccelerator(option) = NULL;

    /* Add event handlers for catching keyboard input */
    GetTopManager (option, &topManager);
    XtAddEventHandler(option, KeyPressMask|KeyReleaseMask,
        False, KeyboardInputHandler, option);
    XtAddEventHandler(topManager, KeyPressMask|KeyReleaseMask,
        False, KeyboardInputHandler, option);

    if (RC_Mnemonic(option))
        DoProcessMenuTree(option, XmADD);

    /* This forces tab group - should probably make this a dynamic default! */
    option->manager.navigation_type = XmNONE;
}

static void WorkAreaInitialize (work)
XmRowColumnWidget work;
{
    MGR_ShadowThickness(work) = 0;

    if (RC_PostButton(work) == -1)
        RC_PostButton(work) = Button1;
    
    if (RC_Packing(work) == XmNO_PACKING)
        RC_Packing(work) = XmPACK_TIGHT;

    if (RC_Orientation(work) == (char) XmNO_ORIENTATION)
        RC_Orientation(work) = XmVERTICAL;

    if (RC_HelpPb(work) != NULL)
    {
        _XmWarning(work, BadWorkAreaHelpMsg);
        RC_HelpPb(work) = NULL;
    }

    if (work->row_column.radio &&
        (RC_Packing(work) == XmNO_PACKING))
        RC_Packing(work) = XmPACK_TIGHT;

    if (RC_Spacing(work) == XmINVALID_DIMENSION)
        RC_Spacing(work) = 3;

    if (work->core.tm.translations == NULL)
        work->core.tm.translations =
	    (XtTranslations) xmManagerClassRec.core_class.tm_table;

    XtOverrideTranslations(work, ((XmManagerClassRec *)XtClass(work))->
                          manager_class.translations);

    RC_MenuAccelerator(work) = NULL;
    
}

/*
 * Initialize a row column widget
 */

static void Initialize (req, m)
XmRowColumnWidget req;              /* build from arglist */
XmRowColumnWidget m;              /* after superclass */
{
    if (!XtWidth(req))
    {
        XtWidth(m) = 16;
    }

    if (!XtHeight(req))
    {
        XtHeight(m) = 16;
    }

    if (IsPulldown(m) || IsPopup(m))
    {
	if (RC_MarginW(m) == XmINVALID_DIMENSION)
	    RC_MarginW(m) = 0;
	if (RC_MarginH(m) == XmINVALID_DIMENSION)
	    RC_MarginH(m) = 0;
    } else
    {
	if (RC_MarginW(m) == XmINVALID_DIMENSION)
	    RC_MarginW(m) = 3;
	if (RC_MarginH(m) == XmINVALID_DIMENSION)
	    RC_MarginH(m) = 3;
    }

    switch (RC_Orientation(req))
    {
        case XmNO_ORIENTATION:
        case XmVERTICAL:
        case XmHORIZONTAL:
        break;
        default:
            _XmWarning(m,BadOrientationMsg);
            RC_Orientation(m) = XmNO_ORIENTATION;
        break;
    }

    switch (RC_Packing(req))
    {
        case XmNO_PACKING:
        case XmPACK_TIGHT:
        case XmPACK_COLUMN:
        case XmPACK_NONE:
        break;
        default:
            _XmWarning(m,BadPackingMsg);
            RC_Packing(m) = XmNO_PACKING;
        break;
    }

    switch (RC_Type(req))
    {
        case XmWORK_AREA:
        case XmMENU_BAR:
        case XmMENU_OPTION:
            break;
        case XmMENU_POPUP:
        case XmMENU_PULLDOWN:
	    if (!XmIsMenuShell(XtParent(req)) ||
		!XtParent(XtParent(req)))
	    {
		_XmWarning(m,BadTypeParentMsg);
		RC_Type(m) = XmWORK_AREA;
	    }
	    break;
        default:
            _XmWarning(m,BadTypeMsg);
            RC_Type(m) = XmWORK_AREA;
            break;
    }

    switch (RC_EntryAlignment(req))
    {
        case XmALIGNMENT_BEGINNING:
        case XmALIGNMENT_CENTER:
        case XmALIGNMENT_END:
        break;
        default:
            _XmWarning(m,BadAlignmentMsg);
            RC_EntryAlignment(m) = XmALIGNMENT_BEGINNING;
        break;
    }

    RC_CascadeBtn(m) = NULL;
    RC_Boxes(m) = NULL;

    RC_SetExpose (m, TRUE);             /* and ready to paint gadgets */
    RC_SetWidgetMoved  (m, TRUE);       /* and menu and shell are not */
    RC_SetWindowMoved  (m, TRUE);       /* in synch, positiongally */
    RC_SetArmed  (m, TRUE);             /* are always armed */
    RC_SetPoppingDown  (m, FALSE);      /* not popping down */
    RC_PopupPosted(m) = NULL;		/* no popup submenus posted */

    /* create the menu cursor for this display, if there isn't one already */
    _XmCreateMenuCursor(m);

    if (m->manager.shadow_thickness == XmINVALID_DIMENSION)
	m->manager.shadow_thickness = 2;

    m->row_column.old_width = XtWidth(m);
    m->row_column.old_height = XtHeight(m);
    m->row_column.old_shadow_thickness = m->manager.shadow_thickness;

    /* Post initialization for whichButton - done before PopupInitialize
     * because RC_PostModifiers used in eventual XtGrabButton()
     */
    RC_PostModifiers(m) = AnyModifier;
    RC_PostEventType(m) = ButtonPress;

    if (IsBar(m))
        MenuBarInitialize(m);
    else if (IsPopup(m))
        PopupInitialize(m);
    else if (IsPulldown(m))
        PulldownInitialize(m);
    else if (IsOption(m))
        OptionInitialize(m);
    else
        WorkAreaInitialize(m);

    /* allow menuPost override */
    if ((RC_MenuPost(m) != NULL) && !IsPulldown(m)) {
        if (_XmMapBtnEvent(RC_MenuPost(m), &RC_PostEventType(m),
            &RC_PostButton(m), &RC_PostModifiers(m)) == FALSE)
        {
            _XmWarning(m,BadMenuPostMsg);
        }
    }



    SetMenuHistory (m, RC_MemWidget (m));
}

/* ARGSUSED */
static void ConstraintInitialize (req,new)
Widget req;             /* build from arglist */
Widget new;             /* after superclass */
{

    if (!XtIsRectObj(new)) return;

    WasManaged(new) = False;
}


/*
 * the main create section, mostly just tacks on the type to the arg
 * list
 */

static Widget create (p, name, old_al, old_ac, type, is_radio)
Widget  p;                  /* parent widget */
char   *name;
ArgList old_al;
Cardinal     old_ac;
int     type;               /* menu kind to create */
int     is_radio;               /* the radio flag */
{
    Arg al[50];
    Widget m;
    int i, ac = 0;

    if (is_radio)               /* get ours in ahead of the */
    {                           /* caller's, so his override */
        XtSetArg (al[ac], XmNpacking, XmPACK_COLUMN);    ac++;
        XtSetArg (al[ac], XmNradioBehavior, is_radio); ac++;
        XtSetArg (al[ac], XmNisHomogeneous, TRUE); ac++;
        XtSetArg (al[ac], XmNentryClass, xmToggleButtonGadgetClass); ac++;
    }

    for (i=0; i<old_ac; i++) al[ac++] = old_al[i];  /* copy into our list */

    if (type != UNDEFINED_TYPE)
    {
       XtSetArg (al[ac], XmNrowColumnType,  type);
       ac++;
    }

    /*
     * decide if we need to build a popup shell widget
     */

    if ((type == XmMENU_PULLDOWN) || (type == XmMENU_POPUP))
    {
        Arg s_al[25];
        XmMenuShellWidget pop = NULL;
        Widget pw;
        int s_ac = 0;
        char b[200];

        /*
         * if this is a pulldown of a pulldown or popup then the parent
         * should really be the shell of the parent not the indicated 
         * parent, this keeps the cascade tree correct
         */

        if ((XtParent(p) != NULL) && XmIsMenuShell(XtParent (p)))
            pw = XtParent (p);
        else
            pw = p;

        /* 
         * Shared menupanes are supported for all menu types but the option
         * menu.  If this is not an option menupane, then see if a shell is
         * already present; if so, then we'll use it.
         */
        if (XmIsRowColumn(p) && (IsBar(p) || IsPopup(p) || IsPulldown(p)))
        {
            for (i = 0; i < pw->core.num_popups; i++)
            {
                if ((XmIsMenuShell(pw->core.popup_list[i])) &&
                   (((XmMenuShellWidget)pw->core.popup_list[i])->menu_shell.
                                                            private_shell) &&
                   (!(pw->core.popup_list[i])->core.being_destroyed))
                {
                    pop = (XmMenuShellWidget)pw->core.popup_list[i];
                    break;
                }
            }
        }

        /* No shell - create a new one */
        if (pop == NULL)
        {
            XtSetArg (s_al[s_ac], XmNwidth,             5);     s_ac++;
            XtSetArg (s_al[s_ac], XmNheight,        5);     s_ac++;
            XtSetArg (s_al[s_ac], XmNallowShellResize, TRUE);   s_ac++;
            XtSetArg (s_al[s_ac], XtNoverrideRedirect, TRUE);   s_ac++;

            sprintf (b, "popup_%s", name);

            pop = (XmMenuShellWidget)XtCreatePopupShell(b, 
                     xmMenuShellWidgetClass, pw, s_al, s_ac);

            /* Mark the shell as having been created by us */
            pop->menu_shell.private_shell = True;
        }

        m = XtCreateWidget (name, xmRowColumnWidgetClass, pop, al, ac);
    }
    else
        m = XtCreateWidget (name, xmRowColumnWidgetClass, p, al, ac);

    return (m);
}


/*
 *************************************************************************
 *
 * Public Routines                                                        
 *
 *************************************************************************
 */

void XmMenuPosition (Widget p, XButtonPressedEvent *event)
{
   PositionMenu (p, event);
}


Widget XmCreateRowColumn (Widget p, char *name, ArgList al, Cardinal ac)
{
    return (create (p, name, al, ac, UNDEFINED_TYPE, FALSE));
}

Widget XmCreateWorkArea (Widget p, char *name, ArgList al, Cardinal ac)
{
    return (create (p, name, al, ac, XmWORK_AREA, FALSE));
}


Widget XmCreateRadioBox (Widget p, char *name, ArgList al, Cardinal ac)
{
    return (create (p, name, al, ac, XmWORK_AREA, TRUE));
}


Widget XmCreateOptionMenu (Widget p, char *name, ArgList al, Cardinal ac)
{
    return (create (p, name, al, ac, XmMENU_OPTION, FALSE));
}

Widget XmOptionLabelGadget (Widget m)
{
   int i;
   Widget child;
   
   if (XmIsRowColumn(m) && IsOption(m))
   {
      XmRowColumnWidget rowcol = (XmRowColumnWidget) m;
      
      for (i = 0; i < rowcol->composite.num_children; i++)
      {
	 child = rowcol->composite.children[i];

	 if (XtClass(child) == xmLabelGadgetClass)
	     return (child);
      }
   }

   /* did not find a label gadget in the child list */
   return (NULL);
}

Widget XmOptionButtonGadget (Widget m)
{
   int i;
   Widget child;
   
   if (XmIsRowColumn(m) && IsOption(m))
   {
      XmRowColumnWidget rowcol = (XmRowColumnWidget) m;
      
      for (i = 0; i < rowcol->composite.num_children; i++)
      {
	 child = rowcol->composite.children[i];

	 if (XmIsCascadeButtonGadget(child))
	     return (child);
      }
   }

   /* did not find a cascadebuttongadget in the child list */
   return (NULL);
}


Widget XmCreateMenuBar (Widget p, char *name, ArgList al, Cardinal ac)
{
    return (create (p, name, al, ac, XmMENU_BAR, FALSE));
}


Widget XmCreatePopupMenu (Widget p, char *name, ArgList al, Cardinal ac)
{
    return (create (p, name, al, ac, XmMENU_POPUP, FALSE));
}


Widget XmCreatePulldownMenu (Widget p, char *name, ArgList al, Cardinal ac)
{
    return (create (p, name, al, ac, XmMENU_PULLDOWN, FALSE));
}

void XmAddToPostFromList (XmRowColumnWidget menu, Widget widget)
{
   Arg args[1];

   /* only continue if its a vailid widget and a popup or pulldown menu */
   if (! XmIsRowColumn(menu) ||
       ! (IsPopup(menu) || IsPulldown(menu)) ||
       ! widget)
       return;
   
   if (OnPostFromList(menu, widget) == -1)
   {
      if (IsPulldown(menu))
      {
	 XtSetArg (args[0], XmNsubMenuId, menu);
	 XtSetValues (widget, args, 1);
      }
      else 
      {
	 AddToPostFromList (menu, widget);
	 AddHandlersToPostFromWidget (menu, widget);
      }
   }
}

void XmRemoveFromPostFromList (XmRowColumnWidget menu, Widget widget)
{
   Arg args[1];

   /* only continue if its a vailid widget and a popup or pulldown menu */
   if (! XmIsRowColumn(menu) ||
       ! (IsPopup(menu) || IsPulldown(menu)) ||
       ! widget)
       return;
	
   if ((OnPostFromList(menu, widget)) == -1)
   {
      if (IsPulldown(menu))
      {
	 XtSetArg (args[0], XmNsubMenuId, NULL);
	 XtSetValues (widget, args, 1);
      }
      else 
      {
	 RemoveFromPostFromList (menu, widget);
	 RemoveHandlersFromPostFromWidget (menu, widget);
      }
   }
}

/*
 * Return the widget which the menu was posted from.  If this is in a popup,
 * it is the widget which initiated the post (via positioning & managing or
 * via armAndActivate).  If it is in a pulldown from a menubar or option menu,
 * then the returned widget is the menubar or option menu.
 */
Widget XmGetPostedFromWidget (Widget menu)
{
   Widget toplevel = 
     ((XmRowColumnWidget) menu)->row_column.lastSelectToplevel;

   if (XmIsRowColumn(menu))
   {
      if (IsPopup(toplevel))
      {
	 /* active widget is kept in cascadeBtn field for popups */
	 return (RC_CascadeBtn(toplevel));
      }
      else
	 return (toplevel);
   }
   return (NULL);
}


/*
 * class initialization
 */

/*
 * needed for funky menubar mode so that the traversal can be restored
 * to the correct tabgroup when we are done.
 */
static Widget tabGroup;

static void ClassInitialize ()
{
   /*
    * parse the various translation tables
    */

   menu_parsed   = XtParseTranslationTable (menu_table);
   option_parsed    = XtParseTranslationTable (option_table);
   menu_traversal_parsed = XtParseTranslationTable (menu_traversal_table);

   /* initialize the menu cursor context */
   _XmInitializeMenuCursor();

   /* set up the menu procedure entry for button children to access */
   _XmSaveMenuProcContext( (caddr_t) MenuProcedureEntry);
   
   tabGroup = NULL;
}


static void ClassPartInitialize (rcc)
    XmRowColumnWidgetClass  rcc;
{
    _XmFastSubclassInit(rcc,XmROW_COLUMN_BIT);
}


/***************************************************************************
 *
 *
 * next section is action routines, these are called by the row column event
 * handler in response to events.  The handler parses its way through
 * the actions table calling these routines as directed.  It is these
 * routines which will typically decide the application should be
 * called via a callback.  This is where callback reasons are produced.
 *
 */

    

static XmRowColumnWidget find_menu (w)
Widget w;
{
    if (XmIsRowColumn(w))
        return ((XmRowColumnWidget) w);        /* row column itself */
    else
        return ((XmRowColumnWidget) XtParent (w)); /* subwidget */
}



/*
 * popdown anything that should go away
 */

static void MenuPopDown (w, event)
Widget  w;
XEvent * event;
{
   XmRowColumnWidget rc = find_menu(w);

   /*
    * find the top of this cascade and popdown anything posted on it
    */
   _XmGetActiveTopLevelMenu (rc, &rc);

   if (IsPopup(rc))
       (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
	  menu_shell_class.popdownDone))(rc, event);
       
   else if (RC_PopupPosted(rc))
   {
      (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
	 menu_shell_class.popdownDone))(RC_PopupPosted(rc), event);

      /* This kludges a fix so that F10 followed by mnemonic selection
       * doesn't call MenuFocusIn and leave the cascade button highlighted.
       */
      if (IsBar(rc))
	 (void)XmProcessTraversal(rc, XmTRAVERSE_CURRENT);
   }
}


static void MenuArm (w)
Widget  w;
{
    XmRowColumnWidget m = find_menu(w);

    if (!RC_IsArmed(m))
    {
        if (IsBar(m))
        {
            /*
             * Menubars need their own exclusive/SL grab, so that they will
             * still get input even when a cascade button without a submenu
             * has the focus.
             */
            _XmSetTransientFlag(m, True);
            XtAddGrab(m, True, True);
            RC_SetBeingArmed(m, True);
        }
        RC_SetArmed (m, True);
    }
}



static void MenuDisarm (w)
Widget  w;
{
    XmRowColumnWidget m = find_menu(w);

    if (RC_IsArmed(m))
    {
        if (IsBar(m))
        {
            _XmSetTransientFlag(m, False);
            XtRemoveGrab(m);
            RC_SetBeingArmed(m, False);
        }

        RC_SetArmed (m, FALSE);
    }
}




/**********************************************************************
 *
 * next section knows how to composite row column entries
 */

static void FixEventBindings (m, w)
XmRowColumnWidget  m;       /* row column (parent) widget */
Widget      w;          /* subwidget */
{
   if (XtIsWidget(w) &&
       ((IsPopup(m) || IsBar(m) || IsPulldown(m)) && 
	 XmIsLabel(w) && (w->core.widget_class != xmLabelWidgetClass)))
   {
      XtAddEventHandler(w, KeyPressMask|KeyReleaseMask, False,
			KeyboardInputHandler, m);

   }

   /* set up accelerators and mnemonics */
   ProcessSingleWidget (w, XmADD);
}


/*
 * Action routines for controlling traversal.
 */

static void KickOnTraversal (m)
XmRowColumnWidget m;
{
   XEvent event;
   Widget menupane;

   if (XmIsRowColumn(m))
   {
      if (RC_PopupPosted(m))
      {
	 menupane =
	     ((XmMenuShellWidget) RC_PopupPosted(m))->composite.children[0];

	 event.type = FocusIn;
	 event.xfocus.send_event = True;
	 (void) XmProcessTraversal (menupane, XmTRAVERSE_CURRENT);
	 _XmManagerFocusInInternal(menupane, &event);

	 if (!IsBar(m))
	     m->manager.active_child = RC_CascadeBtn(menupane);
	 return;
      }
       
      /* Set it to ourselves */
      event.type = FocusIn;
      event.xfocus.send_event = True;
      (void) XmProcessTraversal (m, XmTRAVERSE_CURRENT);
      _XmManagerFocusInInternal(m, &event);
   }
}


static void KickOffTraversal (m)
XmRowColumnWidget m;
{
    Widget rc;
    XmGadget gadget;
    CompositeWidget shell;
    CompositeWidget oldShell = NULL;

    shell = (CompositeWidget) RC_PopupPosted(m);

    /* find last posted menushell */
    while (oldShell != shell)
    {
       oldShell = shell;

       if (RC_PopupPosted(shell->composite.children[0]))
       {
	  shell =
	      (CompositeWidget) RC_PopupPosted(shell->composite.children[0]);
       }
    }

    if (shell)
	rc = shell->composite.children[0];
    else
	rc = (Widget) m;

    /* 
     * Clear focus entries for each posted menupane.
     */
    while (rc && 
           ((RC_Type(rc) == XmMENU_POPUP) || 
            (RC_Type(rc) == XmMENU_PULLDOWN)))
    {
       /*
	* Because a gadget will not get focus events, and because the
	* parent may also not get one (because of a toolkit optimization),
	* we need to handle gadget specially.
	*/
       gadget = (XmGadget)((XmManagerWidget)rc)->manager.active_child;

       /* Inform the gadget that it should unhilite & clean itself up */
       if (gadget && XmIsGadget(gadget))
       {
	  /* Direct focus to rc to handle focus events for gadgets */
	  _XmSetFocusResetFlag(rc, True); 
	  (void) XmProcessTraversal (rc, XmTRAVERSE_CURRENT);
	  _XmSetFocusResetFlag(rc, False);

	  if (ShouldDispatchFocusOut(gadget))
	  {
	     /* have to set active_child to NULL for CascadeBG to unhighlight */
             ((XmManagerWidget)rc)->manager.active_child = NULL;

	     _XmDispatchGadgetInput(gadget, NULL, XmFOCUS_OUT_EVENT);
	     gadget->gadget.have_traversal = False;
	  }
       }

       /* clears the focus_item so that next TraverseToChild() will work */
       _XmClearFocusPath(rc);

       if (! IsPopup(rc) && RC_CascadeBtn(rc))
	   rc = XtParent(RC_CascadeBtn(rc));

       else
	   rc = NULL;
    }
}


/*
 * When we drop back into traversal mode, we need to set the active child
 * field for each visible menupane.
 */

static void SetActiveChildren (toplevel)
XmRowColumnWidget toplevel;
{
   XmRowColumnWidget rc;
   XmRowColumnWidget subpane;

   if (IsOption(toplevel))
   {
      /* Link option submenu and its memory widget */
      rc = (XmRowColumnWidget)toplevel->row_column.option_submenu;
      rc->manager.active_child = (Widget)RC_MemWidget(rc);
      return;
   }

   while (1)
   {
      if (RC_PopupPosted(toplevel))
      {
	 subpane = (XmRowColumnWidget)
            ((CompositeWidget) RC_PopupPosted(toplevel))->composite.children[0];
	    
	 if (! IsBar(toplevel))
	 {
	    toplevel->manager.active_child = RC_CascadeBtn(subpane);
	 }

	 toplevel = subpane;
      }
      else
      {
	 /* No shells popped up; use first available widget */
	 toplevel->manager.active_child = NULL;
	 return;
      }
   }
}

/*
 * Class function used to enable or disable traversal.  This is called
 * when the menu system is active.
 */

static void SetMenuTraversal (m, on)
XmRowColumnWidget m;
Boolean on;
{
   XmRowColumnWidget topLevel;
   XmRowColumnWidget pane;

   _XmGetActiveTopLevelMenu (m, &topLevel);
   
   if (on)
   {
      /* The order here is important */
      SetActiveChildren(topLevel);
      SetTraversal(topLevel, True);
      KickOnTraversal(m);
   }
   else
   {
      /* Do something only if traversal is currently enabled */
      if (((IsBar(topLevel) || IsPopup(topLevel)) &&
	   topLevel->manager.traversal_on) ||
	  (IsOption(topLevel) &&
	   (pane = (XmRowColumnWidget)topLevel->row_column.option_submenu) &&
	   (pane->manager.traversal_on)))
      {
          /* The order here is important */
          KickOffTraversal(m);
          SetTraversal(topLevel, False);
      }
   }
}


/*
 * Class function which is used to clean up the menubar when it is leaving
 * the mode where the user has pressed F10 to start traversal in the
 * menubar.
 */

static void MenuBarCleanup (rc)
XmRowColumnWidget rc;
{
    /*
     * We can tell if this mode is active by looking at the 'active_child'
     * field in the menubar; it should only be set when we are in this mode.
     */
    if (rc->manager.active_child)
    {
        if (XmIsPrimitive(rc->manager.active_child))
        {
            (*(((XmPrimitiveClassRec *)XtClass(rc->manager.active_child))->
              primitive_class.border_unhighlight))(rc->manager.active_child);
        }

        else if (XmIsGadget(rc->manager.active_child))
        {
            (*(((XmGadgetClassRec *)XtClass(rc->manager.active_child))->
              gadget_class.border_unhighlight))(rc->manager.active_child);
        }

        _XmSetFocusResetFlag(rc, True);
        (void) XmProcessTraversal (rc, XmTRAVERSE_CURRENT);
        _XmSetFocusResetFlag(rc, False);
        rc->manager.active_child = NULL;
    }

    /*
     * restore the tabgroup to the widget that had the focus BEFORE the
     * menubar mode was entered.
     */
    if (tabGroup)
    {
       (void) XmProcessTraversal (tabGroup, XmTRAVERSE_CURRENT);
       tabGroup = NULL;
    }
}


/* ARGSUSED */
void _XmMenuFocus (Widget w, int operation, Time _time)
{
    static int	oldFocus = NULL;
    static int	oldRevert = 0;
 
   switch (operation)
      {
	case XmMENU_END:
	  if (oldFocus)
	    {
		XSetInputFocus(XtDisplay(w),
			       oldFocus,
			       oldRevert,
			       CurrentTime);
		oldFocus = oldRevert = NULL;
		XtUngrabKeyboard(w, CurrentTime);
	    }
	  break;
	case XmMENU_BEGIN:
	  /* We must grab the keyboard before the InputFocus is set for mwm
	   * to work correctly.
	   */
	  XtGrabKeyboard(w, True, GrabModeSync, GrabModeSync, CurrentTime);
	  XGetInputFocus(XtDisplay(w), &oldFocus, &oldRevert);
	  XSetInputFocus(XtDisplay(w), XtWindow(w), oldRevert,
			 CurrentTime);

	  /* These two round trips are to support the broken >= R4 server
	   * which does not unfreeze properly when XAllowEvents is called
	   * with AsyncBoth
	   */
	  XAllowEvents(XtDisplay(w), AsyncKeyboard, CurrentTime);
	  XAllowEvents(XtDisplay(w), AsyncPointer, CurrentTime);
	  XFlush(XtDisplay(w));

	  break;
	case XmMENU_MIDDLE:
	  XSetInputFocus(XtDisplay(w), XtWindow(w), oldRevert,
			 CurrentTime);
	  break;
      }
}


/*
 * Class function which is invoked when the post accelerator is received
 * for a popup menu or the menubar, or the post mnemonic is received for 
 * an option menu.
 */

static void ArmAndActivate (m, event)
XmRowColumnWidget m;
XKeyPressedEvent * event;
{
   int i;
   XmCascadeButtonWidget child;
   XRectangle visRect;
   Cursor cursor;

   if (IsPopup(m))
   {
      if (!XtIsManaged(m))
      {
	 Position x, y;

	 /* the posted from widget is saved in RC_CascadeBtn */
	 RC_CascadeBtn(m) = XtWindowToWidget(XtDisplay(m), event->window);
	 
	 /* Position & post menupane; then enable traversal */
	 RC_SetWidgetMoved(m, True);
	 /* Position the pane off of the parent of this rowcolumn's menushell.
	  * Place it in the upper left corner.
	  */
	 XtTranslateCoords(XtParent(XtParent(m)), 0, 0, &x, &y);

	 /* Verify popup for MenuShell's manage_set_changed() */
	 _XmButtonEventStatus.time = event->time;
	 _XmButtonEventStatus.verified = True;

	 XtX(m) = x;
	 XtY(m) = y;
	 XtManageChild(m);
	 SetActiveChildren(m);
	 SetTraversal(m, True);
	 KickOnTraversal(m);
      }
      else
      {
         /* Let the menushell widget clean things up */
         (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
            menu_shell_class.popdownDone))(XtParent(m), event);
      }
   }
   else if (IsOption(m))
   {
      XmGadget g = (XmGadget) XmOptionButtonGadget(m);

      /* Let the cascade button gadget do the work */
      (*(((XmGadgetClassRec *)XtClass(g))->gadget_class.
          arm_and_activate)) (g, event);
   }
   else if (IsBar(m))
   {
      if (RC_IsArmed(m))
      {
         /*
          * If the menubar is already armed, then F10 unposts and disarms
          * the menu system.  Handle PM menubar mode specially.
          */
         if (!m->row_column.popupPosted)
         {
            /* No submenus posted; must clean up ourselves */
            MenuDisarm(m);

	    _XmMenuFocus(m, XmMENU_END, CurrentTime);
            XtUngrabKeyboard(m, CurrentTime);
            XtUngrabPointer(m, CurrentTime);

            SetMenuTraversal(m, False);
            MenuBarCleanup(m);
         }
         else
         {
            /* Submenus are posted; let MenuPopDown() clean up */
            MenuPopDown(m, event);
         }
      }
      else
      {
         /*
          * If the menubar is not armed, then look to see if there is a
          * cascade button with a submenu; if none found, then return.
          * Otherwise, highlight and arm that button.
          */

         _XmCreateVisibilityRect(m, &visRect);
         for (i = 0; i < m->composite.num_children; i++)
         {
	    child = (XmCascadeButtonWidget)m->composite.children[i];

	    /* You can't traverse to a button which has no submenu */
	    if ((XmIsCascadeButton(child) && (CB_Submenu(child) == NULL)) ||
		(XmIsCascadeButtonGadget(child) &&
		 (CBG_Submenu(child) == NULL)))
                continue;

	    if (_XmTestTraversability(child, &visRect))
                break;
         }

         /* See if we found one */
         if (i >= m->composite.num_children)
            return;

         /*
          * Menubars need their own exclusive/SL grab, so that they will
          * still get input even when a cascade button without a submenu
          * has the focus.
          *
          * We can't call MenuArm() here, because it does the pointer grab
          * before our keyboard grab.
          */
         _XmSetTransientFlag(m, True);
         XtAddGrab(m, True, True);
         RC_SetArmed(m, True);

         /* Force the application to unhilite itself */
         m->manager.active_child = m->composite.children[i];

	 /*
	  * save the current tabgroup so that it can be restored after
	  * the menubar traversal mode is finished.
	  */
	 tabGroup = _XmGetTabGroup(m);
         (void) XmProcessTraversal (m, XmTRAVERSE_CURRENT);

         if (XtIsWidget(m->manager.active_child))
            (void) XmProcessTraversal (m->manager.active_child, XmTRAVERSE_CURRENT);
	 
	 cursor = XmGetMenuCursor(XtDisplay(m));
	 _XmMenuFocus(m, XmMENU_BEGIN, CurrentTime);
         XtGrabPointer (m, True, ButtonPressMask | ButtonReleaseMask |
			EnterWindowMask | LeaveWindowMask,
			GrabModeAsync, GrabModeAsync, None, cursor,
			CurrentTime);
         SetTraversal(m, True);

         if (XmIsPrimitive(m->manager.active_child))
         {
            (*(((XmPrimitiveClassRec *)XtClass(m->manager.active_child))->
                primitive_class.border_highlight)) (m->manager.active_child);
         }
         else if (XmIsGadget(m->manager.active_child))
         {
            (*(((XmGadgetClassRec *)XtClass(m->manager.active_child))->
                gadget_class.border_highlight)) (m->manager.active_child);
         }
      }
   }
   else if (IsPulldown(m))	/* Catch the Escape in a cascading menu! */
   {
      /* Let the menushell widget clean things up */
      (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
         menu_shell_class.popdownOne))(XtParent(m), event);
   }
}


/*
 * The following functions are used to manipulate lists of keyboard events
 * which are of interest to the menu system; i.e. accelerators and mnemonics.
 * The top level component within a menu system (the menubar, or the popup
 * menupane, or the option menu, or the work area menu) keeps a list of
 * the events it cares about.  Items are only added to the list when the
 * associated menu component has a complete path to the top level component.
 * Items are removed when the complete path is broken.
 *
 * The times at which a complete path may be formed is 1) When a button
 * in a menupane becomes managed, when a pulldown menupane is attached
 * to a cascading button, when the application sets the popupEnabled
 * resource to True, or when an option menu is created.  A complete path 
 * may be broken when 1) a button in a menupane is unmanaged, a pulldown 
 * menupane is detached from a cascading button, when the application clears 
 * the popupEnabled resource or an option menu is destroyed.
 *
 * The event handler for catching keyboard events is added by the row column
 * widget during its initialize process, and removed when the row column
 * widget is destroyed.  Keyboard grabs, which are needed to make accelerators
 * work, are added either when the accelerator is registered, or when the 
 * associated widget is realized; grabs cannot be added to a widget which 
 * does not have a window!
 */ 


/*
 * This function is used both by the row column widget, and components which
 * are contained within a menu (toggle, pushbutton and cascadebutton).  The
 * row column widget uses it to process a tree of menu components; when a
 * menupane is linked to a cascade button, the new menupane, along with any
 * submenus cascading from it, will be processed.  The row column widget
 * will use the XmADD or XmDELETE mode to to this.  When a menu component
 * needs to change its accelerator or mnemonics, it will use the XmREPLACE
 * mode.
 *
 * When this function is used to delete a tree of keyboard actions, the
 * link between the menupane at the root (parameter 'w') and the cascade
 * button it is attached to must not yet have been broken.  This allows
 * the function to trace up the hierarchy and find the top level widget.
 */

static void DoProcessMenuTree (w, mode)
Widget w;
int    mode;                    /* Add, Replace or Delete */
{
   /*
    * Depending upon the type of widget 'w' is, we may end up adding/deleting
    * keyboard actions for no widgets, the specified widget only, or the
    * specified widget and all others which cascade from it.
    */
   if (XmIsCascadeButton(w) || XmIsCascadeButtonGadget(w))
   {
      /* If our parent is not a row column, then abort */
      if (XmIsRowColumn(XtParent(w)))
      {
         if (IsOption(XtParent(w)))
         {
            if (mode == XmREPLACE)
               return;

            /*
             * A cascade button in an option menu does not have an
             * accelerator or a mnemonic associated with it.  However,
             * its submenu may, so we need to process it.
             */
            if (XmIsCascadeButtonGadget(w))
                w = (Widget)CBG_Submenu(w);
         }
         else if (IsWorkArea(XtParent(w)))
         {
            /*
             * Since work area menus do not support cascade buttons
             * with submenus, we will treat this like a pushbutton.
             */
            if (mode == XmREPLACE)
            {  
               /* Remove old one, if it exists */
               ProcessSingleWidget(w, XmDELETE);
               mode = XmADD;
            }

            ProcessSingleWidget(w, mode);
            return;
         }
         else if (IsBar(XtParent(w)) || 
                  IsPopup(XtParent(w)) ||
                  IsPulldown(XtParent(w)))
         {
            if (mode == XmREPLACE)
            {
               /* When replacing, don't worry about submenus */
               ProcessSingleWidget(w, XmDELETE);
               ProcessSingleWidget(w, XmADD);
               return;
            }

            /*
             * If we are in a menubar, a popup menupane or a pulldown
             * menupane, then we need to not only modify our keyboard 
             * event, but also any which are defined in our submenu.
             */
            ProcessSingleWidget(w, mode);

            if (XmIsCascadeButtonGadget(w))
                w = (Widget)CBG_Submenu(w);
            else
                w = (Widget)CB_Submenu(w);
         }
      }
   }
   else if (XmIsToggleButtonGadget(w) || XmIsToggleButton(w) ||
            XmIsPushButtonGadget(w) || XmIsPushButton(w))
   {
      if (mode == XmREPLACE)
      {
         /* Remove old one */
         ProcessSingleWidget(w, XmDELETE);
         mode = XmADD;
      }

      /*
       * In both of these cases, we need only modify the keyboard
       * event associated with this widget.
       */
      ProcessSingleWidget(w, mode);
      return;
   }
   else if (XmIsRowColumn(w))
   {
      /*
       * When the popupEnabled resource is enabled for a popup menupane,
       * we need to add the accelerator associated with the menu, followed 
       * by all keyboard events associated with submenus.  The reverse
       * happens when the resource is disabled.
       *
       * When an option menu is created, we will add its posting mnemonic;
       * its submenu is taken care of when it is attached to the cascading
       * button.
       */
      if (IsPopup(w))
      {
         if (mode == XmREPLACE)
         {
            /* Don't worry about submenus during a replace operation */
            ProcessSingleWidget(w, XmDELETE);
            ProcessSingleWidget(w, XmADD);
            return;
         }

         ProcessSingleWidget(w, mode);
      }
      else if (IsOption(w) || IsBar(w))
      {
         if (mode == XmREPLACE)
         {
            ProcessSingleWidget(w, XmDELETE);
            mode = XmADD;
         }

         ProcessSingleWidget(w, mode);
         return;
      }
   }
   else
   {
      /* Unknown widget type; do nothing */
      return;
   }

   /* Process any submenus */
   ProcessMenuTree(w, mode);
}


/*
 * Given a row column widget, all keyboard events are processed
 * for the items within the row column widget, and then recursively
 * for any submenus cascading from this row column widget.
 */

static void ProcessMenuTree (w,  mode)
XmRowColumnWidget w;
int mode;
{
   int i;
   Widget child;

   if (w == NULL)
      return;

   for (i = 0; i < w->composite.num_children; i++)
   {
      if (XtIsManaged((child = w->composite.children[i])))
      {
         ProcessSingleWidget(child, mode);

         if (XmIsCascadeButtonGadget(child))
         {
            ProcessMenuTree(CBG_Submenu(child), mode);
         }
         else if (XmIsCascadeButton(child))
         {
            ProcessMenuTree(CB_Submenu((XmCascadeButtonWidget)child), mode);
         }
      }
   }
}


/*
 * This function adds/deletes the mnemonic and/or accelerator associated
 * with the specified widget.  The work that is done is dependent both
 * on the widget in question, and sometimes the parent of the widget.
 *
 * When adding a keyboard event, we first check the component to see if
 * it has any keyboard events defined; if not, then nothing is done.  However,
 * when removing a keyboard event, we simply attempt to remove the entry for
 * the specified widget; we can't check to see if the widget had one defined,
 * because the instance structure may no longer contain the information.
 */

static void ProcessSingleWidget (w, mode)
Widget w;
int mode;
{
   char buf[20];
   Arg args[2];
   Widget child;

   if (XmIsCascadeButtonGadget(w))
   {
      XmCascadeButtonGadget c = (XmCascadeButtonGadget)w;

      if (XmIsRowColumn(XtParent(w)) &&
          (IsBar(XtParent(w))))
      {
         if (mode == XmADD)
         {
            /* Menubar mnemonics are prefixed with the Mod1 modifier */
            if (LabG_Mnemonic(c) != NULL)
            {
               strcpy (buf, "Mod1<KeyUp>");
	       strcat (buf,  XKeysymToString(LabG_Mnemonic(c)));

	       /* although this is technically a mnemonic, it will be 
                * treated like an acclerator */ 
               AddToKeyboardList(w, buf, True, False);

	       /* save it again as a mnemonic so that it is available w/o
                * the Mod1 when the menu system is posted */
               strcpy (buf, "<KeyUp>");
	       strcat (buf,  XKeysymToString(LabG_Mnemonic(c)));
               AddToKeyboardList(w, buf, False, True);

            }
         }
         else
            RemoveFromKeyboardList(w);
      }
      else
      {
         if (mode == XmADD)
         {
            /* All other mnemonics are done without any modifiers */
            if (LabG_Mnemonic(c) != NULL)
            {
               strcpy (buf, "<KeyUp>");
	       strcat (buf,  XKeysymToString(LabG_Mnemonic(c)));
               AddToKeyboardList(w, buf, False, True);
            }
         }
         else
            RemoveFromKeyboardList(w);
      }
   }
   else if (XmIsCascadeButton(w))
   {
      XmCascadeButtonWidget c = (XmCascadeButtonWidget)w;

      if (XmIsRowColumn(XtParent(w)) &&
          (IsBar(XtParent(w))))
      {
         if (mode == XmADD)
         {
            /* Menubar mnemonics are prefixed with the Mod1 modifier */
            if (Lab_Mnemonic(c) != NULL)
            {
               strcpy (buf, "Mod1<KeyUp>");
	       strcat (buf,  XKeysymToString(Lab_Mnemonic(c)));

	       /* save as an accelerator since it is available anytime */
               AddToKeyboardList(w, buf, True, False);

	       /* save again as a mnemonic so its available w/o Mod1
		* once the menu system is active */ 
               strcpy (buf, "<KeyUp>");
	       strcat (buf,  XKeysymToString(Lab_Mnemonic(c)));
               AddToKeyboardList(w, buf, False, True);
            }
         }
         else
            RemoveFromKeyboardList(w);
      }
      else
      {
         if (mode == XmADD)
         {
            /* All other mnemonics are done without any modifiers */
            if (Lab_Mnemonic(c) != NULL)
            {
               strcpy (buf, "<KeyUp>");
	       strcat (buf,  XKeysymToString(Lab_Mnemonic(c)));
               AddToKeyboardList(w, buf, False, True);
            }
         }
         else
            RemoveFromKeyboardList(w);
      }
   }
   else if (XmIsToggleButtonGadget(w) ||
            XmIsPushButtonGadget(w))
   {
      XmLabelGadget l = (XmLabelGadget) w;

      if (mode == XmADD)
      {
         /* These can have both an accelerator and a mnemonic */
         if (LabG_Mnemonic(l) != NULL)
         {
            strcpy (buf, "<KeyUp>");
	    strcat (buf,  XKeysymToString(LabG_Mnemonic(l)));
            AddToKeyboardList(w, buf, False, True);
         }

         if (LabG_Accelerator(l) && (strlen(LabG_Accelerator(l)) > 0))
         {
            AddToKeyboardList(w, LabG_Accelerator(l), True, False);
         }
      }
      else
         RemoveFromKeyboardList(w);
   }
   else if (XmIsToggleButton(w) ||
            XmIsPushButton(w))
   {
      XmLabelWidget l = (XmLabelWidget) w;

      if (mode == XmADD)
      {
         /* These can have both an accelerator and a mnemonic */
         if (Lab_Mnemonic(l) != NULL)
         {
            strcpy (buf, "<KeyUp>");
	    strcat (buf,  XKeysymToString(Lab_Mnemonic(l)));
            AddToKeyboardList(w, buf, False, True);
         }

         if (Lab_Accelerator(l) && (strlen(Lab_Accelerator(l)) > 0))
         {
            AddToKeyboardList(w, Lab_Accelerator(l), True, False);
         }
      }
      else
         RemoveFromKeyboardList(w);
   }
   else if (XmIsRowColumn(w))
   {
      XmRowColumnWidget m = (XmRowColumnWidget) w;

      if (IsPopup(m) || IsBar(m))
      {
         /* 
          * Popup Menus and the menubar may have an accelerator associated 
          * with them 
          */
         if (mode == XmADD)
         {
            if (RC_MenuAccelerator(m) && (strlen(RC_MenuAccelerator(m)) > 0))
            {
               AddToKeyboardList(w, RC_MenuAccelerator(m), True, False);
            }
         }
         else
            RemoveFromKeyboardList(w);
      }
      else if (IsOption(m))
      {
         /* Option menus may have a mnemonics associated with them */
         if (mode == XmADD)
         {
            if (RC_Mnemonic(m))
            {
               strcpy (buf, "Mod1<KeyUp>");
	       strcat (buf,  XKeysymToString(RC_Mnemonic(m)));
               AddToKeyboardList(w, buf, True, True);
            
               /* Tell the label gadget */
               XtSetArg(args[0], XmNmnemonic, RC_Mnemonic(m));
               XtSetArg(args[1], XmNmnemonicCharSet, RC_MnemonicCharSet(m));
	       if (child = XmOptionLabelGadget(m))
		   XtSetValues(child, args, 2);
            }
         }
         else
         {
            RemoveFromKeyboardList(w);
            
            /* Tell the label gadget */
            if (child = XmOptionLabelGadget(m))
            {
               XtSetArg(args[0], XmNmnemonic, '\0');
	       XtSetValues(child, args, 1);
            }
         }
      }
   }
}


/*
 * This function actually does the work of converting the accelerator
 * or mnemonic string into a workable format, and registering the keyboard 
 * grab, if possible.
 */

static void AddToKeyboardList (w, kbdEvent, needGrab, isMnemonic)
Widget w;
char * kbdEvent;
Boolean needGrab;
Boolean isMnemonic;
{
   Widget rowcol;
   unsigned int eventType;
   KeySym keysym;
   unsigned int modifiers;
   KeyCode detail;
   XmKeyboardData * list;
   int i;

   if ((kbdEvent == NULL) ||
       (_XmMapKeyEvent(kbdEvent, &eventType, &keysym, &modifiers) == False))
   {
      return;
   }

   /* Convert keysym to keycode; needed by X grab call */
   if ((detail = XKeysymToKeycode(XtDisplay(w), keysym)) == NoSymbol)
   {
      return;
   }

   if (XmIsRowColumn(w))
       rowcol = w;
   else
       rowcol = XtParent(w);
   
   /* Add to the list of keyboard entries */
   if (MGR_NumKeyboardEntries(rowcol) >= MGR_SizeKeyboardList(rowcol))
   {
      /* Grow list */
      MGR_SizeKeyboardList(rowcol) += 10;
      MGR_KeyboardList(rowcol) = 
             (XmKeyboardData *)XtRealloc(MGR_KeyboardList(rowcol), 
                  (MGR_SizeKeyboardList(rowcol) * sizeof(XmKeyboardData)));
   }

   list = MGR_KeyboardList(rowcol);
   i = MGR_NumKeyboardEntries(rowcol);
   list[i].eventType = eventType;
   list[i].keysym = keysym;
   list[i].key = detail;
   list[i].modifiers = isMnemonic ? 
      (modifiers &  ~(ShiftMask | LockMask)) : modifiers;
   list[i].component = w;
   list[i].needGrab = needGrab;
   list[i].isMnemonic = isMnemonic;
   MGR_NumKeyboardEntries(rowcol)++;

   if (needGrab)
   {
      GrabKeyOnAssocWidgets (rowcol, detail, modifiers);
   }
}


/*
 * This function removes all keyboard entries associated with a particular
 * component within a row column widget.
 */

static void RemoveFromKeyboardList (w)
Widget w;
{
   Widget rowcol;
   XmKeyboardData * list;
   int count;
   int i, j;

   if (XmIsRowColumn(w))
       rowcol = w;
   else
       rowcol = XtParent(w);

   list = MGR_KeyboardList(rowcol);
   count = MGR_NumKeyboardEntries(rowcol);
   
   for (i = 0; i < count; )
   {
      if (list[i].component == w)
      {
	 /* NOTE that the ungrabs on the associate widgets are not done
	  * for completeness, they probably should be.  The problem is that
	  * it is difficult to tell whether an item should really be
	  * ungrabbed since the sharing of menupanes could mean that this
	  * item exists somewhere else on this hierarchy. 
	  */

         /* Move the rest of the entries up 1 slot */
         for (j = i; j < count -1; j++)
	     list[j] = list[j+1];

         MGR_NumKeyboardEntries(rowcol) = MGR_NumKeyboardEntries(rowcol)-1;
         count--;
      }
      else
         i++;
   }
}


/*
 * This function searches the list of keyboard events associated with the
 * specified  row column widget to see if any of them match the
 * passed in X event.  This function can be called multiple times, to get
 * all entries which match.
 */

static int _XmMatchInKeyboardList (rowcol, event, startIndex)
XmRowColumnWidget rowcol;
XKeyEvent * event;
int startIndex;
{
   XmKeyboardData * list = MGR_KeyboardList(rowcol);
   int count = MGR_NumKeyboardEntries(rowcol);
   int i;

   if (list == NULL)
      return(-1);

   for (i = startIndex; i < count; i++)
   {
      /*
       * We want to ignore shift and shift-lock for mnemonics.  So, OR the 
       * event's two bits with the (previously two bits initialized to zero) 
       * list.modifier
       */
      if (_XmMatchKeyEvent(event, list[i].eventType, list[i].key, 
           list[i].isMnemonic ? 
	      list[i].modifiers | (event->state & (ShiftMask | LockMask)) :
	      list[i].modifiers)) 
      {
         return(i);
      }
   }

   /* No match */
   return (-1);
}

/*
 * search the postFromList and return the index of the found widget.  If it
 * is not found, return -1
 */
static int OnPostFromList (menu, widget)
XmRowColumnWidget menu;
Widget widget;
{
   int i;

   for (i = 0; i < menu->row_column.postFromCount; i++)
   {
      if (menu->row_column.postFromList[i] == widget)
	  return (i);
   }

   return (-1);
}
   
/*
 * Useful for MenuBars and Option Menus to determine where to set up the
 * event handlers and grabs.
 */
static void GetTopManager (w, topManager)
Widget w;
Widget * topManager;
{
   while (XmIsManager(XtParent(w)))
       w = XtParent(w);

   * topManager = w;
}

/*
 * Returns the toplevel menu widget in an acive menu hierarchy.
 *
 * This function is only useful when the menu system is active.  That is
 * the only time that the CascadeBtn field in the RowColumn in guarrenteed
 * to be valid.  
 */
void _XmGetActiveTopLevelMenu (XmRowColumnWidget w, XmRowColumnWidget *topLevel)
{
   /*
    * find toplevel by following up the chain. Popups use CascadeBtn to
    * keep the active widget in the postFromList.
    */
   while (RC_CascadeBtn(w) && (!IsPopup(w)))
       w = (XmRowColumnWidget) XtParent(RC_CascadeBtn(w));

   * topLevel = w;
} 

/*
 * set up the grabs on the appropriate assoc widgets.  For a popup, this
 * is all of the widgets on the postFromList.  For a menubar and option
 * menu, this is the top manager widget in their hierarchy.  For a
 * pulldown, the assoc widgets can only be determined by following the
 * chain up the postFromList.
 */
 
static void GrabKeyOnAssocWidgets (rowcol, detail, modifiers)
XmRowColumnWidget rowcol;
KeyCode detail;
unsigned int modifiers;
{
   Widget topManager;
   int i;
   
   if (IsPopup(rowcol))
   {
      for (i=0; i < rowcol->row_column.postFromCount; i++)
	  XtGrabKey(rowcol->row_column.postFromList[i], detail, modifiers,
		    False, GrabModeAsync, GrabModeAsync);
   }
   else if (IsBar(rowcol) || IsOption(rowcol))
   {
      GetTopManager (rowcol, &topManager);
      XtGrabKey(topManager, detail, modifiers,
		False, GrabModeAsync, GrabModeAsync);
   }
   else if (IsPulldown(rowcol))
   {
      for (i=0; i<rowcol->row_column.postFromCount; i++)
	  GrabKeyOnAssocWidgets (XtParent(rowcol->row_column.postFromList[i]),
				 detail, modifiers);
   }
}  
       

/*
 * Given a menupane, this function traverses down through any posted submenus,
 * setting the state of the traversal_on field to the indicated value.
 */

static void SetTraversal (m, traversalOn)
XmRowColumnWidget m;
Boolean traversalOn;
{
   if (m == NULL)
      return;

   if (!IsOption(m))
      m->manager.traversal_on = traversalOn;

   if (XmIsMenuShell(XtParent(m)))
      MS_FocusPolicy(XtParent(m)) = traversalOn ? XmEXPLICIT : XmPOINTER;

   if (RC_PopupPosted(m))
       SetTraversal(((CompositeWidget)RC_PopupPosted(m))->
		    composite.children[0], traversalOn);
}

/*
 * This is the event handler which catches, verifies and dispatches all
 * accelerators and mnemonics defined for a given menu hierarchy.  It
 * is attached to the menu's associated widget, along with an assortment
 * of other widgets.
 */

static void KeyboardInputHandler (reportingWidget, topLevel, event)
Widget reportingWidget;
XmRowColumnWidget topLevel;
XEvent * event;
{
   /* Process the event only if not already processed */
   if (!_XmIsEventUnique(event))
      return;

   if (IsBar(topLevel) || IsOption(topLevel))
       if (! _XmAllWidgetsAccessible(topLevel))
	   return;

   /* 
    * XmGetPostFromWidget() requires help to identify the topLevel widget
    * when a menupane is posted via accelerators.
    */
   if (IsBar(topLevel) || IsOption(topLevel))
      lastSelectToplevel = (Widget) topLevel;
   else if (IsPopup(topLevel))
   {
      lastSelectToplevel = reportingWidget;	/* popup */
   }
   else
      lastSelectToplevel = NULL;
   
   ProcessKey (topLevel, event);

   lastSelectToplevel = NULL;	/* reset toplevel "accelerator" state to NULL */
}

/*
 * try to find a match in the menu for the key event.   Cascade down the
 * submenus if necessary
 */
static Boolean ProcessKey (rowcol, event)
XmRowColumnWidget rowcol;
XEvent * event;
{
   Boolean found = FALSE;
   int i;
   Widget child;

   /* Try to use it on the current rowcol */
   if (! CheckKey (rowcol, event))
   {
      /* not used, try moving down the cascade */
      for (i=0; (i < rowcol->composite.num_children) && (! found); i++)
      {
	 child = rowcol->composite.children[i];

	 /* only check sensitive and managed cascade buttons */
	 if (XtIsSensitive(child) && XtIsManaged(child))
	 {
	    if (XmIsCascadeButtonGadget(child))
	    {
	       if (CBG_Submenu(child))
	       {
		   /* Build the menu cascade for menuHistory */
		   RC_CascadeBtn(CBG_Submenu(child)) = child;
		   found = ProcessKey
		       (((XmCascadeButtonGadget)child)->cascade_button.submenu,
			event);
	       }
	    }
	    else if (XmIsCascadeButton(child))
	    {
	       if (CB_Submenu(child))
	       {
		   RC_CascadeBtn(CB_Submenu(child)) = child;
		   found = ProcessKey
		       (((XmCascadeButtonWidget)child)->cascade_button.submenu,
			event);
	       }
	    }
	 }
      }
      return (found);
   }
   else
       return (True);
}

/*
 * Check if the key event is used in the rowcol
 */
static Boolean CheckKey (rowcol, event)
XmRowColumnWidget rowcol;
XEvent * event;
{
   int menu_index = 0;
   XmKeyboardData * entry;
   ShellWidget shell;
   
   /* Process all matching key events */
   while ((menu_index = _XmMatchInKeyboardList(rowcol, event, menu_index)) != -1)
   {
      entry = MGR_KeyboardList(rowcol) + menu_index;

      /* Ignore this entry if it is not accessible to the user */
      if (XmIsRowColumn(entry->component))
      {
	 /*
	  * Rowcols are not accessible if they are insensitive or
	  * if menubars or optionmenus are unmanaged.
	  */
	 if (! XtIsSensitive(entry->component) ||
	     ((RC_Type(entry->component) != XmMENU_POPUP) &&
	      (RC_Type(entry->component) != XmMENU_PULLDOWN) &&
	      (! XtIsManaged(entry->component))))
	 {
	    menu_index++;
	    continue;
	 }
      }

      /* buttons are not accessible if they are insensitive or unmanaged */
      else if (! XtIsSensitive(entry->component) ||
	       ! XtIsManaged(entry->component))
      {
	 menu_index++;
	 continue;
      }

      /* 
       * For a mnemonic, the associated component must be visible, and
       * it must be in the last menupane posted.
       * This only needs to be checked for a popup or pulldown menu pane.
       */
      if (entry->isMnemonic)
      {
         if ((XmIsLabel(entry->component) || 
              XmIsLabelGadget(entry->component)))
	 {
	    if (IsBar(XtParent(entry->component)) &&
		! RC_PopupPosted(XtParent(entry->component)) &&
		((XmManagerWidget) XtParent(entry->component))->
		     manager.active_child == NULL)
	    {
	      menu_index++;
	      continue;
	    }

	    else if (IsPopup(XtParent(entry->component)) ||
		     IsPulldown(XtParent(entry->component)))
	      {
		/* See if the associated shell is visible */
		shell = (ShellWidget)XtParent(XtParent(entry->component));
		
		/*
		 * Verify the pane is popped up, and the active pane is our 
		 * parent (this is necessary because of shared menupanes.
		 */
		if ((!shell->shell.popped_up) ||
		    (shell->composite.children[0] != 
		     XtParent(entry->component)))
		  {
		    menu_index++;
		    continue;
		  }

		/* Verify we are the last pane */
		if (RC_PopupPosted(shell->composite.children[0]))
		  {
		    menu_index++;
		    continue;
		  }
	      }
	  }
         else if (XmIsRowColumn(entry->component))
         {
	    /*
	     * Ignore the posting mnemonic for an option menu, if its
	     * menupane is already posted.
	     */
	    if (RC_PopupPosted(entry->component))
	    {
	       menu_index++;
	       continue;
	    }
	 }
      }

      /* Perform the action associated with the keyboard event */
      if (XmIsPrimitive(entry->component))
      {
         XmPrimitiveClassRec * prim;

         prim = (XmPrimitiveClassRec *)XtClass(entry->component);
         (*(prim->primitive_class.arm_and_activate)) (entry->component, event);
      }
      else if (XmIsGadget(entry->component))
      {
         XmGadgetClassRec * gadget;

         gadget = (XmGadgetClassRec *)XtClass(entry->component);
         (*(gadget->gadget_class.arm_and_activate)) (entry->component, event);
      }
      else if (XmIsRowColumn(entry->component))
      {
         XmRowColumnClassRec * rc;

         rc = (XmRowColumnClassRec *)XtClass(entry->component);
         (*(rc->row_column_class.armAndActivate)) (entry->component, event);
      }

      /* used the key */
      _XmRecordEvent(event);
      return (True);
   }

   /* did not use the key */
   return (False);
}


static void AddToPostFromList (m, widget)
XmRowColumnWidget m;
Widget widget;
{
   
   if (m->row_column.postFromListSize == m->row_column.postFromCount)
   {
      /* increase the size to fit the new one and one more */
      m->row_column.postFromListSize += 2;
      m->row_column.postFromList = (Widget *)
	  XtRealloc (m->row_column.postFromList,
		     m->row_column.postFromListSize * sizeof(Widget));
   }

   m->row_column.postFromList[m->row_column.postFromCount++] = widget;
}

static void RemoveFromPostFromList (m, widget)
XmRowColumnWidget m;
Widget widget;
{
   int i;
   Boolean found = False;

   for (i=0; i < m->row_column.postFromCount; i++)
   {
      if (!found)
      {
	 if (widget == m->row_column.postFromList[i])
	 {
	    /* remove this entry */
	    found = True;
	 }
      }
      else
	  m->row_column.postFromList[i-1] = m->row_column.postFromList[i];
   }
   if (found)
      m->row_column.postFromCount--;
}


/*
 * This is a class function exported by the RowColumn widget.  It is used
 * by the CascadeButton widget to signal that a menupane has either been
 * attached to a cascade button widget, or detached from a cascade button
 * widget.
 */

static void SetCascadeField (m, cascadeBtn, attach)
XmRowColumnWidget m;
Widget cascadeBtn;
Boolean attach;
{
   int mode;

   if (attach)
   {
      mode = XmADD;

      if (OnPostFromList (m, cascadeBtn) != -1)
	  /* already in the list, this means no work to do */
	  return;

      AddToPostFromList (m, cascadeBtn);
      
      /* if being attached to an option menu, set the option menus submenu */
      if (RC_Type(XtParent(cascadeBtn)) == XmMENU_OPTION)
	  RC_OptionSubMenu(XtParent(cascadeBtn)) = (Widget) m;
   }

   else
   {
      mode = XmDELETE;
      RemoveFromPostFromList (m, cascadeBtn);

      /* if being removed from an option menu, set the option menus submenu */
      if (RC_Type(XtParent(cascadeBtn)) == XmMENU_OPTION)
	  RC_OptionSubMenu(XtParent(cascadeBtn)) = (Widget) NULL;
   }

   /* process the accelerators and mnemonics */
   DoProcessMenuTree(m, mode);

}


/*
 * This function determines if the widget to which a menu is 
 * attached is accessible to the user.  The widget is considered
 * accessible if it, and its ancestors, are both sensitive and
 * managed.  This is useful for MenuBars and Option Menus only.
 */

static Boolean _XmAllWidgetsAccessible (w)
Widget w;
{
   while (w && XtParent(w) && !XtIsShell(w))
   {
      if (!XtIsSensitive(w) || !XtIsManaged(w) || !w->core.mapped_when_managed)
         return (False);

      w = XtParent(w);
   }

   return (True);
}


/*
 * Button Action Procs
 */

static void _XmMenuBtnDown (w, event)
XmRowColumnWidget  w;
XButtonPressedEvent *event;
{
   XmGadget gadget;

   if (! _XmMatchBtnEvent( event, RC_PostEventType(w), RC_PostButton(w),
			  RC_PostModifiers(w))  ||
       ! _XmIsEventUnique(event))
       return;

   /* Overload _XmButtonEventStatus's time for MenuShell's managed_set_changed 
    * routine to determine if an option menu is trying to post using BSelect 
    * Click.  _XmButtonEventStatus's verified is irrelevant.
    */
   if (IsOption(w)) 
   {
      _XmButtonEventStatus.time = event->time;
   }

   /*
    * It's possible that this event has been passed to this widget/window
    * due to a grabbed pointer.  If so, it's probable that it doesn't belong 
    * to any gadget children of the event's window (widget) where the pointer
    * is currently positioned.
    */
   if (w->core.window == event->window)
      gadget = _XmInputInGadget(w, event->x, event->y);
   else
      gadget = NULL;

   if (gadget != NULL)
   {
      _XmDispatchGadgetInput(gadget, event, XmARM_EVENT);
   }
/*
 * We'll popdown the menubar on the button up transition.  Don't set
 * menu traversal off for the armed menubar.  This it to make
 * sure that the up event is used by the menubar and not allowed to
 * accidentally be passed on to a possibly unarmed widget for activation.
 */
   else if (!(IsBar(w) && RC_IsArmed(w)))
       SetMenuTraversal(w, False);
}

static void _XmMenuBtnUp (w, event)
XmRowColumnWidget  w;
XButtonPressedEvent *event;
{
   XmGadget gadget;
   
   if (! _XmIsEventUnique(event) || 
       ! _XmMatchBtnEvent( event, XmIGNORE_EVENTTYPE, RC_PostButton(w),
       RC_PostModifiers(w)) ||
       (IsBar(w) && ! RC_IsArmed(w)))
       return;

   if (w->core.window == event->window)
      gadget = _XmInputInGadget(w, event->x, event->y);
   else
      gadget = NULL;

   if (gadget != NULL)
   {
      _XmDispatchGadgetInput(gadget, event, XmACTIVATE_EVENT);
   }

   else if (IsBar(w))
   {
      /* Only drop in here when no other widget took the event */
      MenuPopDown(w, event);
      MenuBarCleanup(w);
      MenuDisarm(w);
      _XmMenuFocus(w, XmMENU_END, CurrentTime);
      XtUngrabPointer(w, CurrentTime);
   }
}


static void UpdateOptionMenuCBG (cbg, memWidget)
Widget cbg;
Widget memWidget;
{
   char *thing = NULL, *thing_name = NULL;
   int thing_type;
   Boolean freeThing = FALSE;
   Arg al[4];
   int ac = 0;

   if (XmIsLabelGadget(memWidget))
   {
      XmLabelGadget lg = (XmLabelGadget) memWidget;

      if (LabG_IsText (lg))
      {
	 thing = (char *) _XmStringCreateExternal(LabG_Font(lg),
						  LabG__label(lg));
	 thing_type = XmSTRING;
	 thing_name = XmNlabelString;
	 freeThing = True;
      }
      else
      {
	 thing = (char *) LabG_Pixmap(lg);
	 thing_type = XmPIXMAP;
	 thing_name = XmNlabelPixmap;
      }
   }
   else if (XmIsLabel(memWidget))
   {
      XmLabelWidget lw = (XmLabelWidget) memWidget;
      
      if (Lab_IsText (lw))
      {
	 thing = (char *) _XmStringCreateExternal(lw->label.font,
						  lw->label._label);
	 thing_type = XmSTRING;
	 thing_name = XmNlabelString;
	 freeThing = True;
      }
      else
      {
	 thing = (char *)lw->label.pixmap;
	 thing_type = XmPIXMAP;
	 thing_name = XmNlabelPixmap;
      }
   }

   if ( ! IsNull (thing))
   {
      XtSetArg (al[ac], XmNlabelType, thing_type);    ac++;
      XtSetArg (al[ac], thing_name,  thing);      ac++;
      XtSetValues (cbg, al, ac);
   }

   if (freeThing)
       XmStringFree(thing);
}	


static int is_in_widget_list (m, w)
register XmRowColumnWidget m;
RectObj w;
{
    register Widget *q;
    register int i;

    if ((m == NULL) || (w == NULL)) return (FALSE);

    for (i = 0, q = m->composite.children;
         i < m->composite.num_children;
         i++, q++) 

        if ((*q == (Widget) w) && IsManaged (*q)) return (TRUE);

    return (FALSE);
}


static int in_menu (search_m, parent_m, child, w)
XmRowColumnWidget search_m;
XmRowColumnWidget *parent_m;
RectObj child;
Widget *w;
{
    if (is_in_widget_list (search_m, child))
    {
        *parent_m = search_m;
        *w = (Widget) child;
        return (TRUE);
    }

    return (FALSE);
}



static Boolean search_menu (search_m, parent_m, child, w)
XmRowColumnWidget search_m;
XmRowColumnWidget *parent_m;
RectObj child;
Widget *w;
{
    register Widget *q;
    register int i;

    if ( ! in_menu (search_m, parent_m, child, w))
    {
        for (i = 0, q = search_m->composite.children;
             i < search_m->composite.num_children;
             i++, q++) 
        {
            if (XtIsManaged(*q))
            {
                if (XmIsCascadeButtonGadget(*q))
                {
                    XmCascadeButtonGadget p = 
                        (XmCascadeButtonGadget) *q;

                    if (CBG_Submenu(p) &&
                        search_menu (CBG_Submenu(p),parent_m,child,w)) 
                    {
                        return (TRUE);
                    }
                }
                else if (XmIsCascadeButton(*q))
                {
                    XmCascadeButtonWidget p =
                        (XmCascadeButtonWidget) *q;

                    if (CB_Submenu(p) &&
                        search_menu (CB_Submenu(p),parent_m,child,w)) 
                    {
                        return (TRUE);
                    }
                }
            }
        }
        return (FALSE);
    }
    return (TRUE);
}


static void lota_magic (m, child, parent_m, w)
XmRowColumnWidget m;
RectObj child;
XmRowColumnWidget *parent_m;
Widget *w;
{
    *parent_m = NULL;
    *w = NULL;

    search_menu (m, parent_m, child, w);
}

/*
 * called by the buttons to verify that the passed in event is one that
 * should be acted upon.  This is called through the menuProcs handle
 */
static void VerifyMenuButton (w, event, valid)
Widget w;
XEvent * event;
Boolean * valid;
{
   *valid = event &&  _XmMatchBtnEvent( event, XmIGNORE_EVENTTYPE,
			 RC_PostButton(w), RC_PostModifiers(w)); 
}

/*
 * This routine is called at Initialize or SetValues time.  It updates
 * the memory widget in the current rowcolumn and up to the top level(s)
 * menus.  If there is a postFromList on the pulldown, it goes up each
 * branch.  If an option menu is found at the top, then its cascadebutton
 * is updated with the latest stuff.
 */
static void UpdateMenuHistory (menu, child)
XmRowColumnWidget menu;
Widget child;
{
   int i;
   
   RC_MemWidget(menu) = child;

   if (IsOption(menu))
   {
      for (i = 0; i < menu->composite.num_children; i++)
      {
	 if (XmIsCascadeButtonGadget(menu->composite.children[i]))
	 {
	    UpdateOptionMenuCBG (menu->composite.children[i], child);
	    return;
	 }
      }
   }
   else if (IsPulldown(menu))
   {
      for (i=0; i < menu->row_column.postFromCount; i++)
      {
	 UpdateMenuHistory (XtParent(menu->row_column.postFromList[i]),
			    child);
      }
   }
}

/*
 * this is a mess... the menu spec'd is the menu to set the history for;
 * the child spec'd is the child who we are pretending fired-off.   The
 * problem is the child may be in any sub-menu of this cascade.  This is
 * called by Initialize or SetValues.
 */

static void SetMenuHistory (m, child)
XmRowColumnWidget m;
RectObj child;
{
   XmRowColumnWidget parent_m;
   Widget w;

   if (IsNull (child))
       return;

   /* make sure that the child is in the menu hierarchy */
   lota_magic (m, child, &parent_m, &w);

   if (w)
       UpdateMenuHistory (parent_m, w);       
}


static void all_off_except (m, w)
XmRowColumnWidget m;
Widget w;
{
    register Widget *q;
    register int i;

    if (w)  /* then all widgets except this one go off */
    {
        ForManagedChildren (m, i, q)
        {
            if (*q != w)
            {
                if (XmIsToggleButtonGadget(*q))
                {
                   if (XmToggleButtonGadgetGetState (*q))
                        XmToggleButtonGadgetSetState (*q, FALSE, TRUE);
                }
                else if (XmIsToggleButton(*q))
                {
                   if (XmToggleButtonGetState (*q))
                        XmToggleButtonSetState (*q, FALSE, TRUE);
                }
            }
        }
    }
}


static int no_toggles_on (m)
XmRowColumnWidget m;
{
    register Widget *q;
    register int i;

    ForManagedChildren (m, i, q)
    {
        if (XmIsToggleButtonGadget(*q))
        {
            if (XmToggleButtonGadgetGetState (*q)) 
               return (FALSE);
        }
        else if (XmIsToggleButton(*q))
        {
            if (XmToggleButtonGetState (*q)) 
               return (FALSE);
        }
    }

    return (TRUE);
}


/* 
 * note that this is potentially recursive, setting the state of a 
 * toggle in this row column widget may re-enter this routine...
 */

static void RadioBehaviorAndMenuHistory (m, w)
XmRowColumnWidget m;
Widget w;
{
   XmRowColumnWidget menu;
   Widget cb;
   Boolean done = FALSE;
   
   if (! IsManaged(w))
       return;
   
   if (RC_RadioBehavior(m))
   {
      if (XmIsToggleButtonGadget(w))
      {
	 /* turned on */
	 if (XmToggleButtonGadgetGetState (w)) 
	     all_off_except (m, w);

	 /* he turned off */
	 else  
	 {
            if (RC_RadioAlwaysOne(m))
                if (no_toggles_on (m))
		    /* can't allow that */
                    XmToggleButtonGadgetSetState (w, TRUE, TRUE);
	 }
      }
      else if (XmIsToggleButton (w))
      {
	 /* turned on */
	 if (XmToggleButtonGetState (w)) 
	     all_off_except (m, w);

	 /* turned off */
	 else
	 {
            if (RC_RadioAlwaysOne(m))
                if (no_toggles_on (m))  
		    /* can't allow that */
                    XmToggleButtonSetState (w, TRUE, TRUE);
	 }
      }
      
      /* record for posterity */
      RC_MemWidget (m) = w; 
   }

   /* record the mouse memory and history widget all the way up the cascade */
   menu = m;
   while ( ! done)
   {
      RC_MemWidget (menu) = w;
      
      if (! IsPopup(menu) && RC_CascadeBtn(menu))
      {
	cb = RC_CascadeBtn(menu);
	menu = (XmRowColumnWidget) XtParent (cb);
      }

      else
	  done = TRUE;
   }

   /* option menu cascade button gadget must be updated */
   if (IsOption(menu))
       UpdateOptionMenuCBG (cb, w);
}


static char * which_callback (w)
Widget w;
{
    if (XmIsPushButtonGadget(w) || XmIsPushButton(w) || 
        XmIsCascadeButton(w) || XmIsCascadeButtonGadget(w) ||
	XmIsDrawnButton(w))
       return (XmNactivateCallback);

    if (XmIsToggleButtonGadget(w) || XmIsToggleButton(w))
       return (XmNvalueChangedCallback);

    return (NULL);
}

/*
 * This routine is used to emulate the override callback functionality that
 * was available in the R3 library used by Xm and to do the radio behavior
 * and menu history functionality for RowColumns.  The buttons call this
 * function through the MenuProcs interface.
 */

static void ChildsActivateCallback (rowcol, child, call_value)
XmRowColumnWidget rowcol;
Widget child;
Opaque call_value;
{
   Arg arg[1];
   int i;
   XtCallbackList callbacks;
   XmRowColumnWidget topLevel;
   char *c = which_callback (child);       /* which callback to use */

  /* 
   * Save the last selected toplevel in each menupane.
   * "lastSelectToplevel" only set for accelerators via KeyboardInputHandler().
   * Do this before entrycallback because the application may call
   * XmGetPostedFromWidget() from there.
   */
   if (lastSelectToplevel)
      rowcol->row_column.lastSelectToplevel = lastSelectToplevel;
   else
   {
      _XmGetActiveTopLevelMenu (rowcol, &topLevel);
      rowcol->row_column.lastSelectToplevel = (Widget) topLevel;
   }

   if (rowcol->row_column.entry_callback != NULL)
   {
      XtSetArg (arg[0], c, &callbacks);
      XtGetValues (child, arg, 1);

      /* make sure the all of the drawing has been done before the callback */
      XFlush (XtDisplay (rowcol));

      /*
       * cycle through the list and call the entry fired routine for each
       * entry in this callback list, sending in the data for each entry.
       * If the list is NULL, or empty, call the entry fired function once.
       */
      if ((callbacks == NULL) || (callbacks[0].callback == NULL))
	  EntryFired (child, NULL, call_value);

      else
      {
	 for (i=0; callbacks[i].callback != NULL; i++)
	    EntryFired (child, callbacks[i].closure, call_value);
      }
   }
   else
       /* no entry callback, but must do radio behavior & menu history */
       EntryFired (child, NULL, call_value);
}
        
/*
 * This is the callback for widgets which are composited into row column
 * widgets.  It notifies the menu that some individual widget fired off; 
 * this allows * the row column widget to tell the application if it wants 
 * to know.  also to do various other automagical things
 */

static void EntryFired (w, client_data, callback)
Widget   w;                 /* ptr to widget that fired */
caddr_t  client_data;
XmAnyCallbackStruct *callback;
{
    XmRowColumnWidget m = (XmRowColumnWidget) XtParent (w);
    XmRowColumnCallbackStruct mr;

    mr.reason       = XmCR_ACTIVATE;    /* menu activated */
    mr.widget       = w;
    mr.data         = client_data;
    mr.callbackstruct   = (char *) callback;  /* subwidget structure */
    mr.event            = callback->event;

    XtCallCallbackList ((Widget) NULL, m->row_column.entry_callback, &mr);

    RadioBehaviorAndMenuHistory (m, w);
}


/*************************************************************************
 * 
 * this section is all the layout stuff, the whole thing is messy because
 * it has to operate in two different modes, one: a read-only mode which
 * is nice for making decisions about the size of the row column vs. the size
 * of the children.  two: a change everything mode which implements the
 * change.
 *
 * further complicated by the xtoolkit restriction that a subwidget making
 * a geo request (referred to as the 'instigator') of the row column may not 
 * have his resize proc called but all other widget children must.
 *
 * this is done by building a set of XtWidgetGeometry request blocks, one
 * for each child (widget and gadget), which holds the changes we would like to 
 * make for this child.  If needed then another pass is made over the requests 
 * to actually implement the changes.
 */


/*
 * count the widest & tallest entry dimensions
 * and compute entries per row/column
 */

static int get_info (m, w, h, items_per)
XmRowColumnWidget m;
Dimension *w, *h;
int *items_per;
{
    XtWidgetGeometry *b;
    int i, n = 0;;

    *w = *h = 0;

    for (i=0; RC_Boxes (m) [i].kid != NULL; i++)
    {
       b = &(RC_Boxes (m) [i].box);
       n++;

       if (*w < BWidth  (b))
	   *w = BWidth (b);
       if (*h < BHeight (b))
	   *h = BHeight (b);
    }

    *items_per = n / RC_NCol (m);       /* calc column size */

    if ((n % RC_NCol (m)) != 0)         /* some left overs */
        (*items_per)++;             /* add another row/col */
}


/*
 * Make sure that entries in the right most column/row extend all the 
 * way to the right/bottom edge of the row column widget.  This keeps 
 * 'dead space' in the row column widget to a minimum.  For single 
 * column widgets, the only column is the right most.  
 *
 */

static int adjust_last (m, start_i, w, h)
XmRowColumnWidget m;
int start_i;
Dimension w, h;
{
   XmKidGeometry kg = RC_Boxes (m);
   XtWidgetGeometry *b;
   Dimension a;
   register Dimension subtrahend;

   for ( ; kg [start_i].kid != NULL; start_i++)
   {
      b = &(kg[start_i].box);

      if (IsVertical (m))
      {
         subtrahend = MGR_ShadowThickness(m) + RC_MarginW (m) + BX (b)
	     + Double (BBorder (b));

	 /* if w (rowcol width) is greater than subtrahend (the smallest
	  * width of the child, we'll guarantee at least a width of 1.
	  */
	 if (w > subtrahend) 
	     BWidth (b) = w-subtrahend;
      }
      else
      {
         subtrahend =  MGR_ShadowThickness(m) + RC_MarginH (m) + BY (b)
	     + Double (BBorder (b));

	 if (h > subtrahend) 
	     BHeight (b) = h-subtrahend;
      }
   }
}


/*
 * decide exactly the dimensions of the row column widget we will return to 
 * an asking caller based on the accumulated layout information.
 */

static void set_asking (m, m_width, m_height, b, max_x, max_y, x, y, w, h)
XmRowColumnWidget m;
Dimension   *m_width, *m_height;            /* if 0 then caller's asking */
Dimension    b;
Position     max_x, max_y, x, y;
Dimension    w, h;
{
    if (IsVertical (m))             /* tell caller what he wants */
    {
        if (Asking (*m_width)) 
            *m_width =   x + w + b      /* right edge of last child */
                   + MGR_ShadowThickness(m)
                   + RC_MarginW (m);    /* plus margin on right */
    
        if (Asking (*m_height))
            *m_height =   max_y         /* last unused y */
                - RC_Spacing (m)    /* up by unused spacing */
                + MGR_ShadowThickness(m)
                + RC_MarginH (m);   /* plus margin on bottom */
    }
    else
    {
        if (Asking (*m_width)) 
            *m_width = max_x - RC_Spacing (m) 
                + MGR_ShadowThickness(m)
                + RC_MarginW (m);

        if (Asking (*m_height))
            *m_height = y + h + b
                + MGR_ShadowThickness(m)
                + RC_MarginH (m);
    }
}




/*
 * Decide where to put the help child.  He better be the last one
 * 'cuz we may trash the x, y's
 */

static void calc_help (m, m_width, m_height, b, max_x, max_y, x, y, w, h)
XmRowColumnWidget m;
Dimension *m_width, *m_height,            /* if 0 then caller's asking */
           b;
Position    max_x, max_y, *x, *y;
Dimension   w, h;
{
   register Dimension subtrahend;

   if (IsVertical (m))             /* glue to bottom edge of ... */
   {
      if (Asking (*m_height))
      {
	 if (RC_NCol (m) == 1)       /* just use max_y */
	     *y = max_y;
	 else                /* go up from max_y */
	 {
	     subtrahend = RC_Spacing (m) + h + b;
	     *y = (max_y > subtrahend) ? max_y - subtrahend : 0; 
	 }
      }
      else
      {   
	  subtrahend = MGR_ShadowThickness(m) + RC_MarginH (m) + h + b;
	  *y = (*m_height > subtrahend) ? *m_height - subtrahend : 0;
      }
   }
   else                    /* glue to right edge of ... */
   {
      if (Asking (*m_width))
      {
	 if (RC_NCol (m) == 1)
	     *x = max_x;
	 else
	 {
	     subtrahend = RC_Spacing (m) + w + b;
	     *x = (max_x > subtrahend) ? max_x - subtrahend : 0;
	 }
      }
      else
      {
	 subtrahend = MGR_ShadowThickness(m) + RC_MarginW (m) + w + b;
	 *x = (*m_width > subtrahend) ? *m_width - subtrahend : 0;
      }
   }
}


/*
 * figure out where all the children of a column style widget go.  The
 * border widths are already set.  
 *
 * In columnar mode, all heights and widths are identical.  They are the
 * size of the largest item.
 *
 * For vertical widgets the items are laid out in columns, going down the
 * first column and then down the second.  For horizonatal, think of the
 * columns as rows. 
 *
 * By convention incoming row column size can be zero, indicating a request
 * for preferred size, this means lay it out and record the needed size.
 *
 * NOTE: the layout is predicated on the help child being the last one since
 * it messes up the x, y for a following child.
 */

static int layout_column (m, m_width, m_height)
XmRowColumnWidget m;
Dimension *m_width, *m_height;            /* if 0 then caller's asking */
{
    XmKidGeometry kg = RC_Boxes (m);
    XtWidgetGeometry *bx;
    Position x, y, max_x = 0, max_y = 0;
    int items_per_column,
        child_i   = 0,              /* which child we are doing */
        col_c   = 0,                /* items in col being done */
        start_i = 0;                /* index of first item in col */
    Dimension w, h;
    Dimension b = Double (RC_EntryBorder (m));

    /* loc of first item */
    x   = MGR_ShadowThickness(m) + RC_MarginW (m);
    y   = MGR_ShadowThickness(m) + RC_MarginH (m); 

    get_info (m, &w, &h, &items_per_column);

    for (child_i=0; kg [child_i].kid != NULL; child_i++)
    {
        bx = &(kg[child_i].box);
        if (!RC_EntryBorder(m) && XtIsWidget(kg[child_i].kid))
             b = Double(kg[child_i].kid->core.border_width);

        BWidth  (bx) = w;           /* all have same dimensions */
        BHeight (bx) = h;

        if (++col_c > items_per_column)     /* start a new column */
        {
	   if (IsVertical (m))         /* calc loc of new column */
	   {
	      x += w + b + RC_Spacing (m);    /* to the right */

	      /*back at top of menu */
	      y = MGR_ShadowThickness(m) + RC_MarginH (m);
	   }
	   else                /* calc loc of new row */
	   {
	      /* back to left edge */
	      x = MGR_ShadowThickness(m) + RC_MarginW (m);
	      /* down a row */
	      y += h + b + RC_Spacing (m);
	   }

	   col_c = 1;              /* already doing this one */
	   start_i = child_i;          /* record index */
        }

        if (IsHelp (m, ((Widget) kg[child_i].kid))) 
            calc_help (m, m_width, m_height, b, max_x, max_y, &x, &y, w, h);

        SetPosition (bx, x, y);         /* plunk him down */

        if (IsVertical (m))         /* get ready for next item */
            y += h + b + RC_Spacing (m);
        else
            x += w + b + RC_Spacing (m);

        if (max_y < y)
	    max_y = y;       /* record for use later */
        if (max_x < x)
	    max_x = x;
     }

    set_asking (m, m_width, m_height, b, max_x, max_y, x, y, w, h);
    
    if (RC_AdjLast(m))
        adjust_last (m, start_i, *m_width, *m_height);
}


/*
 * do a vertical tight (non-column) layout.
 *
 * In a tight layout one dimension of the items is left alone and the other
 * is kept uniform.  In a vertical row column widgets, the widths of each child 
 * are uniform for each column, the heights are never changed.  In a horiz 
 * row column widget, the widths are never changed and the heights are kept 
 * uniform for each row.
 *
 * It gets messy w.r.t. the help child because we don't know if there will
 * be room in the last column/row for it.  If there isn't room then a whole
 * new column/row has to be added.
 *
 * NOTE: the layout is predicated on the help child being the last one since
 * it messes up the x, y for a following child.
 */

static int layout_vertical_tight (m, m_width, m_height)
XmRowColumnWidget m;
Dimension *m_width, *m_height;            /* if 0 then caller's asking */
{
    XmKidGeometry kg = RC_Boxes (m);
    XtWidgetGeometry *bx;
    Position x, y, max_y = 0;
    Dimension h;
    Dimension w = 0;                /* widest item width in col */
    int child_i = 0,
       start_i = 0;                /* index of first item in col */
    Dimension
        b = Double (RC_EntryBorder (m));

    /* first item location */
    x = MGR_ShadowThickness(m) + RC_MarginW (m);
    y = MGR_ShadowThickness(m) + RC_MarginH (m);
        
    for (child_i=0; kg [child_i].kid != NULL; child_i++)
    {
       bx = &(kg[child_i].box);
       if (!RC_EntryBorder(m) && XtIsWidget(kg[child_i].kid))
	   b = Double(kg[child_i].kid->core.border_width);

       h = BHeight (bx) + b;           /* calc this item's height */

       if (((y + h) > *m_height) && 
	   ( ! Asking (*m_height)) &&
	   (child_i))
       {                   /* start new column */
	  while (start_i < child_i)
	      kg[start_i++].box.width = w;    /* set uniform width */

	  x += w + b 
	      + MGR_ShadowThickness(m)
		  + RC_MarginW (m);       /* go right and */
            
	  y = MGR_ShadowThickness(m)
	      + RC_MarginH (m);           /* back to top of menu */
	  w = BWidth (bx);            /* reset for new column */
       }

       if (IsHelp (m, ((Widget) kg[child_i].kid))) 
	   calc_help (m, m_width, m_height, b, 0, max_y, &x, &y, w, h);

       SetPosition (bx, x, y);

       if (w < BWidth (bx))
	   w = BWidth (bx);

       y += h + RC_Spacing (m);        /* loc of next item */

       if (max_y < y)
	   max_y = y;       /* record for use later */
    }

    set_asking (m, m_width, m_height, b, 0, max_y, x, y, w, h);

    if (RC_AdjLast(m))
        adjust_last (m, start_i, *m_width, *m_height);
    else
	while (start_i < child_i)
	    kg[start_i++].box.width = w;    /* set uniform width */
}


static int layout_horizontal_tight (m, m_width, m_height)
XmRowColumnWidget m;
Dimension *m_width, *m_height;            /* if 0 then caller's asking */
{
   XmKidGeometry kg = RC_Boxes (m);
   XtWidgetGeometry *bx;
   Position x, y, max_x = 0;
   Dimension w;
   Dimension h = 0;                   /* tallest item height in row */
   int child_i = 0,
     start_i = 0;                /* index of first item in row */
   Dimension b = Double (RC_EntryBorder (m));

   /* first item location */
   x = MGR_ShadowThickness(m) + RC_MarginW (m);
   y = MGR_ShadowThickness(m) + RC_MarginH (m);
        
   for (child_i=0; kg [child_i].kid != NULL; child_i++)
   {
      bx = &(kg[child_i].box);
      if (!RC_EntryBorder(m) && XtIsWidget(kg[child_i].kid))
	  b = Double(kg[child_i].kid->core.border_width);

      w = BWidth (bx) + b;            /* item's width */

      if (((x + w) > *m_width) && 
	  ( ! Asking (*m_width)) &&
	  (child_i))
      {                   /* start a new row */
	 while (start_i < child_i)
	     kg[start_i++].box.height = h;   /* set uniform height */

	 x = MGR_ShadowThickness(m) 
	     + RC_MarginW (m);       /* left edge of menu */
	 y += h + b 
	     + MGR_ShadowThickness(m) 
		 + RC_MarginH (m);       /* down to top of next row */
	 h = BHeight (bx);           /* reset for this row */
      }
      
      if (IsHelp (m, ((Widget) kg[child_i].kid))) 
	  calc_help (m, m_width, m_height, b, max_x, 0, &x, &y, w, h);
      
      SetPosition (bx, x, y);

      if (h < BHeight (bx))
	  h = BHeight (bx);

      x += w + RC_Spacing (m);        /* loc of next item */

      if (max_x < x)
	  max_x = x;       /* record for use later */
   }

   set_asking (m, m_width, m_height, b, max_x, 0, x, y, w, h);
   
   if (RC_AdjLast(m))
       adjust_last (m, start_i, *m_width, *m_height);
   else
       while (start_i < child_i)
	   kg[start_i++].box.height = h;   /* set uniform height */
}


static int layout_vertical (m, m_width, m_height)
XmRowColumnWidget m;
Dimension *m_width, *m_height;            /* if 0 then caller's asking */
{
   if (PackColumn (m))
       layout_column (m, m_width, m_height);
   else
       layout_vertical_tight (m, m_width, m_height);
}


static int layout_horizontal (m, m_width, m_height)
XmRowColumnWidget m;
Dimension *m_width, *m_height;            /* if 0 then caller's asking */
{
   if (PackColumn (m))
       layout_column (m, m_width, m_height);
   else
       layout_horizontal_tight (m, m_width, m_height);
}

/*
 * wrap a box around the entries, used with packing mode of none.
 *
 * we ignore negative positioning, ie. only worry about being wide enough
 * for the right edge of the rightmost entry (similarly for height)
 */

static int bound_entries (m, m_width, m_height)
XmRowColumnWidget m;
Dimension *m_width, *m_height;            /* if 0 then caller's asking */
{
    XtWidgetGeometry *b;
    XmKidGeometry kg = RC_Boxes (m);
    int i = 0;
    Dimension w, max_w = 0, max_h = 0;
    Dimension bw = Double(RC_EntryBorder(m));

    for (i=0; kg [i].kid != NULL; i++)
    {
        b = &(kg[i].box);
        if (!RC_EntryBorder(m) && XtIsWidget(kg[i].kid))
             bw = Double(kg[i].kid->core.border_width);

        if (Asking (*m_width))
        {
            w = BWidth (b) + bw + BX (b);
            if (w > max_w)
		max_w = w;
        }

        if (Asking (*m_height))
        {
            w = BHeight (b) + Double (bw) + BY (b);
            if (w > max_h)
		max_h = w;
        }
    }

    if (Asking (*m_width))
	*m_width  = max_w;
    if (Asking (*m_height))
	*m_height = max_h;
}

/*

   This routine (option_layout) does the layout for option menus.
   This layout is a tiled layout with the option label widget to the
   left of the option button widget.

*/
/* ARGSUSED */
static void option_layout (menu, width, height)
XmRowColumnWidget menu;
Dimension *width, *height;
{
   Dimension h;
   XmKidGeometry kg;
   XtWidgetGeometry    *label_box, *button_box;
   register int i; 
   register Dimension c_width=0; 
   register Dimension c_height=0;
   register XmRowColumnWidget p = (XmRowColumnWidget) RC_OptionSubMenu(menu);
   Widget cb = XmOptionButtonGadget(menu);

   /* Find the interesting boxes */

   kg = RC_Boxes(menu);
   label_box = &(kg[0].box);
   button_box = &(kg[1].box);

   if (p && p->composite.num_children)
   {
      for(i=0; i < p->composite.num_children; i++)
	  if (XtIsManaged(p->composite.children[i]))
	  {
	     if ((p->composite.children[i])->core.width > c_width)
		 c_width = (p->composite.children[i])->core.width;
	     if ((p->composite.children[i])->core.height > c_height)
		 c_height = (p->composite.children[i])->core.height;
	  }
      /*
       * make sure the width of the cascade button will hold the longest
       * label plus the cascade pixmap.
       */
      BWidth(button_box) = c_width + Double(G_HighlightThickness(cb))
	  + LabG_MarginRight(cb);
      BHeight(button_box) = c_height + Double(G_HighlightThickness(cb));
   }
   else
   {
      /* Option menu draws a toggle indicator with a childless submenu */
      if (p && !p->composite.num_children && IsOption(menu))
	 BWidth(button_box) = LabG_TextRect(cb).width +
	         LabG_MarginRight(cb) + LabG_MarginLeft(cb) +
	         (2*(LabG_MarginWidth(cb) + 
		    G_ShadowThickness(cb) + G_HighlightThickness(cb)));
      else
	 BWidth(button_box) = XtWidth(cb);
      BHeight(button_box) = XtHeight(cb);
   }

   /* Set the height to the highest of the two */

   h = MAX(BHeight(label_box),BHeight(button_box));
   BHeight(label_box) = h;
   BHeight(button_box) = h;

   /* The label box is placed at... */

   BX(label_box) = RC_MarginW(menu);
   BY(label_box) = RC_MarginH(menu);

   /* The button is placed just next to the label */

   BX(button_box) = BX(label_box) + BWidth(label_box) + RC_Spacing(menu);
   BY(button_box) = RC_MarginH(menu);
}

static void Layout (m, w, h)
XmRowColumnWidget m;
Dimension *w, *h;
{
   if (IsOption(m))
       option_layout(m,w,h);
   else if (IsVertical (m))
       layout_vertical (m, w, h);
   else
       layout_horizontal (m, w, h);
}

   
/**************************************************************************
 *
 * class support procedures
 */

/*
 * position then row column widget where it wants to be; normally, this
 * is used only for popup or pulldown menupanes.
 */

static void PositionMenu (m, event)
register XmRowColumnWidget  m;
XButtonPressedEvent *event;
{
    XmRowColumnWidget root = NULL;
    XmCascadeButtonWidget p;

    if (m == NULL) 
       return;

    switch (m->row_column.type)
    {
        case XmMENU_OPTION:
        case XmMENU_BAR:
        case XmWORK_AREA:
            break;

        /*
         * remaining cases take advantage of the fact that positioning of
         * a popup shells' only child is not normal.  Just change the widget
         * itself, when popped up the shell will put itself at this location
         * and move the child to 0,0.  Saves a bunch of thrashing about.
         */

        case XmMENU_POPUP:          /* position near mouse */
            XtX (m) = event->x_root;
            XtY (m) = event->y_root;
            RC_SetWidgetMoved (m, TRUE);

	    /* popups keep active widget in postFromLIst in cascadeBtn field */
	    RC_CascadeBtn(m) = XtWindowToWidget (XtDisplay(m),
						 event->window);
            break;

        case XmMENU_PULLDOWN:
            p = (XmCascadeButtonWidget) m->row_column.cascadeBtn;

            if (p != NULL) 
               root = (XmRowColumnWidget) XtParent (p);
            else
               return;

            if (! XmIsRowColumn(root)) 
               root = NULL;

            LocatePulldown(
                root,           /* menu above */
                p,              /* pulldown linking the two */
                m,              /* menu to position */
                event);         /* event causing pulldown */

            if ((XtX (m) == XtX (XtParent(m))) &&
                (XtY (m) == XtY (XtParent(m))))
            {
               XtX (m) = 0;
               XtY (m) = 0;
            }
            else
               RC_SetWidgetMoved (m, TRUE);

            break;
    }
}

static Widget find_first_managed_child(m, first_button)
CompositeWidget m;
Boolean first_button;
{
    register Widget *kid = m->composite.children;
    register int i = 0;
    register int n = m->composite.num_children;

    while( (i < n) && 
	   (!XtIsManaged(*kid) || 
	   (first_button && 
	    !(XmIsPushButton(*kid) ||
	      XmIsPushButtonGadget(*kid) ||
	      XmIsToggleButton(*kid) ||
	      XmIsToggleButtonGadget(*kid)) 
	   )) )
        kid++, i++;

    if (i >= n)
        return(NULL);
    else
        return(*kid);
}

/*
 * called by cascadebutton (or CBG) before cascading a menu.  The interface
 * is through the menuProcs handle.
 */
static void PrepareToCascade (cb, submenu, event)
Widget cb;
XmRowColumnWidget submenu;
XEvent * event;
{
   RC_CascadeBtn(submenu) = cb;
   RC_PostButton(submenu) = RC_PostButton(XtParent(cb));
   RC_PostModifiers(submenu) = RC_PostModifiers(XtParent(cb));
   RC_PostEventType(submenu) = RC_PostEventType(XtParent(cb));
   RC_PopupPosted(XtParent(cb)) = XtParent(submenu);

   if (IsOption(XtParent(cb)))
       RC_MemWidget(submenu) = RC_MemWidget(XtParent(cb));
   
   PositionMenu (submenu, event);

   submenu->manager.traversal_on =
       ((XmManagerWidget)XtParent(cb))->manager.traversal_on;

   if (!submenu->manager.traversal_on)
       submenu->manager.active_child = NULL;

   MS_FocusPolicy(XtParent(submenu)) =
       submenu->manager.traversal_on ? XmEXPLICIT : XmPOINTER;
}

/* ARGSUSED */
static void LocatePulldown (root, p, m, event)
XmRowColumnWidget root;       /* menu holding pulldown */
XmCascadeButtonWidget p;      /* cascadebutton doing the pulling */
XmRowColumnWidget m;          /* sub-menu to position */
{   
    Position x, y, x1, y1;

    if (root == NULL) 
      return;

    x = y = 0;                  /* punt, don't know */

    if (IsOption (root))           /* near hot spot */
    {                              /* of option button (p) */
        if (! XtIsRealized (m))  
           XtRealizeWidget (m);

        x = 0;
        y = RC_MemWidget(m) ? (Half (XtHeight (p)) - (XtY(RC_MemWidget(m)) +
            Half(XtHeight(RC_MemWidget(m))))) : XtY(p);
    }   

    else if (IsBar (root))  /* aligned with left edge of */
    {                       /* cascade button, below cascade button */
        x = 0;
        y = XtHeight (p);
    }

    else if (XmIsCascadeButtonGadget(p) && CBG_HasCascade(p))
    {
        Widget first;
        first = find_first_managed_child(m, ANY_CHILD);

	/* gadget; have to use parent as base for coordinates */
        x = XtX(p) + CBG_Cascade_x(p) + CBG_Cascade_width(p);
        y = XtY(p) + CBG_Cascade_y(p) + Half(CBG_Cascade_height(p))
            - (first ? (XtY(first) + Half(XtHeight(first))) : 0);

	/* cast only to eliminate warning */
	p = (XmCascadeButtonWidget)XtParent(p);

    }

    else if (XmIsCascadeButton(p) && CB_HasCascade(p))
    {
        Widget first;
        first = find_first_managed_child(m, ANY_CHILD);

        x = CB_Cascade_x(p) + CB_Cascade_width(p);
        y = CB_Cascade_y(p) + Half(CB_Cascade_height(p))
            - (first ? (XtY(first) + Half(XtHeight(first))) : 0);
    }

    /*
     * Take these co-ords which are in the cascade button 
     * widget's co-ord system and convert them to the root 
     * window co-ords.
     */
    XtTranslateCoords(p, x, y, &x1, &y1);

    XtX (m) = x1;
    XtY (m) = y1;
}


/*
 * lay out the row column widget to find it's best size, this is a 
 * read-only operation
 */

/* ARGSUSED */
static void think_about_option_size(m, w, h, instigator, request)
register XmRowColumnWidget m;
Dimension *w, *h;
Widget instigator;
XtWidgetGeometry *request;  
{
   register int i; 
   Dimension width=0, height=0;
   register XmRowColumnWidget p = (XmRowColumnWidget) RC_OptionSubMenu(m);
   Widget cb = XmOptionButtonGadget(m);

   /*
    * The box list was built in the order that the children were
    * created, so the label is first and the cascade button is second
    * (any other children are erroneous).
    */
    
   RC_Boxes(m)[0].box.x = RC_MarginW(m);
   RC_Boxes(m)[0].box.y = RC_MarginH(m);
   *w = RC_MarginW(m) + RC_Boxes(m)[0].box.width + RC_Spacing(m);
   *h = RC_Boxes(m)[0].box.height;

   /*
    * Cascade Button should really prefer to be the size neccessary to
    * display the submenu data, but that's not implemented yet so we
    * just have to kludge a bit.
    */

   if (p && p->composite.num_children)
   {
      for(i=0; i < p->composite.num_children; i++)
      {
         if (XtIsManaged(p->composite.children[i]))
         {
            if ((p->composite.children[i])->core.width > width)
		width = (p->composite.children[i])->core.width;
	    if ((p->composite.children[i])->core.height >height)
		height =(p->composite.children[i])->core.height;
         }
      }

      /*
       * make sure the width of the cascade button will hold the longest
       * label plus the cascade pixmap.
       */
      width += LabG_MarginRight(cb);
   }
   else
   {
      /* Option menu draws a toggle indicator with a childless submenu */
      if (p && !p->composite.num_children && IsOption(m))
	 width = LabG_TextRect(cb).width +
	         LabG_MarginRight(cb) + LabG_MarginLeft(cb) + 
	         (2*(LabG_MarginWidth(cb) +
		    G_ShadowThickness(cb)));
      else
         width = RC_Boxes(m)[1].box.width;
      height = RC_Boxes(m)[1].box.height;
   }

   height += Double(G_HighlightThickness(cb));

   RC_Boxes(m)[1].box.x = *w;
   RC_Boxes(m)[1].box.y = RC_MarginH(m);
   RC_Boxes(m)[1].box.width = width;
   RC_Boxes(m)[1].box.height = height;

   *w += width + RC_MarginW(m) + Double(G_HighlightThickness(cb));
       

   if (height > *h)
   {
      RC_Boxes(m)[0].box.height = height;
      *h = height;
   }

   *h += Double(RC_MarginH(m));
}


static void think_about_size (m, w, h, instigator, request)
register XmRowColumnWidget m;
Dimension *w, *h;
Widget instigator;
XtWidgetGeometry *request;  
{
    if (IsOption(m))
    {
        think_about_option_size(m, w, h, instigator, request);
        return;
    }

    if (PackNone (m))
        bound_entries (m, w, h);
    else
        Layout (m, w, h);

    if (!RC_ResizeWidth(m))  *w = XtWidth  (m);
    if (!RC_ResizeHeight(m)) *h = XtHeight (m);

    if (!RC_ResizeHeight(m) && !RC_ResizeWidth(m))
        return;

    if (*w < resource_min_width)
	*w = resource_min_width;
    if (*h <  resource_min_height)
	*h = resource_min_height;
}

static void PreferredSize (m, w, h)
XmRowColumnWidget m;
Dimension *w, *h;
{
   /*
    * get array built for both widgets and gadgets layout is based only on 
    * this array, adjust width margins &  adjust height margins
    */
   RC_Boxes(m)=
       (XmKidGeometry)_XmGetKidGeo (m, NULL, NULL, RC_EntryBorder(m),
				    RC_EntryBorder (m),
				    (IsVertical (m) && RC_DoMarginAdjust (m)),
				    (IsHorizontal (m) &&
				     RC_DoMarginAdjust (m)),
				    RC_HelpPb (m),
				    XmGET_PREFERRED_SIZE);

   think_about_size (m, w, h, NULL, NULL);

   XtFree (RC_Boxes(m));
}



static XtGeometryResult QueryGeometry (m, intended, reply)
XmRowColumnWidget m;            /* me */
XtWidgetGeometry *intended, *reply;
{
    Dimension w, h;

    if (GMode (intended) & ( ~ (CWWidth | CWHeight)))
        return (XtGeometryYes);

    /*
     * pre-load the reply with input values or flag to preferred_size proc
     * that we are asking for a value
     */

    if ((GMode (intended) & CWWidth) &&
        !(GMode (intended) & CWHeight))
    {
        w = intended->width;
        h = 0;

        PreferredSize (m, &w, &h);

        GMode(reply) = (CWHeight | CWWidth);
        reply->width = w;
        reply->height = h;

        return(XtGeometryAlmost);
    }
    else if ((GMode (intended) & CWHeight) &&
        !(GMode (intended) & CWWidth))
    {
        w = 0;
        h = intended->width;

        PreferredSize (m, &w, &h);

        GMode(reply) = (CWHeight | CWWidth);
        reply->width = w;
        reply->height = h;

        return(XtGeometryAlmost);
    }
    else /* both width and height */
    {
        w = 0;
        h = 0;

        PreferredSize (m, &w, &h);

        if ((w == m->core.width) && (h == m->core.height))
        {
            if (GMode(intended) == (CWWidth | CWHeight))
                return(XtGeometryNo);
            else
            {
                GMode(reply) = (CWHeight | CWWidth);
                reply->width = w;
                reply->height = h;
                return(XtGeometryAlmost);
            }
        }
        else if ((w == intended->width) && (h == intended->height))
            return(XtGeometryYes);
        else
        {
            GMode(reply) = (CWHeight | CWWidth);
            reply->width = w;
            reply->height = h;
            return(XtGeometryAlmost);
        }
    }
}


/*
 * Layout the row column widget to fit it's current size; ignore possible 
 * non-fitting of the entries into a too small row column widget.
 *
 * Don't forget the instigator,
 */
  
static void AdaptToSize (m, instigator, request)
XmRowColumnWidget m;
Widget instigator;
XtWidgetGeometry *request;
{
   Dimension w = XtWidth (m);
   Dimension h = XtHeight (m);

   /*
    * get array built for both widgets and gadgets,
    * layout is based only on this array,
    * adjust width margins and  adjust height margins
    */
   RC_Boxes(m) =
       (XmKidGeometry)_XmGetKidGeo (m, instigator, request, RC_EntryBorder(m),
				    RC_EntryBorder (m),
				    (IsVertical (m) && RC_DoMarginAdjust (m)),
				    (IsHorizontal (m) &&
				     RC_DoMarginAdjust (m)),
				    RC_HelpPb (m),
				    (IsOption(m) ? XmGET_ACTUAL_SIZE :
				     XmGET_PREFERRED_SIZE));

   if (!PackNone (m)) 
       Layout (m, &w, &h);
   
   _XmSetKidGeo (RC_Boxes(m), instigator);
   
   XtFree (RC_Boxes(m));
}


/*
 * Resize the row column widget, and any subwidgets there are.
 * Since the gravity is set to NW, handle shrinking when there may not
 * be a redisplay.
 */

static void Resize(m)
XmRowColumnWidget m;
{
   Boolean		draw_shadow = False;

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

   /*
    * if it is now smaller, redraw the shadow since there may not be a
    * redisplay - DON'T draw shadows for OPTION MENUS!
    */
   if (!IsOption(m) &&
       (m->row_column.old_height > m->core.height ||
        m->row_column.old_width > m->core.width))
       draw_shadow = True;

   m->row_column.old_width = m->core.width;
   m->row_column.old_height = m->core.height;
   m->row_column.old_shadow_thickness = m->manager.shadow_thickness;

   AdaptToSize (m, NULL, NULL);

   if (draw_shadow && XtIsRealized (m) && m->manager.shadow_thickness )
       /* pop-out not pop-in */
       _XmDrawShadow (XtDisplay (m), XtWindow (m),
		      m->manager.top_shadow_GC,
		      m->manager.bottom_shadow_GC,
		      m->manager.shadow_thickness,
		      0, 0, m->core.width, m->core.height);
}


/*
 * class Redisplay proc 
 */

static void Redisplay (m, event, region)
XmRowColumnWidget m;
XEvent *event;
Region region;
{
    XEvent tempEvent;

    /* Ignore exposures generated as we unpost */
    if ((IsPopup (m) || IsPulldown (m)) &&
        !((XmMenuShellWidget)XtParent(m))->shell.popped_up)
    {
       RC_SetExpose (m, TRUE);
       return;
    }

    if (RC_DoExpose (m))            /* a one-shot set on popup */
    {                               /* so we ignore next expose */

        if (event == NULL)          /* Fast exposure is happening */
        {
            event = &tempEvent;
            event->xexpose.x = 0;
            event->xexpose.y = 0;
            event->xexpose.width = m->core.width;
            event->xexpose.height = m->core.height;
        }

        _XmRedisplayGadgets(m,event,region);

        if (IsPopup (m) || IsPulldown (m) || IsBar(m))
        {
            if (MGR_ShadowThickness(m))
                _XmDrawShadow (XtDisplay (m), XtWindow (m),
                    /* pop-out not pop-in */
                    m->manager.top_shadow_GC,
                    m->manager.bottom_shadow_GC,
                    m->manager.shadow_thickness,
                    0, 0, m->core.width, m->core.height);
        }
    }

    RC_SetExpose (m, TRUE);
}


/*
 * Geometry manager for subwidgets of a row column widget; be accomdating, 
 * try to say yes, and then deal with our parent's geometry mgr.
 *
 * I'm not even going to try to figure out what to do with an Almost
 * response from the row column widget's parent; just take it as a 'No'.
 */

static XtGeometryResult GeometryManager (instigator, desired, allowed)
Widget  instigator;
XtWidgetGeometry *desired, *allowed;
{
   XmRowColumnWidget m = (XmRowColumnWidget) XtParent(instigator);
   Dimension w = 0;
   Dimension h = 0;
   XtGeometryResult result = XtGeometryYes;

   /*
    * find out what size we need to be with this new widget
    */
   RC_Boxes(m) = (XmKidGeometry)
       _XmGetKidGeo (m, instigator, desired,
		     RC_EntryBorder(m), RC_EntryBorder (m),
		     (IsVertical (m) && RC_DoMarginAdjust (m)),
		     (IsHorizontal (m) && RC_DoMarginAdjust (m)),
		     RC_HelpPb (m), XmGET_PREFERRED_SIZE);

   think_about_size(m,&w,&h,instigator,desired);

   if (IsOption(m))
   {
      if (instigator == XmOptionButtonGadget(m))
      {
	 /* 
	  * Grow only
	  */
         if ((desired->request_mode & CWWidth) &&
	     (desired->width < RC_Boxes(m)[1].box.width))
         {
	    allowed->width = RC_Boxes(m)[1].box.width;
	    allowed->height = RC_Boxes(m)[1].box.height;
	    allowed->request_mode = (CWHeight | CWWidth);
	    result = XtGeometryAlmost;
         }

         if ((desired->request_mode & CWHeight) &&
	     (desired->height < RC_Boxes(m)[1].box.height))
         {
	    allowed->width = RC_Boxes(m)[1].box.width;
	    allowed->height = RC_Boxes(m)[1].box.height;
	    allowed->request_mode = (CWHeight | CWWidth);
	    result = XtGeometryAlmost;
         }

         if (result != XtGeometryYes)
         {
            XtFree(RC_Boxes(m));
            return(result);
         }
      }

      if (instigator == XmOptionLabelGadget(m))
      {
	 /*
	  * Can't get shorter
	  */
         if ((desired->request_mode & CWHeight) &&
	     (desired->height < RC_Boxes(m)[0].box.height))
         {
	    allowed->width = RC_Boxes(m)[0].box.width;
	    allowed->height = RC_Boxes(m)[0].box.height;
	    allowed->request_mode = (CWHeight | CWWidth);
	    result = XtGeometryAlmost;
         }

         if (result != XtGeometryYes)
         {
            XtFree(RC_Boxes(m));
            return(result);
         }
      }
   }

   /*
    * now decide if the menu needs to change size.
    */

   XtFree(RC_Boxes(m));
   
   if ((w != XtWidth (m)) || (h != XtHeight (m)))
   {
      XtWidgetGeometry menu_desired, menu_allowed;
      menu_desired.request_mode = 0;

      if (w != XtWidth (m))
      {
	 menu_desired.width = w;
	 menu_desired.request_mode |= CWWidth;
      }

      if (h != XtHeight (m))
      {
	 menu_desired.height = h;
	 menu_desired.request_mode |= CWHeight;
      }

      result = XtMakeGeometryRequest(m,&menu_desired,&menu_allowed);
      
      switch (result)
      {
       case XtGeometryAlmost:
       case XtGeometryNo:          /* give it up, don't change */
	 return (XtGeometryNo);      /* us or our children */
       default: /* fall out */
	 break;
      }
   }

   AdaptToSize(m,instigator,desired);

   /*	Clear shadow if necessary. */
   
   if (m->row_column.old_shadow_thickness)
       _XmClearShadowType (m, m->row_column.old_width, 
			   m->row_column.old_height, 
			   m->row_column.old_shadow_thickness, 0);

   m->row_column.old_width = m->core.width;
   m->row_column.old_height = m->core.height;
   m->row_column.old_shadow_thickness = m->manager.shadow_thickness;

   return(XtGeometryDone);
}


/*
 * fix the visual attributes of the subwidget to be what we like
 *
 *  1.  make border width uniform
 *  2.  maybe set the label alignment
 */

static void FixVisual (m, w)
XmRowColumnWidget m;
Widget    w;
{
   Arg al[10];
   int ac;
   
   if (RC_EntryBorder(m))
   {
      _XmConfigureObject(w,w->core.x,w->core.y, 
			 w->core.width, w->core.height, RC_EntryBorder(m));
   }

   if (IsOption(m))
       return;

   if (XmIsLabelGadget(w))
   {
      if (IsAligned (m))
      {
	 if (IsWorkArea(m) ||
	     ((w->core.widget_class != xmLabelWidgetClass) &&
	      (w->core.widget_class != xmLabelGadgetClass)))
	     
	 {
	    ac = 0;
	    XtSetArg (al[ac], XmNalignment, RC_EntryAlignment (m));
	    ac++;
	    XtSetValues (w, al, ac);
	 }
      }
   }
   else if (XmIsLabel (w))
   {
      if (IsAligned (m))
      {
	 if ((w->core.widget_class != xmLabelWidgetClass) ||
	     IsWorkArea(m))
	 {
	    ac = 0;
	    XtSetArg (al[ac], XmNalignment, RC_EntryAlignment (m));
	    ac++;
	    XtSetValues (w, al, ac);
	 }
      }
   }
}

/*
 * If an entryCallback exists, set a flag in the buttons to not do
 * their activate callbacks.
 */

static void FixCallback (m, w)
XmRowColumnWidget  m;
Widget    w;
{
   char *c = which_callback (w);       /* which callback to use */

   if (c == NULL)
       return;          /* can't do it to this guy */

   if (m->row_column.entry_callback)
   {
      /* 
       * Disable the buttons activate callbacks
       */
      if (XmIsLabelGadget(w))
	  (*(((XmLabelGadgetClassRec *)XtClass(w))->
	     label_class.setOverrideCallback)) (w);
      else
	  (*(((XmLabelClassRec *)XtClass(w))->
	     label_class.setOverrideCallback)) (w);
   }
}


static void XmGetMenuKidMargins (m, width, height, left, right, top, bottom)
XmRowColumnWidget m;
Dimension *width, *height, *left, *right, *top, *bottom;
{
   register int i;

    *width = *height = *left = *right = *top = *bottom = 0;

    for (i = 0; i < m->composite.num_children; i++)
    {
        Widget p = (Widget) m->composite.children[i]; 

        if (IsManaged (p))
        {
            if (XmIsLabelGadget(p))
            {
                register XmLabelGadget lg = (XmLabelGadget) p;

                if (LabG_MarginWidth (lg) > *width)  
                       *width  = LabG_MarginWidth  (lg);

                if (LabG_MarginHeight(lg) > *height) 
                       *height = LabG_MarginHeight (lg);

                if (LabG_MarginLeft  (lg) > *left)   
                       *left   = LabG_MarginLeft   (lg);

                if (LabG_MarginRight (lg) > *right)  
                       *right  = LabG_MarginRight  (lg);

                if (LabG_MarginTop   (lg) > *top)    
                       *top = LabG_MarginTop (lg);

                if (LabG_MarginBottom(lg) > *bottom) 
                       *bottom = LabG_MarginBottom (lg);
            }
            else if (XmIsLabel(p))
            {
                register XmLabelWidget lw = (XmLabelWidget) p;

                if (Lab_MarginWidth (lw) > *width)  
                       *width  = Lab_MarginWidth  (lw);

                if (Lab_MarginHeight(lw) > *height) 
                       *height = Lab_MarginHeight (lw);

                if (Lab_MarginLeft  (lw) > *left)   
                       *left   = Lab_MarginLeft   (lw);

                if (Lab_MarginRight (lw) > *right)  
                       *right  = Lab_MarginRight  (lw);

                if (Lab_MarginTop   (lw) > *top)    
                       *top = Lab_MarginTop (lw);

                if (Lab_MarginBottom(lw) > *bottom) 
                       *bottom = Lab_MarginBottom (lw);
            }
        }
    }
}

/*
 * Toggle buttons have this thingy hanging off the left of the
 * widget, before the text.  This dimension is known as the MarginLeft.
 * Pulldown's have hot spots in the MarginRight, accelerators go in the
 * marginRight also.
 *
 * For generality's sake we should insure that all
 * of the current label subclass widgets in the menu have the 
 * margins set to the same value.  
 */

static void DoMarginAdjustment (m)
XmRowColumnWidget m;
{
    register Widget *p;
    register int i; 
    Dimension m_w, m_h, m_l, m_r, m_t, m_b;
    Dimension w, h;

    if ((!RC_DoMarginAdjust (m)) || (IsOption(m)))
       return;

    /*
     * this should almost be part
     * of the layout process, except this requires a setvalue not a resize...
     */

    XmGetMenuKidMargins (m, &m_w, &m_h, &m_l, &m_r, &m_t, &m_b);

    ForManagedChildren (m, i, p)
    {
        if (XmIsLabelGadget(*p))
        {
            XmLabelGadget q;
            /*
             * If in a popup or pulldown pane,
             * don't do labels; i.e. only do buttons.
             */
            if (((*p)->core.widget_class == xmLabelGadgetClass) &&
                (IsPulldown(m) || IsPopup(m)))
                continue;

            w = XtWidth  (*p);
            h = XtHeight (*p);

            q = (XmLabelGadget) (*p);

            if (IsVertical (m)) 
            {
	       /* change horiz margins to  be uniform */
	       if (LabG_MarginLeft(q) != m_l)
	       {
		  w += LabG_MarginLeft(q) - m_l;
		  _XmAssignLabG_MarginLeft(q, m_l);
	       }

	       if (LabG_MarginRight(q) != m_r)
	       {
		  w += LabG_MarginRight(q) - m_r;
		  _XmAssignLabG_MarginRight(q, m_r);
	       }

	       if (LabG_MarginWidth(q) != m_w)
	       {
		  w += LabG_MarginWidth(q) - m_w;
		  _XmAssignLabG_MarginWidth(q, m_w);
	       }
	       
	       _XmReCacheLabG(q);

	       if (q->rectangle.width != w) 
	       {
		  _XmConfigureObject(q,q->rectangle.x,q->rectangle.y,
				     w, q->rectangle.height,
				     q->rectangle.border_width);
	       }
            }
            else 
            {
	       /* change vert margins */
	       if (LabG_MarginTop(q) != m_t)
	       {
		  h += LabG_MarginTop(q) - m_t;
		  _XmAssignLabG_MarginTop(q, m_t);
	       }
	       
	       if (LabG_MarginBottom(q) != m_b)
	       {
		  h += LabG_MarginBottom(q) - m_b;
		  _XmAssignLabG_MarginBottom(q, m_b);
	       }
	       
	       if (LabG_MarginHeight(q) != m_h)
	       {
		  h += LabG_MarginHeight(q) - m_h;
		  _XmAssignLabG_MarginHeight(q, m_h);
	       }
	       
	       _XmReCacheLabG(q);

	       if (q->rectangle.width != h) 
	       {
		  _XmConfigureObject(q,q->rectangle.x,q->rectangle.y,
				     q->rectangle.width, h,
				     q->rectangle.border_width);
	       }
            }
	 }
        else if (XmIsLabel(*p))
        {
            XmLabelWidget lw;
            /*
             * If in a popup or pulldown pane,
             * don't do labels; i.e. only do buttons.
             */
            if (((*p)->core.widget_class == xmLabelWidgetClass) &&
                (IsPulldown(m) || IsPopup(m)))
                continue;

            w = XtWidth  (*p);
            h = XtHeight (*p);

            lw = (XmLabelWidget) (*p);

            if (IsVertical (m)) /* change horiz margins to */
            {                   /* be uniform */
               ChangeMargin (Lab_MarginLeft  (lw), m_l, w);
               ChangeMargin (Lab_MarginRight (lw), m_r, w);
               ChangeMargin (Lab_MarginWidth (lw), m_w, w);

                if (XtWidth (lw) != w) 
                {
                    _XmConfigureObject(lw,lw->core.x,lw->core.y,
                        w, lw->core.height,
                        lw->core.border_width);
                }
            }
            else            /* change vert margins */
            {
                ChangeMargin (Lab_MarginTop (lw), m_t, h);
                ChangeMargin (Lab_MarginBottom (lw), m_b, h);
                ChangeMargin (Lab_MarginHeight (lw), m_h, h);

                if (XtHeight (lw) != h) 
                {
                    _XmConfigureObject(lw,lw->core.x,lw->core.y,
                        lw->core.width, h,
                        lw->core.border_width);
                }
            }
        }
    }
}


/*
 * Action routines specific to traversal.
 */

/* ARGSUSED */
static void _XmMenuUnmap (cb, event, param, num_param)
XmCascadeButtonWidget cb;
XEvent  *event;
char   **param;
int     *num_param;
{
   /*
    * Typically, when a cascade button is in a transient menupane, we just
    * want to ignore unmap requests.  However, when it is in a menubar, we
    * want it handled normally.
    */
   if (RC_Type(XtParent(cb)) == XmMENU_BAR)
      _XmPrimitiveUnmap(cb, event);
}



/* ARGSUSED */
static void _XmMenuFocusOut (cb, event, param, num_param)
XmCascadeButtonWidget cb;
XEvent  *event;
char   **param;
int     *num_param;
{
    /*
     * During traversal, when the focus is moved to the next menupane,
     * the cascade button to which the menupane is attached will get a
     * FocusOut event, thus causing it to unhighlight.  To prevent this,
     * we will discard the FocusOut event if we are moving to a new submenu.
     */
    if (!ShouldDispatchFocusOut(cb))
        return;

    /* Forward the event for normal processing */
    _XmPrimitiveFocusOut(cb, event);
}



/*
 * The normal primitive FocusIn procedure does nothing if a transient
 * operation is happening.  Since we are part of the transient operation,
 * and we still want focus to work, we need to use the internal function.
 */
/* ARGSUSED */
static void _XmMenuFocusIn (cb, event, param, num_param)
XmCascadeButtonWidget cb;
XEvent  *event;
char   **param;
int     *num_param;
{
   /* Forward the event for normal processing */
   _XmPrimitiveFocusInInternal(cb, event);
}



/* ARGSUSED */
static void Noop (cb, event, param, num_param)
XmCascadeButtonWidget cb;
XEvent  *event;
char   **param;
int     *num_param;
{
   /*
    * Do nothing; the purpose is to override the actions taken by the
    * Primitive translations.
    */
}


static void MenuTraverse (w, event, direction)
XmCascadeButtonWidget w;
XEvent * event;
int     direction;
{
   XmManagerWidget parent;

   /*
    * The case may occur where the reporting widget is in fact the
    * RowColumn widget, and not a child.  This will occur if the
    * RowColumn has not traversable children.
    */
   if (XmIsRowColumn(w))
      parent = (XmManagerWidget) w;
   else if (XmIsRowColumn(XtParent(w)))
      parent = (XmManagerWidget) XtParent(w);
   else
      return;

   if ((RC_Type(parent) == XmMENU_POPUP) || 
       (RC_Type(parent) == XmMENU_PULLDOWN) ||
       (RC_Type(parent) == XmMENU_BAR))
   {
      _XmRecordEvent(event);
      (*(((XmRowColumnWidgetClass)XtClass(parent))->row_column_class.
          traversalHandler))(parent, w, direction);
   }
}


/* ARGSUSED */
static void _XmMenuTraverseLeft (cb, event, param, num_param)
XmCascadeButtonWidget cb;
XEvent  *event;
char   **param;
int     *num_param;
{
   XmRowColumnWidget rc;

   if (_XmIsEventUnique(event))
   {
      if (XmIsRowColumn(rc = (XmRowColumnWidget)XtParent(cb)) && 
	  !IsBar(rc) &&
	  (rc->row_column.orientation == XmHORIZONTAL))
	 MenuTraverse(cb, event, XmTRAVERSE_UP);
      else
	 MenuTraverse(cb, event, XmTRAVERSE_LEFT);
   }
}



/* ARGSUSED */
static void _XmMenuTraverseRight (cb, event, param, num_param)
XmCascadeButtonWidget cb;
XEvent  *event;
char   **param;
int     *num_param;
{
   XmRowColumnWidget rc;

   if (_XmIsEventUnique(event))
   {
      if (XmIsRowColumn(rc = (XmRowColumnWidget)XtParent(cb)) && 
	  !IsBar(rc) &&
	  (rc->row_column.orientation == XmHORIZONTAL))
	 MenuTraverse(cb, event, XmTRAVERSE_DOWN);
      else
	 MenuTraverse(cb, event, XmTRAVERSE_RIGHT);
   }
}



/* ARGSUSED */
static void _XmMenuTraverseUp (cb, event, param, num_param)
XmCascadeButtonWidget cb;
XEvent  *event;
char   **param;
int     *num_param;
{
   XmRowColumnWidget rc;

   if (_XmIsEventUnique(event))
   {
      if (XmIsRowColumn(rc = (XmRowColumnWidget)XtParent(cb)) && 
	  !IsBar(rc) &&
	  (rc->row_column.orientation == XmHORIZONTAL))
	 MenuTraverse(cb, event, XmTRAVERSE_LEFT);
      else
	 MenuTraverse(cb, event, XmTRAVERSE_UP);
   }
}



/* ARGSUSED */
static void _XmMenuTraverseDown (cb, event, param, num_param)
XmCascadeButtonWidget cb;
XEvent  *event;
char   **param;
int     *num_param;
{
   XmRowColumnWidget rc;

   if (_XmIsEventUnique(event))
   {
      if (XmIsRowColumn(rc = (XmRowColumnWidget)XtParent(cb)) && 
	  !IsBar(rc) &&
	  (rc->row_column.orientation == XmHORIZONTAL))
	 MenuTraverse(cb, event, XmTRAVERSE_RIGHT);
      else
	 MenuTraverse(cb, event, XmTRAVERSE_DOWN);
   }
}


/* ARGSUSED */
static void _XmMenuEscape (w, event)
Widget w;
XEvent  *event;
{
   /* Process the event only if not already processed */
   if (!_XmIsEventUnique(event))
      return;

   /* Let the menushell widget clean things up */
   (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
      menu_shell_class.popdownOne))(w, event);
}

static void _XmRC_GadgetTraverseDown (rc, event, param, num_param)
XmRowColumnWidget rc;
XEvent  *event;
char   **param;
int     *num_param;
{
   XmGadget gadget = (XmGadget)rc->manager.active_child;

   if (gadget && XmIsGadget(gadget))
      _XmMenuTraverseDown(gadget, event, param, num_param);
}



static void _XmRC_GadgetTraverseUp (rc, event, param, num_param)
XmRowColumnWidget rc;
XEvent  *event;
char   **param;
int     *num_param;
{
   XmGadget gadget = (XmGadget)rc->manager.active_child;

   if (gadget && XmIsGadget(gadget))
      _XmMenuTraverseUp(gadget, event, param, num_param);
}



static void _XmRC_GadgetTraverseLeft (rc, event, param, num_param)
XmRowColumnWidget rc;
XEvent  *event;
char   **param;
int     *num_param;
{
   XmGadget gadget = (XmGadget)rc->manager.active_child;

   /*
    * If there is not active child, then this RowColumn has
    * not traversable children, so it's fielding traversal
    * requests itself.
    */
   if (gadget && XmIsGadget(gadget))
      _XmMenuTraverseLeft(gadget, event, param, num_param);
   else if (gadget == NULL)
      _XmMenuTraverseLeft(rc, event, param, num_param);
}



static void _XmRC_GadgetTraverseRight (rc, event, param, num_param)
XmRowColumnWidget rc;
XEvent  *event;
char   **param;
int     *num_param;
{
   XmGadget gadget = (XmGadget)rc->manager.active_child;

   /*
    * If there is not active child, then this RowColumn has
    * not traversable children, so it's fielding traversal
    * requests itself.
    */
   if (gadget && XmIsGadget(gadget))
      _XmMenuTraverseRight(gadget, event, param, num_param);
   else if (gadget == NULL)
      _XmMenuTraverseRight(rc, event, param, num_param);
}


/*
 * In case we've moved into our out of a gadget, we need to take care
 * of the highlighting ourselves, since the gadget will not get a focus
 * event.
 */

static void GadgetCleanup (rc, oldActiveChild)
XmRowColumnWidget rc;
XmGadget oldActiveChild;
{
    XmGadget newActiveChild = (XmGadget)rc->manager.active_child;

    if (oldActiveChild != newActiveChild)
    {
        if (XmIsGadget(oldActiveChild))
        {
            _XmDispatchGadgetInput(oldActiveChild, NULL, XmFOCUS_OUT_EVENT);
            oldActiveChild->gadget.have_traversal = False;
        }
    }
}



static void _XmMenuTraversalHandler (rc, pw, direction)
XmRowColumnWidget rc;
Widget pw;
int direction;
{
   Widget currentActiveChild;

   if (!rc->manager.traversal_on)
      return;

   switch (direction)
   {
      case XmTRAVERSE_UP:
      {
         if (!IsBar(rc))
         {
             currentActiveChild = rc->manager.active_child;
             _XmProcessTraversal(pw, XmTRAVERSE_PREV, True);
             GadgetCleanup(rc, currentActiveChild);
         }
         else
             Move_Up_Down (rc, pw);
         break;
      }

      case XmTRAVERSE_DOWN:
      {
         if (!IsBar(rc))
         {
             currentActiveChild = rc->manager.active_child;
             _XmProcessTraversal(pw, XmTRAVERSE_NEXT, True);
             GadgetCleanup(rc, currentActiveChild);
         }
         else
             Move_Up_Down (rc, pw);
         break;
      }

      case XmTRAVERSE_LEFT:
      {
         MoveLeft(rc, pw);
         break;
      }

      case XmTRAVERSE_RIGHT:
      {
         MoveRight(rc, pw);
         break;
      }
   }
}


/*
 * When the PM menubar mode is active, either the up or down arrow will
 * cause us to post the menupane associated with the active cascade button
 * in the menubar.
 */

static void Move_Up_Down (rc, pw)
XmRowColumnWidget rc;
Widget pw;
{
    if (rc->manager.active_child == NULL)
        return;

    if (XmIsPrimitive(pw))
    {
        XmPrimitiveClassRec * prim;
      
        prim = (XmPrimitiveClassRec *)XtClass(pw);
        (*(prim->primitive_class.arm_and_activate)) (pw, NULL);
    }

    else if (XmIsGadget(pw))
    {
        XmGadgetClassRec * gad;
      
        gad = (XmGadgetClassRec *)XtClass(pw);
        (*(gad->gadget_class.arm_and_activate)) (pw, NULL);
    }
}

/* ARGSUSED */
static void MoveLeft (rc, pw)
XmRowColumnWidget rc;
Widget pw;
{
    if (IsBar(rc))
    {
        /* Move to the previous item in the menubar */
        FindPrevMenuBarItem(rc);
    }
    else if (IsPulldown(rc))
    {
        /*
         * If we're the topmost pulldown menupane, then unpost and move to
         * the next available item in the menubar, and post its submenu.
         */
        if (RC_Type(XtParent(RC_CascadeBtn(rc))) == XmMENU_BAR)
        {
            FindPrevMenuBarCascade(XtParent(RC_CascadeBtn(rc)));
        }
        else if (RC_Type(XtParent(RC_CascadeBtn(rc))) != XmMENU_OPTION)
        {
            /* Simply unpost the last menupane */
            (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
              menu_shell_class.popdownEveryone)) (XtParent(rc), NULL);
        }
    }
}


static void MoveRight (rc, pw)
XmRowColumnWidget rc;
Widget pw;
{
   XmRowColumnWidget topLevel;
   
   /*
    * If the primitive widget is a cascade button, then post its
    * submenu, if there is one.
    */
   if (!IsBar(rc) && XmIsCascadeButtonGadget(pw) && CBG_Submenu(pw)) 
   {
      (*(((XmGadgetClassRec *)XtClass(pw))->gadget_class.
	 arm_and_activate)) (pw, NULL);
   }
   else if (!IsBar(rc) && XmIsCascadeButton(pw) && CB_Submenu(pw))
   {
      (*(((XmPrimitiveClassRec *)XtClass(pw))->primitive_class.
	 arm_and_activate)) (pw, NULL);
   }
   else if (IsBar(rc))
   {
      /* Move to the next item in the menubar */
      FindNextMenuBarItem(rc);
   }
   else if (IsPulldown(rc))
   {
      /*
       * For most buttons, requesting to move right is a Noop.  The
       * exception is when we are working with a menubar.  Then, we
       * want to unpost the current menu hierarchy, and post the
       * hierarchy associated with the next button in the menubar.
       */
      _XmGetActiveTopLevelMenu (rc, &topLevel);
      if (RC_Type(topLevel) == XmMENU_BAR)
      {
	 FindNextMenuBarCascade(topLevel);
      }
   }
}


/*
 * Find the next cascade button in the menubar which can be traversed to.
 */

static void FindNextMenuBarItem (menubar)
XmRowColumnWidget menubar;
{
   Widget child;
    register int i, j;
    int upper_limit;
    Widget active_child;
    XRectangle visRect;

    /* 
     * We're not in the PM menubar mode if we don't have an active child.
     */
    if (menubar->manager.active_child == NULL)
        return;

    _XmCreateVisibilityRect(menubar, &visRect);
    upper_limit = menubar->composite.num_children;
    active_child = menubar->manager.active_child;

    /* Find the index of the currently active item */
    for (i = 0; i < upper_limit; i++)
    {
       if (menubar->composite.children[i] == active_child)
          break;
    }

    /* Start looking at the next child */
    for (j = 0, i++; j < upper_limit - 1; j++, i++)
    {
        /* Wrap, if necessary */
        if (i >= upper_limit)
           i = 0;

        child = menubar->composite.children[i];

        /* You can't traverse to a button which has no submenu */
        if ((XmIsCascadeButton(child) && (CB_Submenu(child) == NULL)) ||
            (XmIsCascadeButtonGadget(child)) && (CBG_Submenu(child) == NULL))
           continue;

        if (_XmTestTraversability(child, &visRect))
        {
            /* Unhighlight the old active child */
            if (XmIsPrimitive(active_child))
            {
               XmPrimitiveClassRec * prim;
      
               prim = (XmPrimitiveClassRec *)XtClass(active_child);
               (*(prim->primitive_class.border_unhighlight)) (active_child);
            }

            else if (XmIsGadget(active_child))
            {
               XmGadgetClassRec * gadget;
      
               gadget = (XmGadgetClassRec *)XtClass(active_child);
               (*(gadget->gadget_class.border_unhighlight)) (active_child);
            }

            /* Highlight the child */
            menubar->manager.active_child = child;
	    (void) XmProcessTraversal (child, XmTRAVERSE_CURRENT);

            if (XmIsPrimitive(child))
            {
               XmPrimitiveClassRec * prim;
      
               prim = (XmPrimitiveClassRec *)XtClass(child);
               (*(prim->primitive_class.border_highlight)) (child);
            }
            else if (XmIsGadget(child))
            {
               XmGadgetClassRec * gadget;
      
               gadget = (XmGadgetClassRec *)XtClass(child);
               (*(gadget->gadget_class.border_highlight)) (child);
            }
            return;
        }
    }
}


/*
 * Find the previous cascade button in the menubar which can be traversed to.
 */

static void FindPrevMenuBarItem(menubar)
XmRowColumnWidget menubar;
{
    Widget child;
    register int i, j;
    int upper_limit;
    Widget active_child;
    XRectangle visRect;

    /* We're not in the PM menubar mode if we don't have an active child */
    if (menubar->manager.active_child == NULL)
        return;

    _XmCreateVisibilityRect(menubar, &visRect);
    upper_limit = menubar->composite.num_children;
    active_child = menubar->manager.active_child;

    /* Find the index of the currently active item */
    for (i = 0; i < upper_limit; i++)
    {
        if (menubar->composite.children[i] == active_child)
            break;
    }

    /* Start looking at the previous child */
    for (j = 0, --i; j < upper_limit - 1; j++, --i)
    {
        /* Wrap, if necessary */
        if (i < 0)
           i = upper_limit - 1;

        child = menubar->composite.children[i];

        /* You can't traverse to a button which has no submenu */
        if ((XmIsCascadeButton(child) && (CB_Submenu(child) == NULL)) ||
            (XmIsCascadeButtonGadget(child)) && (CBG_Submenu(child) == NULL))
           continue;

        if (_XmTestTraversability(child, &visRect))
        {
            /* Unhighlight the old active child */
            if (XmIsPrimitive(active_child))
            {
               XmPrimitiveClassRec * prim;
      
               prim = (XmPrimitiveClassRec *)XtClass(active_child);
               (*(prim->primitive_class.border_unhighlight)) (active_child);
            }
            else if (XmIsGadget(active_child))
            {
               XmGadgetClassRec * gadget;
      
               gadget = (XmGadgetClassRec *)XtClass(active_child);
               (*(gadget->gadget_class.border_unhighlight)) (active_child);
            }

            /* Highlight the new active child */
            menubar->manager.active_child = (Widget)child;
	    (void) XmProcessTraversal (child, XmTRAVERSE_CURRENT);

            if (XmIsPrimitive(child))
            {
               XmPrimitiveClassRec * prim;
      
               prim = (XmPrimitiveClassRec *)XtClass(child);
               (*(prim->primitive_class.border_highlight)) (child);
            }
            else if (XmIsGadget(child))
            {
               XmGadgetClassRec * gadget;
      
               gadget = (XmGadgetClassRec *)XtClass(child);
               (*(gadget->gadget_class.border_highlight)) (child);
            }
            return;
        }
    }
}


/*
 * Find the next hierarchy in the menubar which can be traversed to.
 */

static void FindNextMenuBarCascade (menubar)
XmRowColumnWidget menubar;
{
   Widget child;
   register int i, j;
   int upper_limit;
   XRectangle visRect;
   ShellWidget shell;

   _XmCreateVisibilityRect(menubar, &visRect);
   upper_limit = menubar->composite.num_children;

   /* 
    * Determine which child is popped up.
    */
   shell = (ShellWidget) RC_PopupPosted(menubar);
   child = RC_CascadeBtn(shell->composite.children[0]);

   /* Find the index of the currently active item */
   for (i = 0; i < upper_limit; i++)
   {
      if (menubar->composite.children[i] == child)
	  break;
   }

   /* Start looking at the next child */
   for (j = 0, i++; j < upper_limit - 1; j++, i++)
   {
      /* Wrap, if necessary */
      if (i >= upper_limit)
	  i = 0;

      child = menubar->composite.children[i];
      
      if (XmIsCascadeButtonGadget(child))
      {
	 /* You can't traverse to a button which has no submenu */
	 if (XmIsCascadeButtonGadget(child) && (CBG_Submenu(child) == NULL))
	     continue;
	 
	 if (_XmTestTraversability(child, &visRect))
	 {
	    XmGadgetClassRec * gadget;
	    
	    gadget = (XmGadgetClassRec *)XtClass(child);
	    (*(gadget->gadget_class.arm_and_activate)) (child, NULL);
	    return;
	 }
      }

      else if (XmIsCascadeButton(child))
      {
	 /* You can't traverse to a button which has no submenu */
	 if (XmIsCascadeButton(child) && (CB_Submenu(child) == NULL))
	     continue;

	 if (_XmTestTraversability(child, &visRect))
	 {
	    XmPrimitiveClassRec * prim;
      
	    prim = (XmPrimitiveClassRec *)XtClass(child);
	    (*(prim->primitive_class.arm_and_activate)) (child, NULL);
	    return;
	 }
      }
   }
}


/*
 * Find the previous hierarchy in the menubar which can be traversed to.
 */

static void FindPrevMenuBarCascade (menubar)
XmRowColumnWidget menubar;
{
    Widget child;
    register int i, j;
    int upper_limit;
    XRectangle visRect;
    ShellWidget shell;

    _XmCreateVisibilityRect(menubar, &visRect);
    upper_limit = menubar->composite.num_children;

    /* Determine which child is popped up */
    shell = (ShellWidget) RC_PopupPosted(menubar);
    child = RC_CascadeBtn(shell->composite.children[0]);

    /* Find the index of the currently active item */
    for (i = 0; i < upper_limit; i++)
    {
        if (menubar->composite.children[i] == child)
           break;
    }

    /* Start looking at the previous child */
    for (j = 0, --i; j < upper_limit - 1; j++, --i)
    {
        /* Wrap, if necessary */
        if (i < 0)
           i = upper_limit - 1;

	child = menubar->composite.children[i];
	
	if (XmIsCascadeButtonGadget(child))
	{
	   /* You can't traverse to a button which has no submenu */
	   if (CBG_Submenu(child) == NULL)
	       continue;

	   if (_XmTestTraversability(child, &visRect))
	   {
	      XmGadgetClassRec * gadget;
      
	      gadget = (XmGadgetClassRec *)XtClass(child);
	      (*(gadget->gadget_class.arm_and_activate)) (child, NULL);
	      return;
	   }
	}
	else if (XmIsCascadeButton (child))
	{
	   /* You can't traverse to a button which has no submenu */
	   if (CB_Submenu(child) == NULL)
	       continue;

	   if (_XmTestTraversability(child, &visRect))
	   {
	      XmPrimitiveClassRec * prim;
      
	      prim = (XmPrimitiveClassRec *)XtClass(child);
	      (*(prim->primitive_class.arm_and_activate)) (child, NULL);
	      return;
	   }
	}
    }
}


/* ARGSUSED */
static void _XmRC_FocusIn (rc, event, params, num_params)
XmRowColumnWidget  rc;
XEvent      *event;
char    **params;
int     *num_params;
{
   /*
    * For popup and pulldown menupanes, we want to ignore focusIn request
    * which occur when we are not visible.
    */

   if (IsBar(rc))
      _XmManagerFocusIn(rc, event);
   else if ((((XmMenuShellWidget)XtParent(rc))->shell.popped_up) &&
            (rc->manager.traversal_on))
      _XmManagerFocusInInternal(rc, event);
}


/* ARGSUSED */
static void _XmRC_FocusOut (rc, event, params, num_params)
XmRowColumnWidget  rc;
XEvent      *event;
char    **params;
int     *num_params;
{
    _XmManagerFocusOut(rc, event);
}



/* ARGSUSED */
static void _XmRC_Unmap (rc, event, params, num_params)
XmRowColumnWidget  rc;
XEvent      *event;
char    **params;
int     *num_params;
{
   /*
    * For popup and pulldown menupanes, we never care about being notified
    * when we are unmapped.  For menubars, we want normal unmapping 
    * processing to occur.
    */

   if (IsBar(rc))
      _XmManagerUnmap(rc, event);
}



static void _XmRC_Enter (rc, event)
XmRowColumnWidget  rc;
XEvent      *event;
{
   if (IsBar(rc) && RC_IsArmed(rc))
       return;

   _XmManagerEnter(rc, event);
}


/*
 * Catch an 'Escape' which occurs within a gadget, and bring down the
 * menu system.
 */
static void _XmRC_GadgetEscape (rc, event)
XmRowColumnWidget  rc;
XEvent      *event;
{
   /* Process the event only if not already processed */
   if (!_XmIsEventUnique(event))
      return;

    if (IsBar(rc))
    {
        /*  
         * We're in the PM menubar mode, so let our own arm and activate 
         * procedure clean things up .
         */
        if (RC_IsArmed(rc))
            ArmAndActivate(rc, NULL);
    }
    else
    {
        /* Let the menushell widget clean things up */
        (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
          menu_shell_class.popdownOne))(XtParent(rc), event);
    }

   _XmRecordEvent(event);
}



/*
 * When the user requests help (F1) for a gadget in a menu, by the time
 * the menu widget gets notified, the menu system has already been cleaned
 * up; the information describing which gadget was active is lost.  So, we
 * will grab this information before disabling the menu system.
 */

/* ARGSUSED */
static void _XmGetGadget (rc, event, params, num_params)
XmRowColumnWidget  rc;
XEvent      *event;
char    **params;
int     *num_params;
{
   if ((rc->manager.active_child) && XmIsGadget(rc->manager.active_child))
      ActiveGadgetChild = (XmGadget)rc->manager.active_child;
   else
      ActiveGadgetChild = NULL;
}




/*
 * During traversal, when the focus is moved to the next menupane,
 * the cascade button to which the menupane is attached will get a
 * FocusOut event, thus causing it to unhighlight.  To prevent this,
 * we will discard the FocusOut event if we are moving to a new submenu.
 */

static Boolean ShouldDispatchFocusOut (widget)
Widget widget;
{
   XmManagerWidget parent = (XmManagerWidget)XtParent(widget);

   if (XmIsRowColumn(parent) && 
       ((RC_Type(parent) == XmMENU_BAR) ||
        (RC_Type(parent) == XmMENU_POPUP) || 
        (RC_Type(parent) == XmMENU_PULLDOWN)))
   {
       if (XmIsCascadeButton(widget))
       {
           if (CB_Submenu(widget) &&
             ((XmMenuShellWidget)XtParent(CB_Submenu(widget)))->shell.popped_up)
           {
               return(False);
           }
       }
       else if (XmIsCascadeButtonGadget(widget))
       {
           if (CBG_Submenu(widget) &&
               ((XmMenuShellWidget)XtParent(CBG_Submenu(widget)))->shell.
                                                                  popped_up)
           {
               return(False);
           }
       }
   }

   return (True);
}


/*
 * this entry is set in label and label gadget's class field so that
 * all communication from the buttons to the RowColumn are done through
 * this entry and then revectored to the appropriate routine.
 */
static void MenuProcedureEntry (proc, widget, flag, data, data2)
int proc;
Widget widget;
Boolean flag;
caddr_t data;
caddr_t data2;
{
   switch (proc)
   {
     case XmMENU_CASCADING:
       PrepareToCascade(widget, data, data2);
       break;
			
     case XmMENU_POPDOWN:
       MenuPopDown (widget, data);
       break;

     case XmMENU_SHELL_POPDOWN:
       (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
	  menu_shell_class.popdownEveryone))(widget, data);
       break;

     case XmMENU_BUTTON:
       VerifyMenuButton (widget, data, data2);
       break;
       
     case XmMENU_CALLBACK:
       /* data points to the widget which was activated */
       ChildsActivateCallback (widget, data, data2);
       break;
       
     case XmMENU_TRAVERSAL:
       /* data points to a boolean */
       SetMenuTraversal (widget, flag);
       break;

     case XmMENU_SUBMENU:
       SetCascadeField (widget, data, flag);
       break;

     case XmMENU_PROCESS_TREE:
       DoProcessMenuTree (widget, XmREPLACE);
       break;

     case XmMENU_ARM:
       MenuArm (widget);
       break;

     case XmMENU_DISARM:
       MenuDisarm (widget);
       break;

     case XmMENU_BAR_CLEANUP:
       MenuBarCleanup (widget);
       break;

     default:
       break;
  }
}
