/* 
 *  @OPENGROUP_COPYRIGHT@
 *  COPYRIGHT NOTICE
 *  Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc.
 *  Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group
 *  ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for
 *  the full copyright text.
 *  
 *  This software is subject to an open license. It may only be
 *  used on, with or for operating systems which are themselves open
 *  source systems. You must contact The Open Group for a license
 *  allowing distribution and sublicensing of this software on, with,
 *  or for operating systems which are not Open Source programs.
 *  
 *  See http://www.opengroup.org/openmotif/license for full
 *  details of the license agreement. Any use, reproduction, or
 *  distribution of the program constitutes recipient's acceptance of
 *  this agreement.
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
 *  PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 *  WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
 *  OR FITNESS FOR A PARTICULAR PURPOSE
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
 *  NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED
 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
 *  EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGES.
*/ 
/* 
 * HISTORY
*/ 
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$TOG: CascadeB.c /main/27 1999/08/11 14:26:35 mgreess $"
#endif
#endif
/* (c) Copyright 1989, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */
/* (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 HEWLETT-PACKARD COMPANY */

#include "XmI.h"		/* ShellP.h doesn't define externalref. */
#include <X11/ShellP.h>
#include <X11/keysymdef.h>
#include <Xm/BaseClassP.h>
#include <Xm/DisplayP.h>
#include <Xm/DrawP.h>
#include <Xm/LabelGP.h>
#include <Xm/LabelP.h>
#include <Xm/MenuShellP.h>
#include <Xm/MenuStateP.h>
#include <Xm/MenuT.h>
#include <Xm/RowColumnP.h>
#include <Xm/TearOffP.h>
#include <Xm/TraitP.h>
#include <Xm/TransltnsP.h>
#include "CascadeBI.h"
#include "CascadeBGI.h"
#include "LabelI.h"
#include "GadgetUtiI.h"
#include "MenuStateI.h"
#include "MenuProcI.h"
#include "MenuUtilI.h"
#include "MessagesI.h"
#include "PrimitiveI.h"
#include "RCMenuI.h"
#include "TearOffI.h"
#include "TraversalI.h"
#include "UniqueEvnI.h"

#define CASCADE_PIX_SPACE    4	 /* pixels between label and bit map */
#define MAP_DELAY_DEFAULT   180
#define EVENTS              ((unsigned int) (ButtonPressMask | \
			     ButtonReleaseMask | EnterWindowMask | \
			     LeaveWindowMask))


#define WRONGPARENT	_XmMMsgCascadeB_0000
#define WRONGSUBMENU	_XmMMsgCascadeB_0001
#define WRONGMAPDELAY	_XmMMsgCascadeB_0002


/********    Static Function Declarations    ********/

static void ClassInitialize( void ) ;
static void ClassPartInitialize( 
                        WidgetClass wc) ;
static void BorderHighlight( 
                        Widget wid) ;
static void BorderUnhighlight( 
                        Widget wid) ;
static void DrawShadow( 
                        register XmCascadeButtonWidget cb) ;
static void DrawCascade( 
                        register XmCascadeButtonWidget cb) ;
static void Redisplay( 
                        register Widget cb,
                        XEvent *event,
                        Region region) ;
static void Arm( 
                        register XmCascadeButtonWidget cb) ;
static void ArmAndPost( 
                        XmCascadeButtonWidget cb,
                        XEvent *event) ;
static void ArmAndActivate( 
                        Widget wid,
                        XEvent *event,
                        String *params,
                        Cardinal *num_params) ;
static void Disarm( 
                        register XmCascadeButtonWidget cb,
#if NeedWidePrototypes
                        int unpost) ;
#else
                        Boolean unpost) ;
#endif /* NeedWidePrototypes */
static void PostTimeout( 
                        XtPointer closure,
                        XtIntervalId *id) ;
static void DelayedArm( 
                        Widget wid,
                        XEvent *event,
                        String *param,
                        Cardinal *num_param) ;
static void CheckDisarm( 
                        Widget wid,
                        XEvent *event,
                        String *param,
                        Cardinal *num_param) ;
static void StartDrag( 
                        Widget wid,
                        XEvent *event,
                        String *param,
                        Cardinal *num_param) ;
static void Select( 
                        register XmCascadeButtonWidget cb,
                        XEvent *event,
#if NeedWidePrototypes
                        int doCascade) ;
#else
                        Boolean doCascade) ;
#endif /* NeedWidePrototypes */
static void DoSelect( 
                        Widget wid,
                        XEvent *event,
                        String *param,
                        Cardinal *num_param) ;
static void KeySelect( 
                        Widget wid,
                        XEvent *event,
                        String *param,
                        Cardinal *num_param) ;
static void MenuBarSelect( 
                        Widget wid,
                        XEvent *event,
                        String *param,
                        Cardinal *num_param) ;
static void MenuBarEnter( 
                        Widget wid,
                        XEvent *event,
                        String *param,
                        Cardinal *num_param) ;
static void MenuBarLeave( 
                        Widget wid,
                        XEvent *event,
                        String *param,
                        Cardinal *num_param) ;
static void CleanupMenuBar( 
                        Widget wid,
                        XEvent *event,
                        String *param,
                        Cardinal *num_param) ;
static void PopdownGrandChildren( 
                        XmRowColumnWidget rowcol) ;
static void Cascading( 
                        Widget w,
                        XEvent *event) ;
static void Popup( 
                        Widget cb,
                        XEvent *event) ;
static void size_cascade( 
                        XmCascadeButtonWidget cascadebtn) ;
static void position_cascade( 
                        XmCascadeButtonWidget cascadebtn) ;
static void setup_cascade( 
                        XmCascadeButtonWidget cascadebtn,
#if NeedWidePrototypes
                        int adjustWidth,
                        int adjustHeight) ;
#else
                        Boolean adjustWidth,
                        Boolean adjustHeight) ;
#endif /* NeedWidePrototypes */
static void Destroy( 
                        Widget wid) ;
static void Resize( 
                        Widget cb) ;
static Boolean SetValuesPrehook( 
			Widget cw,
                        Widget rw,
                        Widget nw,
                        ArgList args,
                        Cardinal *num_args) ;
static Boolean SetValues( 
                        Widget cw,
                        Widget rw,
                        Widget nw,
                        ArgList args,
                        Cardinal *num_args) ;
static void InitializePrehook( 
                        Widget req,
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args) ;
static void InitializePosthook( 
                        Widget req,
                        Widget new_w,
                        ArgList args,
                        Cardinal *num_args) ;
static void GetArmGC( 
                        XmCascadeButtonWidget cb) ;
static void GetBackgroundGC( 
                        XmCascadeButtonWidget cb) ;
static void Initialize( 
                        Widget w_req,
                        Widget w_new,
                        ArgList args,
                        Cardinal *num_args) ;

/********    End Static Function Declarations    ********/


/*
 * event translation tables for cascadebutton.  There are different
 * ones for the different menus a cascadebutton widget can appear in
 */

static XtTranslations menubar_events_parsed;

#define menubar_events	_XmCascadeB_menubar_events

static XtTranslations p_events_parsed;

#define p_events 	_XmCascadeB_p_events

static XtActionsRec action_table [] =
{
    {"DelayedArm",	DelayedArm},
    {"CheckDisarm",	CheckDisarm},
    {"StartDrag",	StartDrag},
    {"DoSelect", 	DoSelect},
    {"KeySelect",	KeySelect},
    {"MenuBarSelect",	MenuBarSelect},
    {"MenuBarEnter",	MenuBarEnter},
    {"MenuBarLeave",	MenuBarLeave},
    {"MenuButtonTakeFocus", _XmMenuButtonTakeFocus },
    {"MenuButtonTakeFocusUp", _XmMenuButtonTakeFocusUp },
    {"CleanupMenuBar",	CleanupMenuBar},
    {"Help",		_XmCBHelp},
};


static XtResource resources[] = 
{
    {	XmNactivateCallback, 
	XmCCallback, 
	XmRCallback,
	sizeof (XtCallbackList),
	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.activate_callback), 
	XmRCallback,
	NULL
    },
    {	XmNcascadingCallback, 
	XmCCallback, 
	XmRCallback,
	sizeof (XtCallbackList),
	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.cascade_callback), 
	XmRCallback,
	NULL
    },
    {	XmNsubMenuId, 
	XmCMenuWidget,				/* submenu */
	XmRMenuWidget, 
	sizeof (Widget),
	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.submenu), 
	XmRMenuWidget, 
	(XtPointer) NULL
    },
    {	XmNcascadePixmap, 
	XmCPixmap, 
	XmRDynamicPixmap,
	sizeof(Pixmap),
	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.cascade_pixmap), 
	XmRImmediate,
	(XtPointer) XmUNSPECIFIED_PIXMAP
    },
    {   XmNmappingDelay,
	XmCMappingDelay,
	XmRInt,
	sizeof (int),
	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.map_delay),
	XmRImmediate,
	(XtPointer) MAP_DELAY_DEFAULT
    },
    {
        XmNshadowThickness,
        XmCShadowThickness,
        XmRHorizontalDimension,
        sizeof (Dimension),
        XtOffsetOf( struct _XmCascadeButtonRec, primitive.shadow_thickness),
        XmRCallProc,
        (XtPointer) _XmSetThickness
    },
    {
	XmNtraversalOn,
	XmCTraversalOn,
	XmRBoolean,
	sizeof(Boolean),
	XtOffsetOf( struct _XmPrimitiveRec, primitive.traversal_on),
	XmRImmediate,
	(XtPointer) True
    },
    {
        XmNhighlightThickness,
        XmCHighlightThickness,
        XmRHorizontalDimension,
        sizeof (Dimension),
        XtOffsetOf( struct _XmPrimitiveRec, primitive.highlight_thickness),
        XmRCallProc,
        (XtPointer) _XmSetThickness
    },
    {
 	XmNmarginWidth, 
	XmCMarginWidth, 
	XmRHorizontalDimension, 
	sizeof (Dimension),
	XtOffsetOf( struct _XmLabelRec, label.margin_width), 
	XmRImmediate,
	(XtPointer) XmINVALID_DIMENSION
    },
};       

/*
 * static initialization of the cascade button widget class record, 
 * must do each field
 */
static XmBaseClassExtRec       cascadeBBaseClassExtRec = {
    NULL,                                     /* Next extension       */
    NULLQUARK,                                /* record type XmQmotif */
    XmBaseClassExtVersion,                    /* version              */
    sizeof(XmBaseClassExtRec),                /* size                 */
    InitializePrehook,                        /* initialize prehook   */
    SetValuesPrehook,			      /* set_values prehook   */
    InitializePosthook,                       /* initialize posthook  */
    XmInheritSetValuesPosthook,               /* set_values posthook  */
    XmInheritClass,                           /* secondary class      */
    XmInheritSecObjectCreate,                 /* creation proc        */
    XmInheritGetSecResData,                   /* getSecResData        */
    {0},                                      /* fast subclass        */
    XmInheritGetValuesPrehook,                /* get_values prehook   */
    XmInheritGetValuesPosthook,               /* get_values posthook  */
    NULL,                                     /* classPartInitPrehook */
    NULL,                                     /* classPartInitPosthook*/
    NULL,                                     /* ext_resources        */
    NULL,                                     /* compiled_ext_resources*/
    0,                                        /* num_ext_resources    */
    FALSE,                                    /* use_sub_resources    */
    XmInheritWidgetNavigable,                 /* widgetNavigable      */
    XmInheritFocusChange,                     /* focusChange          */
};


externaldef(xmcascadebuttonclassrec) XmCascadeButtonClassRec xmCascadeButtonClassRec = 
{
    {			/* core class record */
	(WidgetClass) &xmLabelClassRec,		/* superclass ptr	*/
	"XmCascadeButton",			/* class_name	*/
	sizeof (XmCascadeButtonWidgetRec),	/* size of Pulldown widget */
	ClassInitialize,			/* class init proc */
	ClassPartInitialize,			/* chained class init */
	FALSE,					/* class is not init'ed */
	Initialize,				/* widget init proc */
	NULL,					/* init_hook proc */
    	XtInheritRealize,       		/* 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,					/* compress motion */
    	XtExposeCompressMaximal,		/* compress exposure */
	TRUE,					/* compress enter-leave */
   	FALSE,					/* no VisibilityNotify */
	Destroy,				/* class destroy proc */
	Resize,					/* class resize proc */
	Redisplay,				/* expose proc */
	SetValues,				/* 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,					/* default translation table */
						  /* this is manually set */
	XtInheritQueryGeometry,			/* query geo proc */
	NULL,				        /* display accelerator*/
	(XtPointer)&cascadeBBaseClassExtRec,	/* extension */
    },
	{
			/* Primitive Class record */
	BorderHighlight,			/* border_highlight */
	BorderUnhighlight,			/* border_uhighlight */
	XtInheritTranslations,		        /* translations */
	ArmAndActivate,				/* arm & activate */
	NULL,					/* get resources */
	0,					/* num get_resources */
	NULL,	                                /* extension */
	},
    {			/* Label Class record */
	XmInheritWidgetProc,		        /* set override callback */
	XmInheritMenuProc,		        /* menu procedures       */
	XtInheritTranslations,		        /* menu traversal xlation */
	NULL,					/* extension */
    },
    {			/* cascade_button class record */
        NULL,					/* extension */  
    }
};

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

externaldef(xmcascadebuttonwidgetclass) WidgetClass xmCascadeButtonWidgetClass = (WidgetClass) &xmCascadeButtonClassRec;

/* Menu Savvy trait record */
static XmMenuSavvyTraitRec MenuSavvyRecord = {
    /* version: */
    -1,
    NULL,
    NULL,
    NULL,
    _XmCBNameActivate,
};


/*
 * parse the translation tables for the different menutypes
 */
static void 
ClassInitialize( void )
{
    menubar_events_parsed  = XtParseTranslationTable (menubar_events);
    p_events_parsed	   = XtParseTranslationTable (p_events);
	
   /* set up base class extension quark */
   cascadeBBaseClassExtRec.record_type = XmQmotif;
}

/*
 * set up fast subclassing
 */

static void 
ClassPartInitialize(
        WidgetClass wc )
{
  _XmFastSubclassInit (wc, XmCASCADE_BUTTON_BIT);

  /* Install the menu savvy trait record,  copying fields from XmLabel */
  _XmLabelCloneMenuSavvy (wc, &MenuSavvyRecord);
}


/*
 * The button is armed (does not pop up submenus).
 */
static void 
BorderHighlight(
        Widget wid )
{
      Arm ((XmCascadeButtonWidget) wid);
}


/*
 * The button is disarmed (does not pop down submenus).
 */
static void 
BorderUnhighlight(
        Widget wid )
{
    XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid;
    Boolean popdown;

    if (Lab_IsMenupane(cb) &&
	(((XmManagerWidget)XtParent(cb))->manager.active_child == wid) &&
	CB_Submenu(cb))
    {
	XmMenuShellWidget mshell =
	    (XmMenuShellWidget) XtParent(CB_Submenu(cb));
	
	if ((mshell->composite.children[0] == CB_Submenu(cb)) &&
	    (XmIsMenuShell(mshell)) &&
	    (mshell->shell.popped_up))
	{
	    popdown = True;
	}
	else
	    popdown = False;
    }
    else
	popdown = False;

    Disarm ((XmCascadeButtonWidget) wid, popdown);
}


/*
 * Draw the 3D shadow around the widget if its is armed.
 */
static void 
DrawShadow(
        register XmCascadeButtonWidget cb )
{
  XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay((Widget) cb));
  Boolean etched_in = dpy -> display.enable_etched_in_menu;

  if (CB_IsArmed(cb))
    {
      if (XtIsRealized((Widget)cb))
      {
	 XmeDrawShadows (XtDisplay (cb), XtWindow (cb),
			cb->primitive.top_shadow_GC,
			cb->primitive.bottom_shadow_GC,
			cb->primitive.highlight_thickness,
			cb->primitive.highlight_thickness,
			cb->core.width - 2 * 
			cb->primitive.highlight_thickness,
			cb->core.height - 2 * 
			cb->primitive.highlight_thickness,
			cb->primitive.shadow_thickness,
			etched_in ? XmSHADOW_IN : XmSHADOW_OUT);
      }
   }
}


static void 
DrawCascade(
        register XmCascadeButtonWidget cb )
{
   if ((CB_HasCascade(cb)) && (CB_Cascade_width(cb) != 0))
   {
      Pixmap pixmap ;
      int depth ;

      pixmap = CB_IsArmed(cb) && 
	  (CB_ArmedPixmap(cb) != XmUNSPECIFIED_PIXMAP) ? 
	      CB_ArmedPixmap(cb) : CB_CascadePixmap(cb) ;

      XmeGetPixmapData(XtScreen(cb),
		       pixmap,
		       NULL,    
		       &depth,
		       NULL, NULL,
		       NULL, NULL,
		       NULL, NULL); 

      if (depth == cb->core.depth)
	  XCopyArea (XtDisplay(cb), 
		     pixmap, 
		     XtWindow(cb),
		     cb->label.normal_GC, 0, 0, 
		     CB_Cascade_width(cb), CB_Cascade_height(cb),
		     CB_Cascade_x(cb), CB_Cascade_y(cb));
      else 
      if (depth == 1) 
	  XCopyPlane (XtDisplay(cb), 
		      pixmap, 
		      XtWindow(cb),
		      cb->label.normal_GC, 0, 0, 
		      CB_Cascade_width(cb), CB_Cascade_height(cb),
		      CB_Cascade_x(cb), CB_Cascade_y(cb), 1);
  
  }
}

/*
 * redisplay the widget
 */
static void 
Redisplay(
        register Widget cb,
        XEvent *event,
        Region region )
{

    if (XtIsRealized (cb)) 
    {
	XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(cb));
	Boolean etched_in = dpy->display.enable_etched_in_menu;
	GC tmpGC = NULL;
	XtExposeProc expose;

	if (etched_in) {
	    XFillRectangle(XtDisplay(cb), XtWindow(cb),
		           CB_IsArmed(cb) ? CB_ArmGC(cb) : CB_BackgroundGC(cb),
			   0, 0, cb->core.width, cb->core.height);
	}
	if (etched_in && CB_IsArmed(cb)) {
	    Pixel junk, select_pix;
	    Boolean replaceGC = False;

	    XmGetColors(XtScreen(cb), cb->core.colormap,
			cb->core.background_pixel,
			&junk, &junk, &junk, &select_pix);

	    if (select_pix == 
		((XmCascadeButtonWidget)cb)->primitive.foreground) {
		replaceGC = True;
		tmpGC = ((XmCascadeButtonWidget)cb)->label.normal_GC;
		((XmCascadeButtonWidget)cb)->label.normal_GC = 
		    CB_BackgroundGC(cb);
	    }

	    _XmProcessLock();
	    expose = xmLabelClassRec.core_class.expose;
	    _XmProcessUnlock();
	    (*expose)(cb, event, region);

	    if (replaceGC)
		((XmCascadeButtonWidget)cb)->label.normal_GC = tmpGC;
	}
	else {
	
	    /* Label class does most of the work */

	    _XmProcessLock();
	    expose = xmLabelClassRec.core_class.expose;
	    _XmProcessUnlock();
	    (*expose)(cb, event, region);
	}

	DrawCascade((XmCascadeButtonWidget) cb);
	DrawShadow ((XmCascadeButtonWidget) cb);
    }
}


/*
 * Arming the cascadebutton consists of setting the armed bit
 * and drawing the 3D shadow.
 */
static void 
Arm(
        register XmCascadeButtonWidget cb )
{
  if (!CB_IsArmed(cb))
    {
      XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(cb));
      Boolean etched_in = dpy->display.enable_etched_in_menu;
      
      CB_SetArmed(cb, TRUE);

      if (etched_in) 
	  Redisplay((Widget) cb, NULL, NULL);
      else {
	  DrawCascade(cb);
	  DrawShadow (cb);
      }
    }

  XmProcessTraversal((Widget) cb, XmTRAVERSE_CURRENT);
}



/*
 * Post the submenu and then arm the button.  The arming is done
 * second so that the post can be quickly as possible.
 */
static void 
ArmAndPost(
        XmCascadeButtonWidget cb,
        XEvent *event )
{
   if (!CB_IsArmed(cb))
   {
      _XmCascadingPopup ((Widget) cb, event, TRUE);
      Arm (cb);
   }
}




/*
 * class function to cause the cascade button to be armed and selected
 */
/*ARGSUSED*/
static void 
ArmAndActivate(
        Widget wid,
        XEvent *event,
        String *params,		/* unused */
        Cardinal *num_params )	/* unused */
{
    XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
    XmRowColumnWidget parent = (XmRowColumnWidget) XtParent(cb);
    XmMenuSystemTrait menuSTrait;
    Time _time;

    /* check if event has been processed  - if it's NULL, override processing */
    if (event && !_XmIsEventUnique(event))
       return;

    _time = _XmGetDefaultTime(wid, event);

    menuSTrait = (XmMenuSystemTrait) 
      XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);

    if (menuSTrait == NULL) {
      /* We're in trouble.  This isn't a valid menu that we're 
	 in and Arm and Activate has been called.  Abort ! */
      return;
    }

    switch (Lab_MenuType (cb))
    {
       case XmMENU_BAR:
       {
          ShellWidget myShell = NULL;

          /* Shared menupanes require some additional checks */
          if (CB_Submenu(cb))
              myShell = (ShellWidget)XtParent(CB_Submenu(cb));

          if (myShell && 
	      XmIsMenuShell(myShell) &&         /* not torn ?! */
	      (myShell->shell.popped_up) &&
	      (myShell->composite.children[0] == CB_Submenu(cb)) &&
	      (cb == (XmCascadeButtonWidget)RC_CascadeBtn(CB_Submenu(cb))))
          {
	     menuSTrait -> popdown((Widget) parent, event);
	     Disarm (cb, FALSE);
	  }
          else 
          {
             /* call the cascading callbacks first thing */
             Cascading ((Widget) cb, event);

	     /*
	      * check if the traversing flag is set true.  This indicates
	      * that we are in a traverse and don't want to activate if
	      * there is no submenu attached.  Set during KDown in menubar.
	      */
	     if (CB_Traversing(cb) && !CB_Submenu(cb))
		 return;

             if (! RC_IsArmed (parent))
	     {
		_XmMenuFocus((Widget) parent, XmMENU_BEGIN, _time);

		menuSTrait -> arm((Widget) cb);
	     }
             else
	       menuSTrait -> menuBarCleanup((Widget) parent);

             /* do the select without calling the cascading callbacks again */
             Select (cb, event, FALSE);

	     /* To support menu replay, keep the pointer in sync mode */
	     XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);

             if (CB_Submenu(cb))
             {
                /*
                 * if XmProcessTraversal() fails, it's possible that the pane
                 * has no traversable children, so reset the focus to the pane.
                 */
                if (!XmProcessTraversal(CB_Submenu(cb), XmTRAVERSE_CURRENT))
		{
		   /* Must clear focus path first for shared menushells.
		    * Otherwise, moving the focus back will have old stale
		    * (old) focus_item.
		    */
		   _XmClearFocusPath(CB_Submenu(cb));
                   XtSetKeyboardFocus(XtParent(CB_Submenu(cb)), CB_Submenu(cb));
		}
             }
             else
             {
	        menuSTrait -> disarm((Widget) parent);
		_XmMenuFocus(XtParent(cb), XmMENU_END, _time);
                XtUngrabPointer( (Widget) cb, _time);
             }
          }
	  
          break;
       }

       case XmMENU_PULLDOWN:
       case XmMENU_POPUP:
       {
	  /* In case the tear off is active but not armed or grabbed */
	  menuSTrait -> tearOffArm((Widget) parent);
          Select (cb, event, TRUE);
	  if (CB_Submenu(cb))
	  {
             /*
              * if XmProcessTraversal() fails, it's possible that the pane
              * has no traversable children, so reset the focus to the pane.
              */
             if (!XmProcessTraversal(CB_Submenu(cb), XmTRAVERSE_CURRENT))
	     {
		/* Must clear focus path first for shared menushells.
		 * Otherwise, moving the focus back will have old stale
		 * (old) focus_item.
		 */
		_XmClearFocusPath(CB_Submenu(cb));
                XtSetKeyboardFocus(XtParent(CB_Submenu(cb)), CB_Submenu(cb));
	     }
	  }
          break;
       }
    }
    /* Record so spring loaded DispatchEvent() doesn't recall this routine.  */
    if (event)
       _XmRecordEvent(event);
}


/*
 * disarm the menu.  This may include popping down any submenu that is up or
 * removing the timeout to post a submenu
 */
static void 
Disarm(
        register XmCascadeButtonWidget cb,
#if NeedWidePrototypes
        int unpost )
#else
        Boolean unpost )
#endif /* NeedWidePrototypes */
{
   Widget rowcol = XtParent (cb);

   if (CB_IsArmed(cb))
   {
      CB_SetArmed(cb, FALSE);

      /* popdown any posted submenus */
      if (unpost && RC_PopupPosted(rowcol))
      {
	  (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
	     menu_shell_class.popdownEveryone))(RC_PopupPosted(rowcol),NULL,
						NULL, NULL);
      }

      /* if a delayed arm is pending, remove it */
      if (cb->cascade_button.timer)
      {
         XtRemoveTimeOut (cb->cascade_button.timer);
         cb->cascade_button.timer = 0;
      }

      /* if the shadow is drawn and the menupane is not going down, erase it */
      if ((! RC_PoppingDown(rowcol)) || RC_TornOff(rowcol))
      {
	 if (XtIsRealized ((Widget)cb))
	 {
	     /* etched in menu button */
	     XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(cb));
	     Boolean etched_in = dpy->display.enable_etched_in_menu;

	     if (etched_in) 
		 Redisplay((Widget) cb, NULL, NULL);
	     else
		 XmeClearBorder (XtDisplay (cb), XtWindow (cb),
				 cb->primitive.highlight_thickness,
				 cb->primitive.highlight_thickness,
				 cb->core.width - 2 * 
				 cb->primitive.highlight_thickness,
				 cb->core.height - 2 * 
				 cb->primitive.highlight_thickness,
				 cb->primitive.shadow_thickness);
	 }
      }
      DrawCascade(cb);
   }
}


/*
 * called when the post delay timeout occurs.
 */
/*ARGSUSED*/
static void
PostTimeout(
        XtPointer closure,
        XtIntervalId *id)	/* unused */
{
        XmCascadeButtonWidget cb = (XmCascadeButtonWidget) closure ;

   if (cb->cascade_button.timer)
   {
      cb->cascade_button.timer = 0;
    
      _XmCascadingPopup ((Widget) cb, NULL, TRUE);
   }
}


/*
 * set the timer to post the submenu if a leave event does
 * not occur first.
 */
/*ARGSUSED*/
static void 
DelayedArm(
        Widget wid,
        XEvent *event,
        String *param,
        Cardinal *num_param )
{
        XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
   if ((! CB_IsArmed(cb)) &&
       (((XmMenuShellWidget) XtParent(XtParent(cb)))->shell.popped_up) &&
       _XmGetInDragMode((Widget) cb))
   {
      if (cb->cascade_button.map_delay <= 0)
	 ArmAndPost (cb, event);
  
      else
      {
 	 /* To fix CR 8172,  the following two lines were reversed.  
	    Because calling Arm seems to cause a focus change (temporary)
	    out of the widget,  the timer was incorrectly removed and
	    the menu wouldn't post. */
         Arm(cb);
         cb->cascade_button.timer = 
               XtAppAddTimeOut(XtWidgetToApplicationContext( (Widget) cb), 
			       (unsigned long) cb->cascade_button.map_delay,
                                                 PostTimeout, (XtPointer) cb) ;
      }
   }
}


/*
 * if traversal is not on and the mouse
 * has not entered its cascading submenu, disarm the
 * cascadebutton.
 */
/*ARGSUSED*/
static void 
CheckDisarm(
        Widget wid,
        XEvent *event,
        String *param,
        Cardinal *num_param )
{
   XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
   register XmMenuShellWidget submenushell;
   XEnterWindowEvent * entEvent = (XEnterWindowEvent *) event;

   if (_XmGetInDragMode((Widget) cb) &&
       (/* !ActiveTearOff || */ event->xcrossing.mode == NotifyNormal))
   {
      if ((CB_IsArmed(cb)) && 
          (CB_Submenu(cb)))
      {
         submenushell = (XmMenuShellWidget) XtParent (CB_Submenu(cb));
   
         if (submenushell->shell.popped_up)
         {
            if ((entEvent->x_root >= submenushell->core.x) &&
                (entEvent->x_root <  submenushell->core.x + 
                                     submenushell->core.width +
                                     (submenushell->core.border_width << 1)) &&
                (entEvent->y_root >= submenushell->core.y) &&
                (entEvent->y_root <  submenushell->core.y + 
                                     submenushell->core.height +
	   			     (submenushell->core.border_width << 1)))

  	        /* then we are in the cascading submenu, don't disarm */
 	        return;

             /*
              * When kick-starting a cascading menu from a tear off, we grab
              * the pointer to the parent rc when the cascade has the focus
              * (In StartDrag().  A leave window event is generated (with
              * mode = NotifyGrab) which we don't wish to recognize.
              */
             if ((entEvent->mode == NotifyGrab) &&
                 !XmIsMenuShell(XtParent(XtParent(cb))))
                return;
         }
      }
      Disarm (cb, TRUE);
   }
}


/*
 * post submenu and disable menu's traversal.  The order of these 
 * function calls is critical.
 */
/*ARGSUSED*/
static void 
StartDrag(
        Widget wid,
        XEvent *event,
        String *param,
        Cardinal *num_param )
{
   XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
   Boolean validButton;
   XmRowColumnWidget parent = (XmRowColumnWidget)XtParent(cb);
   XmMenuSystemTrait menuSTrait;

   menuSTrait = (XmMenuSystemTrait) 
     XmeTraitGet((XtPointer) XtClass((Widget) parent), XmQTmenuSystem);

   /* If no menu system trait then parent isn't a menu as it
      should be */
   if (menuSTrait == NULL) return;

   /* Start with posted submenu bit reset */
   CB_SetWasPosted(cb, FALSE);

   if (CB_Submenu(cb) &&
       RC_IsArmed ((XmRowColumnWidget) CB_Submenu(cb))) {
     CB_SetWasPosted(cb, TRUE);
   }

   /*
    * make sure the shell is popped up, this takes care of a corner case
    * that can occur with rapid pressing of the mouse button
    */
   if (Lab_IsMenupane(cb) &&
       (!((XmMenuShellWidget) XtParent(parent))->shell.popped_up))
   {
      /* To support menu replay, keep the pointer in sync mode */
      XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);

      return;
   }

   validButton = menuSTrait -> verifyButton((Widget) parent, event);
   
   if (validButton)
   {
      /* In case the tear off is active but not armed or grabbed */
      menuSTrait -> tearOffArm((Widget) parent);

      _XmSetInDragMode((Widget) cb, True);

      _XmCascadingPopup ((Widget) cb, event, TRUE);
      Arm (cb);

      /* record event so MenuShell does not process it */
      _XmRecordEvent (event);
   } 

   /* To support menu replay, keep the pointer in sync mode */
   XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
}


/*
 * do the popup (either w/ or w/o the cascade callbacks).
 * If there is not a submenu, bring down the menu system.
 */
static void 
Select(
        register XmCascadeButtonWidget cb,
        XEvent *event,
#if NeedWidePrototypes
        int doCascade )
#else
        Boolean doCascade )
#endif /* NeedWidePrototypes */
{
   XmAnyCallbackStruct cback;
   XmMenuSystemTrait menuSTrait;

   menuSTrait = (XmMenuSystemTrait) 
     XmeTraitGet((XtPointer) XtClass(XtParent(cb)), XmQTmenuSystem);
   if (menuSTrait == NULL) return;

   if (CB_WasPosted(cb)) {
     Disarm(cb, TRUE);
     if ((CB_Submenu(cb) != NULL) && (Lab_MenuType(cb) == XmMENU_BAR))
       _XmMenuPopDown(XtParent((Widget) cb), event, NULL);
     return;
   }

   _XmCascadingPopup ((Widget) cb, event, doCascade);

   /*
    * check if there is a submenu here in case this changed during 
    * the cascading callbacks
    */
   if (CB_Submenu(cb) == NULL)
   {
      menuSTrait -> popdown(XtParent(cb), event);

      Disarm (cb, FALSE);

      menuSTrait -> disarm(XtParent(cb));

      cback.event = event;
      cback.reason = XmCR_ACTIVATE;

      if (menuSTrait != NULL)
      {
	menuSTrait -> entryCallback(XtParent(cb), (Widget) cb, &cback);
      }

      if ((! cb->label.skipCallback) &&
	  (cb->cascade_button.activate_callback))
      {
      
	XtCallCallbackList ((Widget) cb, cb->cascade_button.activate_callback, &cback);
      }
    }
   else
     {
       Arm(cb);
     }
}

/*
 * if there is a submenu, enable traversal.
 * call select to do the work
 */
/*ARGSUSED*/
static void 
DoSelect(
        Widget wid,
        XEvent *event,
        String *param,
        Cardinal *num_param )
{
   register XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
   Boolean validButton;
   XmMenuSystemTrait menuSTrait;

   menuSTrait = (XmMenuSystemTrait) 
     XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
   if (menuSTrait == NULL) return;
   
   /* To support menu replay, keep the pointer in sync mode */
   XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);

   if (event && event -> type == ButtonRelease &&
       event -> xbutton.button == 2)
     return;

   if (!CB_IsArmed(cb))
      return;

   if ((Lab_MenuType(cb) == XmMENU_BAR) && !RC_IsArmed (XtParent(cb)))
      return;

   /*
    * make sure the shell is popped up, this takes care of a corner case
    * that can occur with rapid pressing of the mouse button
    */
   if (Lab_IsMenupane(cb) &&
       (!((XmMenuShellWidget) XtParent(XtParent(cb)))->shell.popped_up))
   {
      return;
   }

   validButton = menuSTrait -> verifyButton(XtParent(cb), event);
   
   if (validButton)
   {
      Select (cb, event, (Boolean)(CB_Submenu(cb) != NULL));

      /* don't let the menu shell widget process this event */
      _XmRecordEvent (event);

      _XmSetInDragMode((Widget) cb, False);

      if (CB_Submenu(cb))
      {
         /*
          * if XmProcessTraversal() fails, it's possible that the pane
          * has no traversable children, so reset the focus to the pane.
          */
         if (!XmProcessTraversal(CB_Submenu(cb), XmTRAVERSE_CURRENT))
	 {
	    /* Must clear focus path first for shared menushells.
	     * Otherwise, moving the focus back will have old stale
	     * (old) focus_item.
	     */
	    _XmClearFocusPath(CB_Submenu(cb));
            XtSetKeyboardFocus(XtParent(CB_Submenu(cb)), CB_Submenu(cb));
	 }
      }
      else
      {
	 /* Move this call into Select().
	  *
	  * (* xmLabelClassRec.label_class.menuProcs) (XmMENU_DISARM,
	  *					    XtParent(cb));
	  */

         if (Lab_MenuType(cb) == XmMENU_BAR)
         {
	    _XmMenuFocus(XtParent(cb), XmMENU_END, CurrentTime);
            XtUngrabPointer( (Widget) cb, CurrentTime);
         }
      }
   }
}

/*
 * if the menu system traversal is enabled, do a select
 */
/*ARGSUSED*/
static void 
KeySelect(
        Widget wid,
        XEvent *event,
        String *param,
        Cardinal *num_param )
{
   XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;

   if (!_XmGetInDragMode((Widget) cb) && 
       (RC_IsArmed(XtParent(cb)) ||
	(RC_Type(XtParent(cb)) != XmMENU_BAR &&
	 !XmIsMenuShell(XtParent(XtParent(cb))))))
      (* (((XmCascadeButtonClassRec *)(cb->core.widget_class))->
		primitive_class.arm_and_activate))
			((Widget) cb, event, NULL, NULL);
}


/*
 * If the menu system is not active, arm it and arm this cascadebutton
 * else start the drag mode
 */
static void 
MenuBarSelect(
        Widget wid,
        XEvent *event,
        String *param,
        Cardinal *num_param )
{
   XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
   Boolean validButton;
   Time _time = _XmGetDefaultTime(wid, event);
   XmMenuSystemTrait menuSTrait;

   menuSTrait = (XmMenuSystemTrait) 
     XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
   if (menuSTrait == NULL) return;

   CB_SetWasPosted(cb, FALSE);

   if (RC_IsArmed ((XmRowColumnWidget) XtParent(cb)))
   {
      /* Cleanup the PM menubar mode, if enabled */
      menuSTrait -> menuBarCleanup(XtParent(cb));

      if (!CB_Submenu(cb))
      {
	 _XmMenuFocus(XtParent(cb), XmMENU_MIDDLE, _time);
      }

      StartDrag ((Widget) cb, event, param, num_param);
   }

   else
   {
      /* XAllowEvents() is called here because StartDrag also calls it */
      /* To support menu replay, keep the pointer in sync mode */
      XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);

      validButton = menuSTrait -> verifyButton(XtParent(cb), event);
   
      if (validButton)
      {
         /*
         * Don't post the menu if the menu cannot control grabs!
         */

	 if (_XmMenuGrabKeyboardAndPointer(XtParent(cb), _time) != GrabSuccess)
         {
	     _XmRecordEvent (event);
	     return;
	 }

	 _XmMenuFocus(XtParent(cb), XmMENU_BEGIN, _time);

	 menuSTrait -> arm((Widget) cb);

         _XmSetInDragMode((Widget) cb, True);

         _XmCascadingPopup ((Widget) cb, event, TRUE);

	 if (!CB_Submenu(cb))
	 {  
	    /*
	     * since no submenu is posted, check if the grab has occured
	     * and if not, do the pointer grab now.
	     */
	    if (RC_BeingArmed(XtParent(cb)))
	    {

               _XmGrabPointer(XtParent(cb), True, EVENTS,
                  GrabModeAsync, GrabModeAsync, None, 
		  XmGetMenuCursor(XtDisplay(cb)), _time);

	       RC_SetBeingArmed(XtParent(cb), False);
	    }
	 }

          /* To support menu replay, keep the pointer in sync mode */
          XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);

	 /* record so that menuShell doesn't process this event */
	 _XmRecordEvent (event);
      }
   }
}


/* 
 * If the menu is active, post submenu and arm.
 */
/*ARGSUSED*/
static void 
MenuBarEnter(
        Widget wid,
        XEvent *event,
        String *param,
        Cardinal *num_param )
{
        register XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
   XmRowColumnWidget rc = (XmRowColumnWidget)XtParent(cb);

   if (RC_IsArmed(rc) && !CB_IsArmed(cb) && _XmGetInDragMode((Widget) cb))
   {
      if (!CB_Submenu(cb))
      {
	 _XmMenuFocus((Widget) rc, XmMENU_MIDDLE, 
		      _XmGetDefaultTime(wid, event));
      }

      _XmCascadingPopup ((Widget) cb, event, TRUE);
      Arm(cb);
   }
}


/*
 * unless our submenu is posted or traversal is on, disarm
 */
/*ARGSUSED*/
static void 
MenuBarLeave(
        Widget wid,
        XEvent *event,
        String *param,
        Cardinal *num_param )
{
   register XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
   XmMenuShellWidget submenuShell;

   if (RC_IsArmed (XtParent (cb)))
   {
      /* Reset this bit so that we don't unpost if the user
	 reenters the cascade button */
      CB_SetWasPosted(cb, FALSE);

      if (CB_Submenu(cb))
      {
         submenuShell = (XmMenuShellWidget) XtParent(CB_Submenu(cb));

         if (submenuShell->shell.popped_up)
            return;
      }  
   
      if (_XmGetInDragMode((Widget) cb))
         Disarm (cb, TRUE);   
   }
}

/*
 * Cleanup the menubar, if its in the PM traversal mode
 */
/*ARGSUSED*/
static void 
CleanupMenuBar(
        Widget wid,
        XEvent *event,
        String *param,
        Cardinal *num_param )
{
        XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
    XmRowColumnWidget parent = (XmRowColumnWidget)XtParent(cb);

    if (RC_IsArmed(parent))
    {
        (* ((XmRowColumnWidgetClass) XtClass(parent))->
             row_column_class.armAndActivate) ( (Widget) parent,
                          (XEvent *) NULL, (String *) NULL, (Cardinal *) NULL);
	_XmRecordEvent(event);
    }
}


/*
 * CascadeButton Widget and Gadget help routine - first bring down the
 * menu and then do the help callback.
 */
void 
_XmCBHelp(
        Widget w,
        XEvent *event,
        String *params,
        Cardinal *num_params )
{
   XmRowColumnWidget parent = (XmRowColumnWidget) XtParent(w);

   if (RC_Type(parent) == XmMENU_BAR)
   {
      /* Cannot call CleanupMenubar() 'cause it calls _XmRecordEvent */
      if (RC_IsArmed(parent))
      {
	 (* ((XmRowColumnWidgetClass) XtClass(parent))->
	      row_column_class.armAndActivate) ( (Widget) parent,
			   (XEvent *) NULL, (String *) NULL, (Cardinal *) NULL);
      }
   }

   else if ((RC_Type(parent) == XmMENU_PULLDOWN) ||
            (RC_Type(parent) == XmMENU_POPUP))
   {
      (*(((XmMenuShellClassRec *) xmMenuShellWidgetClass)->
          menu_shell_class.popdownDone)) (XtParent(parent), event,
                                                           params, num_params);
   }

   if (XmIsGadget(w))
      _XmSocorro(w, event, params, num_params);
   else
      _XmPrimitiveHelp( w, event, params, num_params) ;
}


/*
 * When moving between a shared menupane, we only want to unpost the
 * descendant panes, not the shared one.
 * We only need to check the first popup child, since the menushell
 * has always forced the popped up shell to be the first child.
 */
static void 
PopdownGrandChildren(
        XmRowColumnWidget rowcol )
{
   CompositeWidget menuShell;

   if ((menuShell = (CompositeWidget) RC_PopupPosted(rowcol)) == NULL)
       return;

   if ((menuShell = (CompositeWidget) 
	RC_PopupPosted (menuShell->composite.children[0])) != NULL)
   {
      (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
	 menu_shell_class.popdownEveryone))( (Widget) menuShell, NULL,
                                                                   NULL, NULL);
   }
}


/*
 * call the cascading callbacks.  The cb parameter can be either a 
 * cascadebutton widget or gadget.
 */
static void 
Cascading(
        Widget w,
        XEvent *event )
{
    XmAnyCallbackStruct cback;

    cback.reason = XmCR_CASCADING;
    cback.event = event;

    if (XmIsCascadeButton(w))
    {
       XmCascadeButtonWidget cb = (XmCascadeButtonWidget)w;
       XmRowColumnWidget submenu = (XmRowColumnWidget) CB_Submenu(cb);

       /* if the submenu is already up, just return */
       /* In case of shared menupanes, check the cascade button attachment */

       if (submenu)
       {
           XmMenuShellWidget ms = (XmMenuShellWidget) XtParent(submenu);
	   if (XmIsMenuShell(ms) &&
	       ms->shell.popped_up &&
 	       ms->composite.children[0] == (Widget) submenu &&
 	       submenu->row_column.cascadeBtn == (Widget) cb)
           {
	      return;
           }
       } 
       XtCallCallbackList ((Widget) cb, cb->cascade_button.cascade_callback, &cback);
    }
    else
    {
       XmCascadeButtonGadget cb = (XmCascadeButtonGadget)w;
       XmRowColumnWidget submenu = (XmRowColumnWidget) CBG_Submenu(cb);

       /* if the submenu is already up, just return */
       if (submenu)
       {	
           XmMenuShellWidget ms = (XmMenuShellWidget) XtParent(submenu);
	   if (XmIsMenuShell(ms) &&
	       ms->shell.popped_up &&
 	       ms->composite.children[0] == (Widget) submenu &&
 	       submenu->row_column.cascadeBtn == (Widget) cb)
           {
	      return;
           }
       }
       
       XtCallCallbackList ((Widget) cb, cb->cascade_button.cascade_callback, &cback);
    }
}


/*
 * call the cascading callbacks and the popup any submenu.  This is called
 * by both the cascadebutton widget and gadget.
 */
void 
_XmCascadingPopup(
        Widget cb,
        XEvent *event,
#if NeedWidePrototypes
        int doCascade )
#else
        Boolean doCascade )
#endif /* NeedWidePrototypes */
{
   /* We must make sure the tear off to menushell restoration/callbacks are
    * called before the cascading callback.  Exclude the pane in case Popup()
    * tries to restore it back to a transient if shared and already posted.
    */
   XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(cb));
   XmExcludedParentPaneRec *excPP =
		&(((XmDisplayInfo *)(dd->display.displayInfo))->excParentPane);

   if (!excPP->pane)
   {
      excPP->pane_list_size = 4;
      excPP->pane = (Widget *)XtMalloc(sizeof(Widget) * excPP->pane_list_size);
   }

   if (XmIsCascadeButtonGadget(cb))
      *(excPP->pane) = CBG_Submenu(cb);
   else
      *(excPP->pane) = CB_Submenu(cb);
   
   if (*(excPP->pane))
   {
      excPP->num_panes = 1;

      if (RC_TornOff(*(excPP->pane)) &&
          !XmIsMenuShell(XtParent(*(excPP->pane))))
      {
 	 /* If a subpane is already posted and it is not the pane that
 	  * will be posted from cb.  Then it must be lowered so that
 	  * its tear off can be repainted.
 	  */
 	 if (RC_PopupPosted(XtParent(cb)))
 	 {
 	    XmRowColumnWidget postedPane = (XmRowColumnWidget)
 	       ((CompositeWidget)RC_PopupPosted(XtParent(cb)))->
 	          composite.children[0];
 
 	    if ((Widget)postedPane != *(excPP->pane))
 	    {
 	       _XmLowerTearOffObscuringPoppingDownPanes( (Widget)postedPane,
 		  *(excPP->pane));
 	    }
 	 }
	 _XmRestoreTearOffToMenuShell(*(excPP->pane), event);
      }
   }
   
   if (doCascade)
       Cascading (cb, event);
   Popup (cb, event);
}

/*
 * pop up the pulldown menu associated with this cascadebutton
 */
static void 
Popup(
        Widget cb,
        XEvent *event )
{
    Widget oldActiveChild;
    Boolean popped_up = False;
    register XmRowColumnWidget   submenu;
    XmMenuShellWidget shell = NULL;
    register XmRowColumnWidget	parent   = (XmRowColumnWidget) XtParent (cb);
    XmMenuSystemTrait menuSTrait;
    XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(cb));
    XmExcludedParentPaneRec *excPP =
		&(((XmDisplayInfo *)(dd->display.displayInfo))->excParentPane);

    menuSTrait = (XmMenuSystemTrait) 
      XmeTraitGet((XtPointer) XtClass((Widget) parent), XmQTmenuSystem);
    if (menuSTrait == NULL) return;

    if (XmIsCascadeButtonGadget(cb))
       submenu = (XmRowColumnWidget) CBG_Submenu(cb);
    else
       submenu = (XmRowColumnWidget) CB_Submenu(cb);

    /* if its already up, popdown submenus and then return */
    if (submenu &&
	(shell = (XmMenuShellWidget)XtParent(submenu)) &&
	XmIsMenuShell(shell) &&
	(popped_up = shell->shell.popped_up))
    {

        /* Just in case the menu shell is being shared.
	 * Shell's 0th child is currently posted submenu.  In case of shared
	 * menupanes we must check to make sure that it is not posted from 
	 * the same cascade button before popping down.
	 * Also this is as good a time as any to clear have_traversal field
	 * of submenu's active child.  Updating this internal state allows
	 * this gadget to highlight next time the submenu is posted.
	 */
        if ((XmRowColumnWidget)shell->composite.children[0] == submenu) 
	{
	   if (cb == RC_CascadeBtn(submenu))
	   {
	       if (RC_PopupPosted(submenu))
		   (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
		      menu_shell_class.popdownEveryone))
		       (RC_PopupPosted(submenu),NULL,NULL, NULL);

              if (submenu->manager.active_child)
              {
                 /* update visible focus/highlighting */
                 if (XmIsPrimitive(submenu->manager.active_child))
                 {
                     (*(((XmPrimitiveClassRec *)XtClass(submenu->manager.
                     active_child))->primitive_class.border_unhighlight))
                     (submenu->manager.active_child);
                 }
                 else if (XmIsGadget(submenu->manager.active_child))
                 {
                     (*(((XmGadgetClassRec *)XtClass(submenu->manager.
                     active_child))->gadget_class.border_unhighlight))
                     (submenu->manager.active_child);
                 }
                 /* update internal focus state */
                 _XmClearFocusPath((Widget) submenu);
              }
	      *(excPP->pane) = NULL;
	      excPP->num_panes = 0;
	      return;
	   }
	   else
	   {
	      oldActiveChild = submenu->manager.active_child;
	      if (oldActiveChild && XmIsGadget(oldActiveChild))
		 ((XmGadget)oldActiveChild)->gadget.have_traversal = False;
	   }
	}
    }

    if (XtIsManaged ((Widget)parent))
    {
        if ((RC_Type(parent) == XmMENU_BAR) && !RC_IsArmed (parent))
	   return;

        /*
         * If the old active child for the menupane was a cascadeB gadget, 
         * and it did not have its submenu posted, then
         * we need to manually send it FocusOut notification, since
         * when we managed our submenu, the active_child field for
         * our parent was set to us, and the parent now no longer knows
         * who previously had the focus.
         */
        oldActiveChild = parent->manager.active_child;
        if (oldActiveChild && 
            (oldActiveChild != (Widget)cb) &&
            XmIsCascadeButtonGadget(oldActiveChild) &&
            CBG_Submenu(oldActiveChild) &&
            (((XmMenuShellWidget)XtParent(CBG_Submenu(oldActiveChild)))->
               shell.popped_up == False))
        {
            parent->manager.active_child = NULL;
            _XmDispatchGadgetInput((Widget) oldActiveChild, NULL,
                                                            XmFOCUS_OUT_EVENT);
            ((XmGadget)oldActiveChild)->gadget.have_traversal = False;
        }
        else 
	  /*
	   * Fix for CR 5683 - If the RC_CascadeBtn == cb, then the menupane 
	   *		       should not be popped down (it probably already
	   *		       is popped down), so do not pop down the 
	   *		       menupane (it messes up the traversal)
	   */
	if (!submenu || 
	    !popped_up || 
	    (RC_PopupPosted(parent) != (Widget)shell) ||
	    (submenu && RC_CascadeBtn(submenu) &&
	     (RC_CascadeBtn(submenu) != cb) &&
	     ((Widget)parent == XtParent(RC_CascadeBtn(submenu)))) )
        {
	   /* popdown all visible subpanes of this parent when:
	    * - moving to a button without a submenu
	    * - between non-shared menushells
	    *   = then menushell will not be popped_up
	    *   = the old shell is different than the new shell
	    *     (old shell nonshared, new shell shared)
	    * - special case when same pane attached to > 1 cb in same parent
	    */
	    if (RC_PopupPosted(parent))
	    {
		(*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
		   menu_shell_class.popdownEveryone))
		    (RC_PopupPosted(parent),NULL,NULL, NULL);
	    }

	   /* Focus is not being handled perfectly for tear offs whose parent
	    * is a top level shell.  So force cascade unhighlighting here.
	    */
	   if (NULL != oldActiveChild &&
	       oldActiveChild != cb &&
	       ((parent->row_column.type == XmMENU_PULLDOWN) ||
		(parent->row_column.type == XmMENU_POPUP)) &&
	       !XmIsMenuShell(XtParent(parent)))
	       XmCascadeButtonHighlight(oldActiveChild, FALSE);
        }
        else
        {
           /*
            * Handle shared menupanes */
           PopdownGrandChildren (parent);
        }

	/* We don't allow the possibility of the submenu to be restored 
	 * from the menushell back to the transient shell during the 
	 * previous popdown code.  This occurs when the tear off is shared 
	 * and previously posted.
	 */
	*(excPP->pane) = NULL;
	excPP->num_panes = 0;

	if (submenu)
	{
           if (((ShellWidget)XtParent(submenu))->composite.num_children == 1)
	   {
	      menuSTrait -> cascade((Widget) submenu, cb, event);

	      /* Map the window first to sync up the server in case the 
	       * menushell was previously shared
	       */
	      XMapWindow(XtDisplay(submenu), XtWindow(submenu));
	      XtManageChild( (Widget) submenu);
	   }
           else
           {
	      /* We will call menuprocs XmMENU_CASCADING from
	       * popupSharedMenuShell routine so that it occurs between
	       * shared menupane window configurations.
	       */
              (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
                   menu_shell_class.popupSharedMenupane))(cb,
                                                      (Widget) submenu, event);
           }
	   /* So help is delivered correctly when in drag mode */
	   if (_XmGetInDragMode((Widget)cb))
	      XtSetKeyboardFocus((Widget)submenu, None);
	}
    }
}


/*
 * get the cascade size set up
 */
static void 
size_cascade(
        XmCascadeButtonWidget cascadebtn )
{
    Window rootwin;
    int x,y;					       /* must be int */
    unsigned int width, height, border, depth;	       /* must be int */

    if (CB_CascadePixmap(cascadebtn) != XmUNSPECIFIED_PIXMAP)
    {
       XGetGeometry(XtDisplay(cascadebtn), CB_CascadePixmap(cascadebtn),
		    &rootwin, &x, &y, &width, &height,
		    &border, &depth);

       CB_Cascade_width(cascadebtn) = (Dimension) width;
       CB_Cascade_height(cascadebtn) = (Dimension) height;
    }
    else
    {
       CB_Cascade_width(cascadebtn) = 0;
       CB_Cascade_height(cascadebtn) = 0;
    }
}


/*
 * set up the cascade position.  
 */
static void 
position_cascade(
        XmCascadeButtonWidget cascadebtn )
{
   Dimension buffer;

   if (CB_HasCascade(cascadebtn))
   { 
      if (LayoutIsRtoLP(cascadebtn))
         CB_Cascade_x(cascadebtn) = cascadebtn->primitive.highlight_thickness +
                                    cascadebtn->primitive.shadow_thickness +
                                    Lab_MarginWidth(cascadebtn);
      else
	CB_Cascade_x(cascadebtn) = XtWidth (cascadebtn) -
                               cascadebtn->primitive.highlight_thickness -
                               cascadebtn->primitive.shadow_thickness -
			       Lab_MarginWidth(cascadebtn) -
                               CB_Cascade_width(cascadebtn);

      buffer = cascadebtn->primitive.highlight_thickness +
             cascadebtn->primitive.shadow_thickness +
             Lab_MarginHeight(cascadebtn);

      CB_Cascade_y(cascadebtn) = buffer +
                               ((XtHeight(cascadebtn) -  2*buffer) -
                                CB_Cascade_height(cascadebtn)) / 2;
   }
   else
   {
      CB_Cascade_y(cascadebtn) = 0;
      CB_Cascade_x(cascadebtn) = 0;
   }
}


/*
 * set up the cascade size and location
 */
static void 
setup_cascade(
        XmCascadeButtonWidget cascadebtn,
#if NeedWidePrototypes
        int adjustWidth,
        int adjustHeight )
#else
        Boolean adjustWidth,
        Boolean adjustHeight )
#endif /* NeedWidePrototypes */
{
   Dimension delta;

   if (CB_HasCascade(cascadebtn))
   {
      /*
       *  modify the size of the cascadebutton to acommadate the cascade, if
       *  needed.  The cascade should fit inside MarginRight.
       */
      if (LayoutIsRtoLP(cascadebtn))
      {
        if ((CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE) >
             Lab_MarginLeft(cascadebtn))
        {
            delta = CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE -
                Lab_MarginLeft(cascadebtn);
            Lab_MarginLeft(cascadebtn) += delta;

            if (adjustWidth)
                XtWidth(cascadebtn) += delta;

            else
            {
               if (cascadebtn->label.alignment == XmALIGNMENT_BEGINNING)
                   Lab_TextRect_x(cascadebtn) += delta;
               else if (cascadebtn->label.alignment == XmALIGNMENT_CENTER)
                   Lab_TextRect_x(cascadebtn) += delta/2;
            }
        }

      }
      else
      {
	if ((CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE) >
	    Lab_MarginRight(cascadebtn))
	  {
	    delta = CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE -
	      Lab_MarginRight(cascadebtn);
	    Lab_MarginRight(cascadebtn) += delta;
	    
	    if (adjustWidth)
	      XtWidth(cascadebtn) += delta;
	    
	    else
	      {
		if (cascadebtn->label.alignment == XmALIGNMENT_END)
		  Lab_TextRect_x(cascadebtn) -= delta;
		else if (cascadebtn->label.alignment == XmALIGNMENT_CENTER)
		  Lab_TextRect_x(cascadebtn) -= delta/2;
	      }
	  }
      }
	
      /*
       * the cascade height should fit inside of 
       * TextRect + marginTop + marginBottom
       */
      delta = CB_Cascade_height(cascadebtn) +
	  2 * (Lab_MarginHeight(cascadebtn) +
	       cascadebtn->primitive.shadow_thickness +
	       cascadebtn->primitive.highlight_thickness);
		
      if (delta > XtHeight(cascadebtn))
      {
	 delta -= XtHeight(cascadebtn);
	 Lab_MarginTop(cascadebtn) += delta/2;
	 Lab_TextRect_y(cascadebtn) += delta/2;
	 Lab_MarginBottom(cascadebtn) += delta - (delta/2);
	 
	 if (adjustHeight)
	     XtHeight(cascadebtn) += delta;
      }
   }

   position_cascade(cascadebtn);
}


/*
 * Destroy the widget
 */
static void 
Destroy(
        Widget wid )
{
  XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
  XmRowColumnWidget submenu = (XmRowColumnWidget) CB_Submenu(cb);
  XmMenuSystemTrait menuSTrait;

  menuSTrait = (XmMenuSystemTrait) 
    XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);


  /*
   * If the armed pixmap exists, both pixmaps must be cached arrows
   */
  if (CB_ArmedPixmap(cb))
    {
	_XmProcessLock();
	_XmArrowPixmapCacheDelete((XtPointer) CB_ArmedPixmap(cb));
	_XmArrowPixmapCacheDelete((XtPointer) CB_CascadePixmap(cb));
	_XmProcessUnlock();
    }

  /*
   * break the submenu link
   */
  if (submenu != NULL && menuSTrait != NULL)
    menuSTrait -> recordPostFromWidget((Widget) submenu, (Widget) cb, FALSE);

  if (cb->cascade_button.timer)
    XtRemoveTimeOut (cb->cascade_button.timer);

  /* Release the GCs */

  XtReleaseGC (wid, CB_ArmGC(wid));
  XtReleaseGC (wid, CB_BackgroundGC(wid));
  
}
                         

/*
 * routine to resize a cascade button, called by the parent
 * geometery manager
 */
static void 
Resize(
        Widget cb )
{
    /*
     */
     if (cb)
     {
	XtWidgetProc resize;

	/* Label class does it's work */

	_XmProcessLock();
        resize = xmLabelClassRec.core_class.resize;
	_XmProcessUnlock();
	(* resize) (cb);

	/* move the cascade too */
	position_cascade ((XmCascadeButtonWidget) cb);
     }
}

/************************************************************************
 *
 *  SetValuesPrehook
 *
 ************************************************************************/

/*ARGSUSED*/
static Boolean 
SetValuesPrehook(
        Widget cw,		/* unused */
        Widget rw,		/* unused */
        Widget nw,
        ArgList args,		/* unused */
        Cardinal *num_args )	/* unused */
{
  XmCascadeButtonWidget new_w = (XmCascadeButtonWidget) nw ;

  /* CR 2990: Use XmNbuttonFontList as the default font. */
  if (new_w->label.font == NULL)
    new_w->label.font = XmeGetDefaultRenderTable (nw, XmBUTTON_FONTLIST);

  return False;
}

/*
 * Set Values
 */
/*ARGSUSED*/
static Boolean 
SetValues(
        Widget cw,
        Widget rw,
        Widget nw,
        ArgList args,		/* unused */
        Cardinal *num_args )	/* unused */
{
  XmCascadeButtonWidget old = (XmCascadeButtonWidget) cw ;
  XmCascadeButtonWidget requested = (XmCascadeButtonWidget) rw ;
  XmCascadeButtonWidget new_w = (XmCascadeButtonWidget) nw ;
  Boolean flag = FALSE;
  Boolean adjustWidth = FALSE;
  Boolean adjustHeight = FALSE;
  XmMenuSystemTrait menuSTrait;

  menuSTrait = (XmMenuSystemTrait) 
    XmeTraitGet((XtPointer) XtClass(XtParent(cw)), XmQTmenuSystem);

  if ((CB_Submenu(new_w)) &&
      ((! XmIsRowColumn(CB_Submenu(new_w))) ||
       (RC_Type(CB_Submenu(new_w)) != XmMENU_PULLDOWN)))
    {
      CB_Submenu(new_w) = NULL;
      XmeWarning( (Widget)new_w, WRONGSUBMENU);
    }

    if (new_w->cascade_button.map_delay < 0) 
    {
       new_w->cascade_button.map_delay = old->cascade_button.map_delay;
       XmeWarning( (Widget)new_w, WRONGMAPDELAY);
    }

    /* if there is a change to submenu, notify menu system */
    if (CB_Submenu(old) != CB_Submenu(new_w))
    {
      /* We must pass nw as the parameter to recordPostFromWidget
       * because old is a copy!  The call to recordPostFromWidget() does
       * a widget ID comparison and we must pass the real widget (nw).
       */

      if (CB_Submenu(old) && menuSTrait)
	menuSTrait -> recordPostFromWidget(CB_Submenu(old), nw, FALSE);

      if (CB_Submenu(new_w) && menuSTrait)
	menuSTrait -> recordPostFromWidget(CB_Submenu(new_w), nw, TRUE);
    }

    /* don't let traversal be changed */
    if (Lab_MenuType(new_w) == XmMENU_BAR)
	new_w->primitive.traversal_on = TRUE;

    /* handle the cascade pixmap indicator */
    else if (Lab_IsMenupane(new_w))
    {
       /* don't let traversal be changed */
       new_w->primitive.traversal_on = TRUE;

       if ((new_w->label.recompute_size)  || (requested->core.width <= 0))
	  adjustWidth = TRUE;

       if ((new_w->label.recompute_size)  || (requested->core.height <= 0))
	  adjustHeight = TRUE;

       /* get new pixmap size */
       if (CB_CascadePixmap(old) != CB_CascadePixmap (new_w))
       {
	  if (CB_ArmedPixmap(old) != XmUNSPECIFIED_PIXMAP)
	  {
	     _XmProcessLock();
	     _XmArrowPixmapCacheDelete((XtPointer) CB_ArmedPixmap(old));
	     _XmArrowPixmapCacheDelete((XtPointer) CB_CascadePixmap(old));
	     _XmProcessUnlock();
	  }
	  CB_ArmedPixmap(new_w) = XmUNSPECIFIED_PIXMAP;
	  size_cascade (new_w);
       } else
          if ( ((CB_CascadePixmap(new_w) ==  XmUNSPECIFIED_PIXMAP) &&
                  (!CB_Submenu(old) && CB_Submenu(new_w))) ||
               ((CB_ArmedPixmap(old) != XmUNSPECIFIED_PIXMAP) &&
                  ((Lab_TextRect_height(old) != Lab_TextRect_height(new_w)) ||
		   (old->primitive.foreground != new_w->primitive.foreground) ||
		   (old->core.background_pixel != 
		      new_w->core.background_pixel))))
	  {
	     _XmProcessLock();
	     _XmArrowPixmapCacheDelete((XtPointer) CB_ArmedPixmap(old));
	     _XmArrowPixmapCacheDelete((XtPointer) CB_CascadePixmap(old));
	     CB_ArmedPixmap(new_w) = XmUNSPECIFIED_PIXMAP;
	     CB_CascadePixmap(new_w) = XmUNSPECIFIED_PIXMAP;
	     _XmCreateArrowPixmaps((Widget) new_w);
	     _XmProcessUnlock();
	     size_cascade (new_w);
	  }

       /*
        * resize widget if cascade appeared or disappeared, or if the
	* cascade pixmap changed size.
	*/
       if ((CB_CascadePixmap (old) != CB_CascadePixmap (new_w))  ||
	   (old->label.label_type != new_w->label.label_type) ||
	   (old->cascade_button.submenu != new_w->cascade_button.submenu))
       {
	  setup_cascade (new_w, adjustWidth, adjustHeight);

	  /* if there wasn't a cascade, and still isn't, don't redraw */
	  if (old->cascade_button.submenu || new_w->cascade_button.submenu)
	      flag = TRUE;
       }

       /* make sure that other changes did not scrunch our pixmap */
       else if (new_w->cascade_button.submenu)
       {
	  if ((new_w->primitive.highlight_thickness !=
	       old->primitive.highlight_thickness)               ||
	      (new_w->primitive.shadow_thickness !=
	       old->primitive.shadow_thickness)                  ||
	      (Lab_MarginRight (new_w) != Lab_MarginRight (old))   ||
	      (Lab_MarginHeight (new_w) != Lab_MarginHeight (old)) ||
	      (Lab_MarginTop (new_w) != Lab_MarginTop (old))	 ||
	      (Lab_MarginBottom (new_w) != Lab_MarginBottom (old)))
	  {
	     setup_cascade (new_w,adjustWidth, adjustHeight);
	     flag = TRUE;
	  }

	  else if ((Lab_MarginWidth(new_w) != Lab_MarginWidth(old)) ||
		   (new_w->core.width != old->core.width)           ||
		   (new_w->core.height != old->core.height))

	  {
	     position_cascade (new_w);
	     flag = TRUE;
	  }
       }
    }

    return (flag);
}

/************************************************************
 *
 * InitializePrehook
 *
 * Put the proper translations in core_class tm_table so that
 * the data is massaged correctly
 *
 ************************************************************/
/*ARGSUSED*/
static void
InitializePrehook(
        Widget req,		/* unused */
        Widget new_w,
        ArgList args,		/* unused */
        Cardinal *num_args )	/* unused */
{
  unsigned char type;
  XmMenuSystemTrait menuSTrait;
  XmCascadeButtonWidget bw = (XmCascadeButtonWidget) new_w ;

  _XmSaveCoreClassTranslations (new_w);

  menuSTrait = (XmMenuSystemTrait) 
    XmeTraitGet((XtPointer) XtClass((Widget) XtParent(new_w)), XmQTmenuSystem);

  if (menuSTrait != NULL)
    type = menuSTrait->type(XtParent(new_w));
  else
    type = XmWORK_AREA;

  _XmProcessLock();
  if (type == XmMENU_PULLDOWN ||
      type == XmMENU_POPUP)
    new_w->core.widget_class->core_class.tm_table = (String) p_events_parsed;
  else
    new_w->core.widget_class->core_class.tm_table =(String)menubar_events_parsed;
  _XmProcessUnlock();
  
  /* CR 2990: Use XmNbuttonFontList as the default font. */
  if (bw->label.font == NULL)
    bw->label.font = XmeGetDefaultRenderTable (new_w, XmBUTTON_FONTLIST);
}

/************************************************************
 *
 * InitializePosthook
 *
 * restore core class translations
 *
 ************************************************************/
/*ARGSUSED*/
static void
InitializePosthook(
        Widget req,		/* unused */
        Widget new_w,
        ArgList args,		/* unused */
        Cardinal *num_args )	/* unused */
{
  _XmRestoreCoreClassTranslations (new_w);
}

/************************************************************************
 *
 *  GetArmGC
 *     Get the graphics context used for filling in background of the
 *     cascade button when armed.
 *
 ************************************************************************/

static void 
GetArmGC(
        XmCascadeButtonWidget cb )
{
  XGCValues values;
  XtGCMask  valueMask;
  Pixel     junk, select_pixel;
  
  XmGetColors(XtScreen(cb), cb->core.colormap, cb->core.background_pixel,
	      &junk, &junk, &junk, &select_pixel);

  valueMask = GCForeground | GCBackground | GCGraphicsExposures;
  
  values.foreground = select_pixel;
  values.background = cb->primitive.foreground;
  values.graphics_exposures = False;
  
  CB_ArmGC(cb) = XtGetGC ((Widget) cb, valueMask, &values);
}

/************************************************************************
 *
 *  GetBackgroundGC
 *     Get the graphics context used for filling in background of 
 *     the cascade button when not armed.
 *
 ************************************************************************/
static void 
GetBackgroundGC(
        XmCascadeButtonWidget cb )
{
  XGCValues       values;
  XtGCMask        valueMask;
  XFontStruct     *fs;
  
  valueMask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
  
  values.foreground = cb->core.background_pixel;
  values.background = cb->primitive.foreground;
  values.graphics_exposures = False;
  
  if (XmeRenderTableGetDefaultFont(cb->label.font, &fs))
    values.font = fs->fid;
  else
    valueMask &= ~GCFont;
  
  CB_BackgroundGC(cb) = XtGetGC ((Widget) cb, valueMask, &values);
}

/*
 * Initialize
 */
/*ARGSUSED*/
static void 
Initialize(
        Widget w_req,
        Widget w_new,
        ArgList args,		/* unused */
        Cardinal *num_args )	/* unused */
{
    XmCascadeButtonWidget  req = (XmCascadeButtonWidget) w_req ;
    XmCascadeButtonWidget  new_w = (XmCascadeButtonWidget) w_new ;
    Boolean adjustWidth = FALSE;
    Boolean adjustHeight = FALSE;
    XmMenuSystemTrait menuSTrait;

    XmRowColumnWidget    submenu = (XmRowColumnWidget) CB_Submenu(new_w);
    XmRowColumnWidget    parent = (XmRowColumnWidget) XtParent(new_w);

    menuSTrait = (XmMenuSystemTrait) 
      XmeTraitGet((XtPointer) XtClass((Widget) parent), XmQTmenuSystem);

    if ((! XmIsRowColumn (parent)) &&
	((Lab_MenuType(new_w) == XmMENU_PULLDOWN) ||
	 (Lab_MenuType(new_w) == XmMENU_POPUP)    ||
	 (Lab_MenuType(new_w) == XmMENU_BAR)))
    {
       XmeWarning( (Widget)new_w, WRONGPARENT);
    }

    /* if menuProcs is not set up yet, try again */
    _XmProcessLock();
    if (xmLabelClassRec.label_class.menuProcs == NULL)
	xmLabelClassRec.label_class.menuProcs =
	    (XmMenuProc) _XmGetMenuProcContext();
    _XmProcessUnlock();

    /* CR 7651: Clear before setting. */
    new_w->cascade_button.armed = 0;
    CB_SetArmed(new_w, FALSE);
    new_w->cascade_button.timer = 0;
    CB_SetTraverse (new_w, FALSE);
    CB_SetWasPosted (new_w, FALSE);
    CB_ArmedPixmap(new_w) = XmUNSPECIFIED_PIXMAP;

    /*
     * if the user did not specify a margin width, set the default.
     * The menubar cbs have a larger margin.
     */
    if (Lab_MarginWidth(req) == XmINVALID_DIMENSION)
    {
       if (Lab_MenuType(new_w) == XmMENU_BAR)
          Lab_MarginWidth(new_w) = 6;
       else
          Lab_MarginWidth(new_w) = 2;
    }
	   
    

    if (submenu &&
	(! XmIsRowColumn(submenu) ||
	 (RC_Type(submenu) != XmMENU_PULLDOWN)))
    {
       submenu = NULL;
       XmeWarning( (Widget)new_w, WRONGSUBMENU);
    }

    if (new_w->cascade_button.map_delay < 0) 
    {
       new_w->cascade_button.map_delay = MAP_DELAY_DEFAULT;
       XmeWarning( (Widget)new_w, WRONGMAPDELAY);
    }

    /* call submenu's class function to set the link */
    if (submenu != NULL && menuSTrait != NULL)
      menuSTrait -> recordPostFromWidget((Widget) submenu, (Widget) new_w, TRUE);
    
   if (submenu && (CB_CascadePixmap(new_w) == XmUNSPECIFIED_PIXMAP)) {
      _XmProcessLock();
      _XmCreateArrowPixmaps((Widget) new_w);
      _XmProcessUnlock();
   }
	 
    if (Lab_IsMenupane(new_w))
    {
      if (req->core.width <= 0)
	adjustWidth = TRUE;
      
      if (req->core.height <= 0)
	adjustHeight = TRUE;
      
      /* get pixmap size and set up widget to allow room for it */
      size_cascade (new_w);
      setup_cascade (new_w, adjustWidth, adjustHeight);
    }

    new_w->primitive.traversal_on = TRUE;

    /* Initialize GCs for armed button select and background only */
    GetArmGC (new_w);
    GetBackgroundGC (new_w);

}


/*
 *************************************************************************
 *
 * Public Routines                                                        
 *
 *************************************************************************
 */
Widget 
XmCreateCascadeButton(
        Widget parent,
        char *name,
        ArgList al,
        Cardinal ac )
{
    Widget cb;

    cb = XtCreateWidget(name, xmCascadeButtonWidgetClass, parent, al, ac);

    return (cb);
}


/*
 * This routine is called for both cascadebutton gadgets and widgets.
 * The button is armed or disarmed but it does not pop up or down submenus.
 */
void 
XmCascadeButtonHighlight(
        Widget cb,
#if NeedWidePrototypes
        int highlight )
#else
        Boolean highlight )
#endif /* NeedWidePrototypes */
{
  XtAppContext app;

  if (NULL == cb) return;
  app = XtWidgetToApplicationContext(cb);
  _XmAppLock(app);
   if ((cb) && XmIsCascadeButton(cb))
   {
      if (highlight)
         Arm ((XmCascadeButtonWidget) cb);

      else
         Disarm ((XmCascadeButtonWidget) cb, FALSE);
   }

   else if ((cb) && XmIsCascadeButtonGadget(cb))
      XmCascadeButtonGadgetHighlight ((Widget) cb, highlight);
  _XmAppUnlock(app);
}
